[med-svn] [tab2mage] 10/12: New upstream version 20080626
Andreas Tille
tille at debian.org
Sat Dec 30 16:40:42 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository tab2mage.
commit 5fd54b739dca3b116587f73a456c191fb07b3bcc
Author: Andreas Tille <tille at debian.org>
Date: Sat Dec 30 17:38:55 2017 +0100
New upstream version 20080626
---
COPYING | 44 +
Changes | 364 +
MAGETAB.txt | 135 +
MANIFEST | 706 +
META.yml | 25 +
Makefile.PL | 163 +
README | 187 +
README-windows.txt | 62 +
adv_inst/InstallConfig.yml-example | 51 +
adv_inst/README | 6 +
adv_inst/T2MInstaller.pm | 321 +
adv_inst/deploy.pl | 66 +
arraymage/arraymage.pl | 499 +
arraymage/cdf2mage.pl | 728 +
automation/admin/CHANGELOG | 604 +
automation/admin/INSTALL | 68 +
automation/admin/README | 153 +
automation/admin/README_LOGIN | 96 +
automation/admin/Rakefile | 10 +
.../admin/app/controllers/account_controller.rb | 52 +
automation/admin/app/controllers/application.rb | 7 +
.../app/controllers/array_designs_controller.rb | 124 +
.../admin/app/controllers/category_controller.rb | 70 +
.../admin/app/controllers/design_controller.rb | 66 +
.../admin/app/controllers/event_controller.rb | 21 +
.../app/controllers/experiments_controller.rb | 148 +
.../admin/app/controllers/factor_controller.rb | 6 +
.../admin/app/controllers/material_controller.rb | 66 +
.../admin/app/controllers/miamexps_controller.rb | 35 +
.../admin/app/controllers/organism_controller.rb | 90 +
.../admin/app/controllers/protocols_controller.rb | 90 +
.../controllers/quantitation_type_controller.rb | 6 +
.../admin/app/controllers/tab2mages_controller.rb | 43 +
.../admin/app/controllers/taxon_controller.rb | 71 +
.../admin/app/controllers/users_controller.rb | 89 +
automation/admin/app/helpers/account_helper.rb | 2 +
automation/admin/app/helpers/application_helper.rb | 3 +
.../admin/app/helpers/array_designs_helper.rb | 2 +
automation/admin/app/helpers/category_helper.rb | 2 +
automation/admin/app/helpers/design_helper.rb | 2 +
automation/admin/app/helpers/event_helper.rb | 2 +
automation/admin/app/helpers/experiments_helper.rb | 2 +
automation/admin/app/helpers/factor_helper.rb | 2 +
automation/admin/app/helpers/material_helper.rb | 2 +
automation/admin/app/helpers/miamexps_helper.rb | 2 +
automation/admin/app/helpers/organism_helper.rb | 2 +
automation/admin/app/helpers/protocols_helper.rb | 2 +
.../admin/app/helpers/quantitation_type_helper.rb | 2 +
automation/admin/app/helpers/tab2mages_helper.rb | 2 +
automation/admin/app/helpers/taxon_helper.rb | 2 +
automation/admin/app/helpers/users_helper.rb | 2 +
automation/admin/app/models/array_design.rb | 12 +
automation/admin/app/models/category.rb | 7 +
automation/admin/app/models/data_file.rb | 3 +
automation/admin/app/models/design.rb | 7 +
automation/admin/app/models/design_instance.rb | 4 +
automation/admin/app/models/event.rb | 8 +
automation/admin/app/models/experiment.rb | 43 +
automation/admin/app/models/factor.rb | 7 +
automation/admin/app/models/material.rb | 7 +
automation/admin/app/models/material_instance.rb | 4 +
automation/admin/app/models/organism.rb | 10 +
automation/admin/app/models/organism_instance.rb | 4 +
automation/admin/app/models/permission.rb | 6 +
automation/admin/app/models/protocol.rb | 4 +
automation/admin/app/models/quantitation_type.rb | 7 +
automation/admin/app/models/role.rb | 7 +
automation/admin/app/models/spreadsheet.rb | 3 +
automation/admin/app/models/taxon.rb | 8 +
automation/admin/app/models/user.rb | 59 +
automation/admin/app/views/account/login.rhtml | 22 +
automation/admin/app/views/account/logout.rhtml | 10 +
automation/admin/app/views/account/signup.rhtml | 18 +
automation/admin/app/views/account/welcome.rhtml | 12 +
.../admin/app/views/array_designs/_aedwinfo.rhtml | 18 +
.../admin/app/views/array_designs/_form.rhtml | 44 +
.../admin/app/views/array_designs/_miameinfo.rhtml | 6 +
.../admin/app/views/array_designs/_title.rhtml | 3 +
.../admin/app/views/array_designs/edit.rhtml | 10 +
.../admin/app/views/array_designs/list.rhtml | 70 +
.../admin/app/views/array_designs/list_all.rhtml | 59 +
automation/admin/app/views/array_designs/new.rhtml | 8 +
.../admin/app/views/array_designs/show.rhtml | 84 +
automation/admin/app/views/category/_form.rhtml | 34 +
automation/admin/app/views/category/_title.rhtml | 3 +
automation/admin/app/views/category/edit.rhtml | 8 +
automation/admin/app/views/category/list.rhtml | 42 +
automation/admin/app/views/category/new.rhtml | 8 +
automation/admin/app/views/design/_form.rhtml | 43 +
automation/admin/app/views/design/_title.rhtml | 3 +
automation/admin/app/views/design/edit.rhtml | 8 +
automation/admin/app/views/design/list.rhtml | 40 +
automation/admin/app/views/design/new.rhtml | 8 +
automation/admin/app/views/event/_form.rhtml | 62 +
automation/admin/app/views/event/_title.rhtml | 3 +
automation/admin/app/views/event/edit.rhtml | 2 +
.../admin/app/views/experiments/_annotate.rhtml | 52 +
automation/admin/app/views/experiments/_form.rhtml | 26 +
.../admin/app/views/experiments/_title.rhtml | 3 +
automation/admin/app/views/experiments/edit.rhtml | 12 +
automation/admin/app/views/experiments/list.rhtml | 71 +
automation/admin/app/views/experiments/new.rhtml | 10 +
automation/admin/app/views/experiments/show.rhtml | 124 +
.../admin/app/views/experiments/today_list.rhtml | 61 +
automation/admin/app/views/layouts/admin.rhtml | 156 +
automation/admin/app/views/material/_form.rhtml | 33 +
automation/admin/app/views/material/_title.rhtml | 3 +
automation/admin/app/views/material/edit.rhtml | 8 +
automation/admin/app/views/material/list.rhtml | 38 +
automation/admin/app/views/material/new.rhtml | 8 +
.../admin/app/views/miamexps/_aedwinfo.rhtml | 9 +
automation/admin/app/views/miamexps/_form.rhtml | 86 +
.../admin/app/views/miamexps/_miameinfo.rhtml | 9 +
automation/admin/app/views/miamexps/_title.rhtml | 3 +
automation/admin/app/views/miamexps/edit.rhtml | 15 +
automation/admin/app/views/miamexps/list.rhtml | 72 +
automation/admin/app/views/organism/_form.rhtml | 25 +
automation/admin/app/views/organism/_title.rhtml | 3 +
automation/admin/app/views/organism/edit.rhtml | 10 +
automation/admin/app/views/organism/list.rhtml | 59 +
automation/admin/app/views/organism/new.rhtml | 8 +
automation/admin/app/views/protocols/_form.rhtml | 28 +
automation/admin/app/views/protocols/_title.rhtml | 3 +
automation/admin/app/views/protocols/edit.rhtml | 10 +
automation/admin/app/views/protocols/list.rhtml | 58 +
automation/admin/app/views/protocols/new.rhtml | 8 +
automation/admin/app/views/protocols/show.rhtml | 8 +
.../admin/app/views/tab2mages/_aedwinfo.rhtml | 9 +
.../admin/app/views/tab2mages/_annotate.rhtml | 52 +
automation/admin/app/views/tab2mages/_form.rhtml | 91 +
.../admin/app/views/tab2mages/_miameinfo.rhtml | 9 +
automation/admin/app/views/tab2mages/_title.rhtml | 3 +
automation/admin/app/views/tab2mages/edit.rhtml | 35 +
automation/admin/app/views/tab2mages/list.rhtml | 97 +
automation/admin/app/views/tab2mages/new.rhtml | 13 +
automation/admin/app/views/taxon/_form.rhtml | 34 +
automation/admin/app/views/taxon/_title.rhtml | 3 +
.../admin/app/views/taxon/category_table.rhtml | 21 +
automation/admin/app/views/taxon/edit.rhtml | 8 +
automation/admin/app/views/taxon/list.rhtml | 43 +
automation/admin/app/views/taxon/new.rhtml | 8 +
automation/admin/app/views/users/_form.rhtml | 31 +
automation/admin/app/views/users/_title.rhtml | 3 +
automation/admin/app/views/users/edit.rhtml | 10 +
automation/admin/app/views/users/list.rhtml | 54 +
automation/admin/app/views/users/new.rhtml | 8 +
automation/admin/app/views/users/show.rhtml | 8 +
automation/admin/config/README | 45 +
automation/admin/config/boot.rb | 19 +
automation/admin/config/database.yml-mysql | 20 +
automation/admin/config/database.yml-sqlite | 29 +
automation/admin/config/environment.rb | 66 +
.../admin/config/environments/development.rb | 20 +
automation/admin/config/environments/production.rb | 19 +
automation/admin/config/environments/test.rb | 19 +
automation/admin/config/lighttpd.conf | 53 +
automation/admin/config/routes.rb | 19 +
automation/admin/db/database-mysql.dump | 932 +
automation/admin/db/database-mysql.schema | 829 +
.../admin/db/migration/migrate_2.0_to_2.1.sql | 183 +
.../admin/db/migration/migrate_2.1_to_2.2.sql | 125 +
.../admin/db/migration/migrate_csv2sqlite.pl | 190 +
.../admin/db/migration/migrate_sqlite2mysql.pl | 64 +
automation/admin/db/schema.pdf | Bin 0 -> 374471 bytes
automation/admin/db/schema.rb | 347 +
automation/admin/doc/README_FOR_APP | 2 +
automation/admin/lib/acl_system.rb | 31 +
automation/admin/lib/annotation.rb | 96 +
automation/admin/lib/login_system.rb | 87 +
automation/admin/log/server.log | 0
automation/admin/public/.htaccess | 40 +
automation/admin/public/404.html | 8 +
automation/admin/public/500.html | 8 +
automation/admin/public/dispatch.cgi | 10 +
automation/admin/public/dispatch.fcgi | 24 +
automation/admin/public/dispatch.rb | 10 +
automation/admin/public/favicon.ico | 0
automation/admin/public/images/rails.png | Bin 0 -> 1786 bytes
automation/admin/public/index.html | 34 +
automation/admin/public/javascripts/controls.js | 750 +
automation/admin/public/javascripts/dragdrop.js | 584 +
automation/admin/public/javascripts/effects.js | 854 +
automation/admin/public/javascripts/prototype.js | 1785 ++
automation/admin/public/robots.txt | 1 +
automation/admin/public/stylesheets/admin.css | 106 +
automation/admin/script/about | 3 +
automation/admin/script/benchmarker | 19 +
automation/admin/script/breakpointer | 3 +
automation/admin/script/console | 3 +
automation/admin/script/destroy | 3 +
automation/admin/script/generate | 3 +
automation/admin/script/performance/benchmarker | 3 +
automation/admin/script/performance/profiler | 3 +
automation/admin/script/plugin | 3 +
automation/admin/script/process/reaper | 3 +
automation/admin/script/process/spawner | 3 +
automation/admin/script/process/spinner | 3 +
automation/admin/script/profiler | 34 +
automation/admin/script/runner | 3 +
automation/admin/script/server | 3 +
automation/admin/test/fixtures/array_designs.yml | 9 +
automation/admin/test/fixtures/categories.yml | 9 +
automation/admin/test/fixtures/data_files.yml | 5 +
automation/admin/test/fixtures/designs.yml | 13 +
automation/admin/test/fixtures/events.yml | 9 +
automation/admin/test/fixtures/experiments.yml | 18 +
automation/admin/test/fixtures/factors.yml | 7 +
automation/admin/test/fixtures/materials.yml | 11 +
automation/admin/test/fixtures/organisms.yml | 13 +
automation/admin/test/fixtures/permissions.yml | 17 +
.../admin/test/fixtures/permissions_roles.yml | 11 +
automation/admin/test/fixtures/protocols.yml | 11 +
.../admin/test/fixtures/quantitation_types.yml | 7 +
automation/admin/test/fixtures/roles.yml | 11 +
automation/admin/test/fixtures/roles_users.yml | 5 +
automation/admin/test/fixtures/spreadsheets.yml | 5 +
automation/admin/test/fixtures/taxons.yml | 11 +
automation/admin/test/fixtures/users.yml | 16 +
.../test/functional/account_controller_test.rb | 81 +
.../functional/array_designs_controller_test.rb | 88 +
.../test/functional/category_controller_test.rb | 21 +
.../test/functional/design_controller_test.rb | 21 +
.../admin/test/functional/event_controller_test.rb | 21 +
.../test/functional/experiments_controller_test.rb | 94 +
.../test/functional/factor_controller_test.rb | 21 +
.../test/functional/material_controller_test.rb | 21 +
.../test/functional/miamexps_controller_test.rb | 18 +
.../test/functional/organism_controller_test.rb | 21 +
.../test/functional/protocols_controller_test.rb | 88 +
.../quantitation_type_controller_test.rb | 21 +
.../test/functional/tab2mages_controller_test.rb | 18 +
.../admin/test/functional/taxon_controller_test.rb | 18 +
.../admin/test/functional/users_controller_test.rb | 18 +
automation/admin/test/test_helper.rb | 28 +
automation/admin/test/unit/array_design_test.rb | 14 +
automation/admin/test/unit/category_test.rb | 14 +
automation/admin/test/unit/data_file_test.rb | 10 +
automation/admin/test/unit/design_test.rb | 10 +
automation/admin/test/unit/event_test.rb | 10 +
automation/admin/test/unit/experiment_test.rb | 10 +
automation/admin/test/unit/factor_test.rb | 10 +
automation/admin/test/unit/material_test.rb | 10 +
automation/admin/test/unit/organism_test.rb | 14 +
automation/admin/test/unit/permission_test.rb | 10 +
automation/admin/test/unit/protocol_test.rb | 10 +
.../admin/test/unit/quantitation_type_test.rb | 10 +
automation/admin/test/unit/role_test.rb | 10 +
automation/admin/test/unit/spreadsheet_test.rb | 10 +
automation/admin/test/unit/taxon_test.rb | 14 +
automation/admin/test/unit/user_test.rb | 101 +
automation/autosubs_checkd.pl | 26 +
automation/autosubs_exportd.pl | 26 +
automation/autosubs_httpd.pl | 298 +
automation/calculate_celqc.pl | 52 +
automation/calculate_datahashes.pl | 289 +
automation/cel_qcstats.pl | 181 +
automation/database_overseer.pl | 170 +
automation/magetab.cgi | 76 +
automation/magetab_checkd.pl | 26 +
automation/magetab_exportd.pl | 26 +
automation/magetab_insert_sub.pl | 121 +
automation/mx_autocheck_daemon.pl | 30 +
automation/mx_export_daemon.pl | 26 +
automation/mx_reset_experiment.pl | 235 +
automation/populate_tracking_info.pl | 599 +
automation/reset_experiment.pl | 168 +
automation/t2m_insert_sub.pl | 77 +
automation/tab2mage.cgi | 61 +
bin/convert_to_mcmr.pl | 116 +
bin/expt_check.pl | 454 +
bin/magetab.pl | 361 +
bin/parse_affy.pl | 249 +
bin/t2m_visualize.pl | 181 +
bin/tab2mage.pl | 388 +
debian/TODO | 41 -
debian/changelog | 12 -
debian/compat | 1 -
debian/control | 40 -
debian/copyright | 24 -
debian/dirs | 2 -
debian/docs | 3 -
debian/examples | 1 -
debian/patches/add-binaries-to-make-install.patch | 14 -
debian/patches/series | 1 -
debian/rules | 8 -
debian/source/format | 1 -
debian/watch | 2 -
docs/ArrayExpress/ArrayMAGE.html | 476 +
docs/ArrayExpress/AutoSubmission/Creator.html | 185 +
docs/ArrayExpress/AutoSubmission/DB.html | 2905 +++
docs/ArrayExpress/AutoSubmission/Spreadsheet.html | 118 +
docs/ArrayExpress/AutoSubmission/WebForm.html | 118 +
docs/ArrayExpress/Curator/Common.html | 201 +
docs/ArrayExpress/Curator/Config.html | 462 +
docs/ArrayExpress/Curator/Database.html | 137 +
docs/ArrayExpress/Curator/Entrez_list.html | 82 +
docs/ArrayExpress/Curator/ExperimentChecker.html | 392 +
docs/ArrayExpress/Curator/Logger.html | 204 +
docs/ArrayExpress/Curator/MAGE/Definitions.html | 712 +
docs/ArrayExpress/Curator/MIAMExpress.html | 193 +
docs/ArrayExpress/Curator/Report.html | 105 +
docs/ArrayExpress/Curator/Standalone.html | 87 +
docs/ArrayExpress/Curator/Validate.html | 184 +
docs/ArrayExpress/Curator/Visualize.html | 86 +
docs/ArrayExpress/Datafile.html | 478 +
docs/ArrayExpress/Datafile/Affymetrix.html | 96 +
docs/ArrayExpress/Datafile/Affymetrix/CDF.html | 79 +
.../Datafile/Affymetrix/CDF/GDAC_CDF.html | 80 +
.../Datafile/Affymetrix/CDF/XDA_CDF.html | 81 +
.../Datafile/Affymetrix/CDFFactory.html | 95 +
docs/ArrayExpress/Datafile/Affymetrix/CEL.html | 128 +
.../Datafile/Affymetrix/CEL/CELv3.html | 81 +
.../Datafile/Affymetrix/CEL/CELv4.html | 102 +
.../Datafile/Affymetrix/CELFactory.html | 95 +
docs/ArrayExpress/Datafile/Affymetrix/CHP.html | 106 +
.../Datafile/Affymetrix/CHP/CHPv12.html | 84 +
.../Datafile/Affymetrix/CHP/CHPv13.html | 84 +
.../Datafile/Affymetrix/CHP/CHPv8.html | 85 +
.../Datafile/Affymetrix/CHP/GDAC_CHP.html | 108 +
.../Datafile/Affymetrix/CHP/XDA_CHP.html | 84 +
.../Datafile/Affymetrix/CHPFactory.html | 95 +
docs/ArrayExpress/Datafile/Affymetrix/EXP.html | 165 +
.../Datafile/Affymetrix/EXPFactory.html | 94 +
docs/ArrayExpress/Datafile/Affymetrix/Parser.html | 183 +
docs/ArrayExpress/Datafile/ArrayDesign.html | 126 +
docs/ArrayExpress/Datafile/Parser.html | 67 +
docs/ArrayExpress/Datafile/QT_list.html | 248 +
docs/ArrayExpress/MAGETAB.html | 286 +
docs/ArrayExpress/MAGETAB/Checker.html | 215 +
docs/MAGETAB_logo.png | Bin 0 -> 7493 bytes
docs/MAGETAB_logo_small.png | Bin 0 -> 4790 bytes
docs/T2M_logo.png | Bin 0 -> 8513 bytes
docs/T2M_logo_small.png | Bin 0 -> 5804 bytes
docs/aelogo.png | Bin 0 -> 4095 bytes
docs/aesubs.html | 182 +
docs/datafiles.html | 465 +
docs/detail.html | 392 +
docs/expt_check.html | 347 +
docs/idf.html | 378 +
docs/index.html | 101 +
docs/magetab.html | 235 +
docs/magetab_docs.html | 429 +
docs/magetab_subs.html | 589 +
docs/magetab_vs_tab2mage.html | 202 +
docs/modules.html | 282 +
docs/parse_affy.html | 104 +
docs/sdrf.html | 941 +
docs/sdrf_layout.png | Bin 0 -> 33976 bytes
docs/spreadsheet.html | 625 +
docs/style.css | 171 +
docs/submit.css | 91 +
docs/t2m_visualize.html | 103 +
docs/tab2mage.html | 289 +
docs/usage.html | 168 +
examples/affymetrix/Data1.CEL | 49 +
examples/affymetrix/Data1.CHP | Bin 0 -> 11922 bytes
examples/affymetrix/Data1.EXP | 49 +
examples/affymetrix/Data2.CEL | Bin 0 -> 670 bytes
examples/affymetrix/Data2.CHP | Bin 0 -> 1013 bytes
examples/affymetrix/Data2.EXP | 49 +
examples/affymetrix/Data3.CEL | 49 +
examples/affymetrix/Data3.CHP | Bin 0 -> 11922 bytes
examples/affymetrix/Data3.EXP | 49 +
examples/affymetrix/Data4.CEL | Bin 0 -> 670 bytes
examples/affymetrix/Data4.CHP | Bin 0 -> 1013 bytes
examples/affymetrix/Data4.EXP | 49 +
examples/affymetrix/YG_S98.cdf | 192 +
examples/affymetrix/affymetrix.png | Bin 0 -> 14687 bytes
examples/affymetrix/affymetrix.txt | 36 +
examples/chip_chip/Data1.txt | 37 +
examples/chip_chip/Data2.txt | 40 +
examples/chip_chip/chip_chip.png | Bin 0 -> 10378 bytes
examples/chip_chip/chip_chip.txt | 39 +
examples/final_data_matrix/Data1.txt | 37 +
examples/final_data_matrix/Data2.txt | 40 +
examples/final_data_matrix/Data3.txt | 37 +
examples/final_data_matrix/Data4.txt | 40 +
examples/final_data_matrix/E-EXML-7.xml.dot.jpg | Bin 0 -> 646047 bytes
examples/final_data_matrix/FGEM.txt | 11 +
examples/final_data_matrix/NormData1.txt | 11 +
examples/final_data_matrix/NormData2.txt | 11 +
examples/final_data_matrix/NormData3.txt | 11 +
examples/final_data_matrix/NormData4.txt | 11 +
examples/final_data_matrix/final_data_matrix.png | Bin 0 -> 28295 bytes
examples/final_data_matrix/final_data_matrix.txt | 46 +
examples/illumina/GeneGroupedData.txt | 18 +
examples/illumina/ProbeData.txt | 18 +
examples/illumina/illumina.png | Bin 0 -> 78951 bytes
examples/illumina/illumina.txt | 46 +
examples/loop_design/loop_design.png | Bin 0 -> 33136 bytes
examples/loop_design/loop_design.txt | 55 +
examples/magetab/affymetrix/ATH1-121501.cdf | 192 +
examples/magetab/affymetrix/Data1.CEL | 49 +
examples/magetab/affymetrix/Data1.CHP | Bin 0 -> 11922 bytes
examples/magetab/affymetrix/Data1.EXP | 49 +
examples/magetab/affymetrix/Data2.CEL | Bin 0 -> 670 bytes
examples/magetab/affymetrix/Data2.CHP | Bin 0 -> 1013 bytes
examples/magetab/affymetrix/Data2.EXP | 49 +
examples/magetab/affymetrix/Data3.CEL | 49 +
examples/magetab/affymetrix/Data3.CHP | Bin 0 -> 11922 bytes
examples/magetab/affymetrix/Data3.EXP | 49 +
examples/magetab/affymetrix/Data4.CEL | Bin 0 -> 670 bytes
examples/magetab/affymetrix/Data4.CHP | Bin 0 -> 1013 bytes
examples/magetab/affymetrix/Data4.EXP | 49 +
examples/magetab/affymetrix/YG_S98.cdf | 192 +
examples/magetab/affymetrix/affy_dm.txt | 14 +
examples/magetab/affymetrix/affy_sdrf.txt | 5 +
examples/magetab/affymetrix/affymetrix.idf | 49 +
examples/magetab/illumina/GeneGroupedData.txt | 18 +
examples/magetab/illumina/ProbeData.txt | 18 +
examples/magetab/illumina/illumina.idf | 28 +
examples/magetab/illumina/illumina_sdrf.txt | 21 +
examples/magetab/real/E-MEXP-880.idf | 27 +
examples/magetab/real/E-MEXP-880_sdrf.txt | 41 +
examples/magetab/real/E-TABM-102.idf | 33 +
examples/magetab/real/E-TABM-102_sdrf.txt | 31 +
examples/magetab/real/E-TABM-134.idf | 27 +
examples/magetab/real/E-TABM-134_sdrf.txt | 26 +
examples/magetab/real/E-TABM-136.idf | 24 +
examples/magetab/real/E-TABM-136_sdrf.txt | 81 +
examples/magetab/real/E-TABM-140.idf | 32 +
examples/magetab/real/E-TABM-140_sdrf.txt | 73 +
examples/magetab/real/E-TABM-16.idf | 26 +
examples/magetab/real/E-TABM-163.idf | 27 +
examples/magetab/real/E-TABM-163_sdrf.txt | 24 +
examples/magetab/real/E-TABM-16_sdrf.txt | 105 +
examples/magetab/real/E-TABM-18.idf | 26 +
examples/magetab/real/E-TABM-18_sdrf.txt | 56 +
examples/magetab/real/E-TABM-22.idf | 24 +
examples/magetab/real/E-TABM-22_sdrf.txt | 252 +
examples/magetab/real/E-TABM-33.idf | 27 +
examples/magetab/real/E-TABM-33_sdrf.txt | 31 +
examples/magetab/real/E-TABM-35.idf | 26 +
examples/magetab/real/E-TABM-35_sdrf.txt | 62 +
examples/magetab/real/E-TABM-54.idf | 21 +
examples/magetab/real/E-TABM-54_sdrf.txt | 275 +
examples/magetab/real/E-TABM-66.idf | 29 +
examples/magetab/real/E-TABM-66_sdrf.txt | 154 +
examples/magetab/real/E-TABM-70.idf | 28 +
examples/magetab/real/E-TABM-70_sdrf.txt | 65 +
examples/magetab/real/solexa_example.idf | 36 +
examples/magetab/real/solexa_example.sdrf.txt | 11 +
examples/magetab/two_color/Data1.txt | 37 +
examples/magetab/two_color/Data2.txt | 40 +
examples/magetab/two_color/Data3.txt | 37 +
examples/magetab/two_color/Data4.txt | 40 +
examples/magetab/two_color/NormData1.txt | 11 +
examples/magetab/two_color/NormData2.txt | 11 +
examples/magetab/two_color/NormData3.txt | 11 +
examples/magetab/two_color/NormData4.txt | 11 +
examples/magetab/two_color/test.idf | 49 +
examples/magetab/two_color/test_sdrf.txt | 6 +
examples/normalized_data/Data1.txt | 37 +
examples/normalized_data/Data2.txt | 40 +
examples/normalized_data/NormData1.txt | 11 +
examples/normalized_data/NormData2.txt | 11 +
examples/normalized_data/normalized_data.png | Bin 0 -> 10751 bytes
examples/normalized_data/normalized_data.txt | 40 +
examples/other_QTs.txt | 27 +
examples/parameters/Data1.txt | 37 +
examples/parameters/Data2.txt | 40 +
examples/parameters/parameters.png | Bin 0 -> 10138 bytes
examples/parameters/parameters.txt | 39 +
examples/real/E-MEXP-880.png | Bin 0 -> 210713 bytes
examples/real/E-MEXP-880.txt | 68 +
examples/real/E-TABM-102.png | Bin 0 -> 103372 bytes
examples/real/E-TABM-102.txt | 56 +
examples/real/E-TABM-134.png | Bin 0 -> 67443 bytes
examples/real/E-TABM-134.txt | 57 +
examples/real/E-TABM-136.png | Bin 0 -> 83344 bytes
examples/real/E-TABM-136.txt | 106 +
examples/real/E-TABM-140.png | Bin 0 -> 138852 bytes
examples/real/E-TABM-140.txt | 108 +
examples/real/E-TABM-16.png | Bin 0 -> 213842 bytes
examples/real/E-TABM-16.txt | 166 +
examples/real/E-TABM-163.png | Bin 0 -> 73767 bytes
examples/real/E-TABM-163.txt | 55 +
examples/real/E-TABM-18.png | Bin 0 -> 85899 bytes
examples/real/E-TABM-18.txt | 82 +
examples/real/E-TABM-22.png | Bin 0 -> 420693 bytes
examples/real/E-TABM-22.txt | 274 +
examples/real/E-TABM-33.png | Bin 0 -> 86924 bytes
examples/real/E-TABM-33.txt | 55 +
examples/real/E-TABM-35.png | Bin 0 -> 118759 bytes
examples/real/E-TABM-35.txt | 94 +
examples/real/E-TABM-54.png | Bin 0 -> 263753 bytes
examples/real/E-TABM-54.txt | 287 +
examples/real/E-TABM-66.png | Bin 0 -> 171814 bytes
examples/real/E-TABM-66.txt | 184 +
examples/real/E-TABM-70.png | Bin 0 -> 63144 bytes
examples/real/E-TABM-70.txt | 101 +
examples/reference/Data1.txt | 37 +
examples/reference/Data2.txt | 40 +
examples/reference/Data3.txt | 11 +
examples/reference/reference.png | Bin 0 -> 13757 bytes
examples/reference/reference.txt | 42 +
examples/two_color/Data1.txt | 37 +
examples/two_color/Data2.txt | 40 +
examples/two_color/two_color.png | Bin 0 -> 11098 bytes
examples/two_color/two_color.txt | 45 +
index.html | 356 +
lib/ArrayExpress/ArrayMAGE.pm | 658 +
lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm | 42 +
lib/ArrayExpress/ArrayMAGE/BioSequence.pm | 96 +
lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm | 84 +
lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm | 119 +
lib/ArrayExpress/ArrayMAGE/Database.pm | 53 +
lib/ArrayExpress/ArrayMAGE/Feature.pm | 80 +
lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm | 88 +
lib/ArrayExpress/ArrayMAGE/NullObject.pm | 25 +
lib/ArrayExpress/ArrayMAGE/ProgressBar.pm | 40 +
lib/ArrayExpress/ArrayMAGE/Reporter.pm | 131 +
lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm | 75 +
lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm | 84 +
lib/ArrayExpress/ArrayMAGE/Zone.pm | 62 +
lib/ArrayExpress/AutoSubmission/Creator.pm | 514 +
lib/ArrayExpress/AutoSubmission/DB.pm | 3539 ++++
.../AutoSubmission/DB/Accessionable.pm | 56 +
lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm | 59 +
.../AutoSubmission/DB/ArrayDesignExperiment.pm | 23 +
.../AutoSubmission/DB/ArrayDesignOrganism.pm | 23 +
lib/ArrayExpress/AutoSubmission/DB/Category.pm | 33 +
.../AutoSubmission/DB/CategoryDesign.pm | 21 +
.../AutoSubmission/DB/CategoryMaterial.pm | 22 +
.../AutoSubmission/DB/CategoryTaxon.pm | 21 +
lib/ArrayExpress/AutoSubmission/DB/DataFile.pm | 23 +
lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm | 23 +
lib/ArrayExpress/AutoSubmission/DB/Design.pm | 32 +
.../AutoSubmission/DB/DesignInstance.pm | 22 +
lib/ArrayExpress/AutoSubmission/DB/Event.pm | 49 +
lib/ArrayExpress/AutoSubmission/DB/Experiment.pm | 324 +
.../AutoSubmission/DB/ExperimentFactor.pm | 23 +
.../AutoSubmission/DB/ExperimentLoadedData.pm | 20 +
lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm | 23 +
lib/ArrayExpress/AutoSubmission/DB/Factor.pm | 24 +
lib/ArrayExpress/AutoSubmission/DB/File.pm | 35 +
lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm | 44 +
lib/ArrayExpress/AutoSubmission/DB/Material.pm | 32 +
.../AutoSubmission/DB/MaterialInstance.pm | 23 +
lib/ArrayExpress/AutoSubmission/DB/Organism.pm | 34 +
.../AutoSubmission/DB/OrganismInstance.pm | 23 +
lib/ArrayExpress/AutoSubmission/DB/Permission.pm | 22 +
.../AutoSubmission/DB/PermissionRole.pm | 21 +
lib/ArrayExpress/AutoSubmission/DB/Platform.pm | 23 +
lib/ArrayExpress/AutoSubmission/DB/Protocol.pm | 115 +
.../AutoSubmission/DB/QualityMetric.pm | 29 +
.../AutoSubmission/DB/QualityMetricInstance.pm | 23 +
.../AutoSubmission/DB/QuantitationType.pm | 24 +
lib/ArrayExpress/AutoSubmission/DB/Role.pm | 27 +
lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm | 20 +
lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm | 22 +
lib/ArrayExpress/AutoSubmission/DB/Taxon.pm | 28 +
lib/ArrayExpress/AutoSubmission/DB/User.pm | 29 +
lib/ArrayExpress/AutoSubmission/Daemon.pm | 435 +
lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm | 295 +
lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm | 219 +
.../AutoSubmission/Daemon/MAGETABChecker.pm | 57 +
.../AutoSubmission/Daemon/MAGETABExporter.pm | 64 +
.../AutoSubmission/Daemon/MXChecker.pm | 271 +
.../AutoSubmission/Daemon/MXExporter.pm | 247 +
.../AutoSubmission/Daemon/T2MChecker.pm | 32 +
.../AutoSubmission/Daemon/T2MExporter.pm | 47 +
lib/ArrayExpress/AutoSubmission/Spreadsheet.pm | 1079 ++
lib/ArrayExpress/AutoSubmission/WebForm.pm | 1601 ++
.../AutoSubmission/templates/_annotate.html | 183 +
.../AutoSubmission/templates/_display.html | 48 +
.../templates/_display_annotation.html | 28 +
.../AutoSubmission/templates/_foot.html | 3 +
.../AutoSubmission/templates/_head.html | 62 +
.../AutoSubmission/templates/choose.html | 49 +
.../AutoSubmission/templates/create.html | 16 +
.../AutoSubmission/templates/create_user.html | 29 +
.../AutoSubmission/templates/delete.html | 14 +
.../AutoSubmission/templates/edit.html | 15 +
.../templates/email_signup_confirmation.txt | 16 +
.../templates/email_subs_confirmation.txt | 14 +
.../AutoSubmission/templates/error.html | 22 +
.../AutoSubmission/templates/file_upload.html | 63 +
.../AutoSubmission/templates/insert.html | 8 +
.../AutoSubmission/templates/intro.html | 65 +
.../AutoSubmission/templates/login.html | 65 +
.../AutoSubmission/templates/logout.html | 13 +
.../AutoSubmission/templates/magetab_intro.html | 42 +
.../AutoSubmission/templates/magetab_template.html | 37 +
.../AutoSubmission/templates/magetab_upload.html | 152 +
.../AutoSubmission/templates/signup.html | 82 +
.../AutoSubmission/templates/submitted.html | 32 +
.../templates/subs_notify_curator.txt | 15 +
.../AutoSubmission/templates/template.html | 37 +
.../AutoSubmission/templates/update.html | 8 +
.../AutoSubmission/templates/upload.html | 148 +
lib/ArrayExpress/Curator/Common.pm | 620 +
lib/ArrayExpress/Curator/Config.pm | 589 +
lib/ArrayExpress/Curator/Config.yml | 89 +
lib/ArrayExpress/Curator/Database.pm | 672 +
lib/ArrayExpress/Curator/Entrez_list.pm | 121 +
lib/ArrayExpress/Curator/Entrez_list.txt | 19188 +++++++++++++++++++
lib/ArrayExpress/Curator/ExperimentChecker.pm | 2428 +++
lib/ArrayExpress/Curator/Logger.pm | 409 +
lib/ArrayExpress/Curator/MAGE.pm | 2504 +++
lib/ArrayExpress/Curator/MAGE/Definitions.pm | 1181 ++
lib/ArrayExpress/Curator/MIAMExpress.pm | 2509 +++
lib/ArrayExpress/Curator/Report.pm | 438 +
lib/ArrayExpress/Curator/Standalone.pm | 167 +
lib/ArrayExpress/Curator/Tab2MAGE.pm | 2602 +++
lib/ArrayExpress/Curator/Validate.pm | 1084 ++
lib/ArrayExpress/Curator/Visualize.pm | 356 +
lib/ArrayExpress/Datafile.pm | 3685 ++++
lib/ArrayExpress/Datafile/Affymetrix.pm | 166 +
lib/ArrayExpress/Datafile/Affymetrix/CDF.pm | 136 +
.../Datafile/Affymetrix/CDF/GDAC_CDF.pm | 201 +
.../Datafile/Affymetrix/CDF/XDA_CDF.pm | 226 +
lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm | 130 +
lib/ArrayExpress/Datafile/Affymetrix/CEL.pm | 294 +
lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm | 281 +
lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm | 298 +
lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm | 131 +
lib/ArrayExpress/Datafile/Affymetrix/CHP.pm | 375 +
lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm | 229 +
lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm | 208 +
lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm | 115 +
.../Datafile/Affymetrix/CHP/GDAC_CHP.pm | 379 +
.../Datafile/Affymetrix/CHP/XDA_CHP.pm | 335 +
lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm | 159 +
.../Datafile/Affymetrix/Calvin/Binary.pm | 145 +
lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm | 125 +
lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm | 83 +
.../Datafile/Affymetrix/Calvin/Component.pm | 105 +
.../Datafile/Affymetrix/Calvin/DataColumn.pm | 68 +
.../Datafile/Affymetrix/Calvin/DataGroup.pm | 105 +
.../Datafile/Affymetrix/Calvin/DataHeader.pm | 80 +
.../Datafile/Affymetrix/Calvin/DataSet.pm | 143 +
.../Datafile/Affymetrix/Calvin/Facade.pm | 127 +
.../Datafile/Affymetrix/Calvin/Generic.pm | 128 +
.../Datafile/Affymetrix/Calvin/Parameter.pm | 19 +
lib/ArrayExpress/Datafile/Affymetrix/EXP.pm | 396 +
lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm | 108 +
lib/ArrayExpress/Datafile/Affymetrix/Parser.pm | 355 +
lib/ArrayExpress/Datafile/ArrayDesign.pm | 215 +
lib/ArrayExpress/Datafile/Binary.pm | 189 +
lib/ArrayExpress/Datafile/DataMatrix.pm | 762 +
lib/ArrayExpress/Datafile/Metrics.pm | 195 +
lib/ArrayExpress/Datafile/Parser.pm | 2016 ++
lib/ArrayExpress/Datafile/QT_list.pm | 342 +
lib/ArrayExpress/Datafile/QT_list.txt | 1087 ++
lib/ArrayExpress/MAGETAB.pm | 911 +
lib/ArrayExpress/MAGETAB/Checker.pm | 2034 ++
lib/ArrayExpress/MAGETAB/Checker/IDF.pm | 76 +
lib/ArrayExpress/MAGETAB/DataMatrix.pm | 548 +
lib/ArrayExpress/MAGETAB/IDF.pm | 881 +
lib/ArrayExpress/MAGETAB/SDRF.pm | 3486 ++++
lib/ArrayExpress/MAGETAB/TabFile.pm | 926 +
lib/ArrayExpress/Tracking/CelQC.pm | 171 +
lib/ArrayExpress/Tracking/Event.pm | 27 +
lib/ArrayExpress/Tracking/QCJobManager.pm | 180 +
lib/ArrayExpress/Tracking/QueryHandler.pm | 769 +
t/affy_calvin.t | 205 +
t/affy_cdf.t | 51 +
t/affy_cel.t | 108 +
t/affy_chp.t | 118 +
t/affy_exp.t | 103 +
t/affy_xda_cdf.t | 26 +
t/affy_xda_cel.t | 103 +
t/affy_xda_chp.t | 127 +
t/array_design.t | 28 +
t/arraymage.t | 11 +
t/binary.t | 23 +
t/common.t | 123 +
t/config.t | 11 +
t/creator.t | 20 +
t/daemon.t | 26 +
t/daemon_checker.t | 24 +
t/daemon_exporter.t | 24 +
t/daemon_mxchecker.t | 23 +
t/daemon_mxexporter.t | 23 +
t/data/dummy.CDF | 192 +
t/data/test.CEL | 49 +
t/data/test.CHP | Bin 0 -> 11922 bytes
t/data/test.EXP | 49 +
t/data/test.gpr | 37 +
t/data/test.txt | 8 +
t/data/test_calvin.CHP | Bin 0 -> 19567 bytes
t/data/test_fgem.txt | 11 +
t/data/test_xda.CEL | Bin 0 -> 670 bytes
t/data/test_xda.CHP | Bin 0 -> 1013 bytes
t/data_metrics.t | 11 +
t/database.t | 11 +
t/datafile.t | 467 +
t/datafile_parser.t | 11 +
t/db.t | 58 +
t/entrez_list.t | 11 +
t/experiment_checker.t | 11 +
t/fgem.t | 107 +
t/logger.t | 11 +
t/mage.t | 19 +
t/mage_defs.t | 11 +
t/magetab.t | 10 +
t/magetab_checker.t | 11 +
t/magetab_datamatrix.t | 10 +
t/magetab_idf.t | 10 +
t/magetab_sdrf.t | 10 +
t/magetab_tabfile.t | 10 +
t/miamexpress.t | 22 +
t/qt_list.t | 11 +
t/report.t | 11 +
t/spreadsheet.t | 21 +
t/standalone.t | 10 +
t/tab2mage.t | 40 +
t/tracking.t | 36 +
t/validate.t | 19 +
t/visualize.t | 11 +
t/web_form.t | 25 +
util/AE_insert_celfiles.pl | 202 +
util/fillin_null_rows.pl | 124 +
util/join_by_first_column.pl | 122 +
util/mx_legacy_check.pl | 155 +
util/mx_qt_update.pl | 94 +
util/t2m_legacy_check.pl | 138 +
util/t2m_update_docs.pl | 93 +
719 files changed, 118226 insertions(+), 150 deletions(-)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4fc5c8b
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,44 @@
+Copyright (c) 2007 The European Bioinformatics Institute. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. The names "Tab2MAGE" and "MAGETabulator" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ arrayexpress at ebi.ac.uk
+
+ 4. Products derived from this software may not be called "Tab2MAGE"
+ or "MAGETabulator", nor may "Tab2MAGE" or "MAGETabulator" appear
+ in their names without prior written permission of the Tab2MAGE
+ developers.
+
+ 5. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by ArrayExpress
+ (http://www.ebi.ac.uk/arrayexpress)"
+
+ THIS SOFTWARE IS PROVIDED BY THE ARRAYEXPRESS GROUP ``AS IS'' AND ANY
+ EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ARRAYEXPRESS GROUP OR
+ ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The European Bioinformatics Institute may publish revised and/or new
+versions of this license with new releases of the Tab2MAGE software.
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..f691a78
--- /dev/null
+++ b/Changes
@@ -0,0 +1,364 @@
+Changelog for Tab2MAGE
+----------------------
+
+2.2.2 Tab2MAGE-20080626.tar.gz Minor bug fixes for:
+ - Reporter Identifier of 0 (zero);
+ - DED generation for Affymetrix SNP CHP files;
+ - empty headings in Tab2MAGE documents;
+ - using cdf2mage.pl to generate CS-only MAGE-ML.
+
+2.2.1 Tab2MAGE-20080604.tar.gz Various bug fixes.
+
+2.2.0 Tab2MAGE-20080428.tar.gz Support for MAGE-TAB version 1.1.
+ Improvements to memory handling during experiment checking.
+ Support for Affymetrix Command Console ("Calvin") data
+ file formats.
+ Updated for use with Rails 2.0.x (tested with 2.0.2).
+ Better reporting of Text::CSV_XS parsing errors.
+ Improved support for files with Mac-style line endings.
+ Added graph visualization to magetab.pl.
+ Bug fixes for MAGE-TAB data matrix parsing, allowing
+ parentheses in QT and hyb/scan/norm names.
+ Removed obsolete mx_add_affy.pl script.
+
+2.1.0 Tab2MAGE-20080110.tar.gz Various bug fixes for MAGE-TAB and Tab2MAGE parsing.
+ Bug fixes for automated submissions processing system.
+ Refactoring of Affymetrix parsing code and API.
+ Modified support for BeadStudio data files following
+ consultation with Illumina.
+ Extended submissions tracking database.
+ Included an extended installer for use in complex
+ deployment situations.
+
+2.0.0 Tab2MAGE-20071002.tar.gz Bug fixes for MAGE-TAB parsing and validation.
+ Added Assay Name, Technology Type to SDRF parser.
+ Added documentation for MAGE-TAB document creation and
+ submission to ArrayExpress.
+ Improvements to submissions tracking system.
+ Support for ImaGene 3.0 data files.
+
+1.9.9 Tab2MAGE-20070629.tar.gz MAGE-TAB validation support now included as an option
+ for the expt_check.pl script.
+ Added greater leniency to MAGE-TAB IDF and SDRF parsing,
+ e.g. allowing extra spaces in SDRF headings/IDF tags.
+ SDRF header parsing now enforces the overall order
+ for columns: material -> hyb -> data.
+ Mac linebreaks now supported when using Text::CSV_XS 0.27
+ or later.
+ Added default attachment point for pooling protocol
+ (to extract) where this protocol is otherwise
+ unused (Tab2MAGE).
+ Improved support for GEO Affymetrix tab-delimited data.
+ Now defaults to Affymetrix-style DesignElement identifiers
+ for all ArrayExpress A-AFFY-* array designs.
+ Initial work on automation of MAGE-TAB submissions
+ processing, spreadsheet template generation. Added
+ support for new, combined IDF+SDRF documents.
+ Improved MAGE-ML output when parsing MAGE-TAB with -x option
+ (Affymetrix data files now coded as native format).
+ Added support for CSIRO Spot data files.
+
+1.9.8.5 Tab2MAGE-20070531.tar.gz Basic support for ImaGene data files.
+ Improved Tab2MAGE handling of single-channel scans
+ of two-channel hybs.
+ Support for ID_REF-style data files downloaded from GEO.
+ Ported the autosubmissions system from SQLite to MySQL.
+ Bug fixes to accommodate recent Text::CSV_XS changes.
+
+1.9.8 Tab2MAGE-20070424.tar.gz Bug fixes for MAGE-TAB SDRF parsing.
+ Added "Term Accession Number", "Protocol REF" prefixes,
+ ImageFormat detection based on filename extensions,
+ relaxed "Scan Name" column requirement for SDRF
+ parsing.
+ Bug fixes for two-color MAGE coding in Tab2MAGE.
+ Improvements to web form submissions template selection,
+ tracking of MIAMExpress Batch Loader submissions.
+ Automated submissions system log for MIAMExpress
+ MAGE-ML generation.
+ Added -C, -R options to tab2mage.pl, magetab.pl and
+ expt_check.pl to allow specification of
+ non-MIAMExpress Reporter/CompositeSequence
+ identifier prefixes.
+ Removed obsolete workaround for MIAMExpress EXP file
+ requirement (see version 1.9.4).
+
+1.9.7 Tab2MAGE-20070323.tar.gz Initial support for MAGE-TAB documents; a new parser
+ script, magetab.pl, is included for testing purposes
+ only at this stage.
+ Best-practice coding of two-color hybridizations in
+ Tab2MAGE MAGE-ML output.
+ Better support for packaging using the PAR system.
+
+1.9.5 Tab2MAGE-20070427.tar.gz Minor bug fixes, better support for building under PAR
+ (backported from 1.9.7).
+
+1.9.4 Tab2MAGE-20070305.tar.gz Support for multi-hyb Illumina data files (all but
+ "differential expression" data files).
+ Refactored ArrayExpress::Datafile and Parser APIs,
+ using Class::Std. New Datafile test suite.
+ Improved handling of "ignored" data file columns,
+ with improved log reporting.
+ Data matrix files now reported in expt_check.pl graph
+ generation.
+ Basic support for CHPv8 Affymetrix data files (coded in
+ native format only; basic header information is
+ extracted).
+ Bug fixes and memory savings for processing of large
+ data files.
+ Workarounds for MIAMExpress 2.0.1 EXP file requirement
+ for Affymetrix submissions.
+
+1.9.3 Tab2MAGE-20070122.tar.gz Various bug-fixes and interface improvements to
+ submissions automation system.
+ Added "URI" field for experiment, changed publication
+ URI field to "publication_URI".
+ Minor bug fixes.
+
+1.9.2 Tab2MAGE-20061221.tar.gz Updated for compatibility with MIAMExpress 2.0.1 and
+ batch loader.
+ Enhancements to curation autosubmissions tracking
+ web pages, including search facility.
+ Support for "NASA-style" Nimblegen data files.
+ Initial support for per-hyb Illumina data files.
+ Improved ScanAlyze, QuantArray and ScanArray support.
+ Fix for GenePix files using arrays with only one
+ MetaColumn.
+ Added BioSourceDescription column to Tab2MAGE format
+ for free-text descriptions.
+ Improved AEDW criteria checking.
+ Real-world example spreadsheets added to documentation.
+ Added -c ("cs-only") mode to cdf2mage.pl for use with
+ very large Affymetrix arrays.
+ Many bug fixes.
+
+1.9.1 Tab2MAGE-20061013.tar.gz Added logs listing problems rendering submissions
+ unsuitable for the ArrayExpress Data Warehouse.
+ Added a basic implementation of MIAME checking for
+ automatic tagging of incoming submissions; note
+ that this is not final and will change with the
+ emerging specification.
+ Improved handling of annotation-only checks (skipping
+ data file checking).
+ Improvements to autosubmissions curation web interface.
+ Various bug fixes.
+
+1.9.0 Tab2MAGE-20060928.tar.gz Moved MIAMExpress submissions automation from DBD::CSV
+ to DBD::Sqlite-based system.
+ Added new automated submissions system for spreadsheet-
+ based submissions; includes back-end processing daemons,
+ user-facing submissions web forms with automated
+ spreadsheet template generation, and a submissions
+ management curation web interface.
+ Protocol log file for expt_check.pl when run in
+ MIAMExpress mode.
+ New YAML-based configuration system, with optional
+ per-user config file option.
+ New checks implemented for data warehouse suitability.
+ Support for CodeLink Expression Analysis data files.
+ Added -p and -d options to scripts to allow redirection
+ of log file output and data file input directory.
+ Support for Affymetrix 100k Mapping (SNP) CHP files.
+ Many bug fixes.
+
+1.8.5 Tab2MAGE-20060903.tar.gz Repointed array design download URLs to work with new
+ ArrayExpress web interface.
+ Added support for ArrayVision "lg2" data files.
+ Other minor bug fixes.
+
+1.8.4 Tab2MAGE-20060729.tar.gz Native file format encoding now the default for Affy CEL
+ and NimbleScan files. New -P option to force plain-text
+ encoding. Old -b option retired.
+ Enabled use of absolute or relative file paths in
+ spreadsheet.
+ New -x option to skip data file checks for quick validation
+ of annotation.
+ Modified Spotfinder file parsing to remove R and C columns.
+ Lightweight support for At-MIAMExpress schema (omitting
+ sample annotation and factor value reporting).
+ Various bug fixes, QuantitationType update.
+ Added dummy data files to affymetrix example.
+
+1.8.3 Tab2MAGE-20060629.tar.gz Support for Affymetrix SNP data.
+ Added new lightweight CDF to MAGE-ML script, cdf2mage.pl
+ Improved test suite.
+ Enabled native format encoding for Nimblegen data files.
+ Various bug fixes.
+
+1.8.2 Tab2MAGE-20060412.tar.gz Memory and CPU-saving changes to data file parsing code.
+ Automated MIAMExpress submissions checking and accession
+ number assignment system (documentation to follow).
+ Added "-k" option to tab2mage.pl, allowing the user to
+ keep all unrecognised QTs without having to specify
+ them separately.
+ Log files backed up before overwriting (one copy only).
+ Raw data files having Reporter/CompSeq identifiers but
+ no feature coordinates can now be processed.
+ Improved factor value checking for MX submissions; new
+ sample annotation reports.
+ New lightweight (and very basic) array ADF to MAGE-ML
+ converter script.
+ Fix for ScanArray Express data file parsing bug.
+ Added BlueFuse QuantitationTypes.
+ Initial work on Applied Biosystems and Nimblegen
+ data file support.
+ Standalone checking modified to check Tab2MAGE-supported
+ software types, rather than just those supported by
+ MIAMExpress.
+ Miscellaneous bug fixes.
+
+1.8.1 Tab2MAGE-20060126.tar.gz Reworked spreadsheet parsing to use Text::CSV_XS, so that
+ spreadsheet fields (e.g. protocol text) can now contain
+ newlines.
+ Release and submission dates now created as ArrayExpress-
+ approved NVTs in MAGE-ML, rather than OntologyEntry terms.
+ Major code clean-up.
+
+1.8.0 Tab2MAGE-20051215.tar.gz Native Affymetrix file coding in MAGE-ML using -b option
+ with tab2mage.pl and mx_add_affy.pl
+ Protocol accession cache mechanism to allow the automatic
+ reassignment of protocol accessions in tab2mage
+ spreadsheets.
+ Constants.pm setting for central Affy library file
+ (CDF) directory.
+ Minor documentation update.
+
+1.7.4 Tab2MAGE-20060411.tar.gz Fix for ScanArray Express data file parsing bug,
+ backported from 1.8.2.
+
+1.7.3 Tab2MAGE-20051214.tar.gz Brief all vs. all check on data files to look for
+ duplicated file contents.
+ Various minor bug fixes.
+ QuantitationTypes update.
+
+1.7.2 Tab2MAGE-20051123.tar.gz Added investigator role, email and address fields to
+ Tab2MAGE spreadsheet format.
+ Basic support for Spotfinder, BlueFuse and UCSF Spot files.
+ Experiment checker now reports on MIAMExpress FactorValues
+ and checks for potentially truncated data files.
+ Included standalone data file conversion script
+ convert_to_mcmr.pl
+ Bug fixes for final data matrix processing.
+ Fix for bug in checker script where the data file/ADF
+ has zero as a feature coordinate.
+ Fix for BioMaterialCharacteristics with value of zero.
+
+1.7.1 Tab2MAGE-20051007.tar.gz Added support for final data matrix files in Tab2MAGE
+ spreadsheet validation and parsing.
+ Documentation update, including notes on supported data
+ file formats.
+ Updated QT definitions, minor bug fixes.
+
+1.7 Tab2MAGE-20050906.tar.gz New -Q option to add QuantitationType information, rather
+ than replacing the built-in values (-q option).
+ Changed to create more LSID-like identifiers.
+ Basic support for indicating data_coder and curator roles
+ in experiment section.
+
+1.6.5 Tab2MAGE-20051004.tar.gz Improved ScanAlyze data file parsing.
+ Relaxed rules on creating FactorValue Measurement_assns.
+ Experiment checker now reports on "Other" values used
+ in MIAMExpress annotation.
+ Updated for compatibility with MIAMExpress v2 release.
+ Minor QT bug fixes.
+
+1.6.4 Tab2MAGE-20050818.tar.gz QT parsing bug fixes.
+ Improved support for QuantArray/ScanArray data files.
+ Minor changes in scanning/feature extraction protocol
+ and software handling.
+ Removed support for use of "m" to indicate meters in
+ measurements (this clashed with "m" for minutes; we
+ find that minutes are used far more often than meters).
+
+1.6.3 Tab2MAGE-20050727.tar.gz Added Descriptions to generated QTs where available.
+ New format QT identifiers incorporating namespaces.
+ Disambiguated quantitation type parsing for column headings
+ shared between multiple software manufacturers.
+ Improved memory efficiency for CEL parsing subroutines.
+ Optimization of tab-delimited data file parsing.
+
+1.6.2 Tab2MAGE-20050627.tar.gz Support for QuantArray data files.
+ Updated Entrez/PubMed publication list (Apr 27 2005 list).
+ Initial AIDA quantitation type support.
+ Added checks for large data files (e.g., Affy tiling path array
+ CEL files); scripts now skip these to avoid out of memory errors.
+ Switched module dependencies from LWP::Simple to LWP::UserAgent.
+ Scripts now return graded error codes - see Constants.pm
+ for details of the error levels. These are intended for
+ use in automated execution of submission pipelines.
+ New -f option for specifying PNG font.
+ Bug fixes (parameters, factor values).
+
+1.6.1 Tab2MAGE-20050610.tar.gz Various bug fixes.
+
+1.6 Tab2MAGE-20050513.tar.gz Array Feature identifiers now retrieved from
+ ArrayExpress database, where available (optional
+ stand-alone mode included).
+ Affymetrix parsing fixed to run on big-endian
+ architectures (e.g. MacOSX).
+ Test suite for Affymetrix parsing included.
+ Major rewrite of core data file parsing module.
+ Initial support for ScanArray data files.
+ Other minor bug fixes.
+
+1.5.1 Tab2MAGE-20050425.tar.gz Finalized Affymetrix parsing API, documentation.
+ ScanAlyze data file parsing support.
+ Enabled expt_check.pl parsing of all Tab2MAGE-supported
+ text data file formats.
+
+1.5 Tab2MAGE-20050414.tar.gz Removed EXP file requirement for Affymetrix data.
+ Added Organization field to Experiment section.
+ Documentation of Affymetrix parsing methods.
+
+1.4.9 Tab2MAGE-20050407.tar.gz Support for Affymetrix CHP files containing comparison data.
+ Initial release of standalone platform-specific
+ binary packages.
+
+1.4.8 Tab2MAGE-20050404.tar.gz Initial spreadsheet validation support in
+ experiment checker script.
+ QuantitationType information updated.
+ Changed PUBLIC ID in the output MAGE-ML to
+ reflect MAGE version.
+ Various bug fixes.
+
+1.4.7 Tab2MAGE-20050314.tar.gz QT information for GenePix and Agilent updated.
+
+1.4.6 Tab2MAGE-20050311.tar.gz Better support for Affymetrix-derived normalized
+ text data files (e.g. from RMA analysis).
+ Fixed bug in normalized BioAssayDimension assignment.
+
+1.4.5 Tab2MAGE-20050309.tar.gz Experiment visualization routine added.
+
+1.4.2 Tab2MAGE-20050211.tar.gz Full documentation including examples.
+ Fixed support for new Affymetrix XDA CHP files.
+ Added parsing of normalized data files with "Reporter Identifier"
+ column and no other Feature coordinates.
+ Various bug fixes.
+ Cleaned up module interfaces.
+
+1.4 Tab2MAGE-20050117.tar.gz Support for user-defined parameters.
+ Improved documentation.
+ Now works on MS Windows (see README for details).
+
+1.3.1 Tab2MAGE-20050112.tar.gz Further documentation.
+ Added check for publication details to expt_check.pl.
+
+1.3 Tab2MAGE-20041211.tar.gz Support for Affymetrix data files.
+ Tab2MAGE now exports expression data, QC and parameter
+ information from CEL, CHP and EXP files and the library CDF file.
+ Added support for Software objects
+ (Scanning, Feature Extraction and Normalization).
+ Enhanced QuantitationType support;
+ Tab2MAGE now creates correct QT subclasses.
+ Various bug fixes and other improvements.
+
+1.2 Tab2MAGE-20041028.tar.gz Fix for Feature identifier bug.
+ Support for image file URIs.
+ Support for bibliographic reference URIs.
+ Initial support for MAGE Software objects.
+ Support for multiple ExperimentDesignTypes
+ and QualityControlDescriptionTypes.
+
+1.1 Tab2MAGE-20041007.tar.gz Agilent data file support added.
+ Minor bug fixes.
+
+1.0 Tab2MAGE-20040928.tar.gz Initial public release.
diff --git a/MAGETAB.txt b/MAGETAB.txt
new file mode 100644
index 0000000..9c40f45
--- /dev/null
+++ b/MAGETAB.txt
@@ -0,0 +1,135 @@
+Implementation notes on ArrayExpress MAGE-TAB $Date:: 2007-09-20 #$
+===================================================================
+
+1. This implementation relies on a recent Perl MAGEstk
+installation. At the moment ArrayExpress uses a release based on the
+subversion repository dated 2006-09-16. This release may be downloaded
+from the following location, pending a more formal release on
+CPAN/mged.sourceforge.net:
+
+http://www.ebi.ac.uk/systems-srv/mp/file-exchange/Bio-MAGE-20060916.tar.gz
+
+2. Some parts of the MAGE-TAB specification have not yet been
+ implemented:
+
+ - ADF parsing has been deferred until a later release.
+
+ - Automatic generation of virtual ArrayDesigns for data matrices
+ which reference database IDs or chromosome coordinates instead of
+ the more usual Reporter or Composite Element names/IDs. Such data
+ matrices are not yet supported.
+
+ - Multiple SDRFs split on any node column.
+
+ - REF:namespaces are currently accepted but discarded by the parser
+ (update: support now added for Array Design and Protocol
+ namespaces).
+
+3. Some parts of the MAGE-TAB specification do not sit well with the
+ MAGE object model:
+
+ - Measurement Characteristics associated with BioMaterials (we have
+ attempted to incorporate these as nested OEs).
+
+ - A single MAGE-TAB Parameter can be associated with many Units, but
+ MAGE ParameterTypes can only have a single Unit association (via
+ DefaultValue); this is handled by creating new MAGE ParameterTypes
+ on the fly whenever a new Unit is encountered, and linking them back
+ to the Protocol.
+
+ - Unit OEs (at the moment these are shoehorned into MAGE Unit classes).
+
+ - Protocol REF / Array Design REF Term Sources. These are ignored at
+ the moment.
+
+ - Protocol Contact (added as a NVT association to Protocol).
+
+ - Arbitrary mapping of any "* Name" column to the DerivedBioAssay
+ represented by a data matrix. MAGE limits this mapping to BioAssays
+ only (i.e., Hybridization, Scan, Normalization).
+
+ - In MAGE, Image must have Format_assn; there is nowhere to specify
+ this in MAGE-TAB at the moment; the parser attempts to determine the
+ image format by its filename extension (e.g., "JPEG" for
+ "filename.jpg"). If this is not possible the format is encoded as
+ "unknown".
+
+4. This MAGE-TAB implementation currently has a few limitations:
+
+ - Since feature extraction does not exist as a separate entity in
+ MAGE-TAB, the corresponding feature extraction protocol has to be
+ included in the scanning (image acquisition) protocol. This
+ limitation could be resolved in a number of ways, but each has a
+ disadvantage:
+
+ - add "Feature Extraction Name" (or some variant thereof) to
+ MAGE-TAB; this slightly complicates the format further, but is
+ probably the cleanest solution for MAGEv1 support. This would also
+ support the use-case where a single image is feature-extracted
+ multiple times using different software (this is currently
+ unsupported by the SDRF).
+
+ - infer the presence of FE from Array Data, and allow FE "Protocol
+ REF" columns to fall between Scan Name and Array Data File
+ columns. This would require reworking the parser protocol code
+ into something more complex and error-prone; side effects would
+ include lack of support for 1..n Scan->FE mapping.
+
+ - infer FE protocol from protocol type. This is also error-prone
+ as it is vulnerable to misspellings or outright omission of
+ protocol type in the IDF.
+
+ - For Affymetrix experiments, EXP files (where available) should be
+ added as a "Comment[EXP]" column. following the "Array Data File"
+ column.
+
+ - Also for Affy experiment, while the CDF file to use for CHP
+ parsing can be derived from the CHP file itself, this is not the
+ case for "Derived Array Data Matrix File". It is possible to parse
+ these files without any CDF information, but the parser needs a hint
+ as to the chip name (e.g. HG-U133A) for use in CompositeSequence
+ identifiers (otherwise the array accession will be used instead). At
+ the moment this hint is given using a "Comment[CDF]" column
+ following the "Derived Array Data Matrix File" column.
+
+ - Currently the parser requires all Parameters to be declared in the
+ IDF alongside their respective Protocols.
+
+5. Other questions and proposed additions to the MAGE-TAB
+ specification (see also the notes above for others):
+
+ - How to handle authority/namespace support; also, how to call out
+ more "global" objects e.g. Database, LabelCompound. Currently these
+ latter default to the ebi.ac.uk namespace, and authority and
+ namespace are hardcoded (although it would be trivial to make this
+ configurable).
+
+ - Should we allow separate annotation file(s) linked from IDF (and
+ which formats do we support)?
+
+ - If supplied in the same file, how do we delimit IDF from SDRF? How
+ about the ADF header and body sections? For combined IDF+SDRF
+ documents these scripts assume that IDF and SDRF sections begin with
+ "[IDF]" and "[SDRF]" respectively.
+
+ - "Term Accession Number" columns may follow "Term Source REF"
+ columns in the SDRF, and should contain the ontology accession
+ number for a given ontology term. No such mechanism is yet supported
+ in the IDF. Neither of these features are in the v1.0 specification.
+
+ - "Experimental Design Term Source REF" is supported as a field in
+ the IDF, although it is not in the v1.0 specification.
+
+ - Lines beginning with a '#' as comments are ignored by the parser;
+ this is not in the v1.0 specification.
+
+ - Comment[] fields currently attached to top-level Experiment only
+ for IDF.
+
+ - Hardware, Software types not captured by MAGE-TAB.
+
+ - PublicationType not captured by MAGE-TAB.
+
+ - How to code the following in MAGE: Protocol Contact; Experiment
+ Date (currently coded as NVTs)?
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..864473a
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,706 @@
+MANIFEST
+Makefile.PL
+README
+README-windows.txt
+MAGETAB.txt
+COPYING
+Changes
+adv_inst/README
+adv_inst/deploy.pl
+adv_inst/T2MInstaller.pm
+adv_inst/InstallConfig.yml-example
+bin/tab2mage.pl
+bin/magetab.pl
+bin/expt_check.pl
+bin/t2m_visualize.pl
+bin/parse_affy.pl
+bin/convert_to_mcmr.pl
+automation/mx_autocheck_daemon.pl
+automation/mx_export_daemon.pl
+automation/mx_reset_experiment.pl
+automation/reset_experiment.pl
+automation/autosubs_httpd.pl
+automation/autosubs_checkd.pl
+automation/autosubs_exportd.pl
+automation/magetab_checkd.pl
+automation/magetab_exportd.pl
+automation/tab2mage.cgi
+automation/magetab.cgi
+automation/t2m_insert_sub.pl
+automation/magetab_insert_sub.pl
+automation/database_overseer.pl
+automation/populate_tracking_info.pl
+automation/calculate_datahashes.pl
+automation/calculate_celqc.pl
+automation/cel_qcstats.pl
+arraymage/arraymage.pl
+arraymage/cdf2mage.pl
+util/mx_qt_update.pl
+util/t2m_update_docs.pl
+util/mx_legacy_check.pl
+util/t2m_legacy_check.pl
+util/join_by_first_column.pl
+util/AE_insert_celfiles.pl
+util/fillin_null_rows.pl
+lib/ArrayExpress/ArrayMAGE.pm
+lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm
+lib/ArrayExpress/ArrayMAGE/BioSequence.pm
+lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm
+lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm
+lib/ArrayExpress/ArrayMAGE/Database.pm
+lib/ArrayExpress/ArrayMAGE/Feature.pm
+lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm
+lib/ArrayExpress/ArrayMAGE/NullObject.pm
+lib/ArrayExpress/ArrayMAGE/ProgressBar.pm
+lib/ArrayExpress/ArrayMAGE/Reporter.pm
+lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm
+lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm
+lib/ArrayExpress/ArrayMAGE/Zone.pm
+lib/ArrayExpress/Datafile/Affymetrix.pm
+lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
+lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
+lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
+lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
+lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
+lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm
+lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm
+lib/ArrayExpress/Datafile/Binary.pm
+lib/ArrayExpress/Datafile/ArrayDesign.pm
+lib/ArrayExpress/Curator/Common.pm
+lib/ArrayExpress/Curator/Config.pm
+lib/ArrayExpress/Datafile.pm
+lib/ArrayExpress/Datafile/Parser.pm
+lib/ArrayExpress/Datafile/Metrics.pm
+lib/ArrayExpress/Curator/Database.pm
+lib/ArrayExpress/Curator/ExperimentChecker.pm
+lib/ArrayExpress/Curator/Logger.pm
+lib/ArrayExpress/Datafile/DataMatrix.pm
+lib/ArrayExpress/Curator/MAGE.pm
+lib/ArrayExpress/Curator/MAGE/Definitions.pm
+lib/ArrayExpress/Curator/MIAMExpress.pm
+lib/ArrayExpress/Curator/Entrez_list.pm
+lib/ArrayExpress/Datafile/QT_list.pm
+lib/ArrayExpress/Curator/Report.pm
+lib/ArrayExpress/Curator/Tab2MAGE.pm
+lib/ArrayExpress/Curator/Validate.pm
+lib/ArrayExpress/Curator/Standalone.pm
+lib/ArrayExpress/Curator/Visualize.pm
+lib/ArrayExpress/Datafile/QT_list.txt
+lib/ArrayExpress/Curator/Entrez_list.txt
+lib/ArrayExpress/Curator/Config.yml
+lib/ArrayExpress/MAGETAB.pm
+lib/ArrayExpress/MAGETAB/TabFile.pm
+lib/ArrayExpress/MAGETAB/IDF.pm
+lib/ArrayExpress/MAGETAB/SDRF.pm
+lib/ArrayExpress/MAGETAB/DataMatrix.pm
+lib/ArrayExpress/MAGETAB/Checker.pm
+lib/ArrayExpress/AutoSubmission/WebForm.pm
+lib/ArrayExpress/AutoSubmission/DB.pm
+lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
+lib/ArrayExpress/AutoSubmission/Daemon.pm
+lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm
+lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm
+lib/ArrayExpress/AutoSubmission/Creator.pm
+lib/ArrayExpress/AutoSubmission/templates/_annotate.html
+lib/ArrayExpress/AutoSubmission/templates/_display.html
+lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html
+lib/ArrayExpress/AutoSubmission/templates/_foot.html
+lib/ArrayExpress/AutoSubmission/templates/_head.html
+lib/ArrayExpress/AutoSubmission/templates/choose.html
+lib/ArrayExpress/AutoSubmission/templates/create.html
+lib/ArrayExpress/AutoSubmission/templates/create_user.html
+lib/ArrayExpress/AutoSubmission/templates/delete.html
+lib/ArrayExpress/AutoSubmission/templates/edit.html
+lib/ArrayExpress/AutoSubmission/templates/insert.html
+lib/ArrayExpress/AutoSubmission/templates/intro.html
+lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html
+lib/ArrayExpress/AutoSubmission/templates/login.html
+lib/ArrayExpress/AutoSubmission/templates/logout.html
+lib/ArrayExpress/AutoSubmission/templates/signup.html
+lib/ArrayExpress/AutoSubmission/templates/update.html
+lib/ArrayExpress/AutoSubmission/templates/upload.html
+lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html
+lib/ArrayExpress/AutoSubmission/templates/error.html
+lib/ArrayExpress/AutoSubmission/templates/file_upload.html
+lib/ArrayExpress/AutoSubmission/templates/submitted.html
+lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt
+lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt
+lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt
+lib/ArrayExpress/AutoSubmission/templates/template.html
+lib/ArrayExpress/AutoSubmission/templates/magetab_template.html
+lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm
+lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm
+lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm
+lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm
+lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm
+lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm
+lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm
+lib/ArrayExpress/AutoSubmission/DB/Category.pm
+lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm
+lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm
+lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm
+lib/ArrayExpress/AutoSubmission/DB/DataFile.pm
+lib/ArrayExpress/AutoSubmission/DB/Design.pm
+lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Event.pm
+lib/ArrayExpress/AutoSubmission/DB/Experiment.pm
+lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm
+lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm
+lib/ArrayExpress/AutoSubmission/DB/Factor.pm
+lib/ArrayExpress/AutoSubmission/DB/File.pm
+lib/ArrayExpress/AutoSubmission/DB/Material.pm
+lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Organism.pm
+lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Permission.pm
+lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm
+lib/ArrayExpress/AutoSubmission/DB/Protocol.pm
+lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm
+lib/ArrayExpress/AutoSubmission/DB/Role.pm
+lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm
+lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm
+lib/ArrayExpress/AutoSubmission/DB/Taxon.pm
+lib/ArrayExpress/AutoSubmission/DB/User.pm
+lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm
+lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm
+lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm
+lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm
+lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm
+lib/ArrayExpress/AutoSubmission/DB/Platform.pm
+lib/ArrayExpress/MAGETAB/Checker/IDF.pm
+lib/ArrayExpress/Tracking/Event.pm
+lib/ArrayExpress/Tracking/QueryHandler.pm
+lib/ArrayExpress/Tracking/CelQC.pm
+lib/ArrayExpress/Tracking/QCJobManager.pm
+examples/other_QTs.txt
+examples/two_color/two_color.txt
+examples/two_color/two_color.png
+examples/two_color/Data1.txt
+examples/two_color/Data2.txt
+examples/affymetrix/affymetrix.txt
+examples/affymetrix/affymetrix.png
+examples/affymetrix/Data1.EXP
+examples/affymetrix/Data2.EXP
+examples/affymetrix/Data3.EXP
+examples/affymetrix/Data4.EXP
+examples/affymetrix/Data1.CEL
+examples/affymetrix/Data2.CEL
+examples/affymetrix/Data3.CEL
+examples/affymetrix/Data4.CEL
+examples/affymetrix/Data1.CHP
+examples/affymetrix/Data2.CHP
+examples/affymetrix/Data3.CHP
+examples/affymetrix/Data4.CHP
+examples/affymetrix/YG_S98.cdf
+examples/chip_chip/chip_chip.txt
+examples/chip_chip/chip_chip.png
+examples/chip_chip/Data1.txt
+examples/chip_chip/Data2.txt
+examples/normalized_data/normalized_data.txt
+examples/normalized_data/normalized_data.png
+examples/normalized_data/Data1.txt
+examples/normalized_data/Data2.txt
+examples/normalized_data/NormData1.txt
+examples/normalized_data/NormData2.txt
+examples/final_data_matrix/final_data_matrix.txt
+examples/final_data_matrix/final_data_matrix.png
+examples/final_data_matrix/E-EXML-7.xml.dot.jpg
+examples/final_data_matrix/Data1.txt
+examples/final_data_matrix/Data2.txt
+examples/final_data_matrix/Data3.txt
+examples/final_data_matrix/Data4.txt
+examples/final_data_matrix/NormData1.txt
+examples/final_data_matrix/NormData2.txt
+examples/final_data_matrix/NormData3.txt
+examples/final_data_matrix/NormData4.txt
+examples/final_data_matrix/FGEM.txt
+examples/reference/reference.txt
+examples/reference/reference.png
+examples/reference/Data1.txt
+examples/reference/Data2.txt
+examples/reference/Data3.txt
+examples/parameters/parameters.txt
+examples/parameters/parameters.png
+examples/parameters/Data1.txt
+examples/parameters/Data2.txt
+examples/loop_design/loop_design.txt
+examples/loop_design/loop_design.png
+examples/illumina/illumina.txt
+examples/illumina/illumina.png
+examples/illumina/ProbeData.txt
+examples/illumina/GeneGroupedData.txt
+examples/real/E-TABM-102.png
+examples/real/E-TABM-102.txt
+examples/real/E-TABM-134.png
+examples/real/E-TABM-134.txt
+examples/real/E-TABM-136.png
+examples/real/E-TABM-136.txt
+examples/real/E-TABM-140.png
+examples/real/E-TABM-140.txt
+examples/real/E-TABM-16.png
+examples/real/E-TABM-16.txt
+examples/real/E-TABM-163.png
+examples/real/E-TABM-163.txt
+examples/real/E-TABM-18.png
+examples/real/E-TABM-18.txt
+examples/real/E-TABM-22.png
+examples/real/E-TABM-22.txt
+examples/real/E-TABM-33.png
+examples/real/E-TABM-33.txt
+examples/real/E-TABM-35.png
+examples/real/E-TABM-35.txt
+examples/real/E-TABM-54.png
+examples/real/E-TABM-54.txt
+examples/real/E-TABM-66.png
+examples/real/E-TABM-66.txt
+examples/real/E-TABM-70.png
+examples/real/E-TABM-70.txt
+examples/real/E-MEXP-880.png
+examples/real/E-MEXP-880.txt
+examples/magetab/affymetrix/affymetrix.idf
+examples/magetab/affymetrix/affy_sdrf.txt
+examples/magetab/affymetrix/affy_dm.txt
+examples/magetab/affymetrix/ATH1-121501.cdf
+examples/magetab/affymetrix/Data1.CEL
+examples/magetab/affymetrix/Data1.CHP
+examples/magetab/affymetrix/Data1.EXP
+examples/magetab/affymetrix/Data2.CEL
+examples/magetab/affymetrix/Data2.CHP
+examples/magetab/affymetrix/Data2.EXP
+examples/magetab/affymetrix/Data3.CEL
+examples/magetab/affymetrix/Data3.CHP
+examples/magetab/affymetrix/Data3.EXP
+examples/magetab/affymetrix/Data4.CEL
+examples/magetab/affymetrix/Data4.CHP
+examples/magetab/affymetrix/Data4.EXP
+examples/magetab/affymetrix/YG_S98.cdf
+examples/magetab/two_color/test.idf
+examples/magetab/two_color/test_sdrf.txt
+examples/magetab/two_color/Data1.txt
+examples/magetab/two_color/Data2.txt
+examples/magetab/two_color/Data3.txt
+examples/magetab/two_color/Data4.txt
+examples/magetab/two_color/NormData1.txt
+examples/magetab/two_color/NormData2.txt
+examples/magetab/two_color/NormData3.txt
+examples/magetab/two_color/NormData4.txt
+examples/magetab/illumina/GeneGroupedData.txt
+examples/magetab/illumina/illumina.idf
+examples/magetab/illumina/illumina_sdrf.txt
+examples/magetab/illumina/ProbeData.txt
+examples/magetab/real/E-TABM-102.idf
+examples/magetab/real/E-TABM-102_sdrf.txt
+examples/magetab/real/E-TABM-134.idf
+examples/magetab/real/E-TABM-134_sdrf.txt
+examples/magetab/real/E-TABM-136.idf
+examples/magetab/real/E-TABM-136_sdrf.txt
+examples/magetab/real/E-TABM-140.idf
+examples/magetab/real/E-TABM-140_sdrf.txt
+examples/magetab/real/E-TABM-16.idf
+examples/magetab/real/E-TABM-163.idf
+examples/magetab/real/E-TABM-163_sdrf.txt
+examples/magetab/real/E-TABM-16_sdrf.txt
+examples/magetab/real/E-TABM-18.idf
+examples/magetab/real/E-TABM-18_sdrf.txt
+examples/magetab/real/E-TABM-22.idf
+examples/magetab/real/E-TABM-22_sdrf.txt
+examples/magetab/real/E-TABM-33.idf
+examples/magetab/real/E-TABM-33_sdrf.txt
+examples/magetab/real/E-TABM-35.idf
+examples/magetab/real/E-TABM-35_sdrf.txt
+examples/magetab/real/E-TABM-54.idf
+examples/magetab/real/E-TABM-54_sdrf.txt
+examples/magetab/real/E-TABM-66.idf
+examples/magetab/real/E-TABM-66_sdrf.txt
+examples/magetab/real/E-TABM-70.idf
+examples/magetab/real/E-TABM-70_sdrf.txt
+examples/magetab/real/E-MEXP-880.idf
+examples/magetab/real/E-MEXP-880_sdrf.txt
+examples/magetab/real/solexa_example.idf
+examples/magetab/real/solexa_example.sdrf.txt
+index.html
+docs/expt_check.html
+docs/index.html
+docs/tab2mage.html
+docs/magetab.html
+docs/usage.html
+docs/spreadsheet.html
+docs/detail.html
+docs/datafiles.html
+docs/aesubs.html
+docs/magetab_subs.html
+docs/magetab_docs.html
+docs/magetab_vs_tab2mage.html
+docs/idf.html
+docs/sdrf.html
+docs/t2m_visualize.html
+docs/parse_affy.html
+docs/modules.html
+docs/ArrayExpress/Datafile/QT_list.html
+docs/ArrayExpress/Curator/MIAMExpress.html
+docs/ArrayExpress/Curator/Config.html
+docs/ArrayExpress/Datafile.html
+docs/ArrayExpress/Datafile/Parser.html
+docs/ArrayExpress/Curator/ExperimentChecker.html
+docs/ArrayExpress/Curator/Validate.html
+docs/ArrayExpress/Curator/Standalone.html
+docs/ArrayExpress/AutoSubmission/DB.html
+docs/ArrayExpress/AutoSubmission/Creator.html
+docs/ArrayExpress/AutoSubmission/Spreadsheet.html
+docs/ArrayExpress/AutoSubmission/WebForm.html
+docs/ArrayExpress/Curator/Database.html
+docs/ArrayExpress/Curator/Logger.html
+docs/ArrayExpress/MAGETAB.html
+docs/ArrayExpress/MAGETAB/Checker.html
+docs/ArrayExpress/Curator/MAGE/Definitions.html
+docs/ArrayExpress/Datafile/Affymetrix.html
+docs/ArrayExpress/Datafile/Affymetrix/CEL.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP.html
+docs/ArrayExpress/Datafile/Affymetrix/CDF.html
+docs/ArrayExpress/Datafile/Affymetrix/EXP.html
+docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html
+docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html
+docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html
+docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html
+docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html
+docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html
+docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html
+docs/ArrayExpress/Datafile/Affymetrix/Parser.html
+docs/ArrayExpress/ArrayMAGE.html
+docs/ArrayExpress/Curator/Common.html
+docs/ArrayExpress/Curator/Entrez_list.html
+docs/ArrayExpress/Curator/Report.html
+docs/ArrayExpress/Curator/Visualize.html
+docs/ArrayExpress/Datafile/ArrayDesign.html
+docs/style.css
+docs/submit.css
+docs/sdrf_layout.png
+docs/T2M_logo.png
+docs/T2M_logo_small.png
+docs/MAGETAB_logo.png
+docs/MAGETAB_logo_small.png
+docs/aelogo.png
+t/db.t
+t/spreadsheet.t
+t/daemon.t
+t/daemon_checker.t
+t/daemon_exporter.t
+t/daemon_mxchecker.t
+t/daemon_mxexporter.t
+t/creator.t
+t/web_form.t
+t/affy_cel.t
+t/affy_chp.t
+t/affy_exp.t
+t/affy_xda_cel.t
+t/affy_xda_chp.t
+t/affy_cdf.t
+t/affy_xda_cdf.t
+t/affy_calvin.t
+t/array_design.t
+t/arraymage.t
+t/binary.t
+t/common.t
+t/config.t
+t/database.t
+t/datafile_parser.t
+t/datafile.t
+t/data_metrics.t
+t/entrez_list.t
+t/experiment_checker.t
+t/fgem.t
+t/logger.t
+t/mage_defs.t
+t/mage.t
+t/magetab.t
+t/magetab_tabfile.t
+t/magetab_sdrf.t
+t/magetab_idf.t
+t/magetab_datamatrix.t
+t/magetab_checker.t
+t/miamexpress.t
+t/qt_list.t
+t/report.t
+t/tab2mage.t
+t/tracking.t
+t/validate.t
+t/standalone.t
+t/visualize.t
+t/data/dummy.CDF
+t/data/test.CEL
+t/data/test.CHP
+t/data/test.EXP
+t/data/test_xda.CEL
+t/data/test_xda.CHP
+t/data/test_calvin.CHP
+t/data/test.txt
+t/data/test_fgem.txt
+t/data/test.gpr
+META.yml Module meta-data (added by MakeMaker)
+automation/admin/db/migration/migrate_csv2sqlite.pl
+automation/admin/db/migration/migrate_sqlite2mysql.pl
+automation/admin/db/migration/migrate_2.0_to_2.1.sql
+automation/admin/db/migration/migrate_2.1_to_2.2.sql
+automation/admin/app/controllers/account_controller.rb
+automation/admin/app/controllers/application.rb
+automation/admin/app/controllers/array_designs_controller.rb
+automation/admin/app/controllers/category_controller.rb
+automation/admin/app/controllers/design_controller.rb
+automation/admin/app/controllers/material_controller.rb
+automation/admin/app/controllers/organism_controller.rb
+automation/admin/app/controllers/protocols_controller.rb
+automation/admin/app/controllers/experiments_controller.rb
+automation/admin/app/controllers/taxon_controller.rb
+automation/admin/app/controllers/miamexps_controller.rb
+automation/admin/app/controllers/tab2mages_controller.rb
+automation/admin/app/controllers/users_controller.rb
+automation/admin/app/helpers/account_helper.rb
+automation/admin/app/helpers/application_helper.rb
+automation/admin/app/helpers/array_designs_helper.rb
+automation/admin/app/helpers/category_helper.rb
+automation/admin/app/helpers/design_helper.rb
+automation/admin/app/helpers/material_helper.rb
+automation/admin/app/helpers/organism_helper.rb
+automation/admin/app/helpers/protocols_helper.rb
+automation/admin/app/helpers/experiments_helper.rb
+automation/admin/app/helpers/taxon_helper.rb
+automation/admin/app/helpers/miamexps_helper.rb
+automation/admin/app/helpers/tab2mages_helper.rb
+automation/admin/app/helpers/users_helper.rb
+automation/admin/app/models/array_design.rb
+automation/admin/app/models/category.rb
+automation/admin/app/models/design.rb
+automation/admin/app/models/design_instance.rb
+automation/admin/app/models/material.rb
+automation/admin/app/models/material_instance.rb
+automation/admin/app/models/organism.rb
+automation/admin/app/models/organism_instance.rb
+automation/admin/app/models/permission.rb
+automation/admin/app/models/protocol.rb
+automation/admin/app/models/role.rb
+automation/admin/app/models/experiment.rb
+automation/admin/app/models/taxon.rb
+automation/admin/app/models/user.rb
+automation/admin/app/models/data_file.rb
+automation/admin/app/models/spreadsheet.rb
+automation/admin/app/views/account/login.rhtml
+automation/admin/app/views/account/logout.rhtml
+automation/admin/app/views/account/signup.rhtml
+automation/admin/app/views/account/welcome.rhtml
+automation/admin/app/views/array_designs/_form.rhtml
+automation/admin/app/views/array_designs/_title.rhtml
+automation/admin/app/views/array_designs/_aedwinfo.rhtml
+automation/admin/app/views/array_designs/_miameinfo.rhtml
+automation/admin/app/views/array_designs/edit.rhtml
+automation/admin/app/views/array_designs/list.rhtml
+automation/admin/app/views/array_designs/new.rhtml
+automation/admin/app/views/array_designs/show.rhtml
+automation/admin/app/views/category/_form.rhtml
+automation/admin/app/views/category/_title.rhtml
+automation/admin/app/views/category/edit.rhtml
+automation/admin/app/views/category/list.rhtml
+automation/admin/app/views/category/new.rhtml
+automation/admin/app/views/design/_form.rhtml
+automation/admin/app/views/design/_title.rhtml
+automation/admin/app/views/design/edit.rhtml
+automation/admin/app/views/design/list.rhtml
+automation/admin/app/views/design/new.rhtml
+automation/admin/app/views/layouts/admin.rhtml
+automation/admin/app/views/material/_form.rhtml
+automation/admin/app/views/material/_title.rhtml
+automation/admin/app/views/material/edit.rhtml
+automation/admin/app/views/material/list.rhtml
+automation/admin/app/views/material/new.rhtml
+automation/admin/app/views/organism/_form.rhtml
+automation/admin/app/views/organism/_title.rhtml
+automation/admin/app/views/organism/edit.rhtml
+automation/admin/app/views/organism/list.rhtml
+automation/admin/app/views/organism/new.rhtml
+automation/admin/app/views/protocols/_form.rhtml
+automation/admin/app/views/protocols/_title.rhtml
+automation/admin/app/views/protocols/edit.rhtml
+automation/admin/app/views/protocols/list.rhtml
+automation/admin/app/views/protocols/new.rhtml
+automation/admin/app/views/protocols/show.rhtml
+automation/admin/app/views/experiments/_form.rhtml
+automation/admin/app/views/experiments/_annotate.rhtml
+automation/admin/app/views/experiments/_title.rhtml
+automation/admin/app/views/experiments/edit.rhtml
+automation/admin/app/views/experiments/list.rhtml
+automation/admin/app/views/experiments/new.rhtml
+automation/admin/app/views/experiments/today_list.rhtml
+automation/admin/app/views/taxon/_form.rhtml
+automation/admin/app/views/taxon/_title.rhtml
+automation/admin/app/views/taxon/edit.rhtml
+automation/admin/app/views/taxon/list.rhtml
+automation/admin/app/views/taxon/new.rhtml
+automation/admin/app/views/taxon/category_table.rhtml
+automation/admin/app/views/miamexps/_form.rhtml
+automation/admin/app/views/miamexps/_title.rhtml
+automation/admin/app/views/miamexps/edit.rhtml
+automation/admin/app/views/miamexps/list.rhtml
+automation/admin/app/views/miamexps/_aedwinfo.rhtml
+automation/admin/app/views/miamexps/_miameinfo.rhtml
+automation/admin/app/views/tab2mages/_form.rhtml
+automation/admin/app/views/tab2mages/_annotate.rhtml
+automation/admin/app/views/tab2mages/_title.rhtml
+automation/admin/app/views/tab2mages/edit.rhtml
+automation/admin/app/views/tab2mages/list.rhtml
+automation/admin/app/views/tab2mages/_aedwinfo.rhtml
+automation/admin/app/views/tab2mages/_miameinfo.rhtml
+automation/admin/app/views/tab2mages/new.rhtml
+automation/admin/app/views/users/_form.rhtml
+automation/admin/app/views/users/_title.rhtml
+automation/admin/app/views/users/edit.rhtml
+automation/admin/app/views/users/list.rhtml
+automation/admin/app/views/users/new.rhtml
+automation/admin/app/views/users/show.rhtml
+automation/admin/app/controllers/event_controller.rb
+automation/admin/app/controllers/factor_controller.rb
+automation/admin/app/controllers/quantitation_type_controller.rb
+automation/admin/app/helpers/event_helper.rb
+automation/admin/app/helpers/factor_helper.rb
+automation/admin/app/helpers/quantitation_type_helper.rb
+automation/admin/app/models/event.rb
+automation/admin/app/models/factor.rb
+automation/admin/app/models/quantitation_type.rb
+automation/admin/app/views/array_designs/list_all.rhtml
+automation/admin/app/views/event/_form.rhtml
+automation/admin/app/views/event/_title.rhtml
+automation/admin/app/views/event/edit.rhtml
+automation/admin/app/views/experiments/show.rhtml
+automation/admin/CHANGELOG
+automation/admin/config/README
+automation/admin/config/boot.rb
+automation/admin/config/database.yml-sqlite
+automation/admin/config/database.yml-mysql
+automation/admin/config/environment.rb
+automation/admin/config/environments/development.rb
+automation/admin/config/environments/production.rb
+automation/admin/config/environments/test.rb
+automation/admin/config/routes.rb
+automation/admin/config/lighttpd.conf
+automation/admin/doc/README_FOR_APP
+automation/admin/db/database-mysql.dump
+automation/admin/db/database-mysql.schema
+automation/admin/db/schema.pdf
+automation/admin/db/schema.rb
+automation/admin/INSTALL
+automation/admin/lib/acl_system.rb
+automation/admin/lib/annotation.rb
+automation/admin/lib/login_system.rb
+automation/admin/log/server.log
+automation/admin/public/.htaccess
+automation/admin/public/404.html
+automation/admin/public/500.html
+automation/admin/public/dispatch.cgi
+automation/admin/public/dispatch.fcgi
+automation/admin/public/dispatch.rb
+automation/admin/public/favicon.ico
+automation/admin/public/images/rails.png
+automation/admin/public/index.html
+automation/admin/public/javascripts/controls.js
+automation/admin/public/javascripts/dragdrop.js
+automation/admin/public/javascripts/effects.js
+automation/admin/public/javascripts/prototype.js
+automation/admin/public/robots.txt
+automation/admin/public/stylesheets/admin.css
+automation/admin/Rakefile
+automation/admin/README
+automation/admin/README_LOGIN
+automation/admin/script/about
+automation/admin/script/benchmarker
+automation/admin/script/breakpointer
+automation/admin/script/console
+automation/admin/script/destroy
+automation/admin/script/generate
+automation/admin/script/performance/benchmarker
+automation/admin/script/performance/profiler
+automation/admin/script/plugin
+automation/admin/script/process/reaper
+automation/admin/script/process/spawner
+automation/admin/script/process/spinner
+automation/admin/script/profiler
+automation/admin/script/runner
+automation/admin/script/server
+automation/admin/test/fixtures/array_designs.yml
+automation/admin/test/fixtures/categories.yml
+automation/admin/test/fixtures/designs.yml
+automation/admin/test/fixtures/materials.yml
+automation/admin/test/fixtures/organisms.yml
+automation/admin/test/fixtures/permissions.yml
+automation/admin/test/fixtures/permissions_roles.yml
+automation/admin/test/fixtures/protocols.yml
+automation/admin/test/fixtures/roles.yml
+automation/admin/test/fixtures/roles_users.yml
+automation/admin/test/fixtures/experiments.yml
+automation/admin/test/fixtures/taxons.yml
+automation/admin/test/fixtures/users.yml
+automation/admin/test/functional/account_controller_test.rb
+automation/admin/test/functional/array_designs_controller_test.rb
+automation/admin/test/functional/category_controller_test.rb
+automation/admin/test/functional/design_controller_test.rb
+automation/admin/test/functional/material_controller_test.rb
+automation/admin/test/functional/organism_controller_test.rb
+automation/admin/test/functional/protocols_controller_test.rb
+automation/admin/test/functional/experiments_controller_test.rb
+automation/admin/test/functional/taxon_controller_test.rb
+automation/admin/test/test_helper.rb
+automation/admin/test/unit/array_design_test.rb
+automation/admin/test/unit/category_test.rb
+automation/admin/test/unit/design_test.rb
+automation/admin/test/unit/material_test.rb
+automation/admin/test/unit/organism_test.rb
+automation/admin/test/unit/permission_test.rb
+automation/admin/test/unit/protocol_test.rb
+automation/admin/test/unit/role_test.rb
+automation/admin/test/unit/experiment_test.rb
+automation/admin/test/unit/taxon_test.rb
+automation/admin/test/unit/user_test.rb
+automation/admin/test/fixtures/data_files.yml
+automation/admin/test/fixtures/spreadsheets.yml
+automation/admin/test/functional/miamexps_controller_test.rb
+automation/admin/test/functional/tab2mages_controller_test.rb
+automation/admin/test/functional/users_controller_test.rb
+automation/admin/test/unit/data_file_test.rb
+automation/admin/test/unit/spreadsheet_test.rb
+automation/admin/test/fixtures/events.yml
+automation/admin/test/fixtures/factors.yml
+automation/admin/test/fixtures/quantitation_types.yml
+automation/admin/test/functional/event_controller_test.rb
+automation/admin/test/functional/factor_controller_test.rb
+automation/admin/test/functional/quantitation_type_controller_test.rb
+automation/admin/test/unit/event_test.rb
+automation/admin/test/unit/factor_test.rb
+automation/admin/test/unit/quantitation_type_test.rb
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..bf13f9b
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,25 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
+name: Tab2MAGE
+version: 20080626
+version_from:
+installdirs: site
+requires:
+ Bio::MAGE: 20030502.3
+ Bio::MAGE::XML::Writer: 20060815
+ Class::Std: 0.0.8
+ Config::YAML: 1.42
+ DBD::mysql: 2.1017
+ DBI: 1.48
+ Digest::MD5: 2.33
+ IO::File: 1.10
+ List::MoreUtils: 0.17
+ LWP::UserAgent: 2.031
+ Parse::RecDescent: 1.94
+ Readonly: 1.03
+ Storable: 1.014
+ Text::CSV_XS: 0.28
+ Tie::IxHash: 1.21
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.30
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..79962e2
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,163 @@
+#!/usr/bin/perl
+#
+# Makefile.PL for use with MakeMaker-style module installation
+#
+# $Id: Makefile.PL 1619 2007-06-18 22:57:59Z tfrayner $
+#
+#
+# Notes on building under PAR:
+#
+# This *can* be made to work, at least on Mac OS X, using something
+# similar to the following:
+#
+# PERL5LIB=./lib pp -o expt_check -c -l libxerces-c.26.dylib -A par_ext_files bin/expt_check.pl
+#
+# Note that the libxerces library file must be named exactly right; if
+# not you will get errors listing the correct name to use when the
+# pp'd executable is run. This has been tested using PAR 0.973 and
+# perl 5.8.8 on Mac OS X 10.4.8 (Intel), with the libxerces file in
+# the Tab2MAGE top-level directory. Note also that the par_ext_files
+# file is not distributed as part of the package and must be retrieved
+# from the svn repository.
+
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+my ( $d, $m, $y ) = ( localtime(time) )[ 3 .. 5 ];
+
+$y += 1900;
+$m = sprintf( "%02d", ++$m );
+$d = sprintf( "%02d", $d );
+
+###########
+# GLOBALS #
+###########
+
+my $VERSION = "$y$m$d";
+my @SCRIPTS = qw(
+ bin/tab2mage.pl
+ bin/magetab.pl
+ bin/expt_check.pl
+ bin/convert_to_mcmr.pl
+);
+my @LIBS = qw(xerces-c);
+
+########
+# SUBS #
+########
+
+sub MY::postamble
+{ # Makefile target to create standalone binaries using PAR/pp
+
+ my $suffix = '';
+ my $installer = 'INSTALL';
+ if ( $^O =~ m/^mswin/i )
+ { # Win2000 ActiveState Perl 5.8.6 registers as "MSWin32"
+ $suffix = '.exe';
+ $installer .= '.bat';
+ }
+ else {
+ $installer .= '.sh'; # Unix assumed to have Bourne-like shell
+ }
+
+ my %binaries = map {
+ my ($bin) = ( $_ =~ m!^bin/(.*)\.p[lmh]$!i );
+ $_ => "$bin$suffix";
+ } @SCRIPTS;
+
+ my $libraries = '';
+ foreach my $lib (@LIBS) { $libraries .= "-l $lib "; }
+
+ my @target;
+
+ push( @target, "\nstandalone: " );
+ push( @target, join( " \\\n\t", @SCRIPTS ), "\n\t" );
+ push( @target,
+ join( " \n\t", map {"pp -o $binaries{$_} $libraries -c -A par_ext_files $_"} @SCRIPTS ),
+ "\n" );
+
+ push( @target, "\nstandalone-clean: \n\t" );
+ push( @target,
+ join( " \n\t", map { '$(RM_F)' . " $_" } values %binaries ), "\n" );
+
+ push( @target, "\nstandalone-distdir: standalone\n\t" );
+
+ push(
+ @target,
+ ( '$(RM_RF) $(DISTVNAME)-' . $^O,
+ "\n\t",
+ '$(PERLRUN) "-MExtUtils::Manifest=manicopy,maniread" \\',
+ "\n\t",
+ '-e "manicopy(maniread(),\'$(DISTVNAME)-' . $^O
+ . '\', \'$(DIST_CP)\');"',
+ "\n\t",
+ )
+ );
+
+ push(
+ @target,
+ join( " \n\t",
+ map { '$(CP)' . " $_ " . '$(DISTVNAME)-' . $^O }
+ values %binaries ),
+ "\n"
+ );
+ push( @target,
+ "\t" . '$(CP)' . " $installer " . '$(DISTVNAME)-' . $^O, "\n" );
+
+ push( @target, "\nstandalone-dist: standalone-distdir \n\t" );
+ push(
+ @target,
+ join( " \n\t",
+ '$(PREOP)',
+ '$(ZIP) $(ZIPFLAGS) $(DISTVNAME)-' . $^O
+ . '.zip $(DISTVNAME)-'
+ . $^O,
+ '$(RM_RF) $(DISTVNAME)-' . $^O,
+ '$(POSTOP)',
+ ),
+ "\n"
+ );
+
+ push( @target, "\nmy-all: all standalone\n" );
+
+ push( @target, "\nmy-dist: dist standalone\n" );
+
+ push( @target, "\nmy-clean: clean standalone-clean\n" );
+
+ return join( '', @target );
+
+}
+
+########
+# MAIN #
+########
+
+WriteMakefile(
+ NAME => 'Tab2MAGE',
+ DISTNAME => 'Tab2MAGE',
+ AUTHOR => 'Tim Rayner <rayner at ebi.ac.uk>',
+ ABSTRACT =>
+ "Scripts and modules for data file checking and MAGE-ML generation.",
+ VERSION => $VERSION,
+ PREREQ_PM => {
+ 'Bio::MAGE' => "20030502.3",
+ 'Bio::MAGE::XML::Writer' => "20060815",
+ 'LWP::UserAgent' => "2.031",
+ 'DBI' => "1.48",
+ 'DBD::mysql' => "2.1017",
+ 'Storable' => "1.014",
+ 'IO::File' => "1.10",
+ 'Digest::MD5' => "2.33",
+ 'Tie::IxHash' => "1.21",
+ 'Readonly' => "1.03",
+ 'Text::CSV_XS' => "0.28",
+ 'List::MoreUtils' => "0.17",
+ 'Config::YAML' => "1.42",
+ 'Class::Std' => "0.0.8",
+ 'Parse::RecDescent' => "1.94",
+ },
+ EXE_FILES => \@SCRIPTS,
+ dist => { COMPRESS => 'gzip', SUFFIX => 'gz' },
+);
+
diff --git a/README b/README
new file mode 100644
index 0000000..76abc62
--- /dev/null
+++ b/README
@@ -0,0 +1,187 @@
+ArrayExpress Tab2MAGE
+=====================
+
+Introduction
+------------
+
+The ArrayExpress Tab2MAGE package consists of a set of scripts and
+modules which can read in microarray experiment annotation in defined
+spreadsheet formats, combine it with a set of experiment data files,
+and output the result in MAGE-ML format. Tools are included for
+validation of these spreadsheet documents, and automation of the
+entire process.
+
+This package can handle two distinct spreadsheet formats: MAGE-TAB,
+the new community-designed spreadsheet format for microarray data
+representation and interchange, and the eponymous Tab2MAGE, an older,
+simpler format upon which the design for MAGE-TAB was based.
+
+Please see the 'docs' and 'examples' directories for detailed usage
+information and some simple spreadsheet examples. This information is
+also available online at http://tab2mage.sourceforge.net/
+
+Automated experiment data checking
+----------------------------------
+
+Included in the package is an "experiment checker" script
+(expt_check.pl) which can examine the data files and annotation for an
+experiment and report on common errors. This checker script can be run
+in one of four modes, either to parse a Tab2MAGE spreadsheet, a
+MAGE-TAB document, check a submission in a local MIAMExpress database,
+or scan a series of data files in stand-alone mode.
+
+Spreadsheet visualization
+-------------------------
+
+The tab2mage.pl, magetab.pl, expt_check.pl and t2m_visualize.pl
+scripts will, as part of their operation, use the Graphviz 'dot'
+application if it has been installed, to produce a PNG format file
+showing the links between biomaterials, hybridizations and data
+files. If Graphviz has not been installed then this step will be
+skipped, and only the dot input file will be generated.
+
+Installation from source
+------------------------
+
+The Tab2MAGE Perl scripts currently require at least Perl version
+5.8.0 (version 5.8.1 or above is recommended), and that the following
+non-core Perl modules are installed:
+
+- Bio::MAGE (a.k.a. Perl MAGEstk. Note that this module requires
+ XML::Xerces and the Xerces C++ library from Apache
+ to be installed.)
+- Bio::MAGE::XML::Writer (part of the Bio-MAGE-Utils package).
+- LWP::UserAgent
+- DBI
+- DBD::mysql
+- Digest::MD5
+- Tie::IxHash
+- List::MoreUtils
+- Text::CSV_XS
+- Readonly (and preferably Readonly::XS, if available)
+- Config::YAML
+- Class::Std
+- Parse::RecDescent
+
+Note that DBI and DBD::mysql are only required if you wish to use this
+module with a local installation of MIAMExpress (in which case you
+will have already had to install this module anyway).
+
+This package has been tested with Bio::MAGE versions 20020902.5 and
+newer; version 20030502.3 is strongly recommended, though, as it fixes
+several bugs in the generated XML which can render the output MAGE-ML
+invalid.
+
+In the special case that you have your own local instance of the
+ArrayExpress Oracle database, you may also benefit from installing
+DBD::Oracle.
+
+Currently the scripts in the Tab2MAGE package requires access to a
+unix-like 'sort' utility on the execution path. Linux and other Unix
+operating systems provides this utility by default; Microsoft Windows
+also provides a sort utility with a slightly different invocation
+syntax. Tab2MAGE is fully supported on all unix-like systems, and has
+been found to work on at least one Windows system (MS Windows 2000
+ActiveState Perl 5.8.6 (build 811) using XML-Xerces 2.3.0). Note that
+the order in which DesignElements are referenced in the
+DesignElementDimensions will differ between Unix and Windows systems
+due to differences in the sort algorithm. This does not affect the
+validity of the output MAGE-ML.
+
+After unpacking the distribution, install this module by typing
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+To install the module in a location other than the main site-perl
+modules directory, please use the following command (suitably
+modified) to generate the makefile:
+
+perl Makefile.PL LIB=<perl module dir> INSTALLSCRIPT=<binary executable dir>
+
+You should also set INSTALLMAN1DIR and INSTALLMAN3DIR if you need to
+install man pages in non-standard locations. Please see the perldoc or
+man page for ExtUtils::MakeMaker for further information.
+
+The modules were originally written to provide functions supporting
+our MIAMExpress experiment checker script and our spreadsheet MAGE-ML
+generating scripts. These scripts are included in the package and will
+be installed in the system binary executable directory, or the
+directory to which INSTALLSCRIPT points. There is perldoc
+documentation available for the main scripts. Please see the
+'examples' subdirectory for an example of the experiment summary
+spreadsheet format to be used with the tab2mage.pl script. Also
+included in the examples directory is a sample listing of non-default
+QuantitationTypes for use with the -q option of tab2mage.pl and
+expt_check.pl.
+
+Automated Submissions System
+----------------------------
+
+As of Tab2MAGE version 1.9.0 an automated submissions system is
+included in the downloadable package. While this is not needed to use
+the expt_check.pl, magetab.pl and tab2mage.pl scripts, it may be of
+interest for sites which wish to automate processing of MAGE-TAB,
+Tab2MAGE or MIAMExpress submissions. The system consists of a number
+of components:
+
+1. A MySQL database to store submissions info and status.
+
+2. Web submissions forms for MAGE-TAB and Tab2MAGE spreadsheets, and
+data files.
+
+3. A back-end web interface for curators to monitor and control
+incoming submissions. This is a Ruby on Rails application - see the
+file automation/admin/INSTALL for details.
+
+4. A set of daemon scripts which automatically run the experiment
+checker scripts, assign scores, and export MAGE-ML from those
+submissions which pass the tests.
+
+There are many prerequisite Perl modules for this system:
+
+Archive::Any
+Archive::Tar
+Class::DBI
+CGI::Application
+CGI::Application::Plugin::Authenticate
+CGI::Application::Plugin::Authenticate::Driver::CDBI
+CGI::Application::Plugin::Session
+CGI::Application::Plugin::ValidateRM
+CGI::Upload
+Data::FormValidator
+Date::Manip
+DBD::mysql
+Email::Valid
+IO::Zlib
+MIME::Lite
+Proc::Daemon
+
+Several of these modules have their own complex dependencies, so it is
+strongly recommended that you use a system such as CPAN or CPANPLUS to
+automate the installation of these modules.
+
+Note: Previous versions of the Tab2MAGE package used a SQLite back-end
+database. As of version 1.9.9, however, we have switched over to using
+MySQL. A migration script, migrate_sqlite2mysql.pl, is included in the
+util/ directory to help you convert a SQLite dump file into MySQL
+syntax.
+
+ArrayMAGE
+---------
+
+Also included in this package are two lightweight scripts for
+generating array design MAGE-ML: arraymage.pl and cdf2mage.pl. If you
+wish to use these scripts, please install the following modules:
+
+Readonly::XS
+
+Note that these scripts are primarily designed for use with very large
+array designs which cannot be exported to MAGE-ML by other
+means. These scripts do not yet support the full MAGE model for array
+designs, and should only be used where other approaches have failed.
+
+---------------------------------------------------------------------
+Copyright Tim Rayner <rayner at ebi.ac.uk> ArrayExpress Team, EBI, 2007.
diff --git a/README-windows.txt b/README-windows.txt
new file mode 100644
index 0000000..a53060a
--- /dev/null
+++ b/README-windows.txt
@@ -0,0 +1,62 @@
+Unfortunately I am unable to fully support MS Windows installations,
+simply due to lack of time and the relative complexity of doing
+so. However, users wishing to try Tab2MAGE on Windows may find the
+following instructions useful.
+
+The following procedure has been found to work on a MS Windows 2000
+installation without using either administrator rights or a compiler.
+
+1. Install ActiveState Perl version 5.8.6 (build 811) or newer. Make a
+note of the location of the perl executable. The default location is
+in C:\Perl\bin. You may need to add this location to your PATH
+variable.
+
+2. Download XML-Xerces from the following directory:
+
+http://www.apache.org/dist/xml/xerces-p/binaries/
+
+You will need the file named "XML-Xerces-2.3.0-4.win32.zip". Newer
+versions are untested, but may work. Unzip the download. The Readme
+file which is extracted should help you install the module, but if it
+is absent or unclear, continue as follows:
+
+Copy or move the unzipped .dll file into the same directory as the
+perl executable (e.g., C:\Perl\bin).
+
+Also unzipped from this package are two directories, "perl5.6" and
+"perl5.8". Open a command prompt, change to the perl5.8 directory, and
+execute the following command:
+
+ ppm install XML-Xerces.ppd
+
+This should install the XML::Xerces module and supporting C library.
+
+3. Use ppm to install the following modules and their dependencies:
+
+ ppm install Tie-IxHash
+ ppm install Bio-MAGE
+ ppm install Readonly
+ ppm install List-MoreUtils
+ ppm install Text-CSV_XS
+ ppm install Config-YAML
+ ppm install Class-Std
+ ppm install Parse-RecDescent
+
+4. Install nmake, downloaded from here:
+
+http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+
+5. Since you've got this far, we'll assume you already have the
+Tab2MAGE package. If not, download it and uncompress it.
+
+6. In the Tab2MAGE top level directory, install the package by
+executing the following:
+
+ perl Makefile.PL
+ nmake
+ nmake install
+
+You should now have a working installation of Tab2MAGE. Congratulations.
+
+---------------------------------------------------------------------
+Copyright Tim Rayner <rayner at ebi.ac.uk> ArrayExpress Team, EBI, 2007.
diff --git a/adv_inst/InstallConfig.yml-example b/adv_inst/InstallConfig.yml-example
new file mode 100644
index 0000000..26b1e4e
--- /dev/null
+++ b/adv_inst/InstallConfig.yml-example
@@ -0,0 +1,51 @@
+--- !site-config
+
+# This file contains settings for a customised deployment of Tab2MAGE
+# and associated components into a production environment. This is
+# used internally by ArrayExpress, and is included here solely for
+# completeness. In most cases you should instead follow the
+# installation instructions in the main Tab2MAGE README file.
+
+# Location of the main source tree to install (defaults to the parent
+# directory).
+source_directories:
+ - ..
+
+# Scripts installed by Makefile.pl (this list is post-processed to set
+# "use lib" pragmas and repoint to use the desired perl interpreter).
+scripts_to_install:
+ - tab2mage.pl
+ - magetab.pl
+ - expt_check.pl
+ - convert_to_mcmr.pl
+
+# Other scripts not installed by default, only used for this installation.
+auxilliary_scripts:
+ - /full/path/to/script1.pl
+ - /full/path/to/script2.pl
+
+# Which perl interpreter to use.
+perl_location: /full/path/to/bin/perl
+
+# Directories to be included in the $PATH environmental variable.
+# Currently needs: dot, sort, perl (the latter only for automated MIAMExpress export).
+path: '/enter/your/full/PATH/var/here'
+
+# The value of the $ORACLE_HOME variable to use (obviously this will
+# vary with your Oracle install).
+oracle_home: /full/path/to/oracle/product/9.2.0
+ora_nls33: /full/path/to/oracle/product/9.2.0/ocommon/nls/admin/data
+
+# The value of $LD_LIBRARY_PATH to use. This path needs to include
+# libraries such as Xerces-C++, MySQL, Oracle, depending on which
+# package components you are using.
+ld_library_path: '/enter/your/full/LD_LIBRARY_PATH/here'
+
+# Location of main Tab2MAGE site config file, to be used in Config.pm
+site_config_location: /path/to/your/tab2mage/config/file/here
+
+# Placeholder strings used in the scripts. These are unlikely to
+# change, as they are now embedded throughout the Tab2MAGE codebase.
+use_lib_placeholder: '# use lib /path/to/directory/containing/Curator/modules;'
+shebang_placeholder: '#!/usr/bin/env perl'
+
diff --git a/adv_inst/README b/adv_inst/README
new file mode 100644
index 0000000..f3c47b6
--- /dev/null
+++ b/adv_inst/README
@@ -0,0 +1,6 @@
+This directory contains a tool that may be used for customised
+deployment of Tab2MAGE and associated components into a production
+environment. This is used internally by ArrayExpress, and is included
+here solely for completeness. In most situations you should instead
+follow the (much simpler) installation procedure described in the main
+Tab2MAGE README file.
diff --git a/adv_inst/T2MInstaller.pm b/adv_inst/T2MInstaller.pm
new file mode 100644
index 0000000..a934da1
--- /dev/null
+++ b/adv_inst/T2MInstaller.pm
@@ -0,0 +1,321 @@
+#!/usr/bin/env perl
+#
+# $Id: T2MInstaller.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+# Module used in automated advanced deployment of tab2mage and related
+# code into the ArrayExpress production environment. This is *not*
+# needed for basic installation of the Tab2MAGE package.
+
+package T2MInstaller;
+
+use strict;
+use warnings;
+
+use File::Spec;
+use File::Basename qw(basename);
+use File::Copy qw(copy);
+
+use Config::YAML;
+use Config;
+
+use Carp;
+
+use base qw(Exporter);
+our @EXPORT_OK = qw(install);
+
+sub get_config {
+
+ my ( $args ) = @_;
+
+ # Default to using a config file named "InstallConfig.yml" in the
+ # same directory as this module. If a config_file argument is
+ # passed, use that instead.
+ my $config_file;
+ unless ( $config_file = $args->{config_file} ) {
+ my @module_dir_array = File::Spec->splitpath(__FILE__);
+ $config_file = File::Spec->catpath(
+ @module_dir_array[ 0, 1 ], 'InstallConfig.yml',
+ );
+ }
+
+ unless ( -f $config_file ) {
+ if ( $args->{config_file} ) {
+ croak(qq{Error: config file "$config_file" not found.\n});
+ }
+ else {
+ croak(qq{Config file InstallConfig.yml not found. Please ensure that you}
+ . qq{ have edited the example config and renamed it to "$config_file".\n});
+ }
+ }
+
+ my $config = Config::YAML->new( config => $config_file );
+
+ return $config;
+}
+
+sub install_aux {
+
+ my ( $auxilliary_scripts, $bindir ) = @_;
+
+ foreach my $aux ( @$auxilliary_scripts ) {
+ my $targetfile = basename( $aux ); # just the filename
+ my $targetpath = File::Spec->catfile( $bindir, $targetfile );
+ print "Copying $aux to $targetpath...\n";
+ copy( $aux, $targetpath )
+ or croak("Error: Unable to copy file $aux: $!\n");
+ }
+
+ return;
+}
+
+sub install_modules {
+
+ my ( $source_directories, $args ) = @_;
+
+ my $perlmakefile_command = 'perl Makefile.PL';
+ $perlmakefile_command .=
+ $args->{inst_lib}
+ ? " LIB=$args->{inst_lib}"
+ : q{};
+
+ $perlmakefile_command .=
+ $args->{bin}
+ ? " INSTALLSCRIPT=$args->{bin}"
+ : q{};
+
+ $perlmakefile_command .=
+ $args->{man1}
+ ? " INSTALLMAN1DIR=$args->{man1} INSTALLSITEMAN1DIR=$args->{man1}"
+ : q{};
+
+ $perlmakefile_command .=
+ $args->{man3}
+ ? " INSTALLMAN3DIR=$args->{man3} INSTALLSITEMAN3DIR=$args->{man3}"
+ : q{};
+
+ foreach my $sourcedir (@$source_directories) {
+
+ chdir($sourcedir)
+ or croak("Unable to change directory to $sourcedir: $!\n");
+
+ if ( -f 'Makefile' ) {
+ system('make distclean') == 0
+ or croak("make distclean failed: $?");
+ }
+
+ system($perlmakefile_command) == 0
+ or croak("perl Makefile.PL failed: $?");
+
+ system('make') == 0
+ or croak("make failed: $?");
+
+ system('make test') == 0
+ or croak("make test failed: $?");
+
+ system('make install') == 0
+ or croak("make install failed: $?");
+
+ }
+}
+
+sub get_bindir {
+
+ my ( $args ) = @_;
+
+ my $bindir = $args->{bin} || $Config{installsitebin};
+
+ return $bindir;
+}
+
+sub get_inst_libdir {
+
+ my ( $args ) = @_;
+
+ my $inst_libdir = $args->{inst_lib} || $Config{installsitelib};
+
+ return $inst_libdir;
+}
+
+sub postprocess_scripts {
+
+ my ( $config, $args ) = @_;
+
+ my $auxilliary_scripts = $config->get_auxilliary_scripts() || [];
+ my $scripts_to_install = $config->get_scripts_to_install() || [];
+
+ my $perl_location = $args->{perl_location}
+ || $config->get_perl_location();
+ my $use_lib_placeholder = $config->get_use_lib_placeholder();
+ my $shebang_placeholder = $config->get_shebang_placeholder();
+
+ my $perl_libdir = $args->{perl_lib}
+ ? join(q{','}, split /:/, $args->{perl_lib})
+ : $Config{installsitelib};
+ my $inst_libdir = get_inst_libdir( $args );
+ my $bindir = get_bindir( $args );
+
+ my $path = $args->{path} || $config->get_path() || q{};
+ my $oracle_home = $args->{oracle_home} || $config->get_oracle_home() || q{};
+ my $ora_nls33 = $args->{ora_nls33} || $config->get_ora_nls33() || q{};
+ my $ld_library_path = $args->{ld_library_path} || $config->get_ld_library_path() || q{};
+
+ my $ld_lib_components = join(", ", map { qq{'$_'} } split /:/, $ld_library_path);
+
+ my $use_lib_declaration = (<<"END_USE_LIB");
+use lib '$perl_libdir';
+use lib '$inst_libdir'; # This takes precedence
+
+# We need to control the environment. We do so with this unholy kludge.
+BEGIN {
+
+ # With thanks to www.perlmonks.org/?node_id=126587 and following, monks tye and mikeraz
+ # (slightly modified from the originals).
+ my \@needed = ($ld_lib_components);
+
+ # Get our LD_LIBRARY_PATH before we mess around with the environment.
+ my \$ld_path = \$ENV{LD_LIBRARY_PATH} || q{};
+
+ # Set the easy things here.
+ my \$tmp_home = \$ENV{HOME};
+ \%ENV = ();
+ \$ENV{PATH} = "$path";
+ \$ENV{HOME} = \$tmp_home;
+ \$ENV{ORACLE_HOME} = "$oracle_home";
+ \$ENV{ORA_NLS33} = "$ora_nls33";
+
+ # Check for the existence of each element of \@needed in the path;
+ # if not found, make a note.
+ my \$changed_path = 0;
+ foreach my \$dir ( \@needed ) {
+ if ( \$ld_path !~ m/(^|:)\\Q\$dir\\E(:|\$)/ ) {
+ \$changed_path = 1;
+ }
+ }
+
+ # If the path needs modification, do so and re-execute the script.
+ if ( \$changed_path ) {
+ \$ENV{LD_LIBRARY_PATH} = join ":", \@needed;
+ exec 'env', \$^X, \$0, \@ARGV;
+ }
+
+ # Otherwise, we're home free! Start our engines...
+}
+
+
+END_USE_LIB
+
+ SCRIPT:
+ foreach my $script ( @$scripts_to_install, @$auxilliary_scripts, ) {
+
+ my $target = basename( $script );
+
+ my $orig = File::Spec->catfile( $bindir, $target );
+ my $temp = File::Spec->catfile( $bindir, "$target.tmp" );
+
+ printf STDOUT ( "Post-processing script: %-25s...", $target );
+
+ open( my $orig_fh, '<', $orig )
+ or do {
+ carp("Error opening $orig (skipping): $!\n");
+ next SCRIPT;
+ };
+ open( my $temp_fh, '>', $temp )
+ or croak("Error opening temporary file $temp: $!\n");
+
+ while ( my $line = <$orig_fh> ) {
+
+ if ( $use_lib_placeholder
+ && $line =~ s!$use_lib_placeholder!$use_lib_declaration! ) {
+ print STDOUT qq{ "use lib" declaration fixed.};
+ }
+
+ if ( $perl_location
+ && $shebang_placeholder
+ && $line =~ s[$shebang_placeholder][#!$perl_location --] ) {
+ print STDOUT qq{ shebang line redirected.};
+ }
+
+ print $temp_fh $line;
+ }
+
+ unlink($orig) or croak("Unable to unlink original file $orig: $!\n");
+ rename( $temp, $orig )
+ or croak("Unable to rename $temp to $orig: $!\n");
+
+ system("chmod 0755 $orig") == 0
+ or croak "chmod 0755 $orig failed: $?";
+
+ close $temp_fh or croak("$!\n");
+ close $orig_fh or croak("$!\n");
+
+ print STDOUT "\n";
+ }
+
+ return;
+}
+
+sub postprocess_siteconfig {
+
+ my ( $config, $args ) = @_;
+
+ my $inst_libdir = get_inst_libdir( $args );
+
+ my $config_pm = "$inst_libdir/ArrayExpress/Curator/Config.pm";
+ my $config_temp = "$config_pm.tmp";
+ my $site_config = $args->{site_config_location}
+ || $config->get_site_config_location;
+
+ if ( -f $config_pm && $site_config ) {
+ print STDOUT "Fixing Config.pm module...";
+
+ open( my $origpm_fh, '<', $config_pm )
+ or croak("Error opening $config_pm: $!\n");
+ open( my $temppm_fh, '>', $config_temp )
+ or croak("Error opening $config_temp: $!\n");
+
+ while ( my $line = <$origpm_fh> ) {
+ $line =~ s/(\$siteconf\s*=\s*)q\{\}/$1q\{$site_config\}/g;
+ print $temppm_fh ($line);
+ }
+
+ unlink($config_pm)
+ or croak("Unable to unlink original file $config_pm: $!\n");
+ rename( $config_temp, $config_pm )
+ or croak("Unable to rename $config_temp to $config_pm: $!\n");
+
+ close $temppm_fh or croak("$!\n");
+ close $origpm_fh or croak("$!\n");
+
+ print STDOUT " Done\n\n";
+ }
+ return;
+}
+
+sub install {
+
+ my ( $args ) = @_;
+
+ my $config = get_config( $args );
+ my $bindir = get_bindir( $args );
+
+ # Scripts not in the Makefile.PL. This is a more likely point of
+ # failure, so we try it first.
+ my $auxilliary_scripts = $config->get_auxilliary_scripts() || [];
+ install_aux( $auxilliary_scripts, $bindir );
+
+ # Run the main install.
+ my $source_directories = $args->{source_directories}
+ || $config->get_source_directories()
+ or croak("Error: No source directories\n");
+ install_modules( $source_directories, $args );
+
+ # Post-install changes.
+ postprocess_scripts( $config, $args );
+
+ # Repoint the main Config.pm module to our site config file.
+ postprocess_siteconfig( $config, $args );
+
+ return;
+}
+
+1;
diff --git a/adv_inst/deploy.pl b/adv_inst/deploy.pl
new file mode 100755
index 0000000..59ed2e5
--- /dev/null
+++ b/adv_inst/deploy.pl
@@ -0,0 +1,66 @@
+#!/usr/bin/env perl
+#
+# $Id: deploy.pl 1864 2008-01-02 09:58:24Z tfrayner $
+#
+# Script to automate advanced deployment of tab2mage and related code
+# into the ArrayExpress production environment. This is *not* needed
+# for basic installation of the Tab2MAGE package.
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use T2MInstaller qw(install);
+
+my ( $config, $wanthelp );
+
+GetOptions(
+ "c|config=s" => \$config,
+ "h|help" => \$wanthelp,
+);
+
+if ( $wanthelp ) {
+
+ print <<"USAGE";
+
+ Usage: ./deploy.pl
+
+ Options:
+
+ -c or --config Deployment configuration file to use.
+ -h or --help Display this help message.
+
+ The script must be launched from within the same directory as the
+ T2MInstaller.pm module. If the config file is not specified the
+ script will look for a file named InstallConfig.yml in the same
+ directory, and fail if it is not found or is unreadable.
+
+USAGE
+
+ exit 255;
+}
+
+install({
+
+ # Optional config file argument.
+ config_file => $config,
+
+ # These options can be used to fine-tune the install
+ # location. Different scripts can be set up to deploy into
+ # e.g. testing and production environments. Uncomment to set these
+ # options.
+
+ # The location to install the perl modules themselves.
+ # inst_lib => '/full/path/to/local/lib/perl5/site_perl/',
+
+ # Where to install the scripts.
+ # bin => '/full/path/to/bin/',
+
+ # The location for the man pages.
+ # man1 => '/full/path/to/local/man/man1',
+ # man3 => '/full/path/to/local/man/man3',
+
+ # Any non-standard locations for the perl modules on which this
+ # package depends should be included here.
+ # perl_lib => '/all/paths:/needed/in/PERL5LIB:/environmental/variable',
+});
diff --git a/arraymage/arraymage.pl b/arraymage/arraymage.pl
new file mode 100755
index 0000000..ff70cea
--- /dev/null
+++ b/arraymage/arraymage.pl
@@ -0,0 +1,499 @@
+#!/usr/bin/env perl
+#
+# Fast ADF to MAGE-ML converter
+#
+# Tim Rayner 2005, EBI Microarray Informatics Team
+#
+# $Id: arraymage.pl 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Readonly;
+use ArrayExpress::ArrayMAGE qw(:ALL);
+
+###########
+# GLOBALS #
+###########
+
+# These are shared with ArrayExpress::ArrayMAGE
+ArrayExpress::ArrayMAGE->set_programname('ArrayMAGE');
+ArrayExpress::ArrayMAGE->set_namespace('ebi.ac.uk:ArrayMAGE');
+
+# We do this ourselves, nothing fancy.
+my $svn_revision = '$Revision: 1938 $';
+our ($VERSION) = ( $svn_revision =~ /^\$Revision: ([\d\.]*)/ );
+
+##########################
+###### Main Package ######
+##########################
+
+package main;
+
+use Getopt::Long;
+use File::Spec;
+use Benchmark;
+
+Readonly my $META_COLUMN => 'MetaColumn';
+Readonly my $META_ROW => 'MetaRow';
+Readonly my $COLUMN => 'Column';
+Readonly my $ROW => 'Row';
+Readonly my $REPORTER_IDENTIFIER => 'Reporter Identifier';
+Readonly my $REPORTER_NAME => 'Reporter Name';
+Readonly my $REPORTER_COMMENT => 'Reporter Comment';
+
+Readonly my $BIOSEQUENCE_TYPE => 'Reporter BioSequence Type';
+Readonly my $BIOSEQUENCE_POLYMERTYPE => 'Reporter BioSequence PolymerType';
+Readonly my $REPORTER_CONTROL_TYPE => 'Reporter Control Type';
+Readonly my $REPORTER_FAIL_TYPE => 'Reporter Fail Type';
+Readonly my $REPORTER_WARNING_TYPE => 'Reporter Warning Type';
+
+Readonly my $REPORTER_ACTUAL_SEQUENCE =>
+ 'Reporter BioSequence [Actual sequence]';
+
+Readonly my $REPORTER_FWD_PRIMER => 'Reporter BioSequence [Forward primer]';
+
+Readonly my $REPORTER_REV_PRIMER => 'Reporter BioSequence [Reverse primer]';
+
+Readonly my $BIOSEQUENCE_DB_ENTRY_PATTERN =>
+ qr{Reporter\s*BioSequence\s*Database\s*Entry\s*\[\s*(.*?)\s*\]}ixms;
+
+Readonly my $REPORTER_DB_ENTRY_PATTERN =>
+ qr{Reporter\s*Description\s*Database\s*Entry\s*\[\s*(.*?)\s*\]}ixms;
+
+Readonly my $REPORTER_GROUP_PATTERN =>
+ qr{Reporter\s*Group\s*\[\s*(.*?)\s*\]}ixms;
+
+sub parse_args {
+
+ # Parse script arguments, do some preprocessing.
+
+ my ( %args, $output, $wantversion );
+
+ GetOptions(
+ "i|adf=s" => \$args{adf},
+ "a|accession=s" => \$args{design},
+ "o|output=s" => \$output,
+ "v|version" => \$wantversion,
+ );
+
+ if ($wantversion) {
+ my $programname = ArrayExpress::ArrayMAGE->get_programname();
+ print STDERR <<VERSION;
+ This is $programname, SVN revision $VERSION
+VERSION
+ exit;
+ }
+
+ unless ( $args{adf} && $output && $args{design} ) {
+ print STDERR <<NOINPUT;
+
+ Usage: $0 -i <ADF filename> -a <accession number> -o <output file>
+
+NOINPUT
+ exit 255;
+ }
+
+ open( $args{output}, '>', $output )
+ or die("Error: unable to open output file $output: $!\n");
+
+ ArrayExpress::ArrayMAGE->set_design($args{design});
+
+ return \%args;
+}
+
+sub coerce_headings {
+
+ # Match up our headings with the set of known constant headings,
+ # warn for unrecognized headings.
+
+ my ($line) = @_;
+ $line =~ s/[\r\n]*$//;
+ my @headings = split /\t/, $line;
+
+ foreach my $name (@headings) {
+
+ my $test = $name;
+ $test =~ s/ //g;
+
+ HEADNAME: {
+
+ ( $test =~ /^metacolumn$/i )
+ && do { $name = $META_COLUMN; last HEADNAME; };
+
+ ( $test =~ /^metarow$/i )
+ && do { $name = $META_ROW; last HEADNAME; };
+
+ ( $test =~ /^column$/i )
+ && do { $name = $COLUMN; last HEADNAME; };
+
+ ( $test =~ /^row$/i )
+ && do { $name = $ROW; last HEADNAME; };
+
+ ( $test =~ /^reporteridentifier$/i )
+ && do { $name = $REPORTER_IDENTIFIER; last HEADNAME; };
+
+ ( $test =~ /^reportername$/i )
+ && do { $name = $REPORTER_NAME; last HEADNAME; };
+
+ ( $test =~ /^reportercomment$/i )
+ && do { $name = $REPORTER_COMMENT; last HEADNAME; };
+
+ ( $test =~ /biosequencetype$/i )
+ && do { $name = $BIOSEQUENCE_TYPE; last HEADNAME; };
+
+ ( $test =~ /biosequencepolymertype$/i )
+ && do { $name = $BIOSEQUENCE_POLYMERTYPE; last HEADNAME; };
+
+ ( $test =~ /controltype$/i )
+ && do { $name = $REPORTER_CONTROL_TYPE; last HEADNAME; };
+
+ ( $test =~ /failtype$/i )
+ && do { $name = $REPORTER_FAIL_TYPE; last HEADNAME; };
+
+ ( $test =~ /warningtype$/i )
+ && do { $name = $REPORTER_WARNING_TYPE; last HEADNAME; };
+
+ ( $test =~ /actualsequence/i )
+ && do { $name = $REPORTER_ACTUAL_SEQUENCE; last HEADNAME; };
+
+ ( $test =~ /(forward|fwd)primer/i )
+ && do { $name = $REPORTER_FWD_PRIMER; last HEADNAME; };
+
+ ( $test =~ /(reverse|rev)primer/i )
+ && do { $name = $REPORTER_REV_PRIMER; last HEADNAME; };
+
+ ( $test =~ $BIOSEQUENCE_DB_ENTRY_PATTERN )
+ && do { last HEADNAME };
+
+ ( $test =~ $REPORTER_DB_ENTRY_PATTERN )
+ && do { last HEADNAME };
+
+ ( $test =~ $REPORTER_GROUP_PATTERN )
+ && do { last HEADNAME };
+
+ warn("WARNING: unrecognized column heading \"$name\"\n");
+
+ }
+
+ }
+
+ return \@headings;
+
+}
+
+sub sort_rows {
+
+ # Use the sort utility on unix OSes to quickly sort on Reporter
+ # Identifier (and CS ID FIXME)
+
+ my ( $fh, $headings, $name ) = @_;
+
+ my $colno;
+ for ( 0 .. $#$headings ) {
+ $colno = $_ if ( $headings->[$_] eq $REPORTER_IDENTIFIER );
+ }
+ unless ($colno) {
+ die( "Error: File contains no "
+ . $REPORTER_IDENTIFIER
+ . " column.\n" );
+ }
+
+ # map 0..(n-1) onto 1..n
+ $colno++;
+
+ my $linecount = 0;
+ open( my $sorted, '|-', "sort -b -t \'\t\' -k $colno -o \"$name\"" )
+ or die("Error opening temporary sort file $name: $!\n");
+
+ print STDOUT ("Sorting ADF on column $colno...\n");
+ while ( my $line = <$fh> ) {
+ print $sorted $line;
+ $linecount++;
+ }
+
+ close $sorted or die("$!\n");
+ open( $sorted, '<', $name ) or die("$!\n");
+
+ return ( $sorted, $linecount );
+}
+
+sub assign_headings {
+
+ # Given a line string and a list of headings, return a heading =>
+ # value hash
+
+ my ( $row, $headings ) = @_;
+
+ $row =~ s/[\r\n]*$//;
+ my @rowlist = split /\t/, $row;
+ my %rowhash;
+
+ for ( 0 .. $#rowlist ) {
+ unless ( $headings->[$_] ) {
+ warn(
+ "WARNING: Skipping entry without column heading $rowlist[$_]\n"
+ );
+ next;
+ }
+ $rowhash{ $headings->[$_] } = $rowlist[$_];
+ }
+ return \%rowhash;
+}
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args;
+
+# Initialise some stuff
+my $starttime = new Benchmark;
+
+# Open the ADF and scan down to the header row
+open( my $adf, "<", $args->{adf} )
+ or die("Error: unable to open ADF file $args->{adf}: $!\n");
+my $line = q{};
+until ( $line =~ m/\bMeta\s*Column\b/i ) { $line = <$adf> }
+
+# Generate a $headings arrayref with standardized values
+my $headings = coerce_headings($line);
+
+# Sort the file to allow FRM and RCM shortcuts, keeping memory usage
+# down.
+my $tempfile
+ = File::Spec->catpath( File::Spec->tmpdir, "$args->{adf}.temporary" );
+my ( $sorted, $num_lines ) = sort_rows( $adf, $headings, $tempfile );
+
+close $adf;
+
+# Create anon temp file objects (only the compulsory classes are
+# listed here).
+my @classes = qw(
+ Feature
+ Reporter
+ BioSequence
+ FeatureReporterMap
+ ArrayDesign
+ Zone
+);
+my %object;
+
+foreach my $type (@classes) {
+ if ( $type eq 'ArrayDesign' ) {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+ accession => $args->{design},
+ });
+ }
+ else {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+ }
+}
+
+# Create database objects
+my %dbtags;
+foreach my $heading (@$headings) {
+ foreach my $pattern ( $BIOSEQUENCE_DB_ENTRY_PATTERN,
+ $REPORTER_DB_ENTRY_PATTERN ) {
+ if ( my ($tag) = ( $heading =~ $pattern ) ) {
+ $dbtags{$tag}++;
+ }
+ }
+}
+if ( scalar( grep { defined $_ } values %dbtags ) ) {
+ $object{Database} = ArrayExpress::ArrayMAGE::Database->new();
+}
+foreach my $tag ( keys %dbtags ) {
+ $object{Database}->add({ tag => $tag });
+}
+
+# Set up a few tracker variables
+my %zone_tracker; # hash to track Zone creation
+my %reporter_group; # hash of ReporterGroup objects
+my $last_reporter = q{};
+my $last_compseq = q{};
+my @frm_features; # will be undefed in the loop
+my @rcm_reporters; # will be undefed in the loop
+
+# Map "other" BioSequences to their respective Types from MO
+my %bioseq_mapping = (
+ $REPORTER_FWD_PRIMER => 'PCR_primer_forward',
+ $REPORTER_REV_PRIMER => 'PCR_primer_reverse',
+);
+
+#############
+# Main loop #
+#############
+
+print STDOUT ("Writing MAGE-ML to temporary files...\n");
+my $progress_bar = ArrayExpress::ArrayMAGE::ProgressBar->new(
+ width => 40,
+ max_val => $num_lines
+);
+my $linecount = 0;
+while ( my $line = <$sorted> ) {
+
+ $linecount++;
+
+ $progress_bar->draw($linecount);
+
+ # skip blank lines
+ next if $line =~ m/^\s*$/;
+
+ $line =~ s/>/>/g;
+ $line =~ s/</</g;
+ $line =~ s/"/"/g;
+ $line =~ s/'/'/g;
+ $line =~ s/&/&/g;
+
+ # {heading => value} hashref
+ my $row = assign_headings( $line, $headings );
+
+ # Zone
+ unless ( $zone_tracker{ ( $row->{$META_COLUMN} . $row->{$META_ROW} ) } ) {
+ $object{Zone}->add({
+ row => $row->{$META_ROW},
+ column => $row->{$META_COLUMN},
+ });
+ $zone_tracker{ ( $row->{$META_COLUMN} . $row->{$META_ROW} ) }++;
+ }
+
+ # Feature
+ $object{Feature}->add({
+ metacolumn => $row->{$META_COLUMN},
+ metarow => $row->{$META_ROW},
+ column => $row->{$COLUMN},
+ row => $row->{$ROW},
+ });
+
+ # FeatureReporterMap, Reporter, BioSequence.
+ # Only do these if we're processing a new reporter id.
+ unless ( $row->{$REPORTER_IDENTIFIER} eq $last_reporter ) {
+
+ # Database or ReporterGroup headings;
+ my ( %bs_dbids, %r_dbids );
+ while ( my ( $rname, $rvalue ) = each %$row ) {
+ if ( my ($type) = ( $rname =~ $REPORTER_GROUP_PATTERN ) ) {
+ my $key = "$type." . $rvalue;
+ unless ( $reporter_group{$key} ) {
+ $reporter_group{$key}
+ = ArrayExpress::ArrayMAGE::ReporterGroup->new({
+ identifier => $key,
+ tag => $rvalue,
+ is_species => ( $type =~ m/species/i ) || 0,
+ });
+ }
+ $reporter_group{$key}
+ ->add({ id_ref => $row->{$REPORTER_IDENTIFIER}, });
+ }
+ if ( my ($tag) = ( $rname =~ $BIOSEQUENCE_DB_ENTRY_PATTERN ) ) {
+
+ # keys are accessions, values are db tags
+ $bs_dbids{$rvalue} = $tag if length($rvalue);
+ }
+ if ( my ($tag) = ( $rname =~ $REPORTER_DB_ENTRY_PATTERN ) ) {
+
+ # keys are accessions, values are db tags
+ $r_dbids{$rvalue} = $tag if length($rvalue);
+ }
+ }
+
+ # BioSequence
+ # Flag to indicate whether BS creation should occur
+ my $has_biosequence = ( $row->{$BIOSEQUENCE_POLYMERTYPE}
+ || $row->{$BIOSEQUENCE_TYPE}
+ || $row->{$REPORTER_ACTUAL_SEQUENCE}
+ || scalar( grep { defined $_ } values %bs_dbids ) );
+ my @biosequences;
+ if ($has_biosequence) {
+ $object{BioSequence}->add({
+ identifier => $row->{$REPORTER_IDENTIFIER},
+ dbrefs => \%bs_dbids,
+ polymertype => $row->{$BIOSEQUENCE_POLYMERTYPE},
+ type => $row->{$BIOSEQUENCE_TYPE},
+ sequence => $row->{$REPORTER_ACTUAL_SEQUENCE},
+ });
+ push( @biosequences, $row->{$REPORTER_IDENTIFIER} );
+ }
+
+ # Primer sequences
+ foreach my $bs_type ( $REPORTER_FWD_PRIMER, $REPORTER_REV_PRIMER ) {
+ if ( $row->{$bs_type} ) {
+ $object{BioSequence}->add({
+ identifier => $row->{$REPORTER_IDENTIFIER}
+ . ".$bioseq_mapping{$bs_type}",
+ polymertype => 'DNA',
+ type => $bioseq_mapping{$bs_type},
+ sequence => $row->{$bs_type},
+ });
+ push( @biosequences,
+ $row->{$REPORTER_IDENTIFIER}
+ . ".$bioseq_mapping{$bs_type}" );
+ $has_biosequence++;
+ }
+ }
+
+ # Reporter
+ $object{Reporter}->add({
+ identifier => $row->{$REPORTER_IDENTIFIER},
+ name => $row->{$REPORTER_NAME},
+ comment => $row->{$REPORTER_COMMENT},
+ biosequences => \@biosequences,
+ controltype => $row->{$REPORTER_CONTROL_TYPE},
+ warningtype => $row->{$REPORTER_WARNING_TYPE},
+ failtype => $row->{$REPORTER_FAIL_TYPE},
+ dbrefs => \%r_dbids,
+ });
+
+ # FeatureReporterMap
+
+ # Skip over creation of first FRM; the last is created outside
+ # the end of this loop.
+ if ($last_reporter) {
+ $object{FeatureReporterMap}->add({
+ identifier => $last_reporter,
+ features => \@frm_features,
+ });
+ }
+ undef @frm_features;
+
+ }
+
+ push( @frm_features,
+ "$row->{$META_COLUMN}.$row->{$META_ROW}.$row->{$COLUMN}.$row->{$ROW}"
+ );
+ $last_reporter = $row->{$REPORTER_IDENTIFIER};
+
+ # same for compseq FIXME
+
+}
+
+# Deal with the last FRM/RCM
+$object{FeatureReporterMap}->add({
+ identifier => $last_reporter,
+ features => \@frm_features,
+});
+
+unlink($tempfile)
+ or warn("Warning: could not delete temporary file $tempfile: $!\n");
+
+# Empty hash is composite_group placeholder FIXME
+ArrayExpress::ArrayMAGE->combine_parts(
+ \%object,
+ \%reporter_group,
+ {},
+ $args->{output},
+);
+
+print STDOUT ("Done.\n");
+
+my $endtime = new Benchmark;
+print STDOUT (
+ "\nTotal run time = ",
+ timestr( timediff( $endtime, $starttime ) ), "\n\n"
+);
+
diff --git a/arraymage/cdf2mage.pl b/arraymage/cdf2mage.pl
new file mode 100755
index 0000000..8cff95d
--- /dev/null
+++ b/arraymage/cdf2mage.pl
@@ -0,0 +1,728 @@
+#!/usr/bin/env perl
+#
+# Fast CDF to MAGE-ML converter; a work in progress.
+#
+# Tim Rayner 2006, EBI Microarray Informatics Team
+#
+# $Id: cdf2mage.pl 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use ArrayExpress::ArrayMAGE qw(:ALL);
+use ArrayExpress::ArrayMAGE::NullObject;
+
+###########
+# GLOBALS #
+###########
+
+ArrayExpress::ArrayMAGE->set_programname('CDF2MAGE');
+ArrayExpress::ArrayMAGE->set_namespace('Affymetrix');
+ArrayExpress::ArrayMAGE->set_separator(':');
+
+# We do this ourselves, nothing fancy.
+my $svn_revision = '$Revision: 1938 $';
+our ($VERSION) = ( $svn_revision =~ /^\$Revision: ([\d\.]*)/ );
+
+# Global flag to determine whether we're CS-only or not
+my $CS_ONLY = 0;
+
+# This is a default, may be overwritten upon initial parsing of the CDF.
+my $CDF_LINEBREAK = '\015\012';
+
+use Getopt::Long;
+use File::Spec;
+use Benchmark;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Common qw(check_linebreaks);
+
+sub parse_args {
+
+ # Parse script arguments, do some preprocessing.
+
+ my ( %args, $output, $wantversion );
+
+ GetOptions(
+ "i|cdf=s" => \$args{cdf},
+ "a|accession=s" => \$args{accession},
+ "o|output=s" => \$output,
+ "c|compseq" => \$CS_ONLY,
+ "v|version" => \$wantversion,
+ );
+
+ if ($wantversion) {
+ my $programname = ArrayExpress::ArrayMAGE->get_programname();
+ print STDERR <<VERSION;
+ This is $programname, SVN revision $VERSION
+VERSION
+ exit;
+ }
+
+ unless ( $args{cdf} && $output && $args{accession} ) {
+ print STDERR <<NOINPUT;
+
+ Usage: $0 -i <CDF filename> -a <accession number> -o <output file>
+
+For very large CDF files, use the -c option to generate only the CompositeSequences.
+
+NOINPUT
+ exit 255;
+ }
+
+ # Basic sanity checks on the CDF file.
+ if ( ! -f $args{cdf} ) {
+ die("Error: CDF file not found: $args{cdf}\n");
+ }
+ elsif ( -B $args{cdf} ) {
+ die("Error: Binary GCOS CDF files not yet supported.\n");
+ }
+
+ open( $args{output}, '>', $output )
+ or die("Error: unable to open output file $output: $!\n");
+
+ return \%args;
+}
+
+sub cs_initialize {
+
+ my ( $accession, $reporter_group_id, $composite_group_id, $design_id ) = @_;
+
+ # Create anon temp file objects (only the compulsory classes are
+ # listed here).
+ my @classes = qw(
+ BioSequence
+ CompositeSequence
+ ArrayDesign
+ );
+ my @null_classes = qw(
+ Feature
+ Reporter
+ FeatureReporterMap
+ ReporterCompositeMap
+ Zone
+ );
+ my %object;
+
+ # Construct a single ReporterGroup
+ my $reporter_group = ArrayExpress::ArrayMAGE::NullObject->new;
+
+ # Construct a single CompositeGroup
+ my $composite_group = ArrayExpress::ArrayMAGE::CompositeGroup->new({
+ identifier => $composite_group_id,
+ tag => $design_id,
+ });
+
+ # Initialize the main classes.
+ foreach my $type (@classes) {
+ if ( $type eq 'ArrayDesign' ) {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+ accession => $accession,
+ });
+ }
+ else {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+ }
+ }
+
+ # Initialize the null classes.
+ foreach my $type (@null_classes) {
+ $object{$type} = ArrayExpress::ArrayMAGE::NullObject->new;
+ }
+
+ # NetAffx database used for ProbeSet ids
+ $object{Database} = ArrayExpress::ArrayMAGE::Database->new();
+ $object{Database}->add({ tag => 'netaffx' });
+
+ return ( \%object, $reporter_group, $composite_group, $reporter_group_id,
+ $composite_group_id );
+}
+
+sub initialize {
+
+ my ( $accession, $reporter_group_id, $composite_group_id, $design_id ) = @_;
+
+ # Create anon temp file objects (only the compulsory classes are
+ # listed here).
+ my @classes = qw(
+ Feature
+ Reporter
+ CompositeSequence
+ BioSequence
+ FeatureReporterMap
+ ReporterCompositeMap
+ ArrayDesign
+ Zone
+ );
+ my %object;
+
+ # Construct a single ReporterGroup
+ my $reporter_group = ArrayExpress::ArrayMAGE::ReporterGroup->new({
+ identifier => $reporter_group_id,
+ tag => $design_id,
+ });
+
+ # Construct a single CompositeGroup
+ my $composite_group = ArrayExpress::ArrayMAGE::CompositeGroup->new({
+ identifier => $composite_group_id,
+ tag => $design_id,
+ });
+
+ # Initialize the main classes.
+ foreach my $type (@classes) {
+ if ( $type eq 'ArrayDesign' ) {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+ accession => $accession,
+ });
+ }
+ else {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+ }
+ }
+
+ # Only one zone for Affy arrays.
+ $object{Zone}->add({
+ row => 1,
+ column => 1,
+ });
+
+ # NetAffx database used for ProbeSet ids
+ $object{Database} = ArrayExpress::ArrayMAGE::Database->new();
+ $object{Database}->add({ tag => 'netaffx' });
+
+ return ( \%object, $reporter_group, $composite_group, $reporter_group_id,
+ $composite_group_id );
+}
+
+sub parse_unit {
+
+ my ( $lines, $object, $reporter_group, $composite_group, $cdf ) = @_;
+
+ # Get the probeset name - useful for SNP arrays
+ my ($unit_probeset) = ( $lines->[1] =~ m/\A Name= ([\w\#\/\-\+\.\,]+) \z/xms );
+ my ($unit_type) = ( $lines->[6] =~ m/\A UnitType= (\d+) \z/xms );
+ my ($number_blocks) = ( $lines->[7] =~ m/\A NumberBlocks= (\d+) \z/xms );
+
+ # Add all the probeset reporters.
+ # $previous_atom only scoped at this level for SNP chips, as those
+ # CDFs often have more than one block per unit.
+ my ( %reporter, %mismatch, $previous_atom );
+ my $compseq = {};
+ foreach my $blocknum ( 1 .. $number_blocks ) {
+ my $chunk = <$cdf>;
+ $chunk =~ s/^($CDF_LINEBREAK)*//;
+ my @lines = split /$CDF_LINEBREAK/, $chunk;
+
+ # $compseq should be a hash of probeset ids => [arrayref of
+ # reporter ids]
+ if ( $unit_type == 3 ) {
+
+ # Gene Expression chip
+ $compseq
+ = parse_ge_block( \@lines, \%reporter, \%mismatch, $object );
+ }
+ elsif ( $unit_type == 2 ) {
+
+ # SNP chip
+ $previous_atom
+ = parse_snp_block( \@lines, \%reporter, \%mismatch, $object,
+ $unit_probeset, $previous_atom );
+ $compseq->{$unit_probeset} = [ keys %reporter ];
+ }
+ elsif ( $unit_type == 7 ) {
+
+ # TAG/GeneFlex chip
+ $compseq
+ = parse_tag_block( \@lines, \%reporter, \%mismatch, $object );
+ }
+ elsif ( $unit_type == 0 ) {
+
+ # Control blocks as found in TAG chip CDFs
+ $compseq = parse_control_block( \@lines, \%reporter, \%mismatch,
+ $object );
+ }
+ elsif ( $unit_type == 1 ) {
+ die("UnitType 1 (resequencing) not yet supported.");
+ }
+ else {
+ die("Unrecognised unit type $unit_type ($unit_probeset).");
+ }
+ }
+
+ # Post-process all the reporters in the compseq (probeset).
+ foreach my $id ( sort keys %reporter ) {
+ $object->{Reporter}->add({
+ identifier => $id,
+ name => $reporter{$id}{name},
+ biosequences => $reporter{$id}{biosequences},
+ });
+ $object->{FeatureReporterMap}->add({
+ identifier => $id,
+ features => $reporter{$id}{features},
+ mismatch_info => \%mismatch,
+ });
+ $reporter_group->add({ id_ref => $id, });
+ }
+
+ foreach my $id ( sort keys %$compseq ) {
+
+ # Add the compseq (probeset) info.
+ $object->{BioSequence}->add({
+ identifier => $id,
+ polymertype => 'nucleic_acid',
+ type => 'located_sequence_feature',
+ dbrefs => { $id => 'netaffx' },
+ });
+ $object->{CompositeSequence}->add({
+ identifier => $id,
+ name => $id,
+ biosequences => [$id],
+ cs_only => $CS_ONLY,
+ });
+ $object->{ReporterCompositeMap}->add({
+ identifier => $id,
+ reporters => [ sort @{ $compseq->{$id} } ],
+ });
+ $composite_group->add({ id_ref => $id });
+ }
+
+ return;
+}
+
+sub parse_control_block {
+
+ my ( $lines, $reporter, $mismatch, $object ) = @_;
+
+ # TAG chip control units appear to also allow multiple probesets
+ # per block. Here we detect them by sequence.
+ my %probeset_cache;
+
+ # Track the sequences in the block.
+ my @sequences;
+
+ PROBE_LINE:
+ foreach my $line (@$lines) {
+
+ my ( $row, $col, $feature_id )
+ = add_feature( $line, $object->{Feature} );
+
+ next PROBE_LINE unless ( $row && $col );
+
+ my ($x, $y, $probe, $feat,
+ $probeset, $expos, $pos, $cbase,
+ $pbase, $tbase, $atom, $index,
+ $codonind, $codon, $regiontype, $region
+ )
+ = split /\s+/, $line;
+
+ # $reporter_id is empty string to prevent uninit warnings below.
+ my $reporter_id = q{};
+ my $biosequence_id;
+
+ # Sequence-based probe pair detection.
+ if ( length($probe) > 1 ) {
+
+ my $probe_substr = $probe;
+ substr( $probe_substr, $pos - 1, 1, q{} );
+
+ SEQUENCE:
+ foreach my $index ( 0 .. $#sequences ) {
+ my $prev_substr = $sequences[$index];
+ my $pm_base = substr( $prev_substr, $pos - 1, 1, q{} );
+
+ # Where the context sequence and tbase match the
+ # PM probe, add the probe to that reporter.
+ if ( ( $probe_substr eq $prev_substr )
+ && ( $tbase eq $pm_base ) ) {
+ $reporter_id = "$probeset:ProbePair$index";
+ $biosequence_id = "$probeset:$index";
+ last SEQUENCE;
+ }
+ }
+
+ unless ( ( $reporter_id && $biosequence_id )
+ || $CS_ONLY ) {
+
+ # In case we see the MM probe first, rebuild the
+ # PM probe sequence.
+ my $pm_probe = $probe;
+ substr( $pm_probe, $pos - 1, 1, $tbase );
+ push( @sequences, $pm_probe );
+ $reporter_id = "$probeset:ProbePair" . $#sequences;
+ $biosequence_id = "$probeset:" . $#sequences;
+
+ $object->{BioSequence}->add({
+ identifier => $biosequence_id,
+ polymertype => 'DNA',
+ type => 'ss_oligo',
+ sequence => $pm_probe,
+ dbrefs => { $probeset => 'netaffx' },
+ });
+ push(
+ @{ $reporter->{$reporter_id}{biosequences} },
+ $biosequence_id
+ );
+ }
+ }
+ else {
+ die("Error: UnitType 0 parsing only supported for units with sequence."
+ );
+ }
+
+ # All probes need a feature and a reporter
+ push( @{ $reporter->{$reporter_id}{features} }, $feature_id );
+ $reporter->{$reporter_id}{name} = $reporter_id;
+
+ # Cache the probeset -> probepair mapping for later.
+ $probeset_cache{$probeset}{$reporter_id}++;
+
+ unless ( $pbase eq $tbase ) {
+
+ # MM probe, just record the mismatch info.
+ $mismatch->{$feature_id} = {
+ start_coord => $pos,
+ new_sequence => lc($pbase),
+ replaced_length => 1,
+ };
+ }
+ }
+ my %compseq;
+ while ( my ( $id, $reporter_id_hash ) = each %probeset_cache ) {
+ $compseq{$id} = [ keys %$reporter_id_hash ];
+ }
+ return \%compseq;
+}
+
+sub parse_tag_block {
+
+ my ( $lines, $reporter, $mismatch, $object ) = @_;
+
+ # Track the probe pairs with this variable.
+ my $previous_probeset = q{};
+ my %probeset_cache;
+
+ PROBE_LINE:
+ foreach my $line (@$lines) {
+
+ my ( $row, $col, $feature_id )
+ = add_feature( $line, $object->{Feature} );
+
+ next PROBE_LINE unless ( $row && $col );
+
+ my ($x, $y, $probe, $feat,
+ $probeset, $expos, $pos, $cbase,
+ $pbase, $tbase, $atom, $index,
+ $codonind, $codon, $regiontype, $region
+ )
+ = split /\s+/, $line;
+
+ my $reporter_id = "$probeset:ProbePair$atom";
+ my $biosequence_id = "$probeset:$atom";
+ $probeset_cache{$probeset}{$reporter_id}++;
+
+ # All probes need a feature and a reporter
+ push( @{ $reporter->{$reporter_id}{features} }, $feature_id );
+ $reporter->{$reporter_id}{name} = $reporter_id;
+
+ # TAG probesets can vary within blocks; don't add BS if probe
+ # seq not known.
+ unless ( $probeset eq $previous_probeset
+ || length($probe) <= 1
+ || $CS_ONLY ) {
+
+ # We rebuild the PM probe here, in case the MM probe
+ # comes first.
+ my $pm_probe = $probe;
+ substr( $pm_probe, $pos - 1, 1, $tbase ) if $pm_probe;
+
+ $object->{BioSequence}->add({
+ identifier => $biosequence_id,
+ polymertype => 'DNA',
+ type => 'ss_oligo',
+ sequence => $pm_probe,
+ dbrefs => { $probeset => 'netaffx' },
+ });
+ push(
+ @{ $reporter->{$reporter_id}{biosequences} },
+ $biosequence_id
+ );
+ }
+
+ # Sort out match and mismatch.
+ unless ( $pbase eq $tbase ) {
+
+ # MM probe, just record the mismatch info.
+ $mismatch->{$feature_id} = {
+ start_coord => $pos,
+ new_sequence => lc($pbase),
+ replaced_length => 1,
+ };
+ }
+
+ $previous_probeset = $probeset;
+ }
+
+ my %compseq;
+ while ( my ( $id, $reporter_id_hash ) = each %probeset_cache ) {
+ $compseq{$id} = [ keys %$reporter_id_hash ];
+ }
+ return \%compseq;
+}
+
+sub parse_snp_block {
+
+ my ( $lines, $reporter, $mismatch, $object, $probeset, $previous_atom )
+ = @_;
+
+ foreach my $line (@$lines) {
+ $previous_atom
+ = parse_probeline( $line, $probeset, $reporter, $mismatch,
+ $object, $previous_atom );
+ }
+ return $previous_atom;
+}
+
+sub parse_ge_block {
+
+ my ( $lines, $reporter, $mismatch, $object ) = @_;
+
+ # Probeset name stored in the block, not the unit, for GE units.
+ my $probeset;
+ if ( $lines->[0] =~ m/^\[Unit\d+_Block\d+\]$/ ) {
+
+ # Get the block name if the unit name was not set.
+ ($probeset) = ( $lines->[1] =~ m/\A Name= ([\w\#\/\-\+\.\,]+) \z/xms );
+ }
+ unless ($probeset) {
+ die(qq{Error: Unparseable block starts with line $lines->[0].});
+ }
+
+ # Track the probe pairs with this variable. Must start as undef.
+ my $previous_atom;
+
+ foreach my $line (@$lines) {
+ $previous_atom
+ = parse_probeline( $line, $probeset, $reporter, $mismatch,
+ $object, $previous_atom );
+ }
+ return { $probeset => [ keys %$reporter ] };
+}
+
+sub parse_probeline {
+
+ my ( $line, $probeset, $reporter, $mismatch, $object, $previous_atom )
+ = @_;
+
+ my ( $row, $col, $feature_id ) = add_feature( $line, $object->{Feature} );
+
+ return $previous_atom unless ( $row && $col );
+
+ my ($x, $y, $probe, $feat, $qual, $expos,
+ $pos, $cbase, $pbase, $tbase, $atom, $index,
+ $codonind, $codon, $regiontype, $region
+ )
+ = split /\s+/, $line;
+
+ my $reporter_id = "$probeset:ProbePair$atom";
+ my $biosequence_id;
+
+ # All probes need a feature and a reporter
+ push( @{ $reporter->{$reporter_id}{features} }, $feature_id );
+ $reporter->{$reporter_id}{name} = $reporter_id;
+
+ # Create the BioSequences where sequence known.
+ if ( length($probe) > 1 && ! $CS_ONLY ) {
+
+ $biosequence_id = "$probeset:$atom";
+
+ # Sequence given, first probe in the pair.
+ unless ( defined($previous_atom) && ( $atom eq $previous_atom ) ) {
+
+ # We rebuild the PM probe here, in case the MM probe
+ # comes first.
+ my $pm_probe = $probe;
+ substr( $pm_probe, $pos - 1, 1, $tbase ) if $pm_probe;
+
+ $object->{BioSequence}->add({
+ identifier => $biosequence_id,
+ polymertype => 'DNA',
+ type => 'ss_oligo',
+ sequence => $pm_probe,
+ dbrefs => { $probeset => 'netaffx' },
+ });
+
+ # Reference the BS once for each atom.
+ push( @{ $reporter->{$reporter_id}{biosequences} }, $biosequence_id );
+ }
+ }
+
+ # Sort out match and mismatch.
+ unless ( $pbase eq $tbase ) {
+
+ # MM probe, just record the mismatch info.
+ $mismatch->{$feature_id} = {
+ start_coord => $pos,
+ new_sequence => lc($pbase),
+ replaced_length => 1,
+ };
+ }
+
+ return $atom;
+}
+
+sub add_feature {
+ my ( $line, $mageobj ) = @_;
+
+ if ( my ( $row, $col ) = ( $line =~ m/Cell\d+=(\d+)\s+(\d+)/ ) ) {
+ my $feature_id = "Probe($row,$col)";
+ $mageobj->add({
+ identifier => $feature_id,
+ metacolumn => 1,
+ metarow => 1,
+ column => $col,
+ row => $row,
+ });
+ return ( $row, $col, $feature_id );
+ }
+ return;
+}
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args();
+
+# Initialise some stuff
+my $starttime = new Benchmark;
+
+# Get the line endings.
+warn("Checking CDF linebreaks...\n");
+my $linebreak_counts;
+($linebreak_counts, $CDF_LINEBREAK) = check_linebreaks( $args->{cdf} );
+
+unless ($CDF_LINEBREAK) {
+ use Data::Dumper;
+ print Dumper $linebreak_counts;
+ die("Error: inconsistent line breaks in CDF.\n");
+}
+
+# Open the CDF and scan down to the header row
+open( my $cdf, "<", $args->{cdf} )
+ or die("Error: unable to open CDF file $args->{cdf}: $!\n");
+
+my ( $object, $reporter_group, $composite_group, $design_id );
+my $reporter_group_id = 'All';
+my $composite_group_id = 'All';
+
+{
+
+ # Paragraph mode.
+ local $INPUT_RECORD_SEPARATOR = $CDF_LINEBREAK . $CDF_LINEBREAK;
+
+ my ( $total_units, $progress_bar );
+
+ # Parse the header.
+ HEADER_LINE:
+ while ( my $chunk = <$cdf> ) {
+
+ # Strip leading empty lines
+ $chunk =~ s/^($CDF_LINEBREAK)*//;
+ my @lines = split /$CDF_LINEBREAK/, $chunk;
+
+ next unless @lines;
+
+ if ( $lines[0] eq q{[Chip]} ) {
+ ($design_id) = ( $lines[1] =~ /^Name=(.*)$/ );
+ ($total_units) = ( $lines[4] =~ /^NumberOfUnits=(\d+)$/ );
+ die("Error: Unable to parse number of units from [Chip] section")
+ unless defined($total_units);
+ ArrayExpress::ArrayMAGE->set_design( $design_id );
+ $progress_bar = ArrayExpress::ArrayMAGE::ProgressBar->new(
+ width => 40,
+ max_val => $total_units
+ );
+ last HEADER_LINE;
+ }
+ }
+
+ # Quick sanity check.
+ unless ( defined($total_units) ) {
+ die("Error parsing [Chip] section.\n");
+ }
+
+ # Initialize the MAGE objects (needs $design_id to be set)
+ if ( $CS_ONLY ) {
+
+ # More lightweight CS-only MAGE-ML to be generated.
+ ( $object, $reporter_group, $composite_group )
+ = cs_initialize(
+ $args->{accession},
+ $reporter_group_id,
+ $composite_group_id,
+ $design_id,
+ );
+
+ }
+ else {
+
+ # Business as usual (full MAGE-ML generation).
+ ( $object, $reporter_group, $composite_group )
+ = initialize(
+ $args->{accession},
+ $reporter_group_id,
+ $composite_group_id,
+ $design_id,
+ );
+ }
+
+ # Parse the rest of the file.
+ my $unit_num;
+ while ( my $chunk = <$cdf> ) {
+
+ # Strip leading empty lines
+ $chunk =~ s/^($CDF_LINEBREAK)*//;
+ my @lines = split /$CDF_LINEBREAK/, $chunk;
+
+ next unless @lines;
+
+ if ( $lines[0] =~ m/^\[QC\d+\]$/ ) {
+
+ # QC cell
+ foreach my $line (@lines) {
+ add_feature( $line, $object->{Feature} );
+ }
+ }
+ elsif ( $lines[0] =~ m/^\[Unit\d+\]$/ ) {
+
+ $progress_bar->draw( $unit_num++ ) if $progress_bar;
+
+ parse_unit( \@lines, $object, $reporter_group, $composite_group,
+ $cdf );
+ }
+ }
+}
+
+close $cdf;
+
+my $rg_hashref = $CS_ONLY
+ ? {}
+ : { $reporter_group_id => $reporter_group };
+ArrayExpress::ArrayMAGE->combine_parts(
+ $object,
+ $rg_hashref,
+ { $composite_group_id => $composite_group },
+ $args->{output},
+);
+
+print STDOUT ("Done.\n");
+
+my $endtime = new Benchmark;
+print STDOUT (
+ "\nTotal run time = ",
+ timestr( timediff( $endtime, $starttime ) ), "\n\n"
+);
diff --git a/automation/admin/CHANGELOG b/automation/admin/CHANGELOG
new file mode 100644
index 0000000..cc0cabe
--- /dev/null
+++ b/automation/admin/CHANGELOG
@@ -0,0 +1,604 @@
+*0.13.1* (11 July, 2005)
+
+* Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson]
+
+* Added support for SQL Server in the database rake tasks #1652 [ken.barker at gmail.com] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
+
+* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
+
+* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay at gmail.com]
+
+* Improved documentation for environment config files #1625 [court3nay at gmail.com]
+
+
+*0.13.0* (6 July, 2005)
+
+* Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged)
+
+* Added migration generator: ./script/generate migration add_system_settings
+
+* Added "migrate" as rake task to execute all the pending migrations from db/migrate
+
+* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina]
+
+* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 [Michael Koziarski]
+
+* SIGTERM also gracefully exits dispatch.fcgi. Ignore SIGUSR1 on Windows.
+
+* Add the option to manually manage garbage collection in the FastCGI dispatcher. Set the number of requests between GC runs in your public/dispatch.fcgi [skaes at web.de]
+
+* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them.
+
+* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush)
+
+* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [michael at schubert.cx]
+
+* ActionMailer::Base subclasses are reloaded with the other rails components #1262
+
+* Made the WEBrick adapter not use a mutex around action performance if ActionController::Base.allow_concurrency is true (default is false)
+
+* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 [Scott Barron]
+
+* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 [Michael Koziarski]
+
+* Fixed the test_helper.rb to be safe for requiring controllers from multiple spots, like app/controllers/article_controller.rb and app/controllers/admin/article_controller.rb, without reloading the environment twice #1390 [Nicholas Seckar]
+
+* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 [Nicholas Seckar]
+
+* Added -e/--environment option to script/runner #1408 [fbeausoleil at ftml.net]
+
+* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id]. #1367
+
+* Added graceful exit from pressing CTRL-C during the run of the rails command #1150 [Caleb Tennis]
+
+* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. [Jamis Buck]
+
+* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. [Jamis Buck]
+
+* Added console --profile for profiling an IRB session #1154 [Jeremy Kemper]
+
+* Changed console_sandbox into console --sandbox #1154 [Jeremy Kemper]
+
+
+*0.12.1* (20th April, 2005)
+
+* Upgraded to Active Record 1.10.1, Action Pack 1.8.1, Action Mailer 0.9.1, Action Web Service 0.7.1
+
+
+*0.12.0* (19th April, 2005)
+
+* Fixed that purge_test_database would use database settings from the development environment when recreating the test database #1122 [rails at cogentdude.com]
+
+* Added script/benchmarker to easily benchmark one or more statement a number of times from within the environment. Examples:
+
+ # runs the one statement 10 times
+ script/benchmarker 10 'Person.expensive_method(10)'
+
+ # pits the two statements against each other with 50 runs each
+ script/benchmarker 50 'Person.expensive_method(10)' 'Person.cheap_method(10)'
+
+* Added script/profiler to easily profile a single statement from within the environment. Examples:
+
+ script/profiler 'Person.expensive_method(10)'
+ script/profiler 'Person.expensive_method(10)' 10 # runs the statement 10 times
+
+* Added Rake target clear_logs that'll truncate all the *.log files in log/ to zero #1079 [Lucas Carlson]
+
+* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 [k at v2studio.com]
+
+* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson at gmail.com]
+
+* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4
+
+
+*0.11.1* (27th March, 2005)
+
+* Fixed the dispatch.fcgi use of a logger
+
+* Upgraded to Active Record 1.9.1, Action Pack 1.7.0, Action Mailer 0.8.1, Action Web Service 0.6.2, Active Support 1.0.3
+
+
+*0.11.0* (22th March, 2005)
+
+* Removed SCRIPT_NAME from the WEBrick environment to prevent conflicts with PATH_INFO #896 [Nicholas Seckar]
+
+* Removed ?$1 from the dispatch.f/cgi redirect line to get rid of 'complete/path/from/request.html' => nil being in the @params now that the ENV["REQUEST_URI"] is used to determine the path #895 [dblack/Nicholas Seckar]
+
+* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process
+
+* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Luetke]
+
+* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 [Nicholas Seckar]
+
+* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Luetke]
+
+* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples:
+
+ ./script/runner 'ReminderService.deliver'
+ ./script/runner 'Mailer.receive(STDIN.read)'
+
+ This makes it easier to do CRON and postfix scripts without actually making a script just to trigger 1 line of code.
+
+* Fixed webrick_server cookie handling to allow multiple cookes to be set at once #800, #813 [dave at cherryville.org]
+
+* Fixed the Rakefile's interaction with postgresql to:
+
+ 1. Use PGPASSWORD and PGHOST in the environment to fix prompting for
+ passwords when connecting to a remote db and local socket connections.
+ 2. Add a '-x' flag to pg_dump which stops it dumping privileges #807 [rasputnik]
+ 3. Quote the user name and use template0 when dumping so the functions doesn't get dumped too #855 [pburleson]
+ 4. Use the port if available #875 [madrobby]
+
+* Upgraded to Active Record 1.9.0, Action Pack 1.6.0, Action Mailer 0.8.0, Action Web Service 0.6.1, Active Support 1.0.2
+
+
+*0.10.1* (7th March, 2005)
+
+* Fixed rake stats to ignore editor backup files like model.rb~ #791 [skanthak]
+
+* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Luetke]
+
+* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence)
+
+* Fixed that scaffold generator new template should use local variable instead of instance variable #778 [Dan Peterson]
+
+* Allow unit tests to run on a remote server for PostgreSQL #781 [adamm at galacticasoftware.com]
+
+* Added web_service generator (run ./script/generate web_service for help) #776 [Leon Bredt]
+
+* Added app/apis and components to code statistics report #729 [Scott Barron]
+
+* Fixed WEBrick server to use ABSOLUTE_RAILS_ROOT instead of working_directory #687 [Nicholas Seckar]
+
+* Fixed rails_generator to be usable without RubyGems #686 [Cristi BALAN]
+
+* Fixed -h/--help for generate and destroy generators #331
+
+* Added begin/rescue around the FCGI dispatcher so no uncaught exceptions can bubble up to kill the process (logs to log/fastcgi.crash.log)
+
+* Fixed that association#count would produce invalid sql when called sequentialy #659 [kanis at comcard.de]
+
+* Fixed test/mocks/testing to the correct test/mocks/test #740
+
+* Added early failure if the Ruby version isn't 1.8.2 or above #735
+
+* Removed the obsolete -i/--index option from the WEBrick servlet #743
+
+* Upgraded to Active Record 1.8.0, Action Pack 1.5.1, Action Mailer 0.7.1, Action Web Service 0.6.0, Active Support 1.0.1
+
+
+*0.10.0* (24th February, 2005)
+
+* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel]
+
+* Fixed that script/server -d was broken so daemon mode couldn't be used #687 [Nicholas Seckar]
+
+* Upgraded to breakpoint 92 which fixes:
+
+ * overload IRB.parse_opts(), fixes #443
+ => breakpoints in tests work even when running them via rake
+ * untaint handlers, might fix an issue discussed on the Rails ML
+ * added verbose mode to breakpoint_client
+ * less noise caused by breakpoint_client by default
+ * ignored TerminateLineInput exception in signal handler
+ => quiet exit on Ctrl-C
+
+* Added support for independent components residing in /components. Example:
+
+ Controller: components/list/items_controller.rb
+ (holds a List::ItemsController class with uses_component_template_root called)
+
+ Model : components/list/item.rb
+ (namespace is still shared, so an Item model in app/models will take precedence)
+
+ Views : components/list/items/show.rhtml
+
+
+* Added --sandbox option to script/console that'll roll back all changes made to the database when you quit #672 [Jeremy Kemper]
+
+* Added 'recent' as a rake target that'll run tests for files that changed in the last 10 minutes #612 [Jeremy Kemper]
+
+* Changed script/console to default to development environment and drop --no-inspect #650 [Jeremy Kemper]
+
+* Added that the 'fixture :posts' syntax can be used for has_and_belongs_to_many fixtures where a model doesn't exist #572 [Jeremy Kemper]
+
+* Added that running test_units and test_functional now performs the clone_structure_to_test as well #566 [rasputnik]
+
+* Added new generator framework that informs about its doings on generation and enables updating and destruction of generated artifacts. See the new script/destroy and script/update for more details #487 [Jeremy Kemper]
+
+* Added Action Web Service as a new add-on framework for Action Pack [Leon Bredt]
+
+* Added Active Support as an independent utility and standard library extension bundle
+
+* Upgraded to Active Record 1.7.0, Action Pack 1.5.0, Action Mailer 0.7.0
+
+
+*0.9.5* (January 25th, 2005)
+
+* Fixed dependency reloading by switching to a remove_const approach where all Active Records, Active Record Observers, and Action Controllers are reloading by undefining their classes. This enables you to remove methods in all three types and see the change reflected immediately and it fixes #539. This also means that only those three types of classes will benefit from the const_missing and reloading approach. If you want other classes (like some in lib/) to reload, you must use require [...]
+
+* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross]
+
+* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 [rasputnik]
+
+* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 [brandt at kurowski.net]
+
+* Fixed handling of syntax errors in models that had already been succesfully required once in the current interpreter
+
+* Fixed that models that weren't referenced in associations weren't being reloaded in the development mode by reinstating the reload
+
+* Fixed that generate scaffold would produce bad functional tests
+
+* Fixed that FCGI can also display SyntaxErrors
+
+* Upgraded to Active Record 1.6.0, Action Pack 1.4.0
+
+
+*0.9.4.1* (January 18th, 2005)
+
+* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina]
+
+* Fixed binding of caller #496 [Alexey]
+
+* Upgraded to Active Record 1.5.1, Action Pack 1.3.1, Action Mailer 0.6.1
+
+
+*0.9.4* (January 17th, 2005)
+
+* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Luetke]
+
+* Flipped code-to-test ratio around to be more readable #468 [Scott Baron]
+
+* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 [Lucas Carlson]
+
+* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Luetke]
+
+* Added rewrite rules to deal with caching to public/.htaccess
+
+* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model.
+
+* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from
+ RAILS_ENV instead of just forcing development #424 [Tobias Luetke]
+
+* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [octopod]
+
+* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby
+
+* Removed Fancy Indexing as a default option on the WEBrick servlet as it made it harder to use various caching schemes
+
+* Upgraded to Active Record 1.5, Action Pack 1.3, Action Mailer 0.6
+
+
+*0.9.3* (January 4th, 2005)
+
+* Added support for SQLite in the auto-dumping/importing of schemas for development -> test #416
+
+* Added automated rewriting of the shebang lines on installs through the gem rails command #379 [Manfred Stienstra]
+
+* Added ActionMailer::Base.deliver_method = :test to the test environment so that mail objects are available in ActionMailer::Base.deliveries
+ for functional testing.
+
+* Added protection for creating a model through the generators with a name of an existing class, like Thread or Date.
+ It'll even offer you a synonym using wordnet.princeton.edu as a look-up. No, I'm not kidding :) [Florian Gross]
+
+* Fixed dependency management to happen in a unified fashion for Active Record and Action Pack using the new Dependencies module. This means that
+ the environment options needs to change from:
+
+ Before in development.rb:
+ ActionController::Base.reload_dependencies = true
+ ActiveRecord::Base.reload_associations = true
+
+ Now in development.rb:
+ Dependencies.mechanism = :load
+
+ Before in production.rb and test.rb:
+ ActionController::Base.reload_dependencies = false
+ ActiveRecord::Base.reload_associations = false
+
+ Now in production.rb and test.rb:
+ Dependencies.mechanism = :require
+
+* Fixed problems with dependency caching and controller hierarchies on Ruby 1.8.2 in development mode #351
+
+* Fixed that generated action_mailers doesnt need to require the action_mailer since thats already done in the environment #382 [Lucas Carlson]
+
+* Upgraded to Action Pack 1.2.0 and Active Record 1.4.0
+
+
+*0.9.2*
+
+* Fixed CTRL-C exists from the Breakpointer to be a clean affair without error dumping [Kent Sibilev]
+
+* Fixed "rake stats" to work with sub-directories in models and controllers and to report the code to test ration [Scott Baron]
+
+* Added that Active Record associations are now reloaded instead of cleared to work with the new const_missing hook in Active Record.
+
+* Added graceful handling of an inaccessible log file by redirecting output to STDERR with a warning #330 [rainmkr]
+
+* Added support for a -h/--help parameter in the generator #331 [Ulysses]
+
+* Fixed that File.expand_path in config/environment.rb would fail when dealing with symlinked public directories [mjobin]
+
+* Upgraded to Action Pack 1.1.0 and Active Record 1.3.0
+
+
+*0.9.1*
+
+* Upgraded to Action Pack 1.0.1 for important bug fix
+
+* Updated gem dependencies
+
+
+*0.9.0*
+
+* Renamed public/dispatch.servlet to script/server -- it wasn't really dispatching anyway as its delegating calls to public/dispatch.rb
+
+* Renamed AbstractApplicationController and abstract_application.rb to ApplicationController and application.rb, so that it will be possible
+ for the framework to automatically pick up on app/views/layouts/application.rhtml and app/helpers/application.rb
+
+* Added script/console that makes it even easier to start an IRB session for interacting with the domain model. Run with no-args to
+ see help.
+
+* Added breakpoint support through the script/breakpointer client. This means that you can break out of execution at any point in
+ the code, investigate and change the model, AND then resume execution! Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find_all
+ breakpoint "Breaking out from the list"
+ end
+ end
+
+ So the controller will accept the action, run the first line, then present you with a IRB prompt in the breakpointer window.
+ Here you can do things like:
+
+ Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
+
+ >> @posts.inspect
+ => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
+ #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+ >> @posts.first.title = "hello from a breakpoint"
+ => "hello from a breakpoint"
+
+ ...and even better is that you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+ Finally, when you're ready to resume execution, you press CTRL-D
+
+* Changed environments to be configurable through an environment variable. By default, the environment is "development", but you
+ can change that and set your own by configuring the Apache vhost with a string like (mod_env must be available on the server):
+
+ SetEnv RAILS_ENV production
+
+ ...if you're using WEBrick, you can pick the environment to use with the command-line parameters -e/--environment, like this:
+
+ ruby public/dispatcher.servlet -e production
+
+* Added a new default environment called "development", which leaves the production environment to be tuned exclusively for that.
+
+* Added a start_server in the root of the Rails application to make it even easier to get started
+
+* Fixed public/.htaccess to use RewriteBase and share the same rewrite rules for all the dispatch methods
+
+* Fixed webrick_server to handle requests in a serialized manner (the Rails reloading infrastructure is not thread-safe)
+
+* Added support for controllers in directories. So you can have:
+
+ app/controllers/account_controller.rb # URL: /account/
+ app/controllers/admin/account_controller.rb # URL: /admin/account/
+
+ NOTE: You need to update your public/.htaccess with the new rules to pick it up
+
+* Added reloading for associations and dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use
+ those environments for development. This is turned on by default, but can be turned off with
+ ActiveRecord::Base.reload_associations = false and ActionController::Base.reload_dependencies = false in production environments.
+
+* Added support for sub-directories in app/models. So now you can have something like Basecamp with:
+
+ app/models/accounting
+ app/models/project
+ app/models/participants
+ app/models/settings
+
+ It's poor man's namespacing, but only for file-system organization. You still require files just like before.
+ Nothing changes inside the files themselves.
+
+
+* Fixed a few references in the tests generated by new_mailer [Jeremy Kemper]
+
+* Added support for mocks in testing with test/mocks
+
+* Cleaned up the environments a bit and added global constant RAILS_ROOT
+
+
+*0.8.5* (9)
+
+* Made dev-util available to all tests, so you can insert breakpoints in any test case to get an IRB prompt at that point [Jeremy Kemper]:
+
+ def test_complex_stuff
+ @david.projects << @new_project
+ breakpoint "Let's have a closer look at @david"
+ end
+
+ You need to install dev-utils yourself for this to work ("gem install dev-util").
+
+* Added shared generator behavior so future upgrades should be possible without manually copying over files [Jeremy Kemper]
+
+* Added the new helper style to both controller and helper templates [Jeremy Kemper]
+
+* Added new_crud generator for creating a model and controller at the same time with explicit scaffolding [Jeremy Kemper]
+
+* Added configuration of Test::Unit::TestCase.fixture_path to test_helper to concide with the new AR fixtures style
+
+* Fixed that new_model was generating singular table/fixture names
+
+* Upgraded to Action Mailer 0.4.0
+
+* Upgraded to Action Pack 0.9.5
+
+* Upgraded to Active Record 1.1.0
+
+
+*0.8.0 (15)*
+
+* Removed custom_table_name option for new_model now that the Inflector is as powerful as it is
+
+* Changed the default rake action to just do testing and separate API generation and coding statistics into a "doc" task.
+
+* Fixed WEBrick dispatcher to handle missing slashes in the URLs gracefully [alexey]
+
+* Added user option for all postgresql tool calls in the rakefile [elvstone]
+
+* Fixed problem with running "ruby public/dispatch.servlet" instead of "cd public; ruby dispatch.servlet" [alexey]
+
+* Fixed WEBrick server so that it no longer hardcodes the ruby interpreter used to "ruby" but will get the one used based
+ on the Ruby runtime configuration. [Marcel Molina Jr.]
+
+* Fixed Dispatcher so it'll route requests to magic_beans to MagicBeansController/magic_beans_controller.rb [Caio Chassot]
+
+* "new_controller MagicBeans" and "new_model SubscriptionPayments" will now both behave properly as they use the new Inflector.
+
+* Fixed problem with MySQL foreign key constraint checks in Rake :clone_production_structure_to_test target [Andreas Schwarz]
+
+* Changed WEBrick server to by default be auto-reloading, which is slower but makes source changes instant.
+ Class compilation cache can be turned on with "-c" or "--cache-classes".
+
+* Added "-b/--binding" option to WEBrick dispatcher to bind the server to a specific IP address (default: 127.0.0.1) [Kevin Temp]
+
+* dispatch.fcgi now DOESN'T set FCGI_PURE_RUBY as it was slowing things down for now reason [Andreas Schwarz]
+
+* Added new_mailer generator to work with Action Mailer
+
+* Included new framework: Action Mailer 0.3
+
+* Upgraded to Action Pack 0.9.0
+
+* Upgraded to Active Record 1.0.0
+
+
+*0.7.0*
+
+* Added an optional second argument to the new_model script that allows the programmer to specify the table name,
+ which will used to generate a custom table_name method in the model and will also be used in the creation of fixtures.
+ [Kevin Radloff]
+
+* script/new_model now turns AccountHolder into account_holder instead of accountholder [Kevin Radloff]
+
+* Fixed the faulty handleing of static files with WEBrick [Andreas Schwarz]
+
+* Unified function_test_helper and unit_test_helper into test_helper
+
+* Fixed bug with the automated production => test database dropping on PostgreSQL [dhawkins]
+
+* create_fixtures in both the functional and unit test helper now turns off the log during fixture generation
+ and can generate more than one fixture at a time. Which makes it possible for assignments like:
+
+ @people, @projects, @project_access, @companies, @accounts =
+ create_fixtures "people", "projects", "project_access", "companies", "accounts"
+
+* Upgraded to Action Pack 0.8.5 (locally-scoped variables, partials, advanced send_file)
+
+* Upgraded to Active Record 0.9.5 (better table_name guessing, cloning, find_all_in_collection)
+
+
+*0.6.5*
+
+* No longer specifies a template for rdoc, so it'll use whatever is default (you can change it in the rakefile)
+
+* The new_model generator will now use the same rules for plural wordings as Active Record
+ (so Category will give categories, not categorys) [Kevin Radloff]
+
+* dispatch.fcgi now sets FCGI_PURE_RUBY to true to ensure that it's the Ruby version that's loaded [danp]
+
+* Made the GEM work with Windows
+
+* Fixed bug where mod_ruby would "forget" the load paths added when switching between controllers
+
+* PostgreSQL are now supported for the automated production => test database dropping [Kevin Radloff]
+
+* Errors thrown by the dispatcher are now properly handled in FCGI.
+
+* Upgraded to Action Pack 0.8.0 (lots and lots and lots of fixes)
+
+* Upgraded to Active Record 0.9.4 (a bunch of fixes)
+
+
+*0.6.0*
+
+* Added AbstractionApplicationController as a superclass for all controllers generated. This class can be used
+ to carry filters and methods that are to be shared by all. It has an accompanying ApplicationHelper that all
+ controllers will also automatically have available.
+
+* Added environments that can be included from any script to get the full Active Record and Action Controller
+ context running. This can be used by maintenance scripts or to interact with the model through IRB. Example:
+
+ require 'config/environments/production'
+
+ for account in Account.find_all
+ account.recalculate_interests
+ end
+
+ A short migration script for an account model that had it's interest calculation strategy changed.
+
+* Accessing the index of a controller with "/weblog" will now redirect to "/weblog/" (only on Apache, not WEBrick)
+
+* Simplified the default Apache config so even remote requests are served off CGI as a default.
+ You'll now have to do something specific to activate mod_ruby and FCGI (like using the force urls).
+ This should make it easier for new comers that start on an external server.
+
+* Added more of the necessary Apache options to .htaccess to make it easier to setup
+
+* Upgraded to Action Pack 0.7.9 (lots of fixes)
+
+* Upgraded to Active Record 0.9.3 (lots of fixes)
+
+
+*0.5.7*
+
+* Fixed bug in the WEBrick dispatcher that prevented it from getting parameters from the URL
+ (through GET requests or otherwise)
+
+* Added lib in root as a place to store app specific libraries
+
+* Added lib and vendor to load_path, so anything store within can be loaded directly.
+ Hence lib/redcloth.rb can be loaded with require "redcloth"
+
+* Upgraded to Action Pack 0.7.8 (lots of fixes)
+
+* Upgraded to Active Record 0.9.2 (minor upgrade)
+
+
+*0.5.6*
+
+* Upgraded to Action Pack 0.7.7 (multipart form fix)
+
+* Updated the generated template stubs to valid XHTML files
+
+* Ensure that controllers generated are capitalized, so "new_controller TodoLists"
+ gives the same as "new_controller Todolists" and "new_controller todolists".
+
+
+*0.5.5*
+
+* Works on Windows out of the box! (Dropped symlinks)
+
+* Added webrick dispatcher: Try "ruby public/dispatch.servlet --help" [Florian Gross]
+
+* Report errors about initialization to browser (instead of attempting to use uninitialized logger)
+
+* Upgraded to Action Pack 0.7.6
+
+* Upgraded to Active Record 0.9.1
+
+* Added distinct 500.html instead of reusing 404.html
+
+* Added MIT license
+
+
+*0.5.0*
+
+* First public release
diff --git a/automation/admin/INSTALL b/automation/admin/INSTALL
new file mode 100644
index 0000000..b5a13b4
--- /dev/null
+++ b/automation/admin/INSTALL
@@ -0,0 +1,68 @@
+AutoSubmissions installation notes
+==================================
+
+These are installation notes for the MySQL/Ruby on Rails automated
+submissions system. There is a set of Perl modules installed as part
+of the main Tab2MAGE installation which are required for the CGI web
+submissions form and automated processing daemons; this document
+covers database initialization, and setup and invocation of the
+curation submissions tracking web interface.
+
+Prerequisites
+-------------
+
+First, install ruby, rubygems and rails. See http://rubyforge.org and
+http://www.rubyonrails.com for details on how to do this. You will
+also need to install MySQL (version 4.1 or 5.0 is recommended; see
+http://www.mysql.org).
+
+You will also most likely need to install the native MySQL interface
+for Ruby. This is most easily accomplished using the rubygems system:
+
+gem install -y mysql -v 2.7 -- --with-mysql-config /the/path/to/your/mysql-config/executable
+
+Database setup
+--------------
+
+Creation of the MySQL database and suitable login privileges is left
+to the user. To set up the database tables, switch to the db/
+directory and issue the following command:
+
+mysql your_database_name < database-mysql.schema
+
+This only creates the tables. Alternatively, you can create a database
+populated with some initial values for experiment annotation and
+default users:
+
+mysql your_database_name < database-mysql.dump
+
+Your database details must be entered in the config/database.yml file;
+See the file, database.yml-mysql, for an example.
+
+Webserver setup
+---------------
+
+To start the included WEBrick server, type:
+
+ruby scripts/server webrick
+
+Now navigate to http://localhost:3000/ to check that everything is
+working.
+
+If you have installed the LightTPD web server, you should be able to
+use it in full backgrounded daemon mode like this:
+
+ruby scripts/server -d
+
+In this case the webserver will be available at
+http://localhost:8087/, with configuration options set in the
+config/lighttpd.conf file.
+
+Once running, please use the user account 'admin' (password 'admin')
+to access all parts of the application. Low-privilege users may be
+added using the web interface; curator- and admin-level users must be
+created by modifying the roles_users table directly in the MySQL
+database. Examples are included in the database-mysql.dump file.
+
+---------------------------------------------------------------------
+Copyright Tim Rayner <rayner at ebi.ac.uk> ArrayExpress Team, EBI, 2007.
diff --git a/automation/admin/README b/automation/admin/README
new file mode 100644
index 0000000..cd9d0ff
--- /dev/null
+++ b/automation/admin/README
@@ -0,0 +1,153 @@
+== Welcome to Rails
+
+Rails is a web-application and persistence framework that includes everything
+needed to create database-backed web-applications according to the
+Model-View-Control pattern of separation. This pattern splits the view (also
+called the presentation) into "dumb" templates that are primarily responsible
+for inserting pre-built data in between HTML tags. The model contains the
+"smart" domain objects (such as Account, Product, Person, Post) that holds all
+the business logic and knows how to persist themselves to a database. The
+controller handles the incoming requests (such as Save New Account, Update
+Product, Show Post) by manipulating the model and directing data to the view.
+
+In Rails, the model is handled by what's called an object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in
+link:files/vendor/rails/activerecord/README.html.
+
+The controller and view are handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails. You can read more about Action Pack in
+link:files/vendor/rails/actionpack/README.html.
+
+
+== Getting started
+
+1. Run the WEBrick servlet: <tt>ruby script/server</tt> (run with --help for options)
+ ...or if you have lighttpd installed: <tt>ruby script/lighttpd</tt> (it's faster)
+2. Go to http://localhost:3000/ and get "Congratulations, you've put Ruby on Rails!"
+3. Follow the guidelines on the "Congratulations, you've put Ruby on Rails!" screen
+
+
+== Example for Apache conf
+
+ <VirtualHost *:80>
+ ServerName rails
+ DocumentRoot /path/application/public/
+ ErrorLog /path/application/log/server.log
+
+ <Directory /path/application/public/>
+ Options ExecCGI FollowSymLinks
+ AllowOverride all
+ Allow from all
+ Order allow,deny
+ </Directory>
+ </VirtualHost>
+
+NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI
+should be on and ".cgi" should respond. All requests from 127.0.0.1 go
+through CGI, so no Apache restart is necessary for changes. All other requests
+go through FCGI (or mod_ruby), which requires a restart to show changes.
+
+
+== Debugging Rails
+
+Have "tail -f" commands running on both the server.log, production.log, and
+test.log files. Rails will automatically display debugging and runtime
+information to these files. Debugging info will also be shown in the browser
+on requests from 127.0.0.1.
+
+
+== Breakpoints
+
+Breakpoint support is available through the script/breakpointer client. This
+means that you can break out of execution at any point in the code, investigate
+and change the model, AND then resume execution! Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find_all
+ breakpoint "Breaking out from the list"
+ end
+ end
+
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the breakpointer window. Here you can do things like:
+
+Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
+
+ >> @posts.inspect
+ => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
+ #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
+ >> @posts.first.title = "hello from a breakpoint"
+ => "hello from a breakpoint"
+
+...and even better is that you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you press CTRL-D
+
+
+== Console
+
+You can interact with the domain model by starting the console through script/console.
+Here you'll have all parts of the application configured, just like it is when the
+application is running. You can inspect domain models, change values, and save to the
+database. Starting the script without arguments will launch it in the development environment.
+Passing an argument will specify a different environment, like <tt>console production</tt>.
+
+
+== Description of contents
+
+app
+ Holds all the code that's specific to this particular application.
+
+app/controllers
+ Holds controllers that should be named like weblog_controller.rb for
+ automated URL mapping. All controllers should descend from
+ ActionController::Base.
+
+app/models
+ Holds models that should be named like post.rb.
+ Most models will descend from ActiveRecord::Base.
+
+app/views
+ Holds the template files for the view that should be named like
+ weblog/index.rhtml for the WeblogController#index action. All views use eRuby
+ syntax. This directory can also be used to keep stylesheets, images, and so on
+ that can be symlinked to public.
+
+app/helpers
+ Holds view helpers that should be named like weblog_helper.rb.
+
+config
+ Configuration files for the Rails environment, the routing map, the database, and other dependencies.
+
+components
+ Self-contained mini-applications that can bundle together controllers, models, and views.
+
+lib
+ Application specific libraries. Basically, any kind of custom code that doesn't
+ belong under controllers, models, or helpers. This directory is in the load path.
+
+public
+ The directory available for the web server. Contains subdirectories for images, stylesheets,
+ and javascripts. Also contains the dispatchers and the default HTML files.
+
+script
+ Helper scripts for automation and generation.
+
+test
+ Unit and functional tests along with fixtures.
+
+vendor
+ External libraries that the application depends on. Also includes the plugins subdirectory.
+ This directory is in the load path.
diff --git a/automation/admin/README_LOGIN b/automation/admin/README_LOGIN
new file mode 100644
index 0000000..7cae805
--- /dev/null
+++ b/automation/admin/README_LOGIN
@@ -0,0 +1,96 @@
+== Installation
+
+Done generating the login system. but there are still a few things you have to
+do manually. First open your application.rb and add
+
+ require_dependency "login_system"
+
+to the top of the file and include the login system with
+
+ include LoginSystem
+
+The beginning of your ApplicationController.
+It should look something like this :
+
+ require_dependency "login_system"
+
+ class ApplicationController < ActionController::Base
+ include LoginSystem
+ model :user
+
+After you have done the modifications the the AbstractController you can import
+the user model into the database. This model is meant as an example and you
+should extend it. If you just want to get things up and running you can find
+some create table syntax in db/user_model.sql.
+
+The model :user is required when you are hitting problems to the degree of
+"Session could not be restored becuase not all items in it are known"
+
+== Requirements
+
+You need a database table corresponding to the User model.
+
+mysql syntax:
+CREATE TABLE users (
+ id int(11) NOT NULL auto_increment,
+ login varchar(80) default NULL,
+ password varchar(40) default NULL,
+ PRIMARY KEY (id)
+);
+
+postgres :
+CREATE TABLE "users" (
+ "id" SERIAL NOT NULL UNIQUE,
+ "login" VARCHAR(80),
+ "password" VARCHAR,
+ PRIMARY KEY("id")
+) WITH OIDS;
+
+
+sqlite:
+CREATE TABLE 'users' (
+ 'id' INTEGER PRIMARY KEY NOT NULL,
+ 'user' VARCHAR(80) DEFAULT NULL,
+ 'password' VARCHAR(40) DEFAULT NULL
+);
+
+Of course your user model can have any amount of extra fields. This is just a
+starting point
+
+== How to use it
+
+Now you can go around and happily add "before_filter :login_required" to the
+controllers which you would like to protect.
+
+After integrating the login system with your rails application navigate to your
+new controller's signup method. There you can create a new account. After you
+are done you should have a look at your DB. Your freshly created user will be
+there but the password will be a sha1 hashed 40 digit mess. I find this should
+be the minimum of security which every page offering login&password should give
+its customers. Now you can move to one of those controllers which you protected
+with the before_filter :login_required snippet. You will automatically be re-
+directed to your freshly created login controller and you are asked for a
+password. After entering valid account data you will be taken back to the
+controller which you requested earlier. Simple huh?
+
+== Tips & Tricks
+
+How do I...
+
+ ... restrict access to only a few methods?
+
+ A: Use before_filters build in scoping.
+ Example:
+ before_filter :login_required :only => [:myaccount, :changepassword]
+ before_filter :login_required :except => [:index]
+
+ ... check if a user is logged-in in my views?
+
+ A: @session['user'] will tell you. Here is an example helper which you can use to make this more pretty:
+ Example:
+ def user?
+ !@session['user'].nil?
+ end
+
+
+You can find more help at http://wiki.rubyonrails.com/rails/show/LoginGenerator
\ No newline at end of file
diff --git a/automation/admin/Rakefile b/automation/admin/Rakefile
new file mode 100644
index 0000000..cffd19f
--- /dev/null
+++ b/automation/admin/Rakefile
@@ -0,0 +1,10 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/switchtower.rake, and they will automatically be available to Rake.
+
+require(File.join(File.dirname(__FILE__), 'config', 'boot'))
+
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+require 'tasks/rails'
\ No newline at end of file
diff --git a/automation/admin/app/controllers/account_controller.rb b/automation/admin/app/controllers/account_controller.rb
new file mode 100644
index 0000000..bbde11d
--- /dev/null
+++ b/automation/admin/app/controllers/account_controller.rb
@@ -0,0 +1,52 @@
+class AccountController < ApplicationController
+
+ layout 'admin'
+
+ def login
+ case request.method
+ when :post
+ if session[:user] = User.authenticate(params[:user_login], params[:user_password])
+
+ flash[:notice] = "Login successful"
+ redirect_back_or_default :action => "welcome"
+ else
+ @login = params[:user_login]
+ @message = "Login unsuccessful"
+ end
+ end
+ end
+
+ def signup
+ case request.method
+ when :post
+
+ # Set the initial deletion state to zero
+ params[:user][:is_deleted] = 0
+ @user = User.new(params[:user])
+
+ if @user.save
+ session[:user] = User.authenticate(@user.login, params[:user][:password])
+ flash[:notice] = "Signup successful"
+ redirect_back_or_default :action => "welcome"
+ end
+ when :get
+ @user = User.new
+ end
+ end
+
+ def delete
+ if params[:id]
+ @user = User.find(params[:id])
+ @user.destroy
+ end
+ redirect_back_or_default :action => "welcome"
+ end
+
+ def logout
+ session[:user] = nil
+ end
+
+ def welcome
+ end
+
+end
diff --git a/automation/admin/app/controllers/application.rb b/automation/admin/app/controllers/application.rb
new file mode 100644
index 0000000..85d8dfa
--- /dev/null
+++ b/automation/admin/app/controllers/application.rb
@@ -0,0 +1,7 @@
+# Filters added to this controller will be run for all controllers in the application.
+# Likewise, all the methods added will be available for all controllers.
+require_dependency 'acl_system'
+
+class ApplicationController < ActionController::Base
+ include ACLSystem
+end
diff --git a/automation/admin/app/controllers/array_designs_controller.rb b/automation/admin/app/controllers/array_designs_controller.rb
new file mode 100644
index 0000000..aa02122
--- /dev/null
+++ b/automation/admin/app/controllers/array_designs_controller.rb
@@ -0,0 +1,124 @@
+class ArrayDesignsController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required
+
+ def index
+ list
+ render :action => 'list'
+ end
+
+ # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+ verify :method => :post, :only => [ :destroy, :create, :update ],
+ :redirect_to => { :action => :list }
+
+ def list
+
+ sql_where_clause = "is_deleted=0 and miamexpress_subid is not null"
+
+ @search_term = ""
+
+ if params[:search_term]
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ sql_where_clause += " and (accession like '#{ sql_search }'" +
+ " or name like '%#{ sql_search }%'" +
+ " or comment like '%#{ sql_search }%'" +
+ " or miamexpress_login like '%#{ sql_search }%'" +
+ " or miamexpress_subid like '#{ sql_search }')"
+ end
+
+ params[:page] ||= 1
+ @array_designs = ArrayDesign.paginate :page => params[:page],
+ :per_page => 40,
+ :conditions => sql_where_clause.to_s,
+ :order => 'miamexpress_subid DESC'
+ end
+
+ def list_all
+
+ num_per_page = params[:list_all].to_i.zero? ? 30 : 1000000
+
+ sql_where_clause = "is_deleted=0 and (accession is not null and accession!='')"
+
+ @search_term = ""
+
+ # Don't search with an empty string.
+ if params[:search_term] && !params[:search_term].eql?("")
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ sql_where_clause += " and accession like '#{ sql_search }'" +
+ " or comment like '%#{ sql_search }%'"
+
+ end
+
+ params[:page] ||= 1
+ @array_designs = ArrayDesign.paginate :page => params[:page],
+ :per_page => num_per_page,
+ :conditions => sql_where_clause.to_s,
+ :order => 'accession'
+
+ end
+
+ def show
+ @array_design = ArrayDesign.find(params[:id])
+ end
+
+ def new
+ @array_design = ArrayDesign.new
+ end
+
+ def edit
+ @array_design = ArrayDesign.find(params[:id])
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:array_design][:is_deleted] = 0
+ @array_design = ArrayDesign.new(params[:array_design])
+
+ if @array_design.save
+ flash[:notice] = 'ArrayDesign was successfully created.'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def update
+ @array_design = ArrayDesign.find(params[:id])
+ if @array_design.update_attributes(params[:array_design])
+ flash[:notice] = 'ArrayDesign was successfully updated.'
+ redirect_to :action => 'list_all',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ array_design = ArrayDesign.find(params[:id])
+ if array_design.experiments.any?
+ flash[:notice] = "Error: There are still experiments using #{ array_design.accession }."
+ redirect_to :action => 'list'
+ elsif array_design.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Array design was successfully deleted'
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/category_controller.rb b/automation/admin/app/controllers/category_controller.rb
new file mode 100644
index 0000000..90dcc6e
--- /dev/null
+++ b/automation/admin/app/controllers/category_controller.rb
@@ -0,0 +1,70 @@
+class CategoryController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required, :except => [ :show, :list ]
+
+ def index
+ list
+ render :action => 'list'
+ end
+
+ # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+ verify :method => :post, :only => [ :destroy, :create, :update ],
+ :redirect_to => { :action => :list }
+
+ def new
+ @category = Category.new
+ end
+
+ def show
+ @category = Category.find(params[:id])
+ end
+
+ def edit
+ @category = Category.find(params[:id])
+ end
+
+ def list
+ params[:page] ||= 1
+ @categories = Category.paginate :page => params[:page],
+ :conditions => 'is_deleted=0',
+ :per_page => 20,
+ :order => 'ontology_term'
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:category][:is_deleted] = 0
+ @category = Category.new(params[:category])
+
+ if @category.save
+ flash[:notice] = 'Category was successfully created'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def update
+ @category = Category.find(params[:id])
+ if @category.update_attributes(params[:category])
+ flash[:notice] = 'Category was successfully updated'
+ redirect_to :action => 'list'
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ category = Category.find(params[:id])
+ if category.taxons.any? || category.designs.any? || category.materials.any?
+ flash[:notice] = "Error: There are still taxons, designs or materials using #{ category.ontology_term }."
+ redirect_to :action => 'list'
+ elsif category.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Category was successfully deleted'
+ redirect_to :action => 'list'
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/design_controller.rb b/automation/admin/app/controllers/design_controller.rb
new file mode 100644
index 0000000..af5df9d
--- /dev/null
+++ b/automation/admin/app/controllers/design_controller.rb
@@ -0,0 +1,66 @@
+class DesignController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required, :except => [ :show, :list ]
+
+ def new
+ if Category.find(:all).any?
+ @design = Design.new
+ else
+ flash[:notice] = 'Please create at least one category:'
+ redirect_to :action => 'new', :controller => 'category'
+ end
+ end
+
+ def edit
+ @design = Design.find(params[:id])
+ end
+
+ def list
+ params[:page] ||= 1
+ @designs = Design.paginate :page => params[:page],
+ :conditions => 'is_deleted=0',
+ :per_page => 20,
+ :order => 'display_label'
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:design][:is_deleted] = 0
+ @design = Design.new(params[:design])
+
+ if @design.save
+ flash[:notice] = 'Design was successfully created'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def update
+ @design = Design.find(params[:id])
+
+ # Fix for empty ids
+ params[:design][:category_ids] ||= []
+
+ if @design.update_attributes(params[:design])
+ flash[:notice] = 'Design was successfully updated'
+ redirect_to :action => 'list'
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ design = Design.find(params[:id])
+ if design.experiments.any?
+ flash[:notice] = "Error: There are still experiments using the #{ design.ontology_value } design."
+ redirect_to :action => 'list'
+ elsif design.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Design was successfully deleted'
+ redirect_to :action => 'list'
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/event_controller.rb b/automation/admin/app/controllers/event_controller.rb
new file mode 100644
index 0000000..7bd174e
--- /dev/null
+++ b/automation/admin/app/controllers/event_controller.rb
@@ -0,0 +1,21 @@
+class EventController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required
+
+ def edit
+ @event = Event.find(params[:id])
+ end
+
+ def update
+ @event = Event.find(params[:id])
+ if @event.update_attributes(params[:event])
+ flash[:notice] = 'Event was successfully updated.'
+ redirect_to :action => 'edit',
+ :id => @event.id
+ else
+ render :action => 'edit'
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/experiments_controller.rb b/automation/admin/app/controllers/experiments_controller.rb
new file mode 100644
index 0000000..c8383ee
--- /dev/null
+++ b/automation/admin/app/controllers/experiments_controller.rb
@@ -0,0 +1,148 @@
+class ExperimentsController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required
+
+ def index
+ list
+ render :action => 'list'
+ end
+
+ # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+ verify :method => :post, :only => [ :destroy, :create, :update ],
+ :redirect_to => { :action => :list }
+
+ def list
+
+ num_per_page = params[:list_all].to_i.zero? ? 30 : 1000000
+
+ sql_where_clause = "is_deleted=0 and (accession is not null and accession!='')"
+
+ @search_term = ""
+
+ # Don't search with an empty string.
+ if params[:search_term] && !params[:search_term].eql?("")
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ sql_where_clause += " and accession like '#{ sql_search }'" +
+ " or comment like '%#{ sql_search }%'"
+
+ end
+
+ params[:page] ||= 1
+ @experiments = Experiment.paginate :page => params[:page],
+ :per_page => num_per_page,
+ :conditions => sql_where_clause.to_s,
+ :order => 'accession'
+
+ end
+
+ def today_list
+ @time_now = Time.now;
+ date_today = @time_now.strftime("%Y-%m-%d %H:%M:%S");
+ date_yesterday = sprintf("%04d-%02d-%02d", @time_now.year, @time_now.month, @time_now.mday - 1);
+ sql_where_clause = sprintf("is_deleted=0 and date_last_processed between '%s' and '%s'", date_yesterday, date_today);
+
+ if params[:experiment_type]
+ sql_where_clause += " and experiment_type='#{ params[:experiment_type] }'"
+ end
+
+ params[:page] ||= 1
+ @experiments = Experiment.paginate :page => params[:page],
+ :per_page => 30,
+ :conditions => sql_where_clause.to_s,
+ :order => 'date_last_processed DESC'
+ end
+
+ def show
+ @experiment = Experiment.find(params[:id])
+ end
+
+ def new
+ @experiment = Experiment.new
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:experiment][:is_deleted] = 0
+ @experiment = Experiment.new(params[:experiment])
+
+ if @experiment.annotate(params[:annotation]) && @experiment.save
+ flash[:notice] = "#{params[:experiment][:experiment_type]} experiment was successfully created."
+ redirect_to :action => 'list',
+ :experiment_type => params[:experiment][:experiment_type]
+ else
+ render :action => 'new'
+ end
+ end
+
+ def edit
+
+ # Abstract superclass method dispatches edit calls to the relevant subclass.
+ @experiment = Experiment.find(params[:id])
+ if @experiment.experiment_type == 'MIAMExpress'
+ redirect_to :controller => 'miamexps',
+ :action => 'edit',
+ :id => @experiment.id,
+ :experiment_type => @experiment.experiment_type,
+ :search_term => params[:search_term],
+ :page => params[:page]
+ else
+ redirect_to :controller => 'tab2mages',
+ :action => 'edit',
+ :id => @experiment.id,
+ :experiment_type => @experiment.experiment_type,
+ :search_term => params[:search_term],
+ :page => params[:page]
+ end
+ end
+
+ def update
+ @experiment = Experiment.find(params[:id])
+ if @experiment.annotate(params[:annotation]) && @experiment.update_attributes(params[:experiment])
+ flash[:notice] = 'Experiment was successfully updated.'
+ redirect_to :action => 'list',
+ :experiment_type => params[:experiment_type],
+ :search_term => params[:search_term],
+ :page => params[:page]
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ experiment = Experiment.find(params[:id])
+ if experiment.spreadsheets.find(:all, :conditions => "is_deleted=0").any?
+ flash[:notice] = "Error: There are still spreadsheets attached to the experiment: #{ experiment.name }"
+ redirect_to :action => 'list',
+ :experiment_type => params[:experiment_type],
+ :search_term => params[:search_term],
+ :page => params[:page]
+ elsif experiment.data_files.find(:all, :conditions => "is_deleted=0").any?
+ flash[:notice] = "Error: There are still data_files attached to the experiment: #{ experiment.name }"
+ redirect_to :action => 'list',
+ :experiment_type => params[:experiment_type],
+ :search_term => params[:search_term],
+ :page => params[:page]
+ elsif experiment.accession?
+ flash[:notice] = "Error: This experiment has been assigned an accession number, and cannot be deleted: #{ experiment.name }"
+ redirect_to :action => 'list',
+ :experiment_type => params[:experiment_type],
+ :search_term => params[:search_term],
+ :page => params[:page]
+ elsif experiment.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Experiment was successfully deleted'
+ redirect_to :action => 'list',
+ :experiment_type => params[:experiment_type],
+ :search_term => params[:search_term],
+ :page => params[:page]
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/factor_controller.rb b/automation/admin/app/controllers/factor_controller.rb
new file mode 100644
index 0000000..3da52bf
--- /dev/null
+++ b/automation/admin/app/controllers/factor_controller.rb
@@ -0,0 +1,6 @@
+class FactorController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required
+
+end
diff --git a/automation/admin/app/controllers/material_controller.rb b/automation/admin/app/controllers/material_controller.rb
new file mode 100644
index 0000000..360a2f2
--- /dev/null
+++ b/automation/admin/app/controllers/material_controller.rb
@@ -0,0 +1,66 @@
+class MaterialController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required, :except => [ :show, :list ]
+
+ def new
+ if Category.find(:all).any?
+ @material = Material.new
+ else
+ flash[:notice] = 'Please create at least one category:'
+ redirect_to :action => 'new', :controller => 'category'
+ end
+ end
+
+ def edit
+ @material = Material.find(params[:id])
+ end
+
+ def list
+ params[:page] ||= 1
+ @materials = Material.paginate :page => params[:page],
+ :conditions => 'is_deleted=0',
+ :per_page => 20,
+ :order => 'display_label'
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:material][:is_deleted] = 0
+ @material = Material.new(params[:material])
+
+ if @material.save
+ flash[:notice] = 'Material was successfully created'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def update
+ @material = Material.find(params[:id])
+
+ # Fix for empty ids
+ params[:material][:category_ids] ||= []
+
+ if @material.update_attributes(params[:material])
+ flash[:notice] = 'Material was successfully updated'
+ redirect_to :action => 'list'
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ material = Material.find(params[:id])
+ if material.experiments.any?
+ flash[:notice] = "Error: There are still experiments using the #{ material.ontology_value } material."
+ redirect_to :action => 'list'
+ elsif material.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Material was successfully deleted'
+ redirect_to :action => 'list'
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/miamexps_controller.rb b/automation/admin/app/controllers/miamexps_controller.rb
new file mode 100644
index 0000000..1d9eb78
--- /dev/null
+++ b/automation/admin/app/controllers/miamexps_controller.rb
@@ -0,0 +1,35 @@
+class MiamexpsController < ExperimentsController
+
+ def list
+ sql_where_clause = "is_deleted=0 and ( miamexpress_subid is not null || experiment_type='MIAMExpress' )"
+
+ @search_term = ""
+
+ if params[:search_term]
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ sql_where_clause += " and (accession like '#{ sql_search }'" +
+ " or name like '%#{ sql_search }%'" +
+ " or comment like '%#{ sql_search }%'" +
+ " or miamexpress_login like '%#{ sql_search }%'" +
+ " or miamexpress_subid like '#{ sql_search }')"
+ end
+
+ params[:page] ||= 1
+ @experiments = Experiment.paginate :page => params[:page],
+ :per_page => 30,
+ :conditions => sql_where_clause.to_s,
+ :order => 'miamexpress_subid DESC'
+
+ end
+
+ def edit
+ @experiment = Experiment.find(params[:id])
+ end
+
+end
diff --git a/automation/admin/app/controllers/organism_controller.rb b/automation/admin/app/controllers/organism_controller.rb
new file mode 100644
index 0000000..897a583
--- /dev/null
+++ b/automation/admin/app/controllers/organism_controller.rb
@@ -0,0 +1,90 @@
+class OrganismController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required, :except => [ :show, :list ]
+
+ def new
+ if Taxon.find(:all).any?
+ @organism = Organism.new
+ else
+ flash[:notice] = 'Please create at least one taxon:'
+ redirect_to :action => 'new', :controller => 'taxon'
+ end
+ end
+
+ def edit
+ @organism = Organism.find(params[:id])
+ end
+
+ def list
+ sql_where_clause = "is_deleted=0"
+
+ @search_term = ""
+
+ # Don't search with an empty string.
+ if params[:search_term] && !params[:search_term].eql?("")
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ sql_where_clause += " and scientific_name like '#{ sql_search }'"
+
+ end
+
+ params[:page] ||= 1
+ @organisms = Organism.paginate :page => params[:page],
+ :conditions => sql_where_clause.to_s,
+ :per_page => 20,
+ :order => 'scientific_name'
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:organism][:is_deleted] = 0
+ @organism = Organism.new(params[:organism])
+
+ if @organism.save
+ flash[:notice] = 'Organism was successfully created'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def update
+ @organism = Organism.find(params[:id])
+ if @organism.update_attributes(params[:organism])
+ flash[:notice] = 'Organism was successfully updated'
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ organism = Organism.find(params[:id])
+ if organism.experiments.any?
+ flash[:notice] = "Error: There are still experiments using #{ organism.scientific_name }."
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ elsif organism.array_designs.any?
+ flash[:notice] = "Error: There are still array designs using #{ organism.scientific_name }."
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ elsif organism.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Organism was successfully deleted'
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/protocols_controller.rb b/automation/admin/app/controllers/protocols_controller.rb
new file mode 100644
index 0000000..c486108
--- /dev/null
+++ b/automation/admin/app/controllers/protocols_controller.rb
@@ -0,0 +1,90 @@
+class ProtocolsController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required
+
+ def index
+ list
+ render :action => 'list'
+ end
+
+ # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+ verify :method => :post, :only => [ :destroy, :create, :update ],
+ :redirect_to => { :action => :list }
+
+ def list
+
+ sql_where_clause = "is_deleted=0"
+
+ @search_term = ""
+
+ if params[:search_term]
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ sql_where_clause += " and (accession like '#{ sql_search }'" +
+ " or user_accession like '%#{ sql_search }%'" +
+ " or name like '%#{ sql_search }%'" +
+ " or comment like '%#{ sql_search }%'" +
+ " or expt_accession like '#{ sql_search }')"
+ end
+
+ params[:page] ||= 1
+ @protocols = Protocol.paginate :page => params[:page],
+ :per_page => 40,
+ :conditions => sql_where_clause.to_s,
+ :order => 'id DESC'
+
+ end
+
+ def show
+ @protocol = Protocol.find(params[:id])
+ end
+
+ def new
+ @protocol = Protocol.new
+ end
+
+ def edit
+ @protocol = Protocol.find(params[:id])
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:protocol][:is_deleted] = 0
+ @protocol = Protocol.new(params[:protocol])
+
+ if @protocol.save
+ flash[:notice] = 'Protocol was successfully created.'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def update
+ @protocol = Protocol.find(params[:id])
+ if @protocol.update_attributes(params[:protocol])
+ flash[:notice] = 'Protocol was successfully updated.'
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ Protocol.find(params[:id]).update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Protocol was successfully deleted.'
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ end
+
+end
diff --git a/automation/admin/app/controllers/quantitation_type_controller.rb b/automation/admin/app/controllers/quantitation_type_controller.rb
new file mode 100644
index 0000000..d2d25fa
--- /dev/null
+++ b/automation/admin/app/controllers/quantitation_type_controller.rb
@@ -0,0 +1,6 @@
+class QuantitationTypeController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required
+
+end
diff --git a/automation/admin/app/controllers/tab2mages_controller.rb b/automation/admin/app/controllers/tab2mages_controller.rb
new file mode 100644
index 0000000..4f091ec
--- /dev/null
+++ b/automation/admin/app/controllers/tab2mages_controller.rb
@@ -0,0 +1,43 @@
+class Tab2magesController < ExperimentsController
+
+ def list
+
+ sql_where_clause = "is_deleted=0"
+
+ @search_term = ""
+
+ if params[:experiment_type]
+ sql_where_clause += " and experiment_type='#{ params[:experiment_type] }'"
+ end
+
+ if params[:search_term]
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ users = User.find(:all, :conditions => "login like '%#{ sql_search }%'")
+ user_clause = users.any? ? " or user_id in (#{ users.collect{|u| u.id}.join(',') })" : ""
+
+ sql_where_clause += " and (accession like '#{ sql_search }'" +
+ " or comment like '%#{ sql_search }%'" +
+ " or id like '#{ sql_search }'" +
+ " or miamexpress_login like '%#{ sql_search }%'" +
+ " or name like '%#{ sql_search }%'#{ user_clause })"
+ end
+
+ params[:page] ||= 1
+ @experiments = Experiment.paginate :page => params[:page],
+ :per_page => 30,
+ :conditions => sql_where_clause.to_s,
+ :order => 'accession is null asc, accession="" asc, cast(substr(accession,8,10) as signed integer) desc'
+
+ end
+
+ def edit
+ @experiment = Experiment.find(params[:id])
+ end
+
+end
diff --git a/automation/admin/app/controllers/taxon_controller.rb b/automation/admin/app/controllers/taxon_controller.rb
new file mode 100644
index 0000000..a88546a
--- /dev/null
+++ b/automation/admin/app/controllers/taxon_controller.rb
@@ -0,0 +1,71 @@
+class TaxonController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required, :except => [ :show, :list ]
+
+ def new
+ if Category.find(:all).any?
+ @taxon = Taxon.new
+ else
+ flash[:notice] = 'Please create at least one category:'
+ redirect_to :action => 'new', :controller => 'category'
+ end
+ end
+
+ def edit
+ @taxon = Taxon.find(params[:id])
+ end
+
+ def list
+ params[:page] ||= 1
+ @taxons = Taxon.paginate :page => params[:page],
+ :conditions => 'is_deleted=0',
+ :per_page => 20,
+ :order => 'scientific_name'
+ end
+
+ def category_table
+ @taxons = Taxon.find(:all, :conditions => 'is_deleted=0')
+ @categories = Category.find(:all, :conditions => 'is_deleted=0')
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:taxon][:is_deleted] = 0
+ @taxon = Taxon.new(params[:taxon])
+
+ if @taxon.save
+ flash[:notice] = 'Taxon was successfully created'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def update
+ @taxon = Taxon.find(params[:id])
+
+ # Fix for empty ids
+ params[:taxon][:category_ids] ||= []
+
+ if @taxon.update_attributes(params[:taxon])
+ flash[:notice] = 'Taxon was successfully updated'
+ redirect_to :action => 'list'
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ taxon = Taxon.find(params[:id])
+ if taxon.organisms.any?
+ flash[:notice] = "Error: There are still organisms in the #{ taxon.common_name } taxon."
+ redirect_to :action => 'list'
+ elsif taxon.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'Taxon was successfully deleted'
+ redirect_to :action => 'list'
+ end
+ end
+
+end
diff --git a/automation/admin/app/controllers/users_controller.rb b/automation/admin/app/controllers/users_controller.rb
new file mode 100644
index 0000000..c04f0d5
--- /dev/null
+++ b/automation/admin/app/controllers/users_controller.rb
@@ -0,0 +1,89 @@
+class UsersController < ApplicationController
+
+ layout "admin"
+ before_filter :login_required
+
+ def list
+ sql_where_clause = "is_deleted=0"
+
+ @search_term = ""
+
+ if params[:search_term]
+
+ # Strip single quotes, otherwise they will cause a crash.
+ @search_term = params[:search_term].gsub(/\'/, "")
+
+ # Silently allow asterisk wildcards
+ sql_search = @search_term.gsub(/\*/, "%").gsub(/\?/, "_")
+
+ sql_where_clause += " and (login like '#{ sql_search }'" +
+ " or name like '%#{ sql_search }%'" +
+ " or email like '%#{ sql_search }%')"
+ end
+
+ params[:page] ||= 1
+ @users = User.paginate :page => params[:page],
+ :conditions => sql_where_clause.to_s,
+ :order => 'login',
+ :per_page => 15
+ end
+
+ def show
+ @user = User.find(params[:id])
+ end
+
+ def new
+ redirect_to :controller => 'account', :action => 'signup'
+ @user = User.new
+ end
+
+ def create
+
+ # Set the initial deletion state to zero
+ params[:user][:is_deleted] = 0
+ @user = User.new(params[:user])
+
+ if @user.save
+ flash[:notice] = 'User was successfully created.'
+ redirect_to :action => 'list'
+ else
+ render :action => 'new'
+ end
+ end
+
+ def edit
+ @user = User.find(params[:id])
+ end
+
+ def update
+ @user = User.find(params[:id])
+
+ # FIXME the Time call below returns the correct string, but it is
+ # not stored correctly in the database.
+ if @user.update_attributes(params[:user]) \
+ && @user.update_attribute(:modified_at, Time.now.getutc.iso8601.to_s)
+ flash[:notice] = 'User was successfully updated.'
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def deprecate
+ user = User.find(params[:id])
+ if user.experiments.any?
+ flash[:notice] = "Error: There are still experiments attached to user #{ user.login }."
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ elsif user.update_attribute(:is_deleted, 1)
+ flash[:notice] = 'User was successfully deleted'
+ redirect_to :action => 'list',
+ :search_term => params[:search_term],
+ :page => params[:page]
+ end
+ end
+
+end
diff --git a/automation/admin/app/helpers/account_helper.rb b/automation/admin/app/helpers/account_helper.rb
new file mode 100644
index 0000000..6d3694a
--- /dev/null
+++ b/automation/admin/app/helpers/account_helper.rb
@@ -0,0 +1,2 @@
+module AccountHelper
+end
diff --git a/automation/admin/app/helpers/application_helper.rb b/automation/admin/app/helpers/application_helper.rb
new file mode 100644
index 0000000..22a7940
--- /dev/null
+++ b/automation/admin/app/helpers/application_helper.rb
@@ -0,0 +1,3 @@
+# Methods added to this helper will be available to all templates in the application.
+module ApplicationHelper
+end
diff --git a/automation/admin/app/helpers/array_designs_helper.rb b/automation/admin/app/helpers/array_designs_helper.rb
new file mode 100644
index 0000000..1b7a249
--- /dev/null
+++ b/automation/admin/app/helpers/array_designs_helper.rb
@@ -0,0 +1,2 @@
+module ArrayDesignsHelper
+end
diff --git a/automation/admin/app/helpers/category_helper.rb b/automation/admin/app/helpers/category_helper.rb
new file mode 100644
index 0000000..ac5cb5b
--- /dev/null
+++ b/automation/admin/app/helpers/category_helper.rb
@@ -0,0 +1,2 @@
+module CategoryHelper
+end
diff --git a/automation/admin/app/helpers/design_helper.rb b/automation/admin/app/helpers/design_helper.rb
new file mode 100644
index 0000000..6293f66
--- /dev/null
+++ b/automation/admin/app/helpers/design_helper.rb
@@ -0,0 +1,2 @@
+module DesignHelper
+end
diff --git a/automation/admin/app/helpers/event_helper.rb b/automation/admin/app/helpers/event_helper.rb
new file mode 100644
index 0000000..837363e
--- /dev/null
+++ b/automation/admin/app/helpers/event_helper.rb
@@ -0,0 +1,2 @@
+module EventHelper
+end
diff --git a/automation/admin/app/helpers/experiments_helper.rb b/automation/admin/app/helpers/experiments_helper.rb
new file mode 100644
index 0000000..694268c
--- /dev/null
+++ b/automation/admin/app/helpers/experiments_helper.rb
@@ -0,0 +1,2 @@
+module ExperimentsHelper
+end
diff --git a/automation/admin/app/helpers/factor_helper.rb b/automation/admin/app/helpers/factor_helper.rb
new file mode 100644
index 0000000..bda7ccb
--- /dev/null
+++ b/automation/admin/app/helpers/factor_helper.rb
@@ -0,0 +1,2 @@
+module FactorHelper
+end
diff --git a/automation/admin/app/helpers/material_helper.rb b/automation/admin/app/helpers/material_helper.rb
new file mode 100644
index 0000000..5b1e197
--- /dev/null
+++ b/automation/admin/app/helpers/material_helper.rb
@@ -0,0 +1,2 @@
+module MaterialHelper
+end
diff --git a/automation/admin/app/helpers/miamexps_helper.rb b/automation/admin/app/helpers/miamexps_helper.rb
new file mode 100644
index 0000000..2d84ff3
--- /dev/null
+++ b/automation/admin/app/helpers/miamexps_helper.rb
@@ -0,0 +1,2 @@
+module MiamexpsHelper
+end
diff --git a/automation/admin/app/helpers/organism_helper.rb b/automation/admin/app/helpers/organism_helper.rb
new file mode 100644
index 0000000..f3c3c92
--- /dev/null
+++ b/automation/admin/app/helpers/organism_helper.rb
@@ -0,0 +1,2 @@
+module OrganismHelper
+end
diff --git a/automation/admin/app/helpers/protocols_helper.rb b/automation/admin/app/helpers/protocols_helper.rb
new file mode 100644
index 0000000..ce23494
--- /dev/null
+++ b/automation/admin/app/helpers/protocols_helper.rb
@@ -0,0 +1,2 @@
+module ProtocolsHelper
+end
diff --git a/automation/admin/app/helpers/quantitation_type_helper.rb b/automation/admin/app/helpers/quantitation_type_helper.rb
new file mode 100644
index 0000000..3e6b317
--- /dev/null
+++ b/automation/admin/app/helpers/quantitation_type_helper.rb
@@ -0,0 +1,2 @@
+module QuantitationTypeHelper
+end
diff --git a/automation/admin/app/helpers/tab2mages_helper.rb b/automation/admin/app/helpers/tab2mages_helper.rb
new file mode 100644
index 0000000..9e8a0d0
--- /dev/null
+++ b/automation/admin/app/helpers/tab2mages_helper.rb
@@ -0,0 +1,2 @@
+module Tab2magesHelper
+end
diff --git a/automation/admin/app/helpers/taxon_helper.rb b/automation/admin/app/helpers/taxon_helper.rb
new file mode 100644
index 0000000..ba38184
--- /dev/null
+++ b/automation/admin/app/helpers/taxon_helper.rb
@@ -0,0 +1,2 @@
+module TaxonHelper
+end
diff --git a/automation/admin/app/helpers/users_helper.rb b/automation/admin/app/helpers/users_helper.rb
new file mode 100644
index 0000000..2310a24
--- /dev/null
+++ b/automation/admin/app/helpers/users_helper.rb
@@ -0,0 +1,2 @@
+module UsersHelper
+end
diff --git a/automation/admin/app/models/array_design.rb b/automation/admin/app/models/array_design.rb
new file mode 100644
index 0000000..6e76dc5
--- /dev/null
+++ b/automation/admin/app/models/array_design.rb
@@ -0,0 +1,12 @@
+class ArrayDesign < ActiveRecord::Base
+
+ has_and_belongs_to_many :organisms
+ has_and_belongs_to_many :experiments
+ has_many :events
+
+ validates_format_of :accession, :with => /^(A-[A-Z]{4}-\d+|unknown)?$/, :message => "is not the correct format"
+
+ # Allow nulls but no other duplicated values
+ validates_uniqueness_of :accession, :if => Proc.new{ |array| array.accession !~ /^(unknown)?$/ }
+
+end
diff --git a/automation/admin/app/models/category.rb b/automation/admin/app/models/category.rb
new file mode 100644
index 0000000..ac3cd97
--- /dev/null
+++ b/automation/admin/app/models/category.rb
@@ -0,0 +1,7 @@
+class Category < ActiveRecord::Base
+ has_and_belongs_to_many :taxons
+ has_and_belongs_to_many :designs
+ has_and_belongs_to_many :materials
+ validates_presence_of :ontology_term, :display_label
+ validates_uniqueness_of :display_label
+end
diff --git a/automation/admin/app/models/data_file.rb b/automation/admin/app/models/data_file.rb
new file mode 100644
index 0000000..034bcfc
--- /dev/null
+++ b/automation/admin/app/models/data_file.rb
@@ -0,0 +1,3 @@
+class DataFile < ActiveRecord::Base
+ has_one :experiment
+end
diff --git a/automation/admin/app/models/design.rb b/automation/admin/app/models/design.rb
new file mode 100644
index 0000000..ec725b0
--- /dev/null
+++ b/automation/admin/app/models/design.rb
@@ -0,0 +1,7 @@
+class Design < ActiveRecord::Base
+ has_and_belongs_to_many :categories
+ has_many :design_instances
+ has_many :experiments, :through => :design_instances
+ validates_presence_of :display_label, :ontology_category, :ontology_value
+ validates_uniqueness_of :display_label
+end
diff --git a/automation/admin/app/models/design_instance.rb b/automation/admin/app/models/design_instance.rb
new file mode 100644
index 0000000..6795f9e
--- /dev/null
+++ b/automation/admin/app/models/design_instance.rb
@@ -0,0 +1,4 @@
+class DesignInstance < ActiveRecord::Base
+ belongs_to :design
+ belongs_to :experiment
+end
diff --git a/automation/admin/app/models/event.rb b/automation/admin/app/models/event.rb
new file mode 100644
index 0000000..9bb9af4
--- /dev/null
+++ b/automation/admin/app/models/event.rb
@@ -0,0 +1,8 @@
+class Event < ActiveRecord::Base
+
+ belongs_to :experiment
+ belongs_to :array_design
+
+ validates_presence_of :event_type
+
+end
diff --git a/automation/admin/app/models/experiment.rb b/automation/admin/app/models/experiment.rb
new file mode 100644
index 0000000..ed6cd31
--- /dev/null
+++ b/automation/admin/app/models/experiment.rb
@@ -0,0 +1,43 @@
+require_dependency 'annotation'
+
+class Experiment < ActiveRecord::Base
+ has_many :design_instances, :dependent => :destroy
+ has_many :designs, :through => :design_instances
+
+ has_many :material_instances, :dependent => :destroy
+ has_many :materials, :through => :material_instances
+
+ has_many :organism_instances, :dependent => :destroy
+ has_many :organisms, :through => :organism_instances
+
+ has_and_belongs_to_many :factors
+ has_and_belongs_to_many :quantitation_types
+ has_and_belongs_to_many :array_designs
+
+ has_many :spreadsheets
+ has_many :data_files
+
+ has_many :events
+
+ belongs_to :user
+
+ validates_numericality_of :miamexpress_subid, :allow_nil => true, :only_integer => true, :message => "is not an integer"
+ validates_numericality_of :checker_score, :allow_nil => true, :only_integer => true, :message => "is not an integer"
+ validates_inclusion_of :is_affymetrix,
+ :in_curation,
+ :allow_nil => true,
+ :in => 0..1,
+ :message => "is not 0 or 1"
+ validates_inclusion_of :experiment_type,
+ :in => %w(Tab2MAGE MIAMExpress MUGEN GEO MAGE-TAB Unknown),
+ :message => "is not recognized"
+ validates_format_of :accession, :with => /^(E-[A-Z]{4}-\d+|unknown)?$/, :message => "is not the correct format"
+
+ # Allow nulls but no other duplicated values
+ validates_uniqueness_of :accession, :if => Proc.new{ |expt| expt.accession !~ /^(unknown)?$/ }
+
+ validates_associated :organisms
+
+ # Annotation mixin
+ include Annotation
+end
diff --git a/automation/admin/app/models/factor.rb b/automation/admin/app/models/factor.rb
new file mode 100644
index 0000000..973081c
--- /dev/null
+++ b/automation/admin/app/models/factor.rb
@@ -0,0 +1,7 @@
+class Factor < ActiveRecord::Base
+
+ has_and_belongs_to_many :experiments
+
+ validates_presence_of :name
+
+end
diff --git a/automation/admin/app/models/material.rb b/automation/admin/app/models/material.rb
new file mode 100644
index 0000000..f53c1dc
--- /dev/null
+++ b/automation/admin/app/models/material.rb
@@ -0,0 +1,7 @@
+class Material < ActiveRecord::Base
+ has_and_belongs_to_many :categories
+ has_many :material_instances
+ has_many :experiments, :through => :material_instances
+ validates_presence_of :display_label, :ontology_category, :ontology_value
+ validates_uniqueness_of :display_label
+end
diff --git a/automation/admin/app/models/material_instance.rb b/automation/admin/app/models/material_instance.rb
new file mode 100644
index 0000000..f2e6a7c
--- /dev/null
+++ b/automation/admin/app/models/material_instance.rb
@@ -0,0 +1,4 @@
+class MaterialInstance < ActiveRecord::Base
+ belongs_to :material
+ belongs_to :experiment
+end
diff --git a/automation/admin/app/models/organism.rb b/automation/admin/app/models/organism.rb
new file mode 100644
index 0000000..541c0d7
--- /dev/null
+++ b/automation/admin/app/models/organism.rb
@@ -0,0 +1,10 @@
+class Organism < ActiveRecord::Base
+ belongs_to :taxon
+ has_many :organism_instances
+ has_many :experiments, :through => :organism_instances
+ has_and_belongs_to_many :array_designs
+ validates_presence_of :accession, :common_name, :scientific_name, :taxon
+ validates_uniqueness_of :accession, :common_name, :scientific_name
+ validates_numericality_of :accession, :only_integer => true, :message => "is not an integer"
+ validates_associated :taxon
+end
diff --git a/automation/admin/app/models/organism_instance.rb b/automation/admin/app/models/organism_instance.rb
new file mode 100644
index 0000000..0654b26
--- /dev/null
+++ b/automation/admin/app/models/organism_instance.rb
@@ -0,0 +1,4 @@
+class OrganismInstance < ActiveRecord::Base
+ belongs_to :organism
+ belongs_to :experiment
+end
diff --git a/automation/admin/app/models/permission.rb b/automation/admin/app/models/permission.rb
new file mode 100644
index 0000000..752ea78
--- /dev/null
+++ b/automation/admin/app/models/permission.rb
@@ -0,0 +1,6 @@
+# See <a href="http://wiki.rubyonrails.com/rails/show/AccessControlListExample">http://wiki.rubyonrails.com/rails/show/AccessControlListExample</a>
+# and <a href="http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList">http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList</a>
+
+class Permission < ActiveRecord::Base
+ has_and_belongs_to_many :roles
+end
diff --git a/automation/admin/app/models/protocol.rb b/automation/admin/app/models/protocol.rb
new file mode 100644
index 0000000..aeeea08
--- /dev/null
+++ b/automation/admin/app/models/protocol.rb
@@ -0,0 +1,4 @@
+class Protocol < ActiveRecord::Base
+ validates_presence_of :user_accession, :expt_accession
+ validates_uniqueness_of :accession
+end
diff --git a/automation/admin/app/models/quantitation_type.rb b/automation/admin/app/models/quantitation_type.rb
new file mode 100644
index 0000000..b75403c
--- /dev/null
+++ b/automation/admin/app/models/quantitation_type.rb
@@ -0,0 +1,7 @@
+class QuantitationType < ActiveRecord::Base
+
+ has_and_belongs_to_many :experiments
+
+ validates_presence_of :name
+
+end
diff --git a/automation/admin/app/models/role.rb b/automation/admin/app/models/role.rb
new file mode 100644
index 0000000..9035ba6
--- /dev/null
+++ b/automation/admin/app/models/role.rb
@@ -0,0 +1,7 @@
+# See <a href="http://wiki.rubyonrails.com/rails/show/AccessControlListExample">http://wiki.rubyonrails.com/rails/show/AccessControlListExample</a>
+# and <a href="http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList">http://wiki.rubyonrails.com/rails/show/LoginGeneratorAccessControlList</a>
+
+class Role < ActiveRecord::Base
+ has_and_belongs_to_many :permissions
+ has_and_belongs_to_many :users
+end
diff --git a/automation/admin/app/models/spreadsheet.rb b/automation/admin/app/models/spreadsheet.rb
new file mode 100644
index 0000000..ece9581
--- /dev/null
+++ b/automation/admin/app/models/spreadsheet.rb
@@ -0,0 +1,3 @@
+class Spreadsheet < ActiveRecord::Base
+ has_one :experiment
+end
diff --git a/automation/admin/app/models/taxon.rb b/automation/admin/app/models/taxon.rb
new file mode 100644
index 0000000..427854b
--- /dev/null
+++ b/automation/admin/app/models/taxon.rb
@@ -0,0 +1,8 @@
+class Taxon < ActiveRecord::Base
+ has_many :organisms
+ has_and_belongs_to_many :categories
+ validates_presence_of :accession, :common_name, :scientific_name
+ validates_uniqueness_of :accession, :common_name, :scientific_name
+ validates_numericality_of :accession, :only_integer => true, :message => "is not an integer"
+ validates_associated :categories
+end
diff --git a/automation/admin/app/models/user.rb b/automation/admin/app/models/user.rb
new file mode 100644
index 0000000..3e4fa7c
--- /dev/null
+++ b/automation/admin/app/models/user.rb
@@ -0,0 +1,59 @@
+#require 'digest/md5'
+
+# this model expects a certain database layout and its based on the name/login pattern.
+class User < ActiveRecord::Base
+
+ has_and_belongs_to_many :roles
+ has_many :experiments
+ validates_presence_of :login
+ validates_uniqueness_of :login
+
+ # Return true/false if User is authorized for resource.
+ def authorized?(resource)
+ match=false
+ permission_strings.each do |p|
+ r = Regexp.new(p)
+ match = match || ((r =~ resource) != nil)
+ end
+ return match
+ end
+
+ # Load permission strings
+ def permission_strings
+ a = []
+ self.roles.each{|r| r.permissions.each{|p| a<< p.name }}
+ a
+ end
+
+ # Autogenerated code below
+
+ def self.authenticate(login, pass)
+
+ # Line below has been changed from md5(pass) to allow cleartext
+ # password storage (yes, I know this is bad, but sometimes curators
+ # want to be able to tell a user their password.
+ find( :first, :conditions => ["login = ? AND password = ?", login, pass])
+ end
+
+ def change_password(pass)
+ update_attribute "password", pass # changed from self.class.md5(pass)
+ end
+
+ protected
+
+# def self.md5(pass)
+# Digest::MD5.hexdigest("#{pass}")
+# end
+
+ before_create :crypt_password
+
+ def crypt_password
+ write_attribute("password", password) # changed from self.class.md5(password)
+ end
+
+ validates_length_of :login, :within => 3..40
+ validates_length_of :password, :within => 5..40
+ validates_presence_of :login, :password, :password_confirmation
+ validates_uniqueness_of :login, :on => :save
+ validates_confirmation_of :password, :on => :save
+end
diff --git a/automation/admin/app/views/account/login.rhtml b/automation/admin/app/views/account/login.rhtml
new file mode 100644
index 0000000..bcb4d97
--- /dev/null
+++ b/automation/admin/app/views/account/login.rhtml
@@ -0,0 +1,22 @@
+<% form_tag :action=> "login" do %>
+
+<div title="Account login" id="loginform" class="form">
+ <h3>Please login</h3>
+
+ <% if @message %>
+ <div id="message"><%= @message %></div>
+ <% end %>
+
+ <label for="user_login">Login:</label><br/>
+ <input type="text" name="user_login" id="user_login" size="30" value=""/><br/>
+
+ <label for="user_password">Password:</label><br/>
+ <input type="password" name="user_password" id="user_password" size="30"/>
+
+ <br/>
+ <input type="submit" name="login" value="Login »" class="primary" />
+
+</div>
+
+<% end %>
+
diff --git a/automation/admin/app/views/account/logout.rhtml b/automation/admin/app/views/account/logout.rhtml
new file mode 100644
index 0000000..6a3d22c
--- /dev/null
+++ b/automation/admin/app/views/account/logout.rhtml
@@ -0,0 +1,10 @@
+
+<div class="memo">
+ <h3>Logoff</h3>
+
+ <p>You are now logged out of the system...</p>
+
+ <%= link_to "« login", :action=>"login"%>
+
+</div>
+
diff --git a/automation/admin/app/views/account/signup.rhtml b/automation/admin/app/views/account/signup.rhtml
new file mode 100644
index 0000000..65cab57
--- /dev/null
+++ b/automation/admin/app/views/account/signup.rhtml
@@ -0,0 +1,18 @@
+<% form_tag :action=> "signup" do %>
+
+<div title="Account signup" id="signupform" class="form">
+ <h3>Signup</h3>
+ <%= error_messages_for 'user' %><br/>
+
+ <label for="user_login">Desired login:</label><br/>
+ <%= text_field "user", "login", :size => 30 %><br/>
+ <label for="user_password">Choose password:</label><br/>
+ <%= password_field "user", "password", :size => 30 %><br/>
+ <label for="user_password_confirmation">Confirm password:</label><br/>
+ <%= password_field "user", "password_confirmation", :size => 30 %><br/>
+
+<input type="submit" value="Signup »" class="primary" />
+
+</div>
+<% end %>
+
diff --git a/automation/admin/app/views/account/welcome.rhtml b/automation/admin/app/views/account/welcome.rhtml
new file mode 100644
index 0000000..a612f10
--- /dev/null
+++ b/automation/admin/app/views/account/welcome.rhtml
@@ -0,0 +1,12 @@
+
+<div class="memo">
+ <h3>Welcome</h3>
+
+ <p>You are now logged into the system...</p>
+ <p>
+ Please select one of the links in the menu panel to the left.
+ </p>
+
+ <%= link_to "« logout", :action=>"logout"%>
+
+</div>
diff --git a/automation/admin/app/views/array_designs/_aedwinfo.rhtml b/automation/admin/app/views/array_designs/_aedwinfo.rhtml
new file mode 100755
index 0000000..85ea2ed
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_aedwinfo.rhtml
@@ -0,0 +1,18 @@
+
+ <p class="text">Explanation of AEDW scores:</p>
+ <ul>
+ <li>There must be a database entry (which matches the expected pattern) from an approved database for at least
+50% of experimental features. At present the list of approved databases is:
+ <ul>
+ <li>swissprot</li>
+ <li>trembl</li>
+ <li>swall</li>
+ <li>flybase</li>
+ <li>genedb</li>
+ <li>mgd</li>
+ <li>sgd</li>
+ <li>rgd</li>
+ <li>wormbase</li>
+ </ul>
+ </li>
+ </ul>
diff --git a/automation/admin/app/views/array_designs/_form.rhtml b/automation/admin/app/views/array_designs/_form.rhtml
new file mode 100644
index 0000000..f8de120
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_form.rhtml
@@ -0,0 +1,44 @@
+<%= error_messages_for 'array_design' %>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Submission ID:") %>
+ <%= content_tag("td", @array_design.miamexpress_subid) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Accession:") %>
+ <%= content_tag("td", input("array_design", "accession")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Name:") %>
+ <%= content_tag("td", @array_design.name) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "User:") %>
+ <%= content_tag("td", @array_design.miamexpress_login) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Status:") %>
+ <%= content_tag("td", input("array_design", "status")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "DW ready:") %>
+ <%= content_tag("td", check_box("array_design", "data_warehouse_ready")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Loaded in DW:") %>
+ <%= content_tag("td", @array_design.in_data_warehouse? ? 'YES' : 'no') %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "MIAME score:") %>
+ <%= content_tag("td", @array_design.miame_score? ? 'Pass' : 'Fail') %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Date last processed:") %>
+ <%= content_tag("td", @array_design.date_last_processed) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Comment:") %>
+ <%= content_tag("td", input("array_design", "comment")) %>
+ </tr>
+</table>
diff --git a/automation/admin/app/views/array_designs/_miameinfo.rhtml b/automation/admin/app/views/array_designs/_miameinfo.rhtml
new file mode 100755
index 0000000..c03e501
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_miameinfo.rhtml
@@ -0,0 +1,6 @@
+
+ <p class="text">Explanation of MIAME scores:</p>
+ <ul>
+ <li>Oligo arrays must have sequence for at least 70% of experimental features.</li>
+ <li>Non-oligo arrays must have sequence or any database entry for at least 70% of experimental features.</li>
+ </ul>
diff --git a/automation/admin/app/views/array_designs/_title.rhtml b/automation/admin/app/views/array_designs/_title.rhtml
new file mode 100644
index 0000000..fd953b9
--- /dev/null
+++ b/automation/admin/app/views/array_designs/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Array Designs", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/array_designs/edit.rhtml b/automation/admin/app/views/array_designs/edit.rhtml
new file mode 100644
index 0000000..2d110db
--- /dev/null
+++ b/automation/admin/app/views/array_designs/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @array_design.id do %>
+ <%= render_partial "form" %>
+ <%= hidden_field_tag("page", params[:page]) %>
+ <%= hidden_field_tag("search_term", params[:search_term]) %>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/array_designs/list.rhtml b/automation/admin/app/views/array_designs/list.rhtml
new file mode 100644
index 0000000..c99608c
--- /dev/null
+++ b/automation/admin/app/views/array_designs/list.rhtml
@@ -0,0 +1,70 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on SubId, Accession, Name or User (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= submit_tag("Search") %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "SubID") %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "Name") %>
+ <%= content_tag("th", "User") %>
+ <%= content_tag("th", "Status") %>
+ <%= content_tag("th", "MIAME") %>
+ <%= content_tag("th", "DW status") %>
+ <%= content_tag("th", "Date last processed") %>
+ <%= content_tag("th", "Comment") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @array_designs.each do |a| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => a.id,
+ :page => params[:page],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", a.miamexpress_subid) %>
+ <%= content_tag("td", a.accession) %>
+ <%= content_tag("td", a.name) %>
+ <%= content_tag("td", a.miamexpress_login) %>
+ <%= content_tag("td", a.status) %>
+ <%= content_tag("td", a.miame_score.nil? ? '' :
+ a.miame_score? ?
+ '<font color="green">PASS</font>' :
+ '<font color="red">fail</font>') %>
+ <%= content_tag("td", a.in_data_warehouse? ? '<font color="blue">Loaded</font>' :
+ a.data_warehouse_ready.nil? ? '' :
+ a.data_warehouse_ready? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", a.date_last_processed ?
+ a.date_last_processed.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", a.comment) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => a.id,
+ :page => params[:page],
+ :search_term => @search_term },
+ :confirm => "Really delete array design \"#{a.accession}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @array_designs, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= render_partial "miameinfo" %>
+ <%= render_partial "aedwinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/array_designs/list_all.rhtml b/automation/admin/app/views/array_designs/list_all.rhtml
new file mode 100644
index 0000000..2b27aa1
--- /dev/null
+++ b/automation/admin/app/views/array_designs/list_all.rhtml
@@ -0,0 +1,59 @@
+<%= content_tag("h1", "All Arrays", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on Accession or Comment (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= submit_tag("Search") %>
+ <%= content_tag("span", " List all results on one page:") %>
+ <%= check_box_tag("list_all", 1, params[:list_all] ) %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "Species") %>
+ <%= content_tag("th", "Annotation Source") %>
+ <%= content_tag("th", "Annotation Version") %>
+ <%= content_tag("th", "BioMart Table Name") %>
+ <%= content_tag("th", "Release Date") %>
+ <%= content_tag("th", "Is Released") %>
+ <%= content_tag("th", "Loaded in DW") %>
+ <%= content_tag("th", "Comment") %>
+ </tr>
+
+ <% @array_designs.each do |a| %>
+ <tr>
+ <td>
+ <%= link_to "Details", :action => "show",
+ :id => a.id %>
+ </td>
+ <%= content_tag("td", a.accession) %>
+ <%= content_tag("td", a.organisms.collect{ |o| o.scientific_name}.join(", ")) %>
+ <%= content_tag("td", a.annotation_source) %>
+ <%= content_tag("td", a.annotation_version) %>
+ <%= content_tag("td", a.biomart_table_name) %>
+ <%= content_tag("td", a.release_date ?
+ a.release_date.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", a.is_released.nil? ? '' :
+ a.is_released? ?
+ '<font color="green">PUBLIC</font>' :
+ '<font color="red">private</font>') %>
+ <%= content_tag("td", a.in_data_warehouse? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", truncate(a.comment, 30)) %>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @array_designs, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+</div>
diff --git a/automation/admin/app/views/array_designs/new.rhtml b/automation/admin/app/views/array_designs/new.rhtml
new file mode 100644
index 0000000..c70be0f
--- /dev/null
+++ b/automation/admin/app/views/array_designs/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= content_tag("p", submit_tag("Create")) %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/array_designs/show.rhtml b/automation/admin/app/views/array_designs/show.rhtml
new file mode 100644
index 0000000..71f5009
--- /dev/null
+++ b/automation/admin/app/views/array_designs/show.rhtml
@@ -0,0 +1,84 @@
+<%= content_tag("h1", @array_design.accession, :class => "title") %>
+<%= content_tag("h4", "This array_design is currently " +
+ (@array_design.is_released.nil? ? '' :
+ @array_design.is_released? ?
+ '<font color="green">Public</font>' :
+ '<font color="red">Private</font>') +
+ " (release date: " +
+ (@array_design.release_date ?
+ @array_design.release_date.strftime('%Y-%m-%d') :
+ "") + ")" ) %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<table width="100%">
+<tr>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("td", @array_design.accession) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Species") %>
+ <%= content_tag("td", @array_design.organisms.collect{ |o| o.scientific_name }.join(", ")) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Annotation Source") %>
+ <%= content_tag("td", @array_design.annotation_source) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Annotation Version") %>
+ <%= content_tag("td", @array_design.annotation_version) %>
+</tr>
+<tr>
+ <%= content_tag("th", "BioMart Table Name") %>
+ <%= content_tag("td", @array_design.biomart_table_name) %>
+</tr>
+</table>
+
+<hr/>
+
+<table width="100%">
+<tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Event") %>
+ <%= content_tag("th", "Target DB") %>
+ <%= content_tag("th", "Operator") %>
+ <%= content_tag("th", "Start Time") %>
+ <%= content_tag("th", "End Time") %>
+ <%= content_tag("th", "Comment") %>
+</tr>
+<% for event in @array_design.events.sort_by{ |e| e.start_time || Time.local(0) } %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :controller => "event",
+ :action => "edit",
+ :id => event.id %>
+ </td>
+ <%= content_tag("td", "<font color='#{ (event.was_successful.nil? ? 'black' :
+ event.was_successful == 0 ? 'red' :
+ 'green') }'>" + event.event_type + '</font>') %>
+ <%= content_tag("td", event.target_db) %>
+ <%= content_tag("td", event.operator) %>
+ <%= content_tag("td", event.start_time) %>
+ <%= content_tag("td", event.end_time) %>
+ <%= content_tag("td", truncate(event.comment, 30)) %>
+ </tr>
+<% end %>
+</table>
+
+<p class="text"><font color="green">Green</font> events were
+successful, while <font color="red">Red</font> events failed. Black
+events are unfinished.</p>
+
+<div>
+ <% form_tag :action => "update", :id => @array_design.id do %>
+<table>
+ <tr>
+ <%= content_tag("td", "Comment:") %>
+ <%= content_tag("td", input("array_design", "comment")) %>
+ </tr>
+</table>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+</div>
+
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/app/views/category/_form.rhtml b/automation/admin/app/views/category/_form.rhtml
new file mode 100644
index 0000000..934b7dd
--- /dev/null
+++ b/automation/admin/app/views/category/_form.rhtml
@@ -0,0 +1,34 @@
+<%= error_messages_for 'category' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+</tr><table>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Ontology term:") %>
+ <%= content_tag("td", input("category", "ontology_term")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Display label:") %>
+ <%= content_tag("td", input("category", "display_label")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Can be a BioMaterialCharacteristic:") %>
+ <%= content_tag("td", check_box("category", "is_bmc")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Can be a FactorValue:") %>
+ <%= content_tag("td", check_box("category", "is_fv")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Is common to all spreadsheets:") %>
+ <%= content_tag("td", check_box("category", "is_common")) %>
+ </tr>
+</table>
+
+<p class="text">Use the check boxes to indicate whether a given
+category can be used as a BioMaterialCharacteristic or FactorValue
+(examples: Compound, Dose are FVs, but not BMCs). If a category should
+be asked of <em>all</em> submissions (e.g. Species,
+BioSourceProvider), check the "common" box.</p>
diff --git a/automation/admin/app/views/category/_title.rhtml b/automation/admin/app/views/category/_title.rhtml
new file mode 100644
index 0000000..66796f1
--- /dev/null
+++ b/automation/admin/app/views/category/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Characteristic Categories", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/category/edit.rhtml b/automation/admin/app/views/category/edit.rhtml
new file mode 100644
index 0000000..9ac789a
--- /dev/null
+++ b/automation/admin/app/views/category/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @category.id do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Update" %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/category/list.rhtml b/automation/admin/app/views/category/list.rhtml
new file mode 100644
index 0000000..97b6fa2
--- /dev/null
+++ b/automation/admin/app/views/category/list.rhtml
@@ -0,0 +1,42 @@
+<%= render_partial "title" %>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Ontology term") %>
+ <%= content_tag("th", "Display label") %>
+ <%= content_tag("th", "Is common") %>
+ <%= content_tag("th", "Can be a BMC") %>
+ <%= content_tag("th", "Can be a FV") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @categories.each do |cat| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => cat.id %>
+ </td>
+ <%= content_tag("td", cat.ontology_term) %>
+ <%= content_tag("td", cat.display_label) %>
+ <%= content_tag("td", cat.is_common? ? 'Yes' : 'No') %>
+ <%= content_tag("td", cat.is_bmc? ? 'Yes' : 'No') %>
+ <%= content_tag("td", cat.is_fv? ? 'Yes' : 'No') %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => cat.id},
+ :confirm => "Really delete category \"#{cat.ontology_term}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @categories, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= content_tag("div", link_to("Add new category",
+ :controller => "category",
+ :action => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/category/new.rhtml b/automation/admin/app/views/category/new.rhtml
new file mode 100644
index 0000000..d920995
--- /dev/null
+++ b/automation/admin/app/views/category/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Create" %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/design/_form.rhtml b/automation/admin/app/views/design/_form.rhtml
new file mode 100644
index 0000000..d7d7aaa
--- /dev/null
+++ b/automation/admin/app/views/design/_form.rhtml
@@ -0,0 +1,43 @@
+<%= error_messages_for 'design' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+</tr><table>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Display label:") %>
+ <%= content_tag("td", input("design", "display_label")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Ontology category:") %>
+ <%= content_tag("td", input("design", "ontology_category")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Ontology value:") %>
+ <%= content_tag("td", input("design", "ontology_value")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Design type:") %>
+ <%= content_tag("td", select(
+ "design",
+ "design_type",
+ ['biological',
+ 'methodological',
+ 'technological'
+ ])) %>
+ </tr>
+</table>
+
+<table>
+ <% Category.find(:all, :conditions => "is_deleted=0").sort_by { |c| c.ontology_term }.each do |cat| %>
+ <tr>
+ <td>
+ <%= check_box_tag("design[category_ids][]",
+ cat.id,
+ @design.categories.include?(cat)) %>
+ </td>
+ <%= content_tag("td", cat.ontology_term) %>
+ </tr>
+ <% end %>
+</table>
diff --git a/automation/admin/app/views/design/_title.rhtml b/automation/admin/app/views/design/_title.rhtml
new file mode 100644
index 0000000..1bbd5b8
--- /dev/null
+++ b/automation/admin/app/views/design/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Designs", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/design/edit.rhtml b/automation/admin/app/views/design/edit.rhtml
new file mode 100644
index 0000000..5ce34ba
--- /dev/null
+++ b/automation/admin/app/views/design/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @design.id do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Update" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/design/list.rhtml b/automation/admin/app/views/design/list.rhtml
new file mode 100644
index 0000000..c3cb44d
--- /dev/null
+++ b/automation/admin/app/views/design/list.rhtml
@@ -0,0 +1,40 @@
+<%= render_partial "title" %>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Display label") %>
+ <%= content_tag("th", "Ontology category") %>
+ <%= content_tag("th", "Ontology value") %>
+ <%= content_tag("th", "Design type") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @designs.each do |d| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => d.id %>
+ </td>
+ <%= content_tag("td", d.display_label) %>
+ <%= content_tag("td", d.ontology_category) %>
+ <%= content_tag("td", d.ontology_value) %>
+ <%= content_tag("td", d.design_type) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => d.id},
+ :confirm => "Really delete design \"#{d.ontology_value}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @designs, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= content_tag("div", link_to("Add new design",
+ :controller => "design",
+ :action => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/design/new.rhtml b/automation/admin/app/views/design/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/design/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Create" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/event/_form.rhtml b/automation/admin/app/views/event/_form.rhtml
new file mode 100644
index 0000000..1345a22
--- /dev/null
+++ b/automation/admin/app/views/event/_form.rhtml
@@ -0,0 +1,62 @@
+<%= error_messages_for 'event' %>
+
+<div>
+ <% form_tag :action => "update", :id => @event.id do %>
+
+<table>
+ <tr>
+ <%= content_tag("th", "Associated Array:") %>
+ <%= content_tag("td", ( @event.array_design.nil? ? '' :
+ link_to( @event.array_design.accession,
+ :controller => 'array_designs',
+ :action => 'show',
+ :id => @event.array_design.id) ) ) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Associated Experiment:") %>
+ <%= content_tag("td", ( @event.experiment.nil? ? '' :
+ link_to( @event.experiment.accession,
+ :controller => 'experiments',
+ :action => 'show',
+ :id => @event.experiment.id) ) ) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Event type:") %>
+ <%= content_tag("td", @event.event_type) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Operator:") %>
+ <%= content_tag("td", @event.operator) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Source Database:") %>
+ <%= content_tag("td", @event.source_db) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Target Database:") %>
+ <%= content_tag("td", @event.target_db) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Was Successful:") %>
+ <%= content_tag("td", @event.was_successful.nil? ? '' :
+ @event.was_successful? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Start Time:") %>
+ <%= content_tag("td", @event.start_time) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "End Time:") %>
+ <%= content_tag("td", @event.end_time) %>
+ </tr>
+ <tr>
+ <%= content_tag("th", "Comment:") %>
+ <%= content_tag("td", input("event", "comment")) %>
+ </tr>
+</table>
+
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/event/_title.rhtml b/automation/admin/app/views/event/_title.rhtml
new file mode 100644
index 0000000..e48f90d
--- /dev/null
+++ b/automation/admin/app/views/event/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Editing Event", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/event/edit.rhtml b/automation/admin/app/views/event/edit.rhtml
new file mode 100644
index 0000000..9a0b0ea
--- /dev/null
+++ b/automation/admin/app/views/event/edit.rhtml
@@ -0,0 +1,2 @@
+<%= render_partial "title" %>
+<%= render_partial "form" %>
diff --git a/automation/admin/app/views/experiments/_annotate.rhtml b/automation/admin/app/views/experiments/_annotate.rhtml
new file mode 100644
index 0000000..595caa0
--- /dev/null
+++ b/automation/admin/app/views/experiments/_annotate.rhtml
@@ -0,0 +1,52 @@
+<table>
+<tr>
+<th>Designs</th>
+<th>Materials</th>
+<th>Organisms</th>
+</tr>
+
+<tr>
+<td valign="top">
+<table>
+ <% Design.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |design| %>
+ <tr>
+ <td>
+ <%= check_box_tag("annotation[design_ids][]",
+ design.id,
+ @experiment.designs.include?(design)) %>
+ </td>
+ <%= content_tag("td", design.display_label) %>
+ </tr>
+ <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+ <% Material.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |material| %>
+ <tr>
+ <td>
+ <%= check_box_tag("annotation[material_ids][]",
+ material.id,
+ @experiment.materials.include?(material)) %>
+ </td>
+ <%= content_tag("td", material.display_label) %>
+ </tr>
+ <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+ <% Organism.find(:all, :conditions => "is_deleted=0 and taxon_id is not null").sort_by { |d| d.scientific_name }.each do |organism| %>
+ <tr>
+ <td>
+ <%= check_box_tag("annotation[organism_ids][]",
+ organism.id,
+ @experiment.organisms.include?(organism)) %>
+ </td>
+ <%= content_tag("td", organism.scientific_name) %>
+ </tr>
+ <% end %>
+</table>
+</td>
+</tr>
+</table>
\ No newline at end of file
diff --git a/automation/admin/app/views/experiments/_form.rhtml b/automation/admin/app/views/experiments/_form.rhtml
new file mode 100644
index 0000000..3aa1780
--- /dev/null
+++ b/automation/admin/app/views/experiments/_form.rhtml
@@ -0,0 +1,26 @@
+<%= error_messages_for 'experiment' %>
+
+<table>
+ <tr>
+ <%= content_tag("td", 'User login:') %>
+ <%= content_tag("td", select(
+ "experiment",
+ "user_id",
+ User.find(:all).collect { |t| [t.login, t.id] }.sort_by { |login, id| login },
+ { :include_blank => true })) %>
+ </tr>
+ <% Experiment.content_columns.each do |c| %>
+ <tr>
+ <%= content_tag("td", c.name.humanize.concat(":")) %>
+ <%= content_tag("td", input("experiment", c.name)) %>
+ </tr>
+ <% end %>
+ <tr>
+ <%= content_tag("td", "Spreadsheet:") %>
+ <%= content_tag("td", @experiment.spreadsheets.collect {|s| s.name}) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Data files:") %>
+ <%= content_tag("td", @experiment.data_files.collect {|d| d.name}) %>
+ </tr>
+</table>
diff --git a/automation/admin/app/views/experiments/_title.rhtml b/automation/admin/app/views/experiments/_title.rhtml
new file mode 100644
index 0000000..7c3e18b
--- /dev/null
+++ b/automation/admin/app/views/experiments/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "All Experiments", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/experiments/edit.rhtml b/automation/admin/app/views/experiments/edit.rhtml
new file mode 100644
index 0000000..b7f86e4
--- /dev/null
+++ b/automation/admin/app/views/experiments/edit.rhtml
@@ -0,0 +1,12 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @experiment.id do %>
+ <%= render_partial "form" %>
+ <%= hidden_field_tag("page", params[:page]) %>
+ <%= hidden_field_tag("search_term", params[:search_term]) %>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <%= render_partial "annotate" %>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/experiments/list.rhtml b/automation/admin/app/views/experiments/list.rhtml
new file mode 100644
index 0000000..9cc4cb7
--- /dev/null
+++ b/automation/admin/app/views/experiments/list.rhtml
@@ -0,0 +1,71 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on Accession or Comment (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= submit_tag("Search") %>
+ <%= content_tag("span", " List all results on one page:") %>
+ <%= check_box_tag("list_all", 1, params[:list_all] ) %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "Curated Name") %>
+ <%= content_tag("th", "No. of Samples") %>
+ <%= content_tag("th", "No. of Hybs") %>
+ <%= content_tag("th", "Raw Data") %>
+ <%= content_tag("th", "Processed Data") %>
+ <%= content_tag("th", "AE MIAME Score") %>
+ <%= content_tag("th", "AE DW Score") %>
+ <%= content_tag("th", "Release Date") %>
+ <%= content_tag("th", "Is Released") %>
+ <%= content_tag("th", "Loaded in DW") %>
+ <%= content_tag("th", "Comment") %>
+ </tr>
+
+ <% @experiments.each do |e| %>
+ <tr>
+ <td>
+ <%= link_to "Details", :action => "show",
+ :id => e.id,
+ :page => params[:page],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", e.accession) %>
+ <%= content_tag("td", e.curated_name) %>
+ <%= content_tag("td", e.num_samples) %>
+ <%= content_tag("td", e.num_hybridizations) %>
+ <%= content_tag("td", e.has_raw_data.nil? ? '' :
+ e.has_raw_data? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", e.has_processed_data.nil? ? '' :
+ e.has_processed_data? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", e.ae_miame_score) %>
+ <%= content_tag("td", e.ae_data_warehouse_score) %>
+ <%= content_tag("td", e.release_date ?
+ e.release_date.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", e.is_released.nil? ? '' :
+ e.is_released? ?
+ '<font color="green">PUBLIC</font>' :
+ '<font color="red">private</font>') %>
+ <%= content_tag("td", e.in_data_warehouse? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", truncate(e.comment, 30)) %>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @experiments, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+</div>
diff --git a/automation/admin/app/views/experiments/new.rhtml b/automation/admin/app/views/experiments/new.rhtml
new file mode 100644
index 0000000..47f9848
--- /dev/null
+++ b/automation/admin/app/views/experiments/new.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= content_tag("p", submit_tag("Create")) %>
+ <%= render_partial "annotate" %>
+ <%= content_tag("p", submit_tag("Create")) %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/experiments/show.rhtml b/automation/admin/app/views/experiments/show.rhtml
new file mode 100644
index 0000000..d973e45
--- /dev/null
+++ b/automation/admin/app/views/experiments/show.rhtml
@@ -0,0 +1,124 @@
+<%= content_tag("h1", @experiment.accession, :class => "title") %>
+<%= content_tag("h4", "This experiment is currently " +
+ (@experiment.is_released.nil? ? '' :
+ @experiment.is_released? ?
+ '<font color="green">Public</font>' :
+ '<font color="red">Private</font>') +
+ " (release date: " +
+ (@experiment.release_date ?
+ @experiment.release_date.strftime('%Y-%m-%d') :
+ "") + ")" ) %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<table width="100%">
+<tr>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("td", @experiment.accession) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Curated Name") %>
+ <%= content_tag("td", @experiment.curated_name) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Submitter's Description") %>
+ <%= content_tag("td", @experiment.submitter_description) %>
+</tr>
+</table>
+
+<hr/>
+
+<table width="100%">
+<tr>
+ <%= content_tag("th", "Species") %>
+ <%= content_tag("td", @experiment.organisms.collect{ |o| o.scientific_name}.join(", ")) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Array Designs") %>
+ <%= content_tag("td", @experiment.array_designs.collect{ |a| a.accession}.join(", ")) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Experimental Factors") %>
+ <%= content_tag("td", @experiment.factors.collect{ |f| f.name}.join(", ")) %>
+</tr>
+<tr>
+ <%= content_tag("th", "Quantitation Types") %>
+ <%= content_tag("td", @experiment.quantitation_types.collect{ |q| q.name}.join(", ")) %>
+</tr>
+</table>
+
+<hr/>
+
+<table width="100%">
+<tr>
+ <%= content_tag("th", "No. of Samples") %>
+ <%= content_tag("td", @experiment.num_samples) %>
+ <%= content_tag("th", "AE MIAME Score") %>
+ <%= content_tag("td", @experiment.ae_miame_score) %>
+ <%= content_tag("th", "Raw Data") %>
+ <%= content_tag("td", @experiment.has_raw_data.nil? ? '' :
+ @experiment.has_raw_data? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+</tr>
+<tr>
+ <%= content_tag("th", "No. of Hybridizations") %>
+ <%= content_tag("td", @experiment.num_hybridizations) %>
+ <%= content_tag("th", "AE DW Score") %>
+ <%= content_tag("td", @experiment.ae_data_warehouse_score) %>
+ <%= content_tag("th", "Processed Data") %>
+ <%= content_tag("td", @experiment.has_processed_data.nil? ? '' :
+ @experiment.has_processed_data? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+</tr>
+</table>
+
+<hr/>
+
+<table width="100%">
+<tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Event") %>
+ <%= content_tag("th", "Target DB") %>
+ <%= content_tag("th", "Operator") %>
+ <%= content_tag("th", "Start Time") %>
+ <%= content_tag("th", "End Time") %>
+ <%= content_tag("th", "Comment") %>
+</tr>
+<% for event in @experiment.events.sort_by{ |e| e.start_time || Time.local(0) } %>
+<tr>
+ <td>
+ <%= link_to "Edit", :controller => "event",
+ :action => "edit",
+ :id => event.id %>
+ </td>
+ <%= content_tag("td", "<font color='#{ (event.was_successful.nil? ? 'black' :
+ event.was_successful == 0 ? 'red' :
+ 'green') }'>" + event.event_type + '</font>') %>
+ <%= content_tag("td", event.target_db) %>
+ <%= content_tag("td", event.operator) %>
+ <%= content_tag("td", event.start_time) %>
+ <%= content_tag("td", event.end_time) %>
+ <%= content_tag("td", truncate(event.comment, 30)) %>
+</tr>
+<% end %>
+</table>
+
+<p class="text"><font color="green">Green</font> events were
+successful, while <font color="red">Red</font> events failed. Black
+events are unfinished.</p>
+
+<div>
+ <% form_tag :action => "update", :id => @experiment.id do %>
+<table>
+ <tr>
+ <%= content_tag("td", "Comment:") %>
+ <%= content_tag("td", input("experiment", "comment")) %>
+ </tr>
+</table>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+</div>
+
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/app/views/experiments/today_list.rhtml b/automation/admin/app/views/experiments/today_list.rhtml
new file mode 100644
index 0000000..df481e8
--- /dev/null
+++ b/automation/admin/app/views/experiments/today_list.rhtml
@@ -0,0 +1,61 @@
+<%= content_tag("h1", "Recently processed experiments (as of #{ @time_now.strftime("%Y-%m-%d") })", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Type") %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "Directory") %>
+ <%= content_tag("th", "Name") %>
+ <%= content_tag("th", "Checker score") %>
+ <%= content_tag("th", "MIAME score") %>
+ <%= content_tag("th", "DW status") %>
+ <%= content_tag("th", "Status") %>
+ <%= content_tag("th", "Date last processed") %>
+ <%= content_tag("th", "Curator") %>
+ <%= content_tag("th", "Comment") %>
+ </tr>
+
+ <% @experiments.each do |e| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => e.id,
+ :page => params[:page],
+ :experiment_type => params[:experiment_type],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", e.experiment_type.to_s) %>
+ <%= content_tag("td", e.accession) %>
+ <%= content_tag("td", e.experiment_type == 'MIAMExpress' ? '' : e.experiment_type + '_' + e.id.to_s) %>
+ <%= content_tag("td", e.name) %>
+ <%= content_tag("td", e.checker_score) %>
+ <%= content_tag("td", (e.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+ (e.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+ (e.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+ (e.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+ (e.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+ <%= content_tag("td", e.in_data_warehouse? ? '<font color="blue">Loaded</font>' :
+ e.data_warehouse_ready.nil? ? '' :
+ e.data_warehouse_ready.to_i.eql?(31) ? '<font color="green">YES</font>' :
+ e.data_warehouse_ready.to_i.eql?(30) ? '<font color="red">maybe</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", e.status) %>
+ <%= content_tag("td", e.date_last_processed ?
+ e.date_last_processed.localtime.strftime('%Y-%m-%d %H:%M:%S') :
+ "") %>
+ <%= content_tag("td", e.curator) %>
+ <%= content_tag("td", truncate(e.comment, 100)) %>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= content_tag("p", "Note that all times are GMT.") %>
+
+ <%= will_paginate @experiments, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+</div>
diff --git a/automation/admin/app/views/layouts/admin.rhtml b/automation/admin/app/views/layouts/admin.rhtml
new file mode 100644
index 0000000..6c24270
--- /dev/null
+++ b/automation/admin/app/views/layouts/admin.rhtml
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Submissions Tracking</title>
+ <%= stylesheet_link_tag "admin" %>
+ </head>
+ <body>
+
+ <div class="link-list" id="sidebar">
+ <div class="submenu">
+ <div class="submenutitle">
+ <%= content_tag("h4", "Submissions") %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Tab2MAGE experiments",
+ :controller => "tab2mages",
+ :action => "list",
+ :experiment_type => "Tab2MAGE" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "MIAMExpress experiments",
+ :controller => "miamexps",
+ :action => "list",
+ :experiment_type => "MIAMExpress" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "MIAMExpress arrays",
+ :controller => "array_designs",
+ :action => "list" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "MUGEN experiments",
+ :controller => "tab2mages",
+ :action => "list",
+ :experiment_type => "MUGEN" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "GEO experiments",
+ :controller => "tab2mages",
+ :action => "list",
+ :experiment_type => "GEO" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "MAGE-TAB experiments",
+ :controller => "tab2mages",
+ :action => "list",
+ :experiment_type => "MAGE-TAB" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Protocols",
+ :controller => "protocols",
+ :action => "list" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Recent experiments",
+ :controller => "experiments",
+ :action => "today_list" %>
+ </div>
+ </div>
+
+ <div class="submenu">
+ <div class="submenutitle">
+ <%= content_tag("h4", "Tracking") %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "All Experiments",
+ :controller => "experiments",
+ :action => "list" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "All Arrays",
+ :controller => "array_designs",
+ :action => "list_all" %>
+ </div>
+
+ </div>
+ <div class="submenu">
+ <div class="submenutitle">
+ <%= content_tag("h4", "Ontology") %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Categories",
+ :controller => "category",
+ :action => "list" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Taxa",
+ :controller => "taxon",
+ :action => "list" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Organisms",
+ :controller => "organism",
+ :action => "list" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Designs",
+ :controller => "design",
+ :action => "list" %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Materials",
+ :controller => "material",
+ :action => "list" %>
+ </div>
+ </div>
+
+ <div class="submenu">
+ <div class="submenutitle">
+ <%= content_tag("h4", "Admin") %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "User Accounts",
+ :controller => "users",
+ :action => "list" %>
+ </div>
+ </div>
+
+ <div class="submenu">
+ <% if !session[:user].nil? %>
+ <div class="submenutitle">
+ <%= content_tag("h4", "Account") %>
+ </div>
+
+ <div class="menu-item">
+ <%= link_to "Log out",
+ :controller => "account",
+ :action => "logout" %>
+ </div>
+ <% end %>
+ </div>
+
+ </div>
+ <div id="main">
+
+ <%= @content_for_layout %>
+
+ </div>
+ </body>
+</html>
\ No newline at end of file
diff --git a/automation/admin/app/views/material/_form.rhtml b/automation/admin/app/views/material/_form.rhtml
new file mode 100644
index 0000000..2bbfcdf
--- /dev/null
+++ b/automation/admin/app/views/material/_form.rhtml
@@ -0,0 +1,33 @@
+<%= error_messages_for 'material' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+</tr><table>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Display label:") %>
+ <%= content_tag("td", input("material", "display_label")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Ontology category:") %>
+ <%= content_tag("td", input("material", "ontology_category")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Ontology value:") %>
+ <%= content_tag("td", input("material", "ontology_value")) %>
+ </tr>
+</table>
+
+<table>
+ <% Category.find(:all).sort_by { |c| c.ontology_term }.each do |cat| %>
+ <tr>
+ <td>
+ <%= check_box_tag("material[category_ids][]",
+ cat.id,
+ @material.categories.include?(cat)) %>
+ </td>
+ <%= content_tag("td", cat.ontology_term) %>
+ </tr>
+ <% end %>
+</table>
diff --git a/automation/admin/app/views/material/_title.rhtml b/automation/admin/app/views/material/_title.rhtml
new file mode 100644
index 0000000..54f390c
--- /dev/null
+++ b/automation/admin/app/views/material/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Material types", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/material/edit.rhtml b/automation/admin/app/views/material/edit.rhtml
new file mode 100644
index 0000000..317f304
--- /dev/null
+++ b/automation/admin/app/views/material/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @material.id do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Update" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/material/list.rhtml b/automation/admin/app/views/material/list.rhtml
new file mode 100644
index 0000000..ae79c5b
--- /dev/null
+++ b/automation/admin/app/views/material/list.rhtml
@@ -0,0 +1,38 @@
+<%= render_partial "title" %>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Display label") %>
+ <%= content_tag("th", "Ontology category") %>
+ <%= content_tag("th", "Ontology value") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @materials.each do |m| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => m.id %>
+ </td>
+ <%= content_tag("td", m.display_label) %>
+ <%= content_tag("td", m.ontology_category) %>
+ <%= content_tag("td", m.ontology_value) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => m.id},
+ :confirm => "Really delete material \"#{m.ontology_value}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @materials, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= content_tag("div", link_to("Add new material",
+ :controller => "material",
+ :action => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/material/new.rhtml b/automation/admin/app/views/material/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/material/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Create" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/miamexps/_aedwinfo.rhtml b/automation/admin/app/views/miamexps/_aedwinfo.rhtml
new file mode 100644
index 0000000..ebbb279
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_aedwinfo.rhtml
@@ -0,0 +1,9 @@
+
+ <p class="text">Explanation of AEDW scores:</p>
+ <ul>
+ <li><b>T</b>: Experiment design type is appropriate.</li>
+ <li><b>H</b>: Number of hybridizations is large enough.</li>
+ <li><b>A</b>: Arrays are loaded into AEDW.</li>
+ <li><b>F</b>: FactorValues provided.</li>
+ <li><b>D</b>: Raw and normalized data provided.</li>
+ </ul>
diff --git a/automation/admin/app/views/miamexps/_form.rhtml b/automation/admin/app/views/miamexps/_form.rhtml
new file mode 100644
index 0000000..729a40e
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_form.rhtml
@@ -0,0 +1,86 @@
+<%= error_messages_for 'experiment' %>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Submission ID") %>
+ <%= content_tag("td", @experiment.miamexpress_subid) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Login") %>
+ <%= content_tag("td", @experiment.miamexpress_login) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Name") %>
+ <%= content_tag("td", @experiment.name) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Accession") %>
+ <%= content_tag("td", input("experiment", "accession")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Status") %>
+ <%= content_tag("td", select(
+ "experiment",
+ "status",
+ ['Waiting',
+ 'Retrieving info from MX',
+ 'Checking in progress',
+ '** CHECKER CRASH **',
+ 'Checking failed',
+ 'Checking passed',
+ 'MAGE-ML export',
+ 'Export postponed',
+ 'Export failed',
+ 'Complete',
+ ])) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Checker score") %>
+ <%= content_tag("td", @experiment.checker_score) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "MIAME score") %>
+ <%= content_tag("td", (@experiment.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+ (@experiment.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+ (@experiment.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+ (@experiment.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+ (@experiment.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "DW ready") %>
+ <%= content_tag("td", (@experiment.data_warehouse_ready.to_i.&(1).nonzero? ? 'T' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(2).nonzero? ? 'H' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(4).nonzero? ? 'A' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(8).nonzero? ? 'F' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(16).nonzero? ? 'D' : '')) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Loaded in DW:") %>
+ <%= content_tag("td", @experiment.in_data_warehouse? ? 'YES' : 'no') %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Software") %>
+ <%= content_tag("td", input("experiment", "software")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Batchloader used:") %>
+ <%= content_tag("td", check_box("experiment", "is_mx_batchloader")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Curator") %>
+ <%= content_tag("td", input("experiment", "curator")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Date submitted:") %>
+ <%= content_tag("td", @experiment.date_submitted) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Date last processed:") %>
+ <%= content_tag("td", @experiment.date_last_processed) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Comment") %>
+ <%= content_tag("td", input("experiment", "comment")) %>
+ </tr>
+</table>
+
diff --git a/automation/admin/app/views/miamexps/_miameinfo.rhtml b/automation/admin/app/views/miamexps/_miameinfo.rhtml
new file mode 100644
index 0000000..c12e465
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_miameinfo.rhtml
@@ -0,0 +1,9 @@
+
+ <p class="text">Explanation of MIAME scores:</p>
+ <ul>
+ <li><b>R</b>: Raw data provided.</li>
+ <li><b>N</b>: Normalized data provided.</li>
+ <li><b>F</b>: FactorValues provided.</li>
+ <li><b>P</b>: Protocols provided (data transformation only at the moment).</li>
+ <li><b>A</b>: Arrays are all MIAME compliant.</li>
+ </ul>
diff --git a/automation/admin/app/views/miamexps/_title.rhtml b/automation/admin/app/views/miamexps/_title.rhtml
new file mode 100644
index 0000000..1554546
--- /dev/null
+++ b/automation/admin/app/views/miamexps/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "#{params[:experiment_type]} experiments", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/miamexps/edit.rhtml b/automation/admin/app/views/miamexps/edit.rhtml
new file mode 100644
index 0000000..8c64832
--- /dev/null
+++ b/automation/admin/app/views/miamexps/edit.rhtml
@@ -0,0 +1,15 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @experiment.id do %>
+ <%= render_partial "form" %>
+ <%= hidden_field_tag("page", params[:page]) %>
+ <%= hidden_field_tag("search_term", params[:search_term]) %>
+ <%= hidden_field_tag("experiment_type", @experiment.experiment_type) %>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+
+ <%= render_partial "miameinfo" %>
+ <%= render_partial "aedwinfo" %>
+
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/miamexps/list.rhtml b/automation/admin/app/views/miamexps/list.rhtml
new file mode 100644
index 0000000..7d8342c
--- /dev/null
+++ b/automation/admin/app/views/miamexps/list.rhtml
@@ -0,0 +1,72 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on SubId, Login, Accession or Name (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= hidden_field_tag("experiment_type", params[:experiment_type]) %>
+ <%= submit_tag("Search") %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "SubId") %>
+ <%= content_tag("th", "Login") %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "Directory") %>
+ <%= content_tag("th", "Name") %>
+ <%= content_tag("th", "Checker score") %>
+ <%= content_tag("th", "MIAME score") %>
+ <%= content_tag("th", "DW status") %>
+ <%= content_tag("th", "Status") %>
+ <%= content_tag("th", "Date last processed") %>
+ <%= content_tag("th", "Curator") %>
+ <%= content_tag("th", "Software") %>
+ <%= content_tag("th", "Comment") %>
+ </tr>
+
+ <% @experiments.each do |e| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => e.id,
+ :page => params[:page],
+ :experiment_type => params[:experiment_type],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", e.miamexpress_subid) %>
+ <%= content_tag("td", e.miamexpress_login) %>
+ <%= content_tag("td", e.accession) %>
+ <%= content_tag("td", e.experiment_type.to_s.eql?('MIAMExpress') ? '' : e.experiment_type.to_s + "_" + e.id.to_s) %>
+ <%= content_tag("td", e.name) %>
+ <%= content_tag("td", e.checker_score) %>
+ <%= content_tag("td", (e.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+ (e.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+ (e.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+ (e.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+ (e.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+ <%= content_tag("td", e.in_data_warehouse? ? '<font color="blue">Loaded</font>' :
+ e.data_warehouse_ready.nil? ? '' :
+ e.data_warehouse_ready.to_i.eql?(31) ? '<font color="green">YES</font>' :
+ e.data_warehouse_ready.to_i.eql?(30) ? '<font color="red">maybe</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", e.status) %>
+ <%= content_tag("td", e.date_last_processed ?
+ e.date_last_processed.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", e.curator) %>
+ <%= content_tag("td", e.software) %>
+ <%= content_tag("td", truncate(e.comment, 30)) %>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @experiments, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= render_partial "miameinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/organism/_form.rhtml b/automation/admin/app/views/organism/_form.rhtml
new file mode 100644
index 0000000..dab395a
--- /dev/null
+++ b/automation/admin/app/views/organism/_form.rhtml
@@ -0,0 +1,25 @@
+<%= error_messages_for 'organism' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+<td><a href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=Taxonomy">NCBI taxonomy webpage</a></td>
+</tr><table>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Scientific name:") %>
+ <%= content_tag("td", input("organism", "scientific_name")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Common name:") %>
+ <%= content_tag("td", input("organism", "common_name")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Accession:") %>
+ <%= content_tag("td", input("organism", "accession")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Taxon common name:") %>
+ <%= content_tag("td", select("organism", "taxon_id", Taxon.find(:all, :conditions => 'is_deleted=0').collect { |t| [t.common_name, t.id] }.sort_by { |name, id| name })) %>
+ </tr>
+</table>
diff --git a/automation/admin/app/views/organism/_title.rhtml b/automation/admin/app/views/organism/_title.rhtml
new file mode 100644
index 0000000..4022a5a
--- /dev/null
+++ b/automation/admin/app/views/organism/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Organisms", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/organism/edit.rhtml b/automation/admin/app/views/organism/edit.rhtml
new file mode 100644
index 0000000..78775e1
--- /dev/null
+++ b/automation/admin/app/views/organism/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @organism.id do %>
+ <%= render_partial "form" %>
+ <%= hidden_field_tag("page", params[:page]) %>
+ <%= hidden_field_tag("search_term", params[:search_term]) %>
+ <%= submit_tag "Update" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/organism/list.rhtml b/automation/admin/app/views/organism/list.rhtml
new file mode 100644
index 0000000..7a72ad5
--- /dev/null
+++ b/automation/admin/app/views/organism/list.rhtml
@@ -0,0 +1,59 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on Scientific Name (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= submit_tag("Search") %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Scientific name") %>
+ <%= content_tag("th", "Common name") %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "Taxon") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @organisms.each do |o| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => o.id,
+ :page => params[:page],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", o.scientific_name) %>
+ <%= content_tag("td", o.common_name) %>
+ <%= content_tag("td", o.accession) %>
+ <%= content_tag("td", o.taxon.nil? ? '' : o.taxon.scientific_name) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => o.id,
+ :page => params[:page],
+ :search_term => @search_term },
+ :confirm => "Really delete organism \"#{o.scientific_name}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @organisms, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <p class="text"><em>Note:</em> Organisms must be
+ assigned to taxa for them to be displayed in the public submissions web
+ interface. If you wish to enable one of the organisms listed above
+ for submissions you need to assign it to a taxon; preferably one
+ with the appropriate annotation categories selected, so that the
+ template spreadsheets contain the desired columns.</p>
+
+ <%= content_tag("div", link_to("Add new organism",
+ :controller => "organism",
+ :action => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/organism/new.rhtml b/automation/admin/app/views/organism/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/organism/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Create" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/protocols/_form.rhtml b/automation/admin/app/views/protocols/_form.rhtml
new file mode 100644
index 0000000..3574d01
--- /dev/null
+++ b/automation/admin/app/views/protocols/_form.rhtml
@@ -0,0 +1,28 @@
+<%= error_messages_for 'protocol' %>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Accession:") %>
+ <%= content_tag("td", input("protocol", "accession")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "User accession:") %>
+ <%= content_tag("td", input("protocol", "user_accession")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Expt accession:") %>
+ <%= content_tag("td", input("protocol", "expt_accession")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Name:") %>
+ <%= content_tag("td", input("protocol", "name")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Date last processed:") %>
+ <%= content_tag("td", @protocol.date_last_processed) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Comment:") %>
+ <%= content_tag("td", input("protocol", "comment")) %>
+ </tr>
+</table>
diff --git a/automation/admin/app/views/protocols/_title.rhtml b/automation/admin/app/views/protocols/_title.rhtml
new file mode 100644
index 0000000..37cb4c7
--- /dev/null
+++ b/automation/admin/app/views/protocols/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Tab2MAGE protocols", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/protocols/edit.rhtml b/automation/admin/app/views/protocols/edit.rhtml
new file mode 100644
index 0000000..2aa3ca2
--- /dev/null
+++ b/automation/admin/app/views/protocols/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @protocol.id do %>
+ <%= render_partial "form" %>
+ <%= hidden_field_tag("page", params[:page]) %>
+ <%= hidden_field_tag("search_term", params[:search_term]) %>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/protocols/list.rhtml b/automation/admin/app/views/protocols/list.rhtml
new file mode 100644
index 0000000..2e13003
--- /dev/null
+++ b/automation/admin/app/views/protocols/list.rhtml
@@ -0,0 +1,58 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on Accession, User Accession, Expt Accession or Name (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= submit_tag("Search") %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "User accession") %>
+ <%= content_tag("th", "Expt accession") %>
+ <%= content_tag("th", "Name") %>
+ <%= content_tag("th", "Date last processed") %>
+ <%= content_tag("th", "Comment") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @protocols.each do |p| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => p.id,
+ :page => params[:page],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", p.accession) %>
+ <%= content_tag("td", p.user_accession) %>
+ <%= content_tag("td", p.expt_accession) %>
+ <%= content_tag("td", p.name) %>
+ <%= content_tag("td", p.date_last_processed ?
+ p.date_last_processed.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", p.comment) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => p.id,
+ :page => params[:page],
+ :search_term => @search_term },
+ :confirm => "Really delete protocol \"#{p.accession}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @protocols, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= content_tag("div", link_to("Add new protocol",
+ :controller => "protocols",
+ :action => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/protocols/new.rhtml b/automation/admin/app/views/protocols/new.rhtml
new file mode 100644
index 0000000..c70be0f
--- /dev/null
+++ b/automation/admin/app/views/protocols/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= content_tag("p", submit_tag("Create")) %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/protocols/show.rhtml b/automation/admin/app/views/protocols/show.rhtml
new file mode 100644
index 0000000..92c6dde
--- /dev/null
+++ b/automation/admin/app/views/protocols/show.rhtml
@@ -0,0 +1,8 @@
+<% for column in Protocol.content_columns %>
+<p>
+ <b><%= column.human_name %>:</b> <%=h @protocol.send(column.name) %>
+</p>
+<% end %>
+
+<%= link_to 'Edit', :action => 'edit', :id => @protocol %> |
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/app/views/tab2mages/_aedwinfo.rhtml b/automation/admin/app/views/tab2mages/_aedwinfo.rhtml
new file mode 100644
index 0000000..ebbb279
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_aedwinfo.rhtml
@@ -0,0 +1,9 @@
+
+ <p class="text">Explanation of AEDW scores:</p>
+ <ul>
+ <li><b>T</b>: Experiment design type is appropriate.</li>
+ <li><b>H</b>: Number of hybridizations is large enough.</li>
+ <li><b>A</b>: Arrays are loaded into AEDW.</li>
+ <li><b>F</b>: FactorValues provided.</li>
+ <li><b>D</b>: Raw and normalized data provided.</li>
+ </ul>
diff --git a/automation/admin/app/views/tab2mages/_annotate.rhtml b/automation/admin/app/views/tab2mages/_annotate.rhtml
new file mode 100644
index 0000000..595caa0
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_annotate.rhtml
@@ -0,0 +1,52 @@
+<table>
+<tr>
+<th>Designs</th>
+<th>Materials</th>
+<th>Organisms</th>
+</tr>
+
+<tr>
+<td valign="top">
+<table>
+ <% Design.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |design| %>
+ <tr>
+ <td>
+ <%= check_box_tag("annotation[design_ids][]",
+ design.id,
+ @experiment.designs.include?(design)) %>
+ </td>
+ <%= content_tag("td", design.display_label) %>
+ </tr>
+ <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+ <% Material.find(:all, :conditions => "is_deleted=0").sort_by { |d| d.ontology_value }.each do |material| %>
+ <tr>
+ <td>
+ <%= check_box_tag("annotation[material_ids][]",
+ material.id,
+ @experiment.materials.include?(material)) %>
+ </td>
+ <%= content_tag("td", material.display_label) %>
+ </tr>
+ <% end %>
+</table>
+</td>
+<td valign="top">
+<table>
+ <% Organism.find(:all, :conditions => "is_deleted=0 and taxon_id is not null").sort_by { |d| d.scientific_name }.each do |organism| %>
+ <tr>
+ <td>
+ <%= check_box_tag("annotation[organism_ids][]",
+ organism.id,
+ @experiment.organisms.include?(organism)) %>
+ </td>
+ <%= content_tag("td", organism.scientific_name) %>
+ </tr>
+ <% end %>
+</table>
+</td>
+</tr>
+</table>
\ No newline at end of file
diff --git a/automation/admin/app/views/tab2mages/_form.rhtml b/automation/admin/app/views/tab2mages/_form.rhtml
new file mode 100644
index 0000000..fef9e9e
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_form.rhtml
@@ -0,0 +1,91 @@
+<%= error_messages_for 'experiment' %>
+
+ <tr>
+ <%= content_tag("td", "Accession:") %>
+ <%= content_tag("td", input("experiment", "accession")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Name:") %>
+ <%= content_tag("td", input("experiment", "name")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Status:") %>
+ <%= content_tag("td", select(
+ "experiment",
+ "status",
+ ['Waiting',
+ 'Retrieving info from MX',
+ 'Checking in progress',
+ '** CHECKER CRASH **',
+ 'Checking failed',
+ 'Checking passed',
+ 'MAGE-ML export',
+ 'Export postponed',
+ 'Export failed',
+ 'Complete',
+ ])) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Directory:") %>
+ <%= content_tag("td", @experiment.experiment_type.to_s + "_" + @experiment.id.to_s) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Spreadsheet:") %>
+ <%= content_tag("td", @experiment.spreadsheets.find(:all, :conditions => "is_deleted=0").collect{|s| s.name}.join("<br>")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Data files:") %>
+ <%= content_tag("td", @experiment.data_files.find(:all, :conditions => "is_deleted=0").collect{|d| d.name}.join("<br>")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Checker score:") %>
+ <%= content_tag("td", @experiment.checker_score) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "MIAME score") %>
+ <%= content_tag("td", (@experiment.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+ (@experiment.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+ (@experiment.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+ (@experiment.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+ (@experiment.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "DW ready:") %>
+ <%= content_tag("td", (@experiment.data_warehouse_ready.to_i.&(1).nonzero? ? 'T' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(2).nonzero? ? 'H' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(4).nonzero? ? 'A' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(8).nonzero? ? 'F' : '') +
+ (@experiment.data_warehouse_ready.to_i.&(16).nonzero? ? 'D' : '')) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Loaded in DW:") %>
+ <%= content_tag("td", @experiment.in_data_warehouse? ? 'YES' : 'no') %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Affymetrix:") %>
+ <%= content_tag("td", check_box("experiment", "is_affymetrix")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Software:") %>
+ <%= content_tag("td", input("experiment", "software")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Curator:") %>
+ <%= content_tag("td", input("experiment", "curator")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Date last edited:") %>
+ <%= content_tag("td", @experiment.date_last_edited) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Date submitted:") %>
+ <%= content_tag("td", @experiment.date_submitted) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Date last processed:") %>
+ <%= content_tag("td", @experiment.date_last_processed) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Comment:") %>
+ <%= content_tag("td", input("experiment", "comment")) %>
+ </tr>
diff --git a/automation/admin/app/views/tab2mages/_miameinfo.rhtml b/automation/admin/app/views/tab2mages/_miameinfo.rhtml
new file mode 100644
index 0000000..c12e465
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_miameinfo.rhtml
@@ -0,0 +1,9 @@
+
+ <p class="text">Explanation of MIAME scores:</p>
+ <ul>
+ <li><b>R</b>: Raw data provided.</li>
+ <li><b>N</b>: Normalized data provided.</li>
+ <li><b>F</b>: FactorValues provided.</li>
+ <li><b>P</b>: Protocols provided (data transformation only at the moment).</li>
+ <li><b>A</b>: Arrays are all MIAME compliant.</li>
+ </ul>
diff --git a/automation/admin/app/views/tab2mages/_title.rhtml b/automation/admin/app/views/tab2mages/_title.rhtml
new file mode 100644
index 0000000..1554546
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "#{params[:experiment_type]} experiments", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/tab2mages/edit.rhtml b/automation/admin/app/views/tab2mages/edit.rhtml
new file mode 100644
index 0000000..22a3b2c
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/edit.rhtml
@@ -0,0 +1,35 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @experiment.id do %>
+
+<table>
+ <tr>
+ <%= content_tag("td", 'User login:') %>
+ <%= content_tag("td", select(
+ "experiment",
+ "user_id",
+ User.find(:all).collect { |t| [t.login, t.id] }.sort_by { |login, id| login },
+ { :include_blank => true })) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "In curation:") %>
+ <%= content_tag("td", check_box("experiment", "in_curation")) %>
+ </tr>
+
+ <%= render_partial "form" %>
+
+</table>
+
+ <%= hidden_field_tag("experiment_type", @experiment.experiment_type) %>
+ <%= hidden_field_tag("page", params[:page]) %>
+ <%= hidden_field_tag("search_term", params[:search_term]) %>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <%= render_partial "annotate" %>
+ <%= content_tag("p", submit_tag("Update")) %>
+ <% end %>
+
+ <%= render_partial "miameinfo" %>
+ <%= render_partial "aedwinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/tab2mages/list.rhtml b/automation/admin/app/views/tab2mages/list.rhtml
new file mode 100644
index 0000000..9a8bf4b
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/list.rhtml
@@ -0,0 +1,97 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on Accession, User, Name or Comment (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= hidden_field_tag("experiment_type", params[:experiment_type]) %>
+ <%= submit_tag("Search") %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", "User") %>
+ <%= content_tag("th", "Directory") %>
+ <%= content_tag("th", "Name") %>
+ <%= content_tag("th", "Checker score") %>
+ <%= content_tag("th", "MIAME score") %>
+ <%= content_tag("th", "DW status") %>
+ <%= content_tag("th", "In curation") %>
+ <%= content_tag("th", "Status") %>
+ <%= content_tag("th", "Software") %>
+ <%= content_tag("th", "Date last edited") %>
+ <%= content_tag("th", "Date submitted") %>
+ <%= content_tag("th", "Date last processed") %>
+ <%= content_tag("th", "Curator") %>
+ <%= content_tag("th", "Comment") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @experiments.each do |e| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => e.id,
+ :page => params[:page],
+ :experiment_type => params[:experiment_type],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", e.accession) %>
+ <%= content_tag("td", e.user ? e.user.login : nil) %>
+ <%= content_tag("td", e.experiment_type.to_s + "_" + e.id.to_s) %>
+ <%= content_tag("td", e.name) %>
+ <%= content_tag("td", e.checker_score) %>
+ <%= content_tag("td", (e.miame_score.to_i.&(1).nonzero? ? 'R' : '') +
+ (e.miame_score.to_i.&(2).nonzero? ? 'N' : '') +
+ (e.miame_score.to_i.&(4).nonzero? ? 'F' : '') +
+ (e.miame_score.to_i.&(8).nonzero? ? 'P' : '') +
+ (e.miame_score.to_i.&(16).nonzero? ? 'A' : '')) %>
+ <%= content_tag("td", e.in_data_warehouse? ? '<font color="blue">Loaded</font>' :
+ e.data_warehouse_ready.nil? ? '' :
+ e.data_warehouse_ready.to_i.eql?(31) ? '<font color="green">YES</font>' :
+ e.data_warehouse_ready.to_i.eql?(30) ? '<font color="red">maybe</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", e.in_curation.nil? ? '' :
+ e.in_curation? ?
+ '<font color="green">YES</font>' :
+ '<font color="red">no</font>') %>
+ <%= content_tag("td", e.status) %>
+ <%= content_tag("td", e.software) %>
+ <%= content_tag("td", e.date_last_edited ?
+ e.date_last_edited.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", e.date_submitted ?
+ e.date_submitted.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", e.date_last_processed ?
+ e.date_last_processed.strftime('%Y-%m-%d') :
+ "") %>
+ <%= content_tag("td", e.curator) %>
+ <%= content_tag("td", truncate(e.comment, 30)) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => e.id,
+ :page => params[:page],
+ :experiment_type => params[:experiment_type],
+ :search_term => @search_term },
+ :confirm => "Really delete experiment \"#{e.id}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @experiments, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= content_tag("div", link_to("Add new #{params[:experiment_type]} experiment",
+ :controller => "tab2mages",
+ :experiment_type => params[:experiment_type],
+ :action => "new"), { :class => 'footer' } ) %>
+
+ <%= render_partial "miameinfo" %>
+
+</div>
diff --git a/automation/admin/app/views/tab2mages/new.rhtml b/automation/admin/app/views/tab2mages/new.rhtml
new file mode 100644
index 0000000..599e318
--- /dev/null
+++ b/automation/admin/app/views/tab2mages/new.rhtml
@@ -0,0 +1,13 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+<table>
+ <%= render_partial "form" %>
+</table>
+ <%= hidden_field_tag("experiment[experiment_type]", params[:experiment_type]) %>
+ <%= content_tag("p", submit_tag("Create")) %>
+ <%= render_partial "annotate" %>
+ <%= content_tag("p", submit_tag("Create")) %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/taxon/_form.rhtml b/automation/admin/app/views/taxon/_form.rhtml
new file mode 100644
index 0000000..9a933ad
--- /dev/null
+++ b/automation/admin/app/views/taxon/_form.rhtml
@@ -0,0 +1,34 @@
+<%= error_messages_for 'taxon' %>
+
+<table><tr>
+<td><a href="http://mged.sourceforge.net/ontologies/MOhtml/">MGED Ontology webpage</a></td>
+<td><a href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=Taxonomy">NCBI taxonomy webpage</a></td>
+</tr><table>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Scientific name:") %>
+ <%= content_tag("td", input("taxon", "scientific_name")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Common name:") %>
+ <%= content_tag("td", input("taxon", "common_name")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Accession:") %>
+ <%= content_tag("td", input("taxon", "accession")) %>
+ </tr>
+</table>
+
+<table>
+ <% Category.find(:all).sort_by { |c| c.ontology_term }.each do |cat| %>
+ <tr>
+ <td>
+ <%= check_box_tag("taxon[category_ids][]",
+ cat.id,
+ @taxon.categories.include?(cat)) %>
+ </td>
+ <%= content_tag("td", cat.ontology_term) %>
+ </tr>
+ <% end %>
+</table>
diff --git a/automation/admin/app/views/taxon/_title.rhtml b/automation/admin/app/views/taxon/_title.rhtml
new file mode 100644
index 0000000..712ed3a
--- /dev/null
+++ b/automation/admin/app/views/taxon/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "Taxa", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/taxon/category_table.rhtml b/automation/admin/app/views/taxon/category_table.rhtml
new file mode 100644
index 0000000..752e2c3
--- /dev/null
+++ b/automation/admin/app/views/taxon/category_table.rhtml
@@ -0,0 +1,21 @@
+<%= render_partial "title" %>
+
+<%= content_tag("p", link_to("Taxa list",
+ :controller => "taxon",
+ :action => "list"), { :class => 'footer' } ) %>
+
+<table>
+ <tr>
+ <%= content_tag("th", "Categories") %>
+ <% @taxons.each do |t| %>
+ <%= content_tag("th", t.common_name) %>
+ <% end %>
+ <% @categories.each do |c| %>
+ <tr>
+ <%= content_tag("th", c.ontology_term) %>
+ <% @taxons.each do |t| %>
+ <%= content_tag("td", (c.taxons.include?(t) ? "X" : ""), "align" => "center" ) %>
+ <% end %>
+ </tr>
+ <% end %>
+</table>
\ No newline at end of file
diff --git a/automation/admin/app/views/taxon/edit.rhtml b/automation/admin/app/views/taxon/edit.rhtml
new file mode 100644
index 0000000..9fc5569
--- /dev/null
+++ b/automation/admin/app/views/taxon/edit.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @taxon.id do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Update" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/taxon/list.rhtml b/automation/admin/app/views/taxon/list.rhtml
new file mode 100644
index 0000000..e60568f
--- /dev/null
+++ b/automation/admin/app/views/taxon/list.rhtml
@@ -0,0 +1,43 @@
+<%= render_partial "title" %>
+
+<%= content_tag("p", link_to("Taxa categories table",
+ :controller => "taxon",
+ :action => "category_table"), { :class => 'footer' } ) %>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Scientific name") %>
+ <%= content_tag("th", "Common name") %>
+ <%= content_tag("th", "Accession") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @taxons.each do |t| %>
+
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => t.id %>
+ </td>
+ <%= content_tag("td", t.scientific_name) %>
+ <%= content_tag("td", t.common_name) %>
+ <%= content_tag("td", t.accession) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => t.id},
+ :confirm => "Really delete taxon \"#{t.scientific_name}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @taxons, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= content_tag("div", link_to("Add new taxon",
+ :controller => "taxon",
+ :action => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/taxon/new.rhtml b/automation/admin/app/views/taxon/new.rhtml
new file mode 100644
index 0000000..c2334f3
--- /dev/null
+++ b/automation/admin/app/views/taxon/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Create" %>
+ <% end %>
+</div>
\ No newline at end of file
diff --git a/automation/admin/app/views/users/_form.rhtml b/automation/admin/app/views/users/_form.rhtml
new file mode 100644
index 0000000..dff793b
--- /dev/null
+++ b/automation/admin/app/views/users/_form.rhtml
@@ -0,0 +1,31 @@
+<%= error_messages_for 'user' %>
+
+<table>
+ <tr>
+ <%= content_tag("td", "Login:") %>
+ <%= content_tag("td", input("user",
+ :login)) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Name:") %>
+ <%= content_tag("td", input("user",
+ :name)) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Password:") %>
+ <%= content_tag("td", input("user",
+ :password)) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Confirm password:") %>
+ <%= content_tag("td", input("user",
+ :password,
+ "id" => "user_password_confirmation",
+ "name" => "user[password_confirmation]")) %>
+ </tr>
+ <tr>
+ <%= content_tag("td", "Email address:") %>
+ <%= content_tag("td", input("user",
+ :email)) %>
+ </tr>
+</table>
diff --git a/automation/admin/app/views/users/_title.rhtml b/automation/admin/app/views/users/_title.rhtml
new file mode 100644
index 0000000..6af30bf
--- /dev/null
+++ b/automation/admin/app/views/users/_title.rhtml
@@ -0,0 +1,3 @@
+<%= content_tag("h1", "User Administration", :class => "title") %>
+
+<% if flash[:notice] %><div class="notice"><%= flash[:notice] %></div><% end %>
diff --git a/automation/admin/app/views/users/edit.rhtml b/automation/admin/app/views/users/edit.rhtml
new file mode 100644
index 0000000..7581e6e
--- /dev/null
+++ b/automation/admin/app/views/users/edit.rhtml
@@ -0,0 +1,10 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => "update", :id => @user.id do %>
+ <%= render_partial "form" %>
+ <%= hidden_field_tag("page", params[:page]) %>
+ <%= hidden_field_tag("search_term", params[:search_term]) %>
+ <%= submit_tag "Update" %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/users/list.rhtml b/automation/admin/app/views/users/list.rhtml
new file mode 100644
index 0000000..7df5675
--- /dev/null
+++ b/automation/admin/app/views/users/list.rhtml
@@ -0,0 +1,54 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag do %>
+ <%= content_tag("span", "Search on Login, Name or Email (wildcards: *, ?): ") %>
+ <%= text_field_tag("search_term", @search_term) %>
+ <%= submit_tag("Search") %>
+ <% end %>
+</div>
+
+<div>
+ <table>
+ <tr>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ <%= content_tag("th", "Login") %>
+ <%= content_tag("th", "Name") %>
+ <%= content_tag("th", "Password") %>
+ <%= content_tag("th", "Email") %>
+ <%= content_tag("th", "Last Accessed") %>
+ <%= content_tag("th", " ", { :class => 'actionlist' } ) %>
+ </tr>
+
+ <% @users.each do |u| %>
+ <tr>
+ <td>
+ <%= link_to "Edit", :action => "edit",
+ :id => u.id,
+ :page => params[:page],
+ :search_term => @search_term %>
+ </td>
+ <%= content_tag("td", u.login) %>
+ <%= content_tag("td", u.name) %>
+ <%= content_tag("td", u.password) %>
+ <%= content_tag("td", u.email) %>
+ <%= content_tag("td", u.access) %>
+ <td>
+ <%= link_to "Delete", {:action => "deprecate",
+ :id => u.id,
+ :page => params[:page],
+ :search_term => @search_term },
+ :confirm => "Really delete user \"#{u.login}\"?" %>
+ </td>
+ </tr>
+ <% end %>
+ </table>
+
+ <%= will_paginate @users, :class => 'footer',
+ :params => { :search_term => params[:search_term] } %>
+
+ <%= content_tag("div", link_to("Add new user",
+ :controller => "users",
+ :action => "new"), { :class => 'footer' } ) %>
+
+</div>
diff --git a/automation/admin/app/views/users/new.rhtml b/automation/admin/app/views/users/new.rhtml
new file mode 100644
index 0000000..d920995
--- /dev/null
+++ b/automation/admin/app/views/users/new.rhtml
@@ -0,0 +1,8 @@
+<%= render_partial "title" %>
+
+<div>
+ <% form_tag :action => 'create' do %>
+ <%= render_partial "form" %>
+ <%= submit_tag "Create" %>
+ <% end %>
+</div>
diff --git a/automation/admin/app/views/users/show.rhtml b/automation/admin/app/views/users/show.rhtml
new file mode 100644
index 0000000..562ade0
--- /dev/null
+++ b/automation/admin/app/views/users/show.rhtml
@@ -0,0 +1,8 @@
+<% for column in User.content_columns %>
+<p>
+ <b><%= column.human_name %>:</b> <%=h @user.send(column.name) %>
+</p>
+<% end %>
+
+<%= link_to 'Edit', :action => 'edit', :id => @user %> |
+<%= link_to 'Back', :action => 'list' %>
diff --git a/automation/admin/config/README b/automation/admin/config/README
new file mode 100644
index 0000000..09c3649
--- /dev/null
+++ b/automation/admin/config/README
@@ -0,0 +1,45 @@
+*** NOTE ***
+
+As of Tab2MAGE version 1.9.9 these notes are obsolete, since we have
+migrated to using MySQL as the back-end database. These instructions
+are included in case future users wish to revert to SQLite
+
+*** END NOTE ***
+
+
+The standard SQLite interface included with ActiveRecord apparently
+has a default timeout of zero when connecting to the database. This
+can cause problems ("SQLite3::BusyException"), especially when the
+SQLite db file is on an NFS mount (which strictly speaking should be
+avoided anyway...). The ticket here:
+
+http://dev.rubyonrails.org/ticket/6126
+
+gives a possible solution, which involves applying the patch below,
+and adding a :timeout value to the database.yml config entry in this
+directory. We are currently evaluating this.
+
+Index: test/connections/native_sqlite3/connection.rb
+===================================================================
+--- test/connections/native_sqlite3/connection.rb (revision 5072)
++++ test/connections/native_sqlite3/connection.rb (working copy)
+@@ -11,7 +11,7 @@
+ sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite3"
+
+ def make_connection(clazz, db_file, db_definitions_file)
+- ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite3', :database => db_file } }
++ ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite3', :database => db_file, :timeout => 5000 } }
+ unless File.exist?(db_file)
+ puts "SQLite3 database not found at #{db_file}. Rebuilding it."
+ sqlite_command = %Q{sqlite3 #{db_file} "create table a (a integer); drop table a;"}
+Index: lib/active_record/connection_adapters/sqlite_adapter.rb
+===================================================================
+--- lib/active_record/connection_adapters/sqlite_adapter.rb (revision 5072)
++++ lib/active_record/connection_adapters/sqlite_adapter.rb (working copy)
+@@ -19,6 +19,7 @@
+ :results_as_hash => true,
+ :type_translation => false
+ )
++ db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
+ ConnectionAdapters::SQLiteAdapter.new(db, logger)
+ end
diff --git a/automation/admin/config/boot.rb b/automation/admin/config/boot.rb
new file mode 100644
index 0000000..9fcd50f
--- /dev/null
+++ b/automation/admin/config/boot.rb
@@ -0,0 +1,19 @@
+# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
+
+unless defined?(RAILS_ROOT)
+ root_path = File.join(File.dirname(__FILE__), '..')
+ unless RUBY_PLATFORM =~ /mswin32/
+ require 'pathname'
+ root_path = Pathname.new(root_path).cleanpath(true).to_s
+ end
+ RAILS_ROOT = root_path
+end
+
+if File.directory?("#{RAILS_ROOT}/vendor/rails")
+ require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
+else
+ require 'rubygems'
+ require 'initializer'
+end
+
+Rails::Initializer.run(:set_load_path)
diff --git a/automation/admin/config/database.yml-mysql b/automation/admin/config/database.yml-mysql
new file mode 100644
index 0000000..7feac9f
--- /dev/null
+++ b/automation/admin/config/database.yml-mysql
@@ -0,0 +1,20 @@
+# Example config file for a simple MySQL database backend.
+#
+development:
+ adapter: mysql
+ database: test
+ host: localhost
+
+production:
+ adapter: mysql
+ database: test
+ host: localhost
+
+# Warning: The database defined as 'test' will be erased and
+# re-generated from your development database when you run 'rake'.
+# Do not set this db to the same as development or production.
+test:
+ adapter: mysql
+ database: test_test
+ host: localhost
+
diff --git a/automation/admin/config/database.yml-sqlite b/automation/admin/config/database.yml-sqlite
new file mode 100644
index 0000000..378d6d9
--- /dev/null
+++ b/automation/admin/config/database.yml-sqlite
@@ -0,0 +1,29 @@
+# Example config file for a simple SQLite database backend.
+#
+# NOTE that currently the controllers assume a MySQL backend, and they
+# will probably need some tweaking of embedded SQL to get them to work
+# correctly (apologies for this blatant violation of MVC!).
+#
+# The timeout value below relates to this rails ticket,
+# which is not currently part of the main rails distribution:
+# http://dev.rubyonrails.org/ticket/6126
+# See also the README in this directory for more information.
+#
+development:
+ adapter: sqlite3
+ dbfile: db/autosubs.db
+ timeout: 10000
+
+production:
+ adapter: sqlite3
+ dbfile: db/autosubs.db
+ timeout: 10000
+
+# Warning: The database defined as 'test' will be erased and
+# re-generated from your development database when you run 'rake'.
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ dbfile: db/autosubs_test.db
+ timeout: 10000
+
diff --git a/automation/admin/config/environment.rb b/automation/admin/config/environment.rb
new file mode 100644
index 0000000..e33cf58
--- /dev/null
+++ b/automation/admin/config/environment.rb
@@ -0,0 +1,66 @@
+# Be sure to restart your web server when you modify this file.
+
+# Uncomment below to force Rails into production mode when
+# you don't control web/app server and can't set it the proper way
+# ENV['RAILS_ENV'] ||= 'production'
+
+# Bootstrap the Rails environment, frameworks, and default configuration
+require File.join(File.dirname(__FILE__), 'boot')
+
+Rails::Initializer.run do |config|
+ # Settings in config/environments/* take precedence those specified here
+
+ # Skip frameworks you're not going to use
+ # config.frameworks -= [ :action_web_service, :action_mailer ]
+
+ # Add additional load paths for your own custom dirs
+ # config.load_paths += %W( #{RAILS_ROOT}/extras )
+
+ # Force all environments to use the same logger level
+ # (by default production uses :info, the others :debug)
+ # config.log_level = :debug
+
+ # Use the database for sessions instead of the file system
+ # (create the session table with 'rake create_sessions_table')
+ # config.action_controller.session_store = :active_record_store
+
+ # Enable page/fragment caching by setting a file-based store
+ # (remember to create the caching directory and make it readable to the application)
+ # config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
+
+ # Activate observers that should always be running
+ # config.active_record.observers = :cacher, :garbage_collector
+
+ # Make Active Record use UTC-base instead of local time
+ # config.active_record.default_timezone = :utc
+
+ # Use Active Record's schema dumper instead of SQL when creating the test database
+ # (enables use of different database adapters for development and test environments)
+ # config.active_record.schema_format = :ruby
+
+ # See Rails::Configuration for more options
+
+ # :secret, below, is a >30 character secret phrase used is generating session cookies.
+ config.action_controller.session = { :session_key => "_myapp_session",
+ :secret => "Lyta had a little Vorlon/her skin was pale as snow./" +
+ "Everywhere that Lyta went/the Vorlon was sure to go." }
+
+end
+
+# Add new inflection rules using the following format
+# (all these examples are active by default):
+# Inflector.inflections do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# Include your application configuration below
+
+# All our database timestamps are GMT.
+ActiveRecord::Base.default_timezone = :utc
+
+# Pagination now uses an external gem. Apparently
+# this is much better than the classic pagination.
+require 'will_paginate'
diff --git a/automation/admin/config/environments/development.rb b/automation/admin/config/environments/development.rb
new file mode 100644
index 0000000..831d73f
--- /dev/null
+++ b/automation/admin/config/environments/development.rb
@@ -0,0 +1,20 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# In the development environment your application's code is reloaded on
+# every request. This slows down response time but is perfect for development
+# since you don't have to restart the webserver when you make code changes.
+config.cache_classes = false
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils = true
+
+# Enable the breakpoint server that script/breakpointer connects to
+# DEPRECATED in rails 2.0.0
+#config.breakpoint_server = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching = false
+
+# Don't care if the mailer can't send
+config.action_mailer.raise_delivery_errors = false
diff --git a/automation/admin/config/environments/production.rb b/automation/admin/config/environments/production.rb
new file mode 100644
index 0000000..c9a4396
--- /dev/null
+++ b/automation/admin/config/environments/production.rb
@@ -0,0 +1,19 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The production environment is meant for finished, "live" apps.
+# Code is not reloaded between requests
+config.cache_classes = true
+
+# Use a different logger for distributed setups
+# config.logger = SyslogLogger.new
+
+
+# Full error reports are disabled and caching is turned on
+config.action_controller.consider_all_requests_local = false
+config.action_controller.perform_caching = true
+
+# Enable serving of images, stylesheets, and javascripts from an asset server
+# config.action_controller.asset_host = "http://assets.example.com"
+
+# Disable delivery errors if you bad email addresses should just be ignored
+# config.action_mailer.raise_delivery_errors = false
diff --git a/automation/admin/config/environments/test.rb b/automation/admin/config/environments/test.rb
new file mode 100644
index 0000000..6a4cddb
--- /dev/null
+++ b/automation/admin/config/environments/test.rb
@@ -0,0 +1,19 @@
+# Settings specified here will take precedence over those in config/environment.rb
+
+# The test environment is used exclusively to run your application's
+# test suite. You never need to work with it otherwise. Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs. Don't rely on the data there!
+config.cache_classes = true
+
+# Log error messages when you accidentally call methods on nil.
+config.whiny_nils = true
+
+# Show full error reports and disable caching
+config.action_controller.consider_all_requests_local = true
+config.action_controller.perform_caching = false
+
+# Tell ActionMailer not to deliver emails to the real world.
+# The :test delivery method accumulates sent emails in the
+# ActionMailer::Base.deliveries array.
+config.action_mailer.delivery_method = :test
\ No newline at end of file
diff --git a/automation/admin/config/lighttpd.conf b/automation/admin/config/lighttpd.conf
new file mode 100644
index 0000000..5f2c087
--- /dev/null
+++ b/automation/admin/config/lighttpd.conf
@@ -0,0 +1,53 @@
+# Default configuration file for the lighttpd web server
+# Start using ./script/server lighttpd
+
+server.bind = "0.0.0.0"
+server.port = 8087
+
+server.modules = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi", "mod_compress", "mod_expire" )
+
+server.error-handler-404 = "/dispatch.fcgi"
+server.document-root = CWD + "/public/"
+
+server.errorlog = CWD + "/log/lighttpd.error.log"
+accesslog.filename = CWD + "/log/lighttpd.access.log"
+
+url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
+
+compress.filetype = ( "text/plain", "text/html", "text/css", "text/javascript" )
+compress.cache-dir = CWD + "/tmp/cache"
+
+expire.url = ( "/favicon.ico" => "access 3 days",
+ "/images/" => "access 3 days",
+ "/stylesheets/" => "access 3 days",
+ "/javascripts/" => "access 3 days" )
+
+
+# Change *-procs to 2 if you need to use Upload Progress or other tasks that
+# *need* to execute a second request while the first is still pending.
+fastcgi.server = ( ".fcgi" => ( "localhost" => (
+ "min-procs" => 1,
+ "max-procs" => 1,
+ "socket" => CWD + "/tmp/sockets/fcgi.socket",
+ "bin-path" => CWD + "/public/dispatch.fcgi",
+ "bin-environment" => ( "RAILS_ENV" => "development" )
+) ) )
+
+mimetype.assign = (
+ ".css" => "text/css",
+ ".gif" => "image/gif",
+ ".htm" => "text/html",
+ ".html" => "text/html",
+ ".jpeg" => "image/jpeg",
+ ".jpg" => "image/jpeg",
+ ".js" => "text/javascript",
+ ".png" => "image/png",
+ ".swf" => "application/x-shockwave-flash",
+ ".txt" => "text/plain"
+)
+
+# Making sure file uploads above 64k always work when using IE or Safari
+# For more information, see http://trac.lighttpd.net/trac/ticket/360
+$HTTP["useragent"] =~ "^(.*MSIE.*)|(.*AppleWebKit.*)$" {
+ server.max-keep-alive-requests = 0
+}
diff --git a/automation/admin/config/routes.rb b/automation/admin/config/routes.rb
new file mode 100644
index 0000000..6f98402
--- /dev/null
+++ b/automation/admin/config/routes.rb
@@ -0,0 +1,19 @@
+ActionController::Routing::Routes.draw do |map|
+ # Add your own custom routes here.
+ # The priority is based upon order of creation: first created -> highest priority.
+
+ # Here's a sample route:
+ # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
+ # Keep in mind you can assign values other than :controller and :action
+
+ # You can have the root of your site routed by hooking up ''
+ # -- just remember to delete public/index.html.
+# map.connect '', :controller => 'tab2mages', :action => 'list'
+
+ # Allow downloading Web Service WSDL as a file with an extension
+ # instead of a file named 'wsdl'
+ map.connect ':controller/service.wsdl', :action => 'wsdl'
+
+ # Install the default route as the lowest priority.
+ map.connect ':controller/:action/:id'
+end
diff --git a/automation/admin/db/database-mysql.dump b/automation/admin/db/database-mysql.dump
new file mode 100644
index 0000000..928de6c
--- /dev/null
+++ b/automation/admin/db/database-mysql.dump
@@ -0,0 +1,932 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost Database: autosubs_test
+-- ------------------------------------------------------
+-- Server version 5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `array_designs`
+--
+
+DROP TABLE IF EXISTS `array_designs`;
+CREATE TABLE `array_designs` (
+ `id` int(11) NOT NULL auto_increment,
+ `miamexpress_subid` int(11) default NULL,
+ `accession` varchar(255) default NULL,
+ `name` varchar(255) default NULL,
+ `miamexpress_login` varchar(50) default NULL,
+ `status` varchar(50) default NULL,
+ `data_warehouse_ready` char(15) default NULL,
+ `date_last_processed` datetime default NULL,
+ `comment` text,
+ `is_deleted` int(11) NOT NULL,
+ `miame_score` int(11) default NULL,
+ `in_data_warehouse` int(11) default NULL,
+ `annotation_source` varchar(50) default NULL,
+ `annotation_version` varchar(50) default NULL,
+ `biomart_table_name` varchar(50) default NULL,
+ `release_date` datetime default NULL,
+ `is_released` int(11) default NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `miamexpress_subid` (`miamexpress_subid`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs`
+--
+
+LOCK TABLES `array_designs` WRITE;
+/*!40000 ALTER TABLE `array_designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `array_designs_experiments`
+--
+
+DROP TABLE IF EXISTS `array_designs_experiments`;
+CREATE TABLE `array_designs_experiments` (
+ `id` int(11) NOT NULL auto_increment,
+ `array_design_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `array_design_id` (`array_design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `array_designs_experiments_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `array_designs_experiments_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs_experiments`
+--
+
+LOCK TABLES `array_designs_experiments` WRITE;
+/*!40000 ALTER TABLE `array_designs_experiments` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs_experiments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `array_designs_organisms`
+--
+
+DROP TABLE IF EXISTS `array_designs_organisms`;
+CREATE TABLE `array_designs_organisms` (
+ `id` int(11) NOT NULL auto_increment,
+ `organism_id` int(11) NOT NULL,
+ `array_design_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `organism_id` (`organism_id`),
+ KEY `array_design_id` (`array_design_id`),
+ CONSTRAINT `array_designs_organisms_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `array_designs_organisms_ibfk_2` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs_organisms`
+--
+
+LOCK TABLES `array_designs_organisms` WRITE;
+/*!40000 ALTER TABLE `array_designs_organisms` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs_organisms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories`
+--
+
+DROP TABLE IF EXISTS `categories`;
+CREATE TABLE `categories` (
+ `id` int(11) NOT NULL auto_increment,
+ `ontology_term` varchar(50) default NULL,
+ `display_label` varchar(50) default NULL,
+ `is_common` int(11) default NULL,
+ `is_bmc` int(11) default NULL,
+ `is_fv` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories`
+--
+
+LOCK TABLES `categories` WRITE;
+/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
+INSERT INTO `categories` VALUES (1,'Age','Age',NULL,1,1,0),(2,'OrganismPart','Organism part',NULL,1,1,0),(3,'DiseaseState','Disease state',NULL,1,1,0),(4,'CellLine','Cell line',NULL,1,1,0),(5,'CellType','Cell type',NULL,1,1,0),(6,'DevelopmentalStage','Developmental stage',NULL,1,1,0),(7,'DiseaseStaging','Disease staging',NULL,1,1,0),(8,'EnvironmentalHistory','Environmental history',NULL,1,1,0),(9,'GeneticModification','Genetic modification',NULL,1,1,0),(10,'Histology','Histology',NULL,1, [...]
+/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_designs`
+--
+
+DROP TABLE IF EXISTS `categories_designs`;
+CREATE TABLE `categories_designs` (
+ `category_id` int(11) NOT NULL,
+ `design_id` int(11) NOT NULL,
+ KEY `category_id` (`category_id`),
+ KEY `design_id` (`design_id`),
+ CONSTRAINT `categories_designs_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `categories_designs_ibfk_2` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_designs`
+--
+
+LOCK TABLES `categories_designs` WRITE;
+/*!40000 ALTER TABLE `categories_designs` DISABLE KEYS */;
+INSERT INTO `categories_designs` VALUES (3,1),(8,3),(12,3),(16,2),(5,4),(6,5),(12,6),(9,7),(12,7),(2,8),(15,9),(16,10),(20,11),(22,13),(23,13),(21,12),(23,18),(3,2),(24,2),(12,21),(24,22),(1,5),(25,12),(29,33);
+/*!40000 ALTER TABLE `categories_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_materials`
+--
+
+DROP TABLE IF EXISTS `categories_materials`;
+CREATE TABLE `categories_materials` (
+ `category_id` int(11) NOT NULL,
+ `material_id` int(11) NOT NULL,
+ KEY `category_id` (`category_id`),
+ KEY `material_id` (`material_id`),
+ CONSTRAINT `categories_materials_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `categories_materials_ibfk_2` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_materials`
+--
+
+LOCK TABLES `categories_materials` WRITE;
+/*!40000 ALTER TABLE `categories_materials` DISABLE KEYS */;
+INSERT INTO `categories_materials` VALUES (2,1),(4,2),(5,2),(1,3),(11,3),(15,3);
+/*!40000 ALTER TABLE `categories_materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_taxons`
+--
+
+DROP TABLE IF EXISTS `categories_taxons`;
+CREATE TABLE `categories_taxons` (
+ `category_id` int(11) NOT NULL,
+ `taxon_id` int(11) NOT NULL,
+ KEY `category_id` (`category_id`),
+ KEY `taxon_id` (`taxon_id`),
+ CONSTRAINT `categories_taxons_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `categories_taxons_ibfk_2` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_taxons`
+--
+
+LOCK TABLES `categories_taxons` WRITE;
+/*!40000 ALTER TABLE `categories_taxons` DISABLE KEYS */;
+INSERT INTO `categories_taxons` VALUES (1,1),(15,1),(12,2),(15,2),(16,2),(12,6),(16,6),(6,5),(12,5),(16,5),(16,3),(6,4),(2,4),(7,5),(25,1),(26,1),(6,7),(12,7),(16,7),(1,8),(6,8),(14,8),(15,8),(16,8),(27,8),(28,8),(1,9),(2,9),(6,9),(8,9),(15,9),(16,9),(24,9),(12,8);
+/*!40000 ALTER TABLE `categories_taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `data_files`
+--
+
+DROP TABLE IF EXISTS `data_files`;
+CREATE TABLE `data_files` (
+ `id` int(11) NOT NULL auto_increment,
+ `experiment_id` int(11) NOT NULL,
+ `name` varchar(255) default NULL,
+ `is_unpacked` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `data_files_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `data_files`
+--
+
+LOCK TABLES `data_files` WRITE;
+/*!40000 ALTER TABLE `data_files` DISABLE KEYS */;
+/*!40000 ALTER TABLE `data_files` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `data_formats`
+--
+
+DROP TABLE IF EXISTS `data_formats`;
+CREATE TABLE `data_formats` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(50) character set latin1 collate latin1_general_cs NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `data_formats`
+--
+
+LOCK TABLES `data_formats` WRITE;
+/*!40000 ALTER TABLE `data_formats` DISABLE KEYS */;
+/*!40000 ALTER TABLE `data_formats` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `design_instances`
+--
+
+DROP TABLE IF EXISTS `design_instances`;
+CREATE TABLE `design_instances` (
+ `id` int(11) NOT NULL auto_increment,
+ `design_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `design_id` (`design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `design_instances_ibfk_1` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `design_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `design_instances`
+--
+
+LOCK TABLES `design_instances` WRITE;
+/*!40000 ALTER TABLE `design_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `design_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `designs`
+--
+
+DROP TABLE IF EXISTS `designs`;
+CREATE TABLE `designs` (
+ `id` int(11) NOT NULL auto_increment,
+ `display_label` varchar(50) default NULL,
+ `ontology_category` varchar(50) default NULL,
+ `ontology_value` varchar(50) default NULL,
+ `design_type` char(15) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `designs`
+--
+
+LOCK TABLES `designs` WRITE;
+/*!40000 ALTER TABLE `designs` DISABLE KEYS */;
+INSERT INTO `designs` VALUES (1,'Disease state','ExperimentalDesignType','disease_state_design','biological',0),(2,'CGH','ExperimentalDesignType','comparative_genome_hybridization_design','technological',0),(3,'Behavior','ExperimentalDesignType','innate_behavior_design','biological',0),(4,'Cell type comparison','ExperimentalDesignType','cell_type_comparison_design','biological',0),(5,'Development or differentiation','ExperimentalDesignType','development_or_differentiation_design','biolog [...]
+/*!40000 ALTER TABLE `designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `events`
+--
+
+DROP TABLE IF EXISTS `events`;
+CREATE TABLE `events` (
+ `id` int(11) NOT NULL auto_increment,
+ `array_design_id` int(11) default NULL,
+ `experiment_id` int(11) default NULL,
+ `event_type` varchar(50) NOT NULL,
+ `was_successful` int(11) default NULL,
+ `source_db` varchar(30) default NULL,
+ `target_db` varchar(30) default NULL,
+ `start_time` datetime default NULL,
+ `end_time` datetime default NULL,
+ `machine` varchar(50) default NULL,
+ `operator` varchar(30) default NULL,
+ `log_file` varchar(511) default NULL,
+ `jobregister_dbid` int(15) default NULL,
+ `comment` text,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `array_design_id` (`array_design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `events_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`),
+ CONSTRAINT `events_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `events`
+--
+
+LOCK TABLES `events` WRITE;
+/*!40000 ALTER TABLE `events` DISABLE KEYS */;
+/*!40000 ALTER TABLE `events` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments`
+--
+
+DROP TABLE IF EXISTS `experiments`;
+CREATE TABLE `experiments` (
+ `id` int(11) NOT NULL auto_increment,
+ `accession` varchar(255) default NULL,
+ `name` varchar(255) default NULL,
+ `user_id` int(11) default NULL,
+ `checker_score` int(11) default NULL,
+ `software` varchar(100) default NULL,
+ `status` varchar(50) default NULL,
+ `data_warehouse_ready` int(11) default NULL,
+ `date_last_edited` datetime default NULL,
+ `date_submitted` datetime default NULL,
+ `date_last_processed` datetime default NULL,
+ `in_curation` int(11) default NULL,
+ `curator` char(30) default NULL,
+ `comment` text,
+ `experiment_type` char(30) default NULL,
+ `miamexpress_login` char(30) default NULL,
+ `miamexpress_subid` int(11) default NULL,
+ `is_affymetrix` int(11) default NULL,
+ `is_mx_batchloader` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ `miame_score` int(11) default NULL,
+ `in_data_warehouse` int(11) default NULL,
+ `num_submissions` int(11) default NULL,
+ `submitter_description` text,
+ `curated_name` varchar(255) default NULL,
+ `num_samples` int(11) default NULL,
+ `num_hybridizations` int(11) default NULL,
+ `has_raw_data` int(11) default NULL,
+ `has_processed_data` int(11) default NULL,
+ `release_date` datetime default NULL,
+ `is_released` int(11) default NULL,
+ `ae_miame_score` int(11) default NULL,
+ `ae_data_warehouse_score` int(11) default NULL,
+ PRIMARY KEY (`id`),
+ KEY `user_id` (`user_id`),
+ CONSTRAINT `experiments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments`
+--
+
+LOCK TABLES `experiments` WRITE;
+/*!40000 ALTER TABLE `experiments` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments_factors`
+--
+
+DROP TABLE IF EXISTS `experiments_factors`;
+CREATE TABLE `experiments_factors` (
+ `id` int(11) NOT NULL auto_increment,
+ `experiment_id` int(11) NOT NULL,
+ `factor_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `experiment_id` (`experiment_id`),
+ KEY `factor_id` (`factor_id`),
+ CONSTRAINT `experiments_factors_ibfk_1` FOREIGN KEY (`factor_id`) REFERENCES `factors` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `experiments_factors_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments_factors`
+--
+
+LOCK TABLES `experiments_factors` WRITE;
+/*!40000 ALTER TABLE `experiments_factors` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments_factors` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments_loaded_data`
+--
+
+DROP TABLE IF EXISTS `experiments_loaded_data`;
+CREATE TABLE `experiments_loaded_data` (
+ `experiment_id` int(11) NOT NULL,
+ `loaded_data_id` int(11) NOT NULL,
+ KEY `experiment_id` (`experiment_id`),
+ KEY `loaded_data_id` (`loaded_data_id`),
+ CONSTRAINT `loaded_data_experiment_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `loaded_data_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments_loaded_data`
+--
+
+LOCK TABLES `experiments_loaded_data` WRITE;
+/*!40000 ALTER TABLE `experiments_loaded_data` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments_loaded_data` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments_quantitation_types`
+--
+
+DROP TABLE IF EXISTS `experiments_quantitation_types`;
+CREATE TABLE `experiments_quantitation_types` (
+ `id` int(11) NOT NULL auto_increment,
+ `quantitation_type_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quantitation_type_id` (`quantitation_type_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `experiments_quantitation_types_ibfk_1` FOREIGN KEY (`quantitation_type_id`) REFERENCES `quantitation_types` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `experiments_quantitation_types_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments_quantitation_types`
+--
+
+LOCK TABLES `experiments_quantitation_types` WRITE;
+/*!40000 ALTER TABLE `experiments_quantitation_types` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments_quantitation_types` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `factors`
+--
+
+DROP TABLE IF EXISTS `factors`;
+CREATE TABLE `factors` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(128) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `factors`
+--
+
+LOCK TABLES `factors` WRITE;
+/*!40000 ALTER TABLE `factors` DISABLE KEYS */;
+/*!40000 ALTER TABLE `factors` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `loaded_data`
+--
+
+DROP TABLE IF EXISTS `loaded_data`;
+CREATE TABLE `loaded_data` (
+ `id` int(11) NOT NULL auto_increment,
+ `identifier` varchar(255) character set latin1 collate latin1_general_cs NOT NULL,
+ `md5_hash` char(35) NOT NULL,
+ `data_format_id` int(11) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ `platform_id` int(11) default NULL,
+ `needs_metrics_calculation` int(11) NOT NULL,
+ `date_hashed` datetime default NULL,
+ PRIMARY KEY (`id`),
+ KEY `data_format_id` (`data_format_id`),
+ KEY `platform_loaded_data_ibfk_1` (`platform_id`),
+ CONSTRAINT `platform_loaded_data_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platforms` (`id`),
+ CONSTRAINT `data_format_loaded_data_ibfk_1` FOREIGN KEY (`data_format_id`) REFERENCES `data_formats` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `loaded_data`
+--
+
+LOCK TABLES `loaded_data` WRITE;
+/*!40000 ALTER TABLE `loaded_data` DISABLE KEYS */;
+/*!40000 ALTER TABLE `loaded_data` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `loaded_data_quality_metrics`
+--
+
+DROP TABLE IF EXISTS `loaded_data_quality_metrics`;
+CREATE TABLE `loaded_data_quality_metrics` (
+ `id` int(11) NOT NULL auto_increment,
+ `value` decimal(12,5) default NULL,
+ `quality_metric_id` int(11) NOT NULL,
+ `loaded_data_id` int(11) NOT NULL,
+ `date_calculated` datetime default NULL,
+ PRIMARY KEY (`id`),
+ KEY `quality_metric_id` (`quality_metric_id`),
+ KEY `loaded_data_id` (`loaded_data_id`),
+ CONSTRAINT `quality_metric_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `quality_metric_quality_metric_ibfk_1` FOREIGN KEY (`quality_metric_id`) REFERENCES `quality_metrics` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `loaded_data_quality_metrics`
+--
+
+LOCK TABLES `loaded_data_quality_metrics` WRITE;
+/*!40000 ALTER TABLE `loaded_data_quality_metrics` DISABLE KEYS */;
+/*!40000 ALTER TABLE `loaded_data_quality_metrics` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `material_instances`
+--
+
+DROP TABLE IF EXISTS `material_instances`;
+CREATE TABLE `material_instances` (
+ `id` int(11) NOT NULL auto_increment,
+ `material_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `material_id` (`material_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `material_instances_ibfk_1` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `material_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `material_instances`
+--
+
+LOCK TABLES `material_instances` WRITE;
+/*!40000 ALTER TABLE `material_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `material_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `materials`
+--
+
+DROP TABLE IF EXISTS `materials`;
+CREATE TABLE `materials` (
+ `id` int(11) NOT NULL auto_increment,
+ `display_label` varchar(50) default NULL,
+ `ontology_category` varchar(50) default NULL,
+ `ontology_value` varchar(50) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `materials`
+--
+
+LOCK TABLES `materials` WRITE;
+/*!40000 ALTER TABLE `materials` DISABLE KEYS */;
+INSERT INTO `materials` VALUES (1,'Organism part','MaterialType','organism_part',0),(2,'Cell culture','MaterialType','cell',0),(3,'Whole organism','MaterialType','whole_organism',0);
+/*!40000 ALTER TABLE `materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `meta`
+--
+
+DROP TABLE IF EXISTS `meta`;
+CREATE TABLE `meta` (
+ `name` varchar(128) NOT NULL default '',
+ `value` varchar(128) NOT NULL default '',
+ PRIMARY KEY (`name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `meta`
+--
+
+LOCK TABLES `meta` WRITE;
+/*!40000 ALTER TABLE `meta` DISABLE KEYS */;
+INSERT INTO `meta` VALUES ('serializer','Storable'),('index_subfeatures','1'),('autoindex','1');
+/*!40000 ALTER TABLE `meta` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organism_instances`
+--
+
+DROP TABLE IF EXISTS `organism_instances`;
+CREATE TABLE `organism_instances` (
+ `id` int(11) NOT NULL auto_increment,
+ `organism_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `organism_id` (`organism_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `organism_instances_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `organism_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organism_instances`
+--
+
+LOCK TABLES `organism_instances` WRITE;
+/*!40000 ALTER TABLE `organism_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `organism_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organisms`
+--
+
+DROP TABLE IF EXISTS `organisms`;
+CREATE TABLE `organisms` (
+ `id` int(11) NOT NULL auto_increment,
+ `scientific_name` varchar(50) default NULL,
+ `common_name` varchar(50) default NULL,
+ `accession` int(11) default NULL,
+ `taxon_id` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `taxon_id` (`taxon_id`),
+ CONSTRAINT `organisms_ibfk_1` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organisms`
+--
+
+LOCK TABLES `organisms` WRITE;
+/*!40000 ALTER TABLE `organisms` DISABLE KEYS */;
+INSERT INTO `organisms` VALUES (1,'Homo sapiens','Human',9606,1,0),(2,'Saccharomyces cerevisiae','Baker\'s yeast',4932,2,0),(3,'Schizosaccharomyces pombe','Fission yeast',4896,2,0),(4,'Mus musculus','House mouse',10090,3,0),(5,'Escherichia coli','E. coli',562,6,0),(6,'Arabidopsis thaliana','Thale cress',3702,4,0),(7,'Rattus norvegicus','Norway rat',10116,3,0),(8,'Pan troglodytes','Chimpanzee',9598,1,0),(9,'Apis mellifera','Honey bee',7460,5,0),(11,'Plasmodium falciparum','Malaria parasit [...]
+/*!40000 ALTER TABLE `organisms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions`
+--
+
+DROP TABLE IF EXISTS `permissions`;
+CREATE TABLE `permissions` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(40) NOT NULL,
+ `info` varchar(80) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions`
+--
+
+LOCK TABLES `permissions` WRITE;
+/*!40000 ALTER TABLE `permissions` DISABLE KEYS */;
+INSERT INTO `permissions` VALUES (1,'.*/.*','Access All Areas',0),(2,'experiments/.*','Full experiment access',0),(4,'array_designs/.*','Full MIAMExpress array access',0),(5,'protocols/.*','Full Tab2MAGE protocol access',0),(6,'tab2mages/.*','Full Tab2MAGE experiment access',0),(7,'miamexps/.*','Full MIAMExpress experiment access',0),(8,'design/.*','Full designs access',0),(9,'material/.*','Full materials access',0),(10,'organism/.*','Full organisms access',0),(11,'taxon/.*','Full taxons [...]
+/*!40000 ALTER TABLE `permissions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions_roles`
+--
+
+DROP TABLE IF EXISTS `permissions_roles`;
+CREATE TABLE `permissions_roles` (
+ `role_id` int(11) NOT NULL,
+ `permission_id` int(11) NOT NULL,
+ KEY `role_id` (`role_id`),
+ KEY `permission_id` (`permission_id`),
+ CONSTRAINT `permissions_roles_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `permissions_roles_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions_roles`
+--
+
+LOCK TABLES `permissions_roles` WRITE;
+/*!40000 ALTER TABLE `permissions_roles` DISABLE KEYS */;
+INSERT INTO `permissions_roles` VALUES (1,1),(2,4),(2,5),(2,6),(2,7),(2,8),(2,9),(2,10),(2,11),(2,12);
+/*!40000 ALTER TABLE `permissions_roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `platforms`
+--
+
+DROP TABLE IF EXISTS `platforms`;
+CREATE TABLE `platforms` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(50) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `platforms`
+--
+
+LOCK TABLES `platforms` WRITE;
+/*!40000 ALTER TABLE `platforms` DISABLE KEYS */;
+/*!40000 ALTER TABLE `platforms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `protocols`
+--
+
+DROP TABLE IF EXISTS `protocols`;
+CREATE TABLE `protocols` (
+ `id` int(11) NOT NULL auto_increment,
+ `accession` char(15) default NULL,
+ `user_accession` varchar(100) default NULL,
+ `expt_accession` char(15) default NULL,
+ `name` varchar(255) default NULL,
+ `date_last_processed` datetime default NULL,
+ `comment` text,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `accession` (`accession`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `protocols`
+--
+
+LOCK TABLES `protocols` WRITE;
+/*!40000 ALTER TABLE `protocols` DISABLE KEYS */;
+/*!40000 ALTER TABLE `protocols` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `quality_metrics`
+--
+
+DROP TABLE IF EXISTS `quality_metrics`;
+CREATE TABLE `quality_metrics` (
+ `id` int(11) NOT NULL auto_increment,
+ `type` varchar(50) NOT NULL,
+ `description` text,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `type` (`type`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `quality_metrics`
+--
+
+LOCK TABLES `quality_metrics` WRITE;
+/*!40000 ALTER TABLE `quality_metrics` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quality_metrics` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `quantitation_types`
+--
+
+DROP TABLE IF EXISTS `quantitation_types`;
+CREATE TABLE `quantitation_types` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(128) character set latin1 collate latin1_general_cs NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `quantitation_types`
+--
+
+LOCK TABLES `quantitation_types` WRITE;
+/*!40000 ALTER TABLE `quantitation_types` DISABLE KEYS */;
+/*!40000 ALTER TABLE `quantitation_types` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles`
+--
+
+DROP TABLE IF EXISTS `roles`;
+CREATE TABLE `roles` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(40) NOT NULL,
+ `info` varchar(80) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles`
+--
+
+LOCK TABLES `roles` WRITE;
+/*!40000 ALTER TABLE `roles` DISABLE KEYS */;
+INSERT INTO `roles` VALUES (1,'admin','System superuser',0),(2,'curator','Annotation and automation access',0),(3,'submitter','Low-privilege user',0);
+/*!40000 ALTER TABLE `roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles_users`
+--
+
+DROP TABLE IF EXISTS `roles_users`;
+CREATE TABLE `roles_users` (
+ `user_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ KEY `user_id` (`user_id`),
+ KEY `role_id` (`role_id`),
+ CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles_users`
+--
+
+LOCK TABLES `roles_users` WRITE;
+/*!40000 ALTER TABLE `roles_users` DISABLE KEYS */;
+INSERT INTO `roles_users` VALUES (1,1),(2,1),(2,2),(3,3);
+/*!40000 ALTER TABLE `roles_users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `spreadsheets`
+--
+
+DROP TABLE IF EXISTS `spreadsheets`;
+CREATE TABLE `spreadsheets` (
+ `id` int(11) NOT NULL auto_increment,
+ `experiment_id` int(11) NOT NULL,
+ `name` varchar(255) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `spreadsheets_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `spreadsheets`
+--
+
+LOCK TABLES `spreadsheets` WRITE;
+/*!40000 ALTER TABLE `spreadsheets` DISABLE KEYS */;
+/*!40000 ALTER TABLE `spreadsheets` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `taxons`
+--
+
+DROP TABLE IF EXISTS `taxons`;
+CREATE TABLE `taxons` (
+ `id` int(11) NOT NULL auto_increment,
+ `scientific_name` varchar(50) default NULL,
+ `common_name` varchar(50) default NULL,
+ `accession` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `taxons`
+--
+
+LOCK TABLES `taxons` WRITE;
+/*!40000 ALTER TABLE `taxons` DISABLE KEYS */;
+INSERT INTO `taxons` VALUES (1,'Primates','Primates',9443,0),(2,'Ascomycota','Yeasts',4890,0),(3,'Rodentia','Rodents',9989,0),(4,'Viridiplantae','Green plants',33090,0),(5,'Insecta','True insects',50557,0),(6,'Bacteria','Eubacteria',2,0),(7,'Plasmodium','Plasmodium',5820,0),(8,'Nematoda','Nematodes',6231,0),(9,'Metazoa','Metazoans',33208,0);
+/*!40000 ALTER TABLE `taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+CREATE TABLE `users` (
+ `id` int(11) NOT NULL auto_increment,
+ `login` varchar(40) NOT NULL,
+ `name` varchar(40) default NULL,
+ `password` varchar(40) NOT NULL,
+ `email` varchar(100) default NULL,
+ `modified_at` datetime default NULL,
+ `created_at` datetime default NULL,
+ `access` datetime default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `login` (`login`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `users`
+--
+
+LOCK TABLES `users` WRITE;
+/*!40000 ALTER TABLE `users` DISABLE KEYS */;
+INSERT INTO `users` VALUES (1,'admin','Administrator','admin','noreply at ebi.ac.uk',NULL,NULL,NULL,0),(2,'curator','Curator','curator','noreply at ebi.ac.uk',NULL,NULL,NULL,0),(3,'test','Test submitter','test','noreply at ebi.ac.uk',NULL,NULL,NULL,0);
+/*!40000 ALTER TABLE `users` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2008-04-28 11:51:32
diff --git a/automation/admin/db/database-mysql.schema b/automation/admin/db/database-mysql.schema
new file mode 100644
index 0000000..23632a7
--- /dev/null
+++ b/automation/admin/db/database-mysql.schema
@@ -0,0 +1,829 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost Database: test
+-- ------------------------------------------------------
+-- Server version 5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `array_designs`
+--
+
+DROP TABLE IF EXISTS `array_designs`;
+CREATE TABLE `array_designs` (
+ `id` int(11) NOT NULL auto_increment,
+ `miamexpress_subid` int(11) default NULL,
+ `accession` char(15) default NULL,
+ `name` varchar(255) default NULL,
+ `miamexpress_login` varchar(50) default NULL,
+ `status` varchar(50) default NULL,
+ `data_warehouse_ready` char(15) default NULL,
+ `date_last_processed` datetime default NULL,
+ `comment` text,
+ `is_deleted` int(11) NOT NULL,
+ `miame_score` int(11) default NULL,
+ `in_data_warehouse` int(11) default NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `miamexpress_subid` (`miamexpress_subid`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `array_designs`
+--
+
+LOCK TABLES `array_designs` WRITE;
+/*!40000 ALTER TABLE `array_designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `array_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories`
+--
+
+DROP TABLE IF EXISTS `categories`;
+CREATE TABLE `categories` (
+ `id` int(11) NOT NULL auto_increment,
+ `ontology_term` varchar(50) default NULL,
+ `display_label` varchar(50) default NULL,
+ `is_common` int(11) default NULL,
+ `is_bmc` int(11) default NULL,
+ `is_fv` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories`
+--
+
+LOCK TABLES `categories` WRITE;
+/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_designs`
+--
+
+DROP TABLE IF EXISTS `categories_designs`;
+CREATE TABLE `categories_designs` (
+ `category_id` int(11) NOT NULL,
+ `design_id` int(11) NOT NULL,
+ KEY `category_id` (`category_id`),
+ KEY `design_id` (`design_id`),
+ CONSTRAINT `categories_designs_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `categories_designs_ibfk_2` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_designs`
+--
+
+LOCK TABLES `categories_designs` WRITE;
+/*!40000 ALTER TABLE `categories_designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories_designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_materials`
+--
+
+DROP TABLE IF EXISTS `categories_materials`;
+CREATE TABLE `categories_materials` (
+ `category_id` int(11) NOT NULL,
+ `material_id` int(11) NOT NULL,
+ KEY `category_id` (`category_id`),
+ KEY `material_id` (`material_id`),
+ CONSTRAINT `categories_materials_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `categories_materials_ibfk_2` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_materials`
+--
+
+LOCK TABLES `categories_materials` WRITE;
+/*!40000 ALTER TABLE `categories_materials` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories_materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `categories_taxons`
+--
+
+DROP TABLE IF EXISTS `categories_taxons`;
+CREATE TABLE `categories_taxons` (
+ `category_id` int(11) NOT NULL,
+ `taxon_id` int(11) NOT NULL,
+ KEY `category_id` (`category_id`),
+ KEY `taxon_id` (`taxon_id`),
+ CONSTRAINT `categories_taxons_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `categories_taxons_ibfk_2` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `categories_taxons`
+--
+
+LOCK TABLES `categories_taxons` WRITE;
+/*!40000 ALTER TABLE `categories_taxons` DISABLE KEYS */;
+/*!40000 ALTER TABLE `categories_taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `data_files`
+--
+
+DROP TABLE IF EXISTS `data_files`;
+CREATE TABLE `data_files` (
+ `id` int(11) NOT NULL auto_increment,
+ `experiment_id` int(11) NOT NULL,
+ `name` varchar(255) default NULL,
+ `is_unpacked` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `data_files_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `data_files`
+--
+
+LOCK TABLES `data_files` WRITE;
+/*!40000 ALTER TABLE `data_files` DISABLE KEYS */;
+/*!40000 ALTER TABLE `data_files` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `design_instances`
+--
+
+DROP TABLE IF EXISTS `design_instances`;
+CREATE TABLE `design_instances` (
+ `id` int(11) NOT NULL auto_increment,
+ `design_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `design_id` (`design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `design_instances_ibfk_1` FOREIGN KEY (`design_id`) REFERENCES `designs` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `design_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `design_instances`
+--
+
+LOCK TABLES `design_instances` WRITE;
+/*!40000 ALTER TABLE `design_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `design_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `designs`
+--
+
+DROP TABLE IF EXISTS `designs`;
+CREATE TABLE `designs` (
+ `id` int(11) NOT NULL auto_increment,
+ `display_label` varchar(50) default NULL,
+ `ontology_category` varchar(50) default NULL,
+ `ontology_value` varchar(50) default NULL,
+ `design_type` char(15) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `designs`
+--
+
+LOCK TABLES `designs` WRITE;
+/*!40000 ALTER TABLE `designs` DISABLE KEYS */;
+/*!40000 ALTER TABLE `designs` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `experiments`
+--
+
+DROP TABLE IF EXISTS `experiments`;
+CREATE TABLE `experiments` (
+ `id` int(11) NOT NULL auto_increment,
+ `accession` char(15) default NULL,
+ `name` varchar(255) default NULL,
+ `user_id` int(11) default NULL,
+ `checker_score` int(11) default NULL,
+ `software` varchar(100) default NULL,
+ `status` varchar(50) default NULL,
+ `data_warehouse_ready` int(11) default NULL,
+ `date_last_edited` datetime default NULL,
+ `date_submitted` datetime default NULL,
+ `date_last_processed` datetime default NULL,
+ `in_curation` int(11) default NULL,
+ `curator` char(30) default NULL,
+ `comment` text,
+ `experiment_type` char(30) default NULL,
+ `miamexpress_login` char(30) default NULL,
+ `miamexpress_subid` int(11) default NULL,
+ `is_affymetrix` int(11) default NULL,
+ `is_mx_batchloader` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ `miame_score` int(11) default NULL,
+ `in_data_warehouse` int(11) default NULL,
+ PRIMARY KEY (`id`),
+ KEY `user_id` (`user_id`),
+ CONSTRAINT `experiments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `experiments`
+--
+
+LOCK TABLES `experiments` WRITE;
+/*!40000 ALTER TABLE `experiments` DISABLE KEYS */;
+/*!40000 ALTER TABLE `experiments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `material_instances`
+--
+
+DROP TABLE IF EXISTS `material_instances`;
+CREATE TABLE `material_instances` (
+ `id` int(11) NOT NULL auto_increment,
+ `material_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `material_id` (`material_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `material_instances_ibfk_1` FOREIGN KEY (`material_id`) REFERENCES `materials` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `material_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `material_instances`
+--
+
+LOCK TABLES `material_instances` WRITE;
+/*!40000 ALTER TABLE `material_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `material_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `materials`
+--
+
+DROP TABLE IF EXISTS `materials`;
+CREATE TABLE `materials` (
+ `id` int(11) NOT NULL auto_increment,
+ `display_label` varchar(50) default NULL,
+ `ontology_category` varchar(50) default NULL,
+ `ontology_value` varchar(50) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `materials`
+--
+
+LOCK TABLES `materials` WRITE;
+/*!40000 ALTER TABLE `materials` DISABLE KEYS */;
+/*!40000 ALTER TABLE `materials` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `meta`
+--
+
+DROP TABLE IF EXISTS `meta`;
+CREATE TABLE `meta` (
+ `name` varchar(128) NOT NULL default '',
+ `value` varchar(128) NOT NULL default '',
+ PRIMARY KEY (`name`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `meta`
+--
+
+LOCK TABLES `meta` WRITE;
+/*!40000 ALTER TABLE `meta` DISABLE KEYS */;
+INSERT INTO `meta` VALUES ('serializer','Storable'),('index_subfeatures','1'),('autoindex','1');
+/*!40000 ALTER TABLE `meta` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organism_instances`
+--
+
+DROP TABLE IF EXISTS `organism_instances`;
+CREATE TABLE `organism_instances` (
+ `id` int(11) NOT NULL auto_increment,
+ `organism_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `organism_id` (`organism_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `organism_instances_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `organism_instances_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organism_instances`
+--
+
+LOCK TABLES `organism_instances` WRITE;
+/*!40000 ALTER TABLE `organism_instances` DISABLE KEYS */;
+/*!40000 ALTER TABLE `organism_instances` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `organisms`
+--
+
+DROP TABLE IF EXISTS `organisms`;
+CREATE TABLE `organisms` (
+ `id` int(11) NOT NULL auto_increment,
+ `scientific_name` varchar(50) default NULL,
+ `common_name` varchar(50) default NULL,
+ `accession` int(11) default NULL,
+ `taxon_id` int(11) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `taxon_id` (`taxon_id`),
+ CONSTRAINT `organisms_ibfk_1` FOREIGN KEY (`taxon_id`) REFERENCES `taxons` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `organisms`
+--
+
+LOCK TABLES `organisms` WRITE;
+/*!40000 ALTER TABLE `organisms` DISABLE KEYS */;
+/*!40000 ALTER TABLE `organisms` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions`
+--
+
+DROP TABLE IF EXISTS `permissions`;
+CREATE TABLE `permissions` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(40) NOT NULL,
+ `info` varchar(80) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions`
+--
+
+LOCK TABLES `permissions` WRITE;
+/*!40000 ALTER TABLE `permissions` DISABLE KEYS */;
+/*!40000 ALTER TABLE `permissions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `permissions_roles`
+--
+
+DROP TABLE IF EXISTS `permissions_roles`;
+CREATE TABLE `permissions_roles` (
+ `role_id` int(11) NOT NULL,
+ `permission_id` int(11) NOT NULL,
+ KEY `role_id` (`role_id`),
+ KEY `permission_id` (`permission_id`),
+ CONSTRAINT `permissions_roles_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `permissions_roles_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `permissions_roles`
+--
+
+LOCK TABLES `permissions_roles` WRITE;
+/*!40000 ALTER TABLE `permissions_roles` DISABLE KEYS */;
+/*!40000 ALTER TABLE `permissions_roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `protocols`
+--
+
+DROP TABLE IF EXISTS `protocols`;
+CREATE TABLE `protocols` (
+ `id` int(11) NOT NULL auto_increment,
+ `accession` char(15) default NULL,
+ `user_accession` varchar(100) default NULL,
+ `expt_accession` char(15) default NULL,
+ `name` varchar(255) default NULL,
+ `date_last_processed` datetime default NULL,
+ `comment` text,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `accession` (`accession`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `protocols`
+--
+
+LOCK TABLES `protocols` WRITE;
+/*!40000 ALTER TABLE `protocols` DISABLE KEYS */;
+/*!40000 ALTER TABLE `protocols` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles`
+--
+
+DROP TABLE IF EXISTS `roles`;
+CREATE TABLE `roles` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(40) NOT NULL,
+ `info` varchar(80) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles`
+--
+
+LOCK TABLES `roles` WRITE;
+/*!40000 ALTER TABLE `roles` DISABLE KEYS */;
+/*!40000 ALTER TABLE `roles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `roles_users`
+--
+
+DROP TABLE IF EXISTS `roles_users`;
+CREATE TABLE `roles_users` (
+ `user_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ KEY `user_id` (`user_id`),
+ KEY `role_id` (`role_id`),
+ CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `roles_users`
+--
+
+LOCK TABLES `roles_users` WRITE;
+/*!40000 ALTER TABLE `roles_users` DISABLE KEYS */;
+/*!40000 ALTER TABLE `roles_users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `spreadsheets`
+--
+
+DROP TABLE IF EXISTS `spreadsheets`;
+CREATE TABLE `spreadsheets` (
+ `id` int(11) NOT NULL auto_increment,
+ `experiment_id` int(11) NOT NULL,
+ `name` varchar(255) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `spreadsheets_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `spreadsheets`
+--
+
+LOCK TABLES `spreadsheets` WRITE;
+/*!40000 ALTER TABLE `spreadsheets` DISABLE KEYS */;
+/*!40000 ALTER TABLE `spreadsheets` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `taxons`
+--
+
+DROP TABLE IF EXISTS `taxons`;
+CREATE TABLE `taxons` (
+ `id` int(11) NOT NULL auto_increment,
+ `scientific_name` varchar(50) default NULL,
+ `common_name` varchar(50) default NULL,
+ `accession` int(11) default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `taxons`
+--
+
+LOCK TABLES `taxons` WRITE;
+/*!40000 ALTER TABLE `taxons` DISABLE KEYS */;
+/*!40000 ALTER TABLE `taxons` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+CREATE TABLE `users` (
+ `id` int(11) NOT NULL auto_increment,
+ `login` varchar(40) NOT NULL,
+ `name` varchar(40) default NULL,
+ `password` varchar(40) NOT NULL,
+ `email` varchar(100) default NULL,
+ `modified_at` datetime default NULL,
+ `created_at` datetime default NULL,
+ `access` datetime default NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `login` (`login`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `users`
+--
+
+LOCK TABLES `users` WRITE;
+/*!40000 ALTER TABLE `users` DISABLE KEYS */;
+/*!40000 ALTER TABLE `users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+-- Dump completed on 2007-04-24 15:37:45
+
+
+-- #####################################################################
+-- The following are additional tables for the extended tracking system.
+-- #####################################################################
+
+--
+-- Table structure for table `array_designs_organisms`
+--
+
+DROP TABLE IF EXISTS `array_designs_organisms`;
+CREATE TABLE `array_designs_organisms` (
+ `id` int(11) NOT NULL auto_increment,
+ `organism_id` int(11) NOT NULL,
+ `array_design_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `organism_id` (`organism_id`),
+ KEY `array_design_id` (`array_design_id`),
+ CONSTRAINT `array_designs_organisms_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `array_designs_organisms_ibfk_2` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quantitation_types`
+--
+
+DROP TABLE IF EXISTS `quantitation_types`;
+CREATE TABLE `quantitation_types` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(128) COLLATE latin1_general_cs NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_quantitation_types`
+--
+
+DROP TABLE IF EXISTS `experiments_quantitation_types`;
+CREATE TABLE `experiments_quantitation_types` (
+ `id` int(11) NOT NULL auto_increment,
+ `quantitation_type_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quantitation_type_id` (`quantitation_type_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `experiments_quantitation_types_ibfk_1` FOREIGN KEY (`quantitation_type_id`) REFERENCES `quantitation_types` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `experiments_quantitation_types_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `array_designs_experiments`
+--
+
+DROP TABLE IF EXISTS `array_designs_experiments`;
+CREATE TABLE `array_designs_experiments` (
+ `id` int(11) NOT NULL auto_increment,
+ `array_design_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `array_design_id` (`array_design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `array_designs_experiments_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `array_designs_experiments_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `factors`
+--
+
+DROP TABLE IF EXISTS `factors`;
+CREATE TABLE `factors` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(128) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_factors`
+--
+
+DROP TABLE IF EXISTS `experiments_factors`;
+CREATE TABLE `experiments_factors` (
+ `id` int(11) NOT NULL auto_increment,
+ `experiment_id` int(11) NOT NULL,
+ `factor_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `experiment_id` (`experiment_id`),
+ KEY `factor_id` (`factor_id`),
+ CONSTRAINT `experiments_factors_ibfk_1` FOREIGN KEY (`factor_id`) REFERENCES `factors` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `experiments_factors_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `events`
+--
+
+DROP TABLE IF EXISTS `events`;
+CREATE TABLE `events` (
+ `id` int(11) NOT NULL auto_increment,
+ `array_design_id` int(11) default NULL,
+ `experiment_id` int(11) default NULL,
+ `event_type` varchar(50) NOT NULL,
+ `was_successful` int(11) default NULL,
+ `source_db` varchar(30) default NULL,
+ `target_db` varchar(30) default NULL,
+ `start_time` datetime default NULL, -- REDUNDANT
+ `end_time` datetime default NULL, -- REDUNDANT
+ `machine` varchar(50) default NULL,
+ `operator` varchar(30) default NULL,
+ `log_file` varchar(511) default NULL,
+ `jobregister_dbid` int(15) default NULL,
+ `comment` text,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `array_design_id` (`array_design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `events_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE RESTRICT,
+ CONSTRAINT `events_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+-- #######################################
+-- Additional columns for existing tables.
+-- #######################################
+
+ALTER TABLE experiments ADD COLUMN (
+ `num_submissions` int(11) default NULL,
+ `submitter_description` text, -- REDUNDANT
+ `curated_name` varchar(255) default NULL, -- REDUNDANT
+ `num_samples` int(11) default NULL, -- REDUNDANT
+ `num_hybridizations` int(11) default NULL, -- REDUNDANT
+ `has_raw_data` int(11) default NULL, -- REDUNDANT
+ `has_processed_data` int(11) default NULL, -- REDUNDANT
+ `release_date` datetime default NULL, -- REDUNDANT
+ `is_released` int(11) default NULL, -- REDUNDANT
+ `ae_miame_score` int(11) default NULL, -- REDUNDANT
+ `ae_data_warehouse_score` int(11) default NULL -- REDUNDANT
+);
+
+ALTER TABLE array_designs ADD COLUMN (
+ `annotation_source` varchar(50) default NULL, -- REDUNDANT?
+ `annotation_version` varchar(50) default NULL, -- REDUNDANT?
+ `biomart_table_name` varchar(50) default NULL, -- REDUNDANT?
+ `release_date` datetime default NULL, -- REDUNDANT
+ `is_released` int(11) default NULL -- REDUNDANT
+);
+
+-- ###############################################################
+-- We need to relax the requirement that all organisms have taxon;
+-- otherwise auto-inserting new organisms gets ugly.
+-- ###############################################################
+ALTER TABLE organisms MODIFY COLUMN taxon_id int(11) DEFAULT NULL;
+ALTER TABLE array_designs MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+ALTER TABLE experiments MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+
+-- Fix our legacy submissions.
+UPDATE experiments SET num_submissions=1 where in_curation=1;
+
+-- #######################################################################
+-- The following are additional tables for the loaded data hashing system.
+-- #######################################################################
+
+--
+-- Table structure for table `data_formats`
+--
+
+DROP TABLE IF EXISTS `data_formats`;
+CREATE TABLE `data_formats` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(50) COLLATE latin1_general_cs UNIQUE NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `platforms`
+--
+
+DROP TABLE IF EXISTS `platforms`;
+CREATE TABLE `platforms` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(50) UNIQUE NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data`
+--
+
+DROP TABLE IF EXISTS `loaded_data`;
+CREATE TABLE `loaded_data` (
+ `id` int(11) NOT NULL auto_increment,
+ `identifier` varchar(255) COLLATE latin1_general_cs NOT NULL,
+ `md5_hash` char(35) NOT NULL,
+ `data_format_id` int(11) NOT NULL,
+ `platform_id` int(11) NOT NULL,
+ `needs_metrics_calculation` int(11) NOT NULL,
+ `date_hashed` datetime,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `data_format_id` (`data_format_id`),
+ CONSTRAINT `data_format_loaded_data_ibfk_1` FOREIGN KEY (`data_format_id`) REFERENCES `data_formats` (`id`) ON DELETE RESTRICT,
+ CONSTRAINT `platform_loaded_data_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platforms` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quality_metrics`
+--
+
+DROP TABLE IF EXISTS `quality_metrics`;
+CREATE TABLE `quality_metrics` (
+ `id` int(11) NOT NULL auto_increment,
+ `type` varchar(50) UNIQUE NOT NULL,
+ `description` text,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_loaded_data`
+--
+
+DROP TABLE IF EXISTS `experiments_loaded_data`;
+CREATE TABLE `experiments_loaded_data` (
+ `experiment_id` int(11) NOT NULL,
+ `loaded_data_id` int(11) NOT NULL,
+ KEY `experiment_id` (`experiment_id`),
+ KEY `loaded_data_id` (`loaded_data_id`),
+ CONSTRAINT `loaded_data_experiment_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `loaded_data_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data_quality_metrics`
+--
+
+DROP TABLE IF EXISTS `loaded_data_quality_metrics`;
+CREATE TABLE `loaded_data_quality_metrics` (
+ `id` int(11) NOT NULL auto_increment,
+ `value` numeric,
+ `date_calculated` datetime,
+ `quality_metric_id` int(11) NOT NULL,
+ `loaded_data_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quality_metric_id` (`quality_metric_id`),
+ KEY `loaded_data_id` (`loaded_data_id`),
+ CONSTRAINT `quality_metric_quality_metric_ibfk_1` FOREIGN KEY (`quality_metric_id`) REFERENCES `quality_metrics` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `quality_metric_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
diff --git a/automation/admin/db/migration/migrate_2.0_to_2.1.sql b/automation/admin/db/migration/migrate_2.0_to_2.1.sql
new file mode 100644
index 0000000..46e21e1
--- /dev/null
+++ b/automation/admin/db/migration/migrate_2.0_to_2.1.sql
@@ -0,0 +1,183 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost Database: test
+-- ------------------------------------------------------
+-- Server version 5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+-- #####################################################################
+-- The following are additional tables for the extended tracking system.
+-- #####################################################################
+
+--
+-- Table structure for table `array_designs_organisms`
+--
+
+DROP TABLE IF EXISTS `array_designs_organisms`;
+CREATE TABLE `array_designs_organisms` (
+ `id` int(11) NOT NULL auto_increment,
+ `organism_id` int(11) NOT NULL,
+ `array_design_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `organism_id` (`organism_id`),
+ KEY `array_design_id` (`array_design_id`),
+ CONSTRAINT `array_designs_organisms_ibfk_1` FOREIGN KEY (`organism_id`) REFERENCES `organisms` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `array_designs_organisms_ibfk_2` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quantitation_types`
+--
+
+DROP TABLE IF EXISTS `quantitation_types`;
+CREATE TABLE `quantitation_types` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(128) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_quantitation_types`
+--
+
+DROP TABLE IF EXISTS `experiments_quantitation_types`;
+CREATE TABLE `experiments_quantitation_types` (
+ `id` int(11) NOT NULL auto_increment,
+ `quantitation_type_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quantitation_type_id` (`quantitation_type_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `experiments_quantitation_types_ibfk_1` FOREIGN KEY (`quantitation_type_id`) REFERENCES `quantitation_types` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `experiments_quantitation_types_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `array_designs_experiments`
+--
+
+DROP TABLE IF EXISTS `array_designs_experiments`;
+CREATE TABLE `array_designs_experiments` (
+ `id` int(11) NOT NULL auto_increment,
+ `array_design_id` int(11) NOT NULL,
+ `experiment_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `array_design_id` (`array_design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `array_designs_experiments_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `array_designs_experiments_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `factors`
+--
+
+DROP TABLE IF EXISTS `factors`;
+CREATE TABLE `factors` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(128) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_factors`
+--
+
+DROP TABLE IF EXISTS `experiments_factors`;
+CREATE TABLE `experiments_factors` (
+ `id` int(11) NOT NULL auto_increment,
+ `experiment_id` int(11) NOT NULL,
+ `factor_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `experiment_id` (`experiment_id`),
+ KEY `factor_id` (`factor_id`),
+ CONSTRAINT `experiments_factors_ibfk_1` FOREIGN KEY (`factor_id`) REFERENCES `factors` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `experiments_factors_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `events`
+--
+
+DROP TABLE IF EXISTS `events`;
+CREATE TABLE `events` (
+ `id` int(11) NOT NULL auto_increment,
+ `array_design_id` int(11) default NULL,
+ `experiment_id` int(11) default NULL,
+ `event_type` varchar(50) NOT NULL,
+ `was_successful` int(11) default NULL,
+ `source_db` varchar(30) default NULL,
+ `target_db` varchar(30) default NULL,
+ `start_time` datetime default NULL, -- REDUNDANT
+ `end_time` datetime default NULL, -- REDUNDANT
+ `machine` varchar(50) default NULL,
+ `operator` varchar(30) default NULL,
+ `log_file` varchar(511) default NULL,
+ `jobregister_dbid` int(15) default NULL,
+ `comment` text,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `array_design_id` (`array_design_id`),
+ KEY `experiment_id` (`experiment_id`),
+ CONSTRAINT `events_ibfk_1` FOREIGN KEY (`array_design_id`) REFERENCES `array_designs` (`id`) ON DELETE RESTRICT,
+ CONSTRAINT `events_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+-- #######################################
+-- Additional columns for existing tables.
+-- #######################################
+
+ALTER TABLE experiments ADD COLUMN (
+ `num_submissions` int(11) default NULL,
+ `submitter_description` text, -- REDUNDANT
+ `curated_name` varchar(255) default NULL, -- REDUNDANT
+ `num_samples` int(11) default NULL, -- REDUNDANT
+ `num_hybridizations` int(11) default NULL, -- REDUNDANT
+ `has_raw_data` int(11) default NULL, -- REDUNDANT
+ `has_processed_data` int(11) default NULL, -- REDUNDANT
+ `release_date` datetime default NULL, -- REDUNDANT
+ `is_released` int(11) default NULL, -- REDUNDANT
+ `ae_miame_score` int(11) default NULL, -- REDUNDANT
+ `ae_data_warehouse_score` int(11) default NULL -- REDUNDANT
+);
+
+ALTER TABLE array_designs ADD COLUMN (
+ `annotation_source` varchar(50) default NULL, -- REDUNDANT?
+ `annotation_version` varchar(50) default NULL, -- REDUNDANT?
+ `biomart_table_name` varchar(50) default NULL, -- REDUNDANT?
+ `release_date` datetime default NULL, -- REDUNDANT
+ `is_released` int(11) default NULL -- REDUNDANT
+);
+
+-- ###############################################################
+-- We need to relax the requirement that all organisms have taxon;
+-- otherwise auto-inserting new organisms gets ugly.
+-- ###############################################################
+ALTER TABLE organisms MODIFY COLUMN taxon_id int(11) DEFAULT NULL;
+ALTER TABLE array_designs MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+ALTER TABLE experiments MODIFY COLUMN accession varchar(255) DEFAULT NULL;
+
+-- Fix our legacy submissions.
+UPDATE experiments SET num_submissions=1 where in_curation=1;
+
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
diff --git a/automation/admin/db/migration/migrate_2.1_to_2.2.sql b/automation/admin/db/migration/migrate_2.1_to_2.2.sql
new file mode 100644
index 0000000..795a3ba
--- /dev/null
+++ b/automation/admin/db/migration/migrate_2.1_to_2.2.sql
@@ -0,0 +1,125 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost Database: test
+-- ------------------------------------------------------
+-- Server version 5.0.27
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+-- #######################################################################
+-- The following are additional tables for the loaded data hashing system.
+-- #######################################################################
+
+--
+-- Table structure for table `data_formats`
+--
+
+DROP TABLE IF EXISTS `data_formats`;
+CREATE TABLE `data_formats` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(50) COLLATE latin1_general_cs UNIQUE NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data`
+--
+
+DROP TABLE IF EXISTS `loaded_data`;
+CREATE TABLE `loaded_data` (
+ `id` int(11) NOT NULL auto_increment,
+ `identifier` varchar(255) COLLATE latin1_general_cs NOT NULL,
+ `md5_hash` char(35) NOT NULL,
+ `data_format_id` int(11) NOT NULL,
+ `is_deleted` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `data_format_id` (`data_format_id`),
+ CONSTRAINT `data_format_loaded_data_ibfk_1` FOREIGN KEY (`data_format_id`) REFERENCES `data_formats` (`id`) ON DELETE RESTRICT
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `quality_metrics`
+--
+
+DROP TABLE IF EXISTS `quality_metrics`;
+CREATE TABLE `quality_metrics` (
+ `id` int(11) NOT NULL auto_increment,
+ `type` varchar(50) UNIQUE NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `experiments_loaded_data`
+--
+
+DROP TABLE IF EXISTS `experiments_loaded_data`;
+CREATE TABLE `experiments_loaded_data` (
+ `experiment_id` int(11) NOT NULL,
+ `loaded_data_id` int(11) NOT NULL,
+ KEY `experiment_id` (`experiment_id`),
+ KEY `loaded_data_id` (`loaded_data_id`),
+ CONSTRAINT `loaded_data_experiment_ibfk_1` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `loaded_data_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `loaded_data_quality_metrics`
+--
+
+DROP TABLE IF EXISTS `loaded_data_quality_metrics`;
+CREATE TABLE `loaded_data_quality_metrics` (
+ `id` int(11) NOT NULL auto_increment,
+ `value` decimal(12,5),
+ `quality_metric_id` int(11) NOT NULL,
+ `loaded_data_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quality_metric_id` (`quality_metric_id`),
+ KEY `loaded_data_id` (`loaded_data_id`),
+ CONSTRAINT `quality_metric_quality_metric_ibfk_1` FOREIGN KEY (`quality_metric_id`) REFERENCES `quality_metrics` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `quality_metric_loaded_data_ibfk_1` FOREIGN KEY (`loaded_data_id`) REFERENCES `loaded_data` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Fix the matching of QT so that it's case sensitive
+--
+ALTER TABLE `quantitation_types` MODIFY COLUMN `name` varchar(128) COLLATE latin1_general_cs NOT NULL;
+
+--
+-- Table structure for table `platforms`
+--
+
+CREATE TABLE `platforms` (
+ `id` int(11) NOT NULL auto_increment,
+ `name` varchar(50) UNIQUE NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Other additions noted during testing.
+--
+
+alter table loaded_data add column platform_id int(11);
+alter table loaded_data add column needs_metrics_calculation int(11) not null;
+alter table loaded_data add column date_hashed datetime;
+alter table loaded_data add constraint `platform_loaded_data_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platforms` (`id`) ON DELETE RESTRICT;
+alter table quality_metrics add column description text;
+alter table loaded_data_quality_metrics add column date_calculated datetime;
+
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
diff --git a/automation/admin/db/migration/migrate_csv2sqlite.pl b/automation/admin/db/migration/migrate_csv2sqlite.pl
new file mode 100755
index 0000000..5448d03
--- /dev/null
+++ b/automation/admin/db/migration/migrate_csv2sqlite.pl
@@ -0,0 +1,190 @@
+#!/usr/bin/env perl
+
+# Script used to migrate 1.8 series DBD::CSV accession cache to 1.9
+# series DBD::SQLite autosubmissions database. Note that the files in
+# question must have Unix, not DOS linebreaks.
+
+# Tim Rayner 2006 ArrayExpress Team, EBI
+# $Id: migrate_csv2sqlite.pl 1862 2007-12-27 16:30:18Z tfrayner $
+
+use strict;
+use warnings;
+
+##############################################
+package AccessionCacheDB;
+use base 'Class::DBI';
+
+unless (@ARGV) {
+ warn <<USAGE;
+ Usage: $0 <DBD::CSV database directory> [ Tab2MAGE experiment web page (optional) ]
+
+USAGE
+ exit 255;
+}
+
+my $csvdir = shift @ARGV;
+__PACKAGE__->connection(
+ "dbi:CSV:f_dir=$csvdir",
+ undef,
+ undef,
+ {csv_sep_char => "\t",
+ csv_eol => "\n",
+ RaiseError => 1,}
+) or die();
+
+##############################################
+package AccessionCacheDB::Experiment;
+use base 'AccessionCacheDB';
+__PACKAGE__->table('mx_experiment');
+__PACKAGE__->columns(
+ All => qw(
+ subid
+ accession
+ name
+ user
+ checker_score
+ software
+ status
+ date_last_processed
+ comment
+ curator
+ data_warehouse_ready
+ )
+);
+
+##############################################
+package AccessionCacheDB::Array;
+use base 'AccessionCacheDB';
+__PACKAGE__->table('mx_array');
+__PACKAGE__->columns(
+ All => qw(
+ subid
+ accession
+ name
+ user
+ status
+ date_last_processed
+ comment
+ data_warehouse_ready
+ )
+);
+
+##############################################
+package AccessionCacheDB::Protocol;
+use base 'AccessionCacheDB';
+__PACKAGE__->table('t2m_protocol');
+__PACKAGE__->columns(
+ All => qw(
+ accession
+ user_accession
+ expt_accession
+ name
+ )
+);
+
+##############################################
+package main;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+require ArrayExpress::AutoSubmission::DB::Protocol;
+use ArrayExpress::Curator::Common qw(date_now);
+
+my $date = date_now();
+
+print STDERR ("Loading MIAMExpress experiments...\n");
+foreach my $expt ( AccessionCacheDB::Experiment->retrieve_all() ) {
+ ArrayExpress::AutoSubmission::DB::Experiment->insert({
+ experiment_type => 'MIAMExpress',
+ accession => $expt->accession,
+ name => $expt->name,
+ checker_score => $expt->checker_score,
+ software => $expt->software,
+ status => $expt->status,
+ data_warehouse_ready => $expt->data_warehouse_ready,
+ date_last_processed => $expt->date_last_processed,
+ curator => $expt->curator,
+ comment => $expt->comment,
+ miamexpress_login => $expt->user,
+ miamexpress_subid => $expt->subid,
+ is_deleted => 0,
+ });
+ print STDERR q{.};
+}
+print STDERR ("\n");
+
+print STDERR ("Loading MIAMExpress arrays...\n");
+foreach my $array ( AccessionCacheDB::Array->retrieve_all() ) {
+ ArrayExpress::AutoSubmission::DB::ArrayDesign->insert({
+ miamexpress_subid => $array->subid,
+ accession => $array->accession,
+ name => $array->name,
+ miamexpress_login => $array->user,
+ status => $array->status,
+ data_warehouse_ready => $array->data_warehouse_ready,
+ date_last_processed => $array->date_last_processed,
+ comment => $array->comment,
+ is_deleted => 0,
+ });
+ print STDERR q{.};
+}
+print STDERR ("\n");
+
+print STDERR ("Loading Tab2MAGE protocols...\n");
+foreach my $protocol ( AccessionCacheDB::Protocol->retrieve_all() ) {
+ ArrayExpress::AutoSubmission::DB::Protocol->insert({
+ accession => $protocol->accession,
+ user_accession => $protocol->user_accession,
+ expt_accession => $protocol->expt_accession,
+ name => $protocol->name,
+ date_last_processed => $date,
+ comment => 'from old system',
+ is_deleted => 0,
+ });
+ print STDERR q{.};
+}
+print STDERR ("\n");
+
+# If possible, parse an optional web page with tab2mage experiment info.
+if ( my $t2m_webpage = shift @ARGV ) {
+
+ print STDERR ("Loading Tab2MAGE experiments...\n");
+
+ require LWP::UserAgent;
+ require HTML::TableExtract;
+
+ my $ua = LWP::UserAgent->new();
+
+ my $response = $ua->get( $t2m_webpage );
+
+ unless ( $response->is_success ) {
+ croak( "Error retrieving tab2mage experiments: "
+ . $response->status_line );
+ }
+
+ my $te = new HTML::TableExtract(
+ headers => [
+ 'Accession',
+ 'Person',
+ 'ExperimentDesignType',
+ 'Experiment Name'
+ ]
+ );
+ $te->parse( $response->content );
+
+ # Go for the first and only table.
+ foreach my $row ($te->rows) {
+ if ( $row->[0] =~ /E-TABM-\d+/ ) {
+ ArrayExpress::AutoSubmission::DB::Experiment->insert({
+ experiment_type => 'Tab2MAGE',
+ accession => $row->[0],
+ name => $row->[3],
+ status => 'Complete',
+ date_last_processed => $date,
+ comment => "submitter: $row->[1]; design: $row->[2]",
+ is_deleted => 0,
+ });
+ print STDERR q{.};
+ }
+ }
+}
+print STDERR ("\n");
diff --git a/automation/admin/db/migration/migrate_sqlite2mysql.pl b/automation/admin/db/migration/migrate_sqlite2mysql.pl
new file mode 100755
index 0000000..cff195e
--- /dev/null
+++ b/automation/admin/db/migration/migrate_sqlite2mysql.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/env perl
+#
+# Very basic script to take the SQLite dump from the old autosubs
+# database and convert it into something MySQL can load. The MySQL
+# output assumes that the tables have already been created using the
+# SQL in the automation/admin/db directory. This is a one-off job,
+# provided here as a convenience.
+#
+# $Id: migrate_sqlite2mysql.pl 1862 2007-12-27 16:30:18Z tfrayner $
+
+use strict;
+use warnings;
+
+my $sqlite_file = shift;
+
+unless ($sqlite_file) {
+ die ("Usage: $0 sqlite_sql_dump_file.txt > mysql_sql.txt\n");
+}
+
+open(my $sqlite, '<', $sqlite_file) or die($!);
+
+# Split on SQL statements, each terminated with a semicolon. Note that
+# we're assuming Unix line-endings here.
+local $/ = ";\n";
+
+my $create_table = qr/\A [ ]* CREATE [ ]+ TABLE [ ]+ `?(\w+)`?/xms;
+my $insert_into = qr/\A [ ]* INSERT [ ]+ INTO/xms;
+
+my %schema;
+CHUNK:
+while ( my $chunk = <$sqlite> ) {
+
+ next CHUNK if ( $chunk =~ /sqlite_sequence/i );
+
+ if ( my ($table) = ($chunk =~ /$create_table/i) ) {
+ $chunk =~ s/$create_table [ ]+ \(\n//xms;
+ $chunk =~ s/\);\n \z//xms;
+ chomp $chunk;
+ my @cols = map { ($_ =~ /\A [ ]* (\w+)/xms) } split /,[\n ]*/, $chunk;
+ $schema{$table} = \@cols;
+ }
+
+ if ( $chunk =~ /$insert_into/i ) {
+ $chunk =~ s/"/`/g;
+ if ( my ($table) = ($chunk =~ /$insert_into [ ]+ `([^`]+)`/ixms) ) {
+
+ unless ( $schema{$table} ) {
+ die(qq{Error: Table "$table" has undefined schema.\n});
+ }
+
+ my $columnlist = q{(} . join(',', map {"`$_`"} @{ $schema{$table} }) . q{)};
+
+ $chunk =~ s/($insert_into [ ]+ `?${table}`?) [ ]+ VALUES/$1 $columnlist VALUES/ixms
+ or die("Error: can't substitute into chunk:\n\n$chunk\n");
+
+ print $chunk;
+ }
+ else {
+ die("Error: can't detect table name in chunk:\n\n$chunk\n");
+ }
+ }
+}
+
+close($sqlite);
diff --git a/automation/admin/db/schema.pdf b/automation/admin/db/schema.pdf
new file mode 100644
index 0000000..cc123ac
Binary files /dev/null and b/automation/admin/db/schema.pdf differ
diff --git a/automation/admin/db/schema.rb b/automation/admin/db/schema.rb
new file mode 100644
index 0000000..24eccb1
--- /dev/null
+++ b/automation/admin/db/schema.rb
@@ -0,0 +1,347 @@
+# This file is auto-generated from the current state of the database. Instead of editing this file,
+# please use the migrations feature of ActiveRecord to incrementally modify your database, and
+# then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your database schema. If you need
+# to create the application database on another system, you should be using db:schema:load, not running
+# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 0) do
+
+ create_table "array_designs", :force => true do |t|
+ t.integer "miamexpress_subid"
+ t.string "accession"
+ t.string "name"
+ t.string "miamexpress_login", :limit => 50
+ t.string "status", :limit => 50
+ t.string "data_warehouse_ready", :limit => 15
+ t.datetime "date_last_processed"
+ t.text "comment"
+ t.integer "is_deleted", :null => false
+ t.integer "miame_score"
+ t.integer "in_data_warehouse"
+ t.string "annotation_source", :limit => 50
+ t.string "annotation_version", :limit => 50
+ t.string "biomart_table_name", :limit => 50
+ t.datetime "release_date"
+ t.integer "is_released"
+ end
+
+ add_index "array_designs", ["miamexpress_subid"], :name => "miamexpress_subid", :unique => true
+
+ create_table "array_designs_experiments", :force => true do |t|
+ t.integer "array_design_id", :null => false
+ t.integer "experiment_id", :null => false
+ end
+
+ add_index "array_designs_experiments", ["array_design_id"], :name => "array_design_id"
+ add_index "array_designs_experiments", ["experiment_id"], :name => "experiment_id"
+
+ create_table "array_designs_organisms", :force => true do |t|
+ t.integer "organism_id", :null => false
+ t.integer "array_design_id", :null => false
+ end
+
+ add_index "array_designs_organisms", ["organism_id"], :name => "organism_id"
+ add_index "array_designs_organisms", ["array_design_id"], :name => "array_design_id"
+
+ create_table "categories", :force => true do |t|
+ t.string "ontology_term", :limit => 50
+ t.string "display_label", :limit => 50
+ t.integer "is_common"
+ t.integer "is_bmc"
+ t.integer "is_fv"
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "categories_designs", :id => false, :force => true do |t|
+ t.integer "category_id", :null => false
+ t.integer "design_id", :null => false
+ end
+
+ add_index "categories_designs", ["category_id"], :name => "category_id"
+ add_index "categories_designs", ["design_id"], :name => "design_id"
+
+ create_table "categories_materials", :id => false, :force => true do |t|
+ t.integer "category_id", :null => false
+ t.integer "material_id", :null => false
+ end
+
+ add_index "categories_materials", ["category_id"], :name => "category_id"
+ add_index "categories_materials", ["material_id"], :name => "material_id"
+
+ create_table "categories_taxons", :id => false, :force => true do |t|
+ t.integer "category_id", :null => false
+ t.integer "taxon_id", :null => false
+ end
+
+ add_index "categories_taxons", ["category_id"], :name => "category_id"
+ add_index "categories_taxons", ["taxon_id"], :name => "taxon_id"
+
+ create_table "data_files", :force => true do |t|
+ t.integer "experiment_id", :null => false
+ t.string "name"
+ t.integer "is_unpacked"
+ t.integer "is_deleted", :null => false
+ end
+
+ add_index "data_files", ["experiment_id"], :name => "experiment_id"
+
+ create_table "data_formats", :force => true do |t|
+ t.string "name", :limit => 50, :default => "", :null => false
+ end
+
+ add_index "data_formats", ["name"], :name => "name", :unique => true
+
+ create_table "design_instances", :force => true do |t|
+ t.integer "design_id", :null => false
+ t.integer "experiment_id", :null => false
+ end
+
+ add_index "design_instances", ["design_id"], :name => "design_id"
+ add_index "design_instances", ["experiment_id"], :name => "experiment_id"
+
+ create_table "designs", :force => true do |t|
+ t.string "display_label", :limit => 50
+ t.string "ontology_category", :limit => 50
+ t.string "ontology_value", :limit => 50
+ t.string "design_type", :limit => 15, :default => "", :null => false
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "events", :force => true do |t|
+ t.integer "array_design_id"
+ t.integer "experiment_id"
+ t.string "event_type", :limit => 50, :default => "", :null => false
+ t.integer "was_successful"
+ t.string "source_db", :limit => 30
+ t.string "target_db", :limit => 30
+ t.datetime "start_time"
+ t.datetime "end_time"
+ t.string "machine", :limit => 50
+ t.string "operator", :limit => 30
+ t.string "log_file", :limit => 511
+ t.integer "jobregister_dbid", :limit => 15
+ t.text "comment"
+ t.integer "is_deleted", :null => false
+ end
+
+ add_index "events", ["array_design_id"], :name => "array_design_id"
+ add_index "events", ["experiment_id"], :name => "experiment_id"
+
+ create_table "experiments", :force => true do |t|
+ t.string "accession"
+ t.string "name"
+ t.integer "user_id"
+ t.integer "checker_score"
+ t.string "software", :limit => 100
+ t.string "status", :limit => 50
+ t.integer "data_warehouse_ready"
+ t.datetime "date_last_edited"
+ t.datetime "date_submitted"
+ t.datetime "date_last_processed"
+ t.integer "in_curation"
+ t.string "curator", :limit => 30
+ t.text "comment"
+ t.string "experiment_type", :limit => 30
+ t.string "miamexpress_login", :limit => 30
+ t.integer "miamexpress_subid"
+ t.integer "is_affymetrix"
+ t.integer "is_mx_batchloader"
+ t.integer "is_deleted", :null => false
+ t.integer "miame_score"
+ t.integer "in_data_warehouse"
+ t.integer "num_submissions"
+ t.text "submitter_description"
+ t.string "curated_name"
+ t.integer "num_samples"
+ t.integer "num_hybridizations"
+ t.integer "has_raw_data"
+ t.integer "has_processed_data"
+ t.datetime "release_date"
+ t.integer "is_released"
+ t.integer "ae_miame_score"
+ t.integer "ae_data_warehouse_score"
+ end
+
+ add_index "experiments", ["user_id"], :name => "user_id"
+
+ create_table "experiments_factors", :force => true do |t|
+ t.integer "experiment_id", :null => false
+ t.integer "factor_id", :null => false
+ end
+
+ add_index "experiments_factors", ["experiment_id"], :name => "experiment_id"
+ add_index "experiments_factors", ["factor_id"], :name => "factor_id"
+
+ create_table "experiments_loaded_data", :id => false, :force => true do |t|
+ t.integer "experiment_id", :null => false
+ t.integer "loaded_data_id", :null => false
+ end
+
+ add_index "experiments_loaded_data", ["experiment_id"], :name => "experiment_id"
+ add_index "experiments_loaded_data", ["loaded_data_id"], :name => "loaded_data_id"
+
+ create_table "experiments_quantitation_types", :force => true do |t|
+ t.integer "quantitation_type_id", :null => false
+ t.integer "experiment_id", :null => false
+ end
+
+ add_index "experiments_quantitation_types", ["quantitation_type_id"], :name => "quantitation_type_id"
+ add_index "experiments_quantitation_types", ["experiment_id"], :name => "experiment_id"
+
+ create_table "factors", :force => true do |t|
+ t.string "name", :limit => 128, :default => "", :null => false
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "loaded_data", :force => true do |t|
+ t.string "identifier", :default => "", :null => false
+ t.string "md5_hash", :limit => 35, :default => "", :null => false
+ t.integer "data_format_id", :null => false
+ t.integer "is_deleted", :null => false
+ t.integer "platform_id"
+ t.integer "needs_metrics_calculation", :null => false
+ t.datetime "date_hashed"
+ end
+
+ add_index "loaded_data", ["data_format_id"], :name => "data_format_id"
+ add_index "loaded_data", ["platform_id"], :name => "platform_loaded_data_ibfk_1"
+
+ create_table "loaded_data_quality_metrics", :force => true do |t|
+ t.decimal "value", :precision => 12, :scale => 5
+ t.integer "quality_metric_id", :null => false
+ t.integer "loaded_data_id", :null => false
+ t.datetime "date_calculated"
+ end
+
+ add_index "loaded_data_quality_metrics", ["quality_metric_id"], :name => "quality_metric_id"
+ add_index "loaded_data_quality_metrics", ["loaded_data_id"], :name => "loaded_data_id"
+
+ create_table "material_instances", :force => true do |t|
+ t.integer "material_id", :null => false
+ t.integer "experiment_id", :null => false
+ end
+
+ add_index "material_instances", ["material_id"], :name => "material_id"
+ add_index "material_instances", ["experiment_id"], :name => "experiment_id"
+
+ create_table "materials", :force => true do |t|
+ t.string "display_label", :limit => 50
+ t.string "ontology_category", :limit => 50
+ t.string "ontology_value", :limit => 50
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "meta", :primary_key => "name", :force => true do |t|
+ t.string "value", :limit => 128, :default => "", :null => false
+ end
+
+ create_table "organism_instances", :force => true do |t|
+ t.integer "organism_id", :null => false
+ t.integer "experiment_id", :null => false
+ end
+
+ add_index "organism_instances", ["organism_id"], :name => "organism_id"
+ add_index "organism_instances", ["experiment_id"], :name => "experiment_id"
+
+ create_table "organisms", :force => true do |t|
+ t.string "scientific_name", :limit => 50
+ t.string "common_name", :limit => 50
+ t.integer "accession"
+ t.integer "taxon_id"
+ t.integer "is_deleted", :null => false
+ end
+
+ add_index "organisms", ["taxon_id"], :name => "taxon_id"
+
+ create_table "permissions", :force => true do |t|
+ t.string "name", :limit => 40, :default => "", :null => false
+ t.string "info", :limit => 80
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "permissions_roles", :id => false, :force => true do |t|
+ t.integer "role_id", :null => false
+ t.integer "permission_id", :null => false
+ end
+
+ add_index "permissions_roles", ["role_id"], :name => "role_id"
+ add_index "permissions_roles", ["permission_id"], :name => "permission_id"
+
+ create_table "platforms", :force => true do |t|
+ t.string "name", :limit => 50, :default => "", :null => false
+ end
+
+ add_index "platforms", ["name"], :name => "name", :unique => true
+
+ create_table "protocols", :force => true do |t|
+ t.string "accession", :limit => 15
+ t.string "user_accession", :limit => 100
+ t.string "expt_accession", :limit => 15
+ t.string "name"
+ t.datetime "date_last_processed"
+ t.text "comment"
+ t.integer "is_deleted", :null => false
+ end
+
+ add_index "protocols", ["accession"], :name => "accession", :unique => true
+
+ create_table "quality_metrics", :force => true do |t|
+ t.string "type", :limit => 50, :default => "", :null => false
+ t.text "description"
+ end
+
+ add_index "quality_metrics", ["type"], :name => "type", :unique => true
+
+ create_table "quantitation_types", :force => true do |t|
+ t.string "name", :limit => 128, :default => "", :null => false
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "roles", :force => true do |t|
+ t.string "name", :limit => 40, :default => "", :null => false
+ t.string "info", :limit => 80
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "roles_users", :id => false, :force => true do |t|
+ t.integer "user_id", :null => false
+ t.integer "role_id", :null => false
+ end
+
+ add_index "roles_users", ["user_id"], :name => "user_id"
+ add_index "roles_users", ["role_id"], :name => "role_id"
+
+ create_table "spreadsheets", :force => true do |t|
+ t.integer "experiment_id", :null => false
+ t.string "name"
+ t.integer "is_deleted", :null => false
+ end
+
+ add_index "spreadsheets", ["experiment_id"], :name => "experiment_id"
+
+ create_table "taxons", :force => true do |t|
+ t.string "scientific_name", :limit => 50
+ t.string "common_name", :limit => 50
+ t.integer "accession"
+ t.integer "is_deleted", :null => false
+ end
+
+ create_table "users", :force => true do |t|
+ t.string "login", :limit => 40, :default => "", :null => false
+ t.string "name", :limit => 40
+ t.string "password", :limit => 40, :default => "", :null => false
+ t.string "email", :limit => 100
+ t.datetime "modified_at"
+ t.datetime "created_at"
+ t.datetime "access"
+ t.integer "is_deleted", :null => false
+ end
+
+ add_index "users", ["login"], :name => "login", :unique => true
+
+end
diff --git a/automation/admin/doc/README_FOR_APP b/automation/admin/doc/README_FOR_APP
new file mode 100644
index 0000000..ac6c149
--- /dev/null
+++ b/automation/admin/doc/README_FOR_APP
@@ -0,0 +1,2 @@
+Use this README file to introduce your application and point to useful places in the API for learning more.
+Run "rake appdoc" to generate API documentation for your models and controllers.
\ No newline at end of file
diff --git a/automation/admin/lib/acl_system.rb b/automation/admin/lib/acl_system.rb
new file mode 100644
index 0000000..006aa0e
--- /dev/null
+++ b/automation/admin/lib/acl_system.rb
@@ -0,0 +1,31 @@
+# See <a href="http://wiki.rubyonrails.com/rails/show/LoginGeneratorACLSystem">http://wiki.rubyonrails.com/rails/show/LoginGeneratorACLSystem</a>
+
+module ACLSystem
+ include LoginSystem
+
+ # This module wires itself into the LoginSystem authorize? method. You
+ # should use the normal:
+ #
+ # before_filter :login_required
+ #
+ # or to leave some actions unprotected:
+ #
+ # before_filter :login_required, :except => [ :list, :show ]
+ #
+
+ protected
+
+ # Authorizes the user for an action.
+ # This works in conjunction with the LoginController.
+ # The LoginController loads the User object.
+ def authorize?(user)
+ required_perm = "%s/%s" % [ params[:controller], params[:action] ]
+
+ if user.authorized? required_perm
+ return true
+ end
+
+ return false
+ end
+end
+
diff --git a/automation/admin/lib/annotation.rb b/automation/admin/lib/annotation.rb
new file mode 100644
index 0000000..e53365e
--- /dev/null
+++ b/automation/admin/lib/annotation.rb
@@ -0,0 +1,96 @@
+module Annotation
+
+ def annotate(annotation)
+
+ if annotation
+ self.map_to_designs(annotation[:design_ids])
+ self.map_to_materials(annotation[:material_ids])
+ self.map_to_organisms(annotation[:organism_ids])
+ else
+
+ # empty annotation, so pass empty arrays to the mapping methods
+ self.map_to_designs([])
+ self.map_to_materials([])
+ self.map_to_organisms([])
+ end
+
+ true # I.e, nothing failed (FIXME to actually test this)...
+
+ end
+
+ def map_to_designs(new_design_ids)
+
+ if new_design_ids && new_design_ids.any?
+
+ # Remove unwanted designs
+ self.design_instances.each do |old|
+ unless new_design_ids.include?(old.design_id)
+ old.destroy
+ end
+ end
+
+ # Add new designs
+ new_design_ids.each do |newid|
+ unless self.design_instances.find(:first, :conditions => "design_id = " + newid.to_s)
+ design = Design.find(newid)
+ self.design_instances.create(:design => design)
+ end
+ end
+
+ else
+ self.design_instances.destroy_all
+ end
+
+ end
+
+ def map_to_materials(new_material_ids)
+
+ if new_material_ids && new_material_ids.any?
+
+ # Remove unwanted materials
+ self.material_instances.each do |old|
+ unless new_material_ids.include?(old.material_id)
+ old.destroy
+ end
+ end
+
+ # Add new materials
+ new_material_ids.each do |newid|
+ unless self.material_instances.find(:first, :conditions => "material_id = " + newid.to_s)
+ material = Material.find(newid)
+ self.material_instances.create(:material => material)
+ end
+ end
+
+ else
+ self.material_instances.destroy_all
+ end
+
+ end
+
+ def map_to_organisms(new_organism_ids)
+
+ if new_organism_ids && new_organism_ids.any?
+
+ # Remove unwanted organisms
+ self.organism_instances.each do |old|
+ unless new_organism_ids.include?(old.organism_id)
+ old.destroy
+ end
+ end
+
+ # Add new organisms
+ new_organism_ids.each do |newid|
+ unless self.organism_instances.find(:first, :conditions => "organism_id = " + newid.to_s)
+ organism = Organism.find(newid)
+ self.organism_instances.create(:organism => organism)
+ end
+ end
+
+ else
+ self.organism_instances.destroy_all
+ end
+
+ end
+
+end
diff --git a/automation/admin/lib/login_system.rb b/automation/admin/lib/login_system.rb
new file mode 100644
index 0000000..2a9243c
--- /dev/null
+++ b/automation/admin/lib/login_system.rb
@@ -0,0 +1,87 @@
+require_dependency "user"
+
+module LoginSystem
+
+ protected
+
+ # overwrite this if you want to restrict access to only a few actions
+ # or if you want to check if the user has the correct rights
+ # example:
+ #
+ # # only allow nonbobs
+ # def authorize?(user)
+ # user.login != "bob"
+ # end
+ def authorize?(user)
+ true
+ end
+
+ # overwrite this method if you only want to protect certain actions of the controller
+ # example:
+ #
+ # # don't protect the login and the about method
+ # def protect?(action)
+ # if ['action', 'about'].include?(action)
+ # return false
+ # else
+ # return true
+ # end
+ # end
+ def protect?(action)
+ true
+ end
+
+ # login_required filter. add
+ #
+ # before_filter :login_required
+ #
+ # if the controller should be under any rights management.
+ # for finer access control you can overwrite
+ #
+ # def authorize?(user)
+ #
+ def login_required
+
+ if not protect?(action_name)
+ return true
+ end
+
+ if session[:user] and authorize?(session[:user])
+ return true
+ end
+
+ # store current location so that we can
+ # come back after the user logged in
+ store_location
+
+ # call overwriteable reaction to unauthorized access
+ access_denied
+ return false
+ end
+
+ # overwrite if you want to have special behavior in case the user is not authorized
+ # to access the current operation.
+ # the default action is to redirect to the login screen
+ # example use :
+ # a popup window might just close itself for instance
+ def access_denied
+ redirect_to :controller=>"/account", :action =>"login"
+ end
+
+ # store current uri in the session.
+ # we can return to this location by calling return_location
+ def store_location
+ session['return-to'] = request.request_uri
+ end
+
+ # move to the last store_location call or to the passed default one
+ def redirect_back_or_default(default)
+ if session['return-to'].nil?
+ redirect_to default
+ else
+ redirect_to( session['return-to'] )
+ session['return-to'] = nil
+ end
+ end
+
+end
diff --git a/automation/admin/log/server.log b/automation/admin/log/server.log
new file mode 100644
index 0000000..e69de29
diff --git a/automation/admin/public/.htaccess b/automation/admin/public/.htaccess
new file mode 100644
index 0000000..d3c9983
--- /dev/null
+++ b/automation/admin/public/.htaccess
@@ -0,0 +1,40 @@
+# General Apache options
+AddHandler fastcgi-script .fcgi
+AddHandler cgi-script .cgi
+Options +FollowSymLinks +ExecCGI
+
+# If you don't want Rails to look in certain directories,
+# use the following rewrite rules so that Apache won't rewrite certain requests
+#
+# Example:
+# RewriteCond %{REQUEST_URI} ^/notrails.*
+# RewriteRule .* - [L]
+
+# Redirect all requests not available on the filesystem to Rails
+# By default the cgi dispatcher is used which is very slow
+#
+# For better performance replace the dispatcher with the fastcgi one
+#
+# Example:
+# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+RewriteEngine On
+
+# If your Rails application is accessed via an Alias directive,
+# then you MUST also set the RewriteBase in this htaccess file.
+#
+# Example:
+# Alias /myrailsapp /path/to/myrailsapp/public
+# RewriteBase /myrailsapp
+
+RewriteRule ^$ index.html [QSA]
+RewriteRule ^([^.]+)$ $1.html [QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
+
+# In case Rails experiences terminal errors
+# Instead of displaying this message you can supply a file here which will be rendered instead
+#
+# Example:
+# ErrorDocument 500 /500.html
+
+ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
\ No newline at end of file
diff --git a/automation/admin/public/404.html b/automation/admin/public/404.html
new file mode 100644
index 0000000..0e18456
--- /dev/null
+++ b/automation/admin/public/404.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<body>
+ <h1>File not found</h1>
+ <p>Change this error message for pages not found in public/404.html</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/automation/admin/public/500.html b/automation/admin/public/500.html
new file mode 100644
index 0000000..a1001a0
--- /dev/null
+++ b/automation/admin/public/500.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<body>
+ <h1>Application error (Apache)</h1>
+ <p>Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/automation/admin/public/dispatch.cgi b/automation/admin/public/dispatch.cgi
new file mode 100755
index 0000000..32fa3b2
--- /dev/null
+++ b/automation/admin/public/dispatch.cgi
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
+
+# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
+# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
+require "dispatcher"
+
+ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
+Dispatcher.dispatch
diff --git a/automation/admin/public/dispatch.fcgi b/automation/admin/public/dispatch.fcgi
new file mode 100755
index 0000000..664dbbb
--- /dev/null
+++ b/automation/admin/public/dispatch.fcgi
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+#
+# You may specify the path to the FastCGI crash log (a log of unhandled
+# exceptions which forced the FastCGI instance to exit, great for debugging)
+# and the number of requests to process before running garbage collection.
+#
+# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
+# and the GC period is nil (turned off). A reasonable number of requests
+# could range from 10-100 depending on the memory footprint of your app.
+#
+# Example:
+# # Default log path, normal GC behavior.
+# RailsFCGIHandler.process!
+#
+# # Default log path, 50 requests between GC.
+# RailsFCGIHandler.process! nil, 50
+#
+# # Custom log path, normal GC behavior.
+# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
+#
+require File.dirname(__FILE__) + "/../config/environment"
+require 'fcgi_handler'
+
+RailsFCGIHandler.process!
diff --git a/automation/admin/public/dispatch.rb b/automation/admin/public/dispatch.rb
new file mode 100755
index 0000000..32fa3b2
--- /dev/null
+++ b/automation/admin/public/dispatch.rb
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
+
+# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
+# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
+require "dispatcher"
+
+ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
+Dispatcher.dispatch
diff --git a/automation/admin/public/favicon.ico b/automation/admin/public/favicon.ico
new file mode 100644
index 0000000..e69de29
diff --git a/automation/admin/public/images/rails.png b/automation/admin/public/images/rails.png
new file mode 100644
index 0000000..837bb9a
Binary files /dev/null and b/automation/admin/public/images/rails.png differ
diff --git a/automation/admin/public/index.html b/automation/admin/public/index.html
new file mode 100644
index 0000000..8fd0f41
--- /dev/null
+++ b/automation/admin/public/index.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Curator Submissions Tracking</title>
+ <link rel="stylesheet" href="stylesheets/admin.css" type="text/css">
+ </head>
+
+ <body>
+ <h1>Curator Submissions Tracking</h1>
+
+ <p class="text">Please select from the following:</p>
+
+ <ul>
+ <li><a href="experiments/today_list"><b>Recent experiments</b></a></li>
+ </ul>
+ <ul>
+ <li><a href="tab2mages/list?experiment_type=Tab2MAGE">Tab2MAGE experiments</a></li>
+ <li><a href="miamexps/list?experiment_type=MIAMExpress">MIAMExpress experiments</a></li>
+ <li><a href="tab2mages/list?experiment_type=MAGE-TAB">MAGE-TAB experiments</a></li>
+ <li><a href="tab2mages/list?experiment_type=GEO">GEO experiments</a></li>
+ <li><a href="tab2mages/list?experiment_type=MUGEN">MUGEN experiments</a></li>
+ <li><a href="array_designs/list">MIAMExpress arrays</a></li>
+ <li><a href="protocols/list">Tab2MAGE protocols</a></li>
+ </ul>
+ <ul>
+ <li><a href="experiments/list">All Experiments</a></li>
+ <li><a href="array_designs/list_all">All Array Designs</a></li>
+ </ul>
+ <ul>
+ <li><a href="account/signup">Sign up new user</a></li>
+ </ul>
+
+ </body>
+</html>
diff --git a/automation/admin/public/javascripts/controls.js b/automation/admin/public/javascripts/controls.js
new file mode 100644
index 0000000..9742b69
--- /dev/null
+++ b/automation/admin/public/javascripts/controls.js
@@ -0,0 +1,750 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+// Rob Wills
+//
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+ baseInitialize: function(element, update, options) {
+ this.element = $(element);
+ this.update = $(update);
+ this.hasFocus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
+ this.entryCount = 0;
+
+ if (this.setOptions)
+ this.setOptions(options);
+ else
+ this.options = options || {};
+
+ this.options.paramName = this.options.paramName || this.element.name;
+ this.options.tokens = this.options.tokens || [];
+ this.options.frequency = this.options.frequency || 0.4;
+ this.options.minChars = this.options.minChars || 1;
+ this.options.onShow = this.options.onShow ||
+ function(element, update){
+ if(!update.style.position || update.style.position=='absolute') {
+ update.style.position = 'absolute';
+ Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+ }
+ Effect.Appear(update,{duration:0.15});
+ };
+ this.options.onHide = this.options.onHide ||
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+ if (typeof(this.options.tokens) == 'string')
+ this.options.tokens = new Array(this.options.tokens);
+
+ this.observer = null;
+
+ this.element.setAttribute('autocomplete','off');
+
+ Element.hide(this.update);
+
+ Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+ Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+ },
+
+ show: function() {
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+ if(!this.iefix &&
+ (navigator.appVersion.indexOf('MSIE')>0) &&
+ (navigator.userAgent.indexOf('Opera')<0) &&
+ (Element.getStyle(this.update, 'position')=='absolute')) {
+ new Insertion.After(this.update,
+ '<iframe id="' + this.update.id + '_iefix" '+
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+ this.iefix = $(this.update.id+'_iefix');
+ }
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+ },
+
+ fixIEOverlapping: function() {
+ Position.clone(this.update, this.iefix);
+ this.iefix.style.zIndex = 1;
+ this.update.style.zIndex = 2;
+ Element.show(this.iefix);
+ },
+
+ hide: function() {
+ this.stopIndicator();
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+ if(this.iefix) Element.hide(this.iefix);
+ },
+
+ startIndicator: function() {
+ if(this.options.indicator) Element.show(this.options.indicator);
+ },
+
+ stopIndicator: function() {
+ if(this.options.indicator) Element.hide(this.options.indicator);
+ },
+
+ onKeyPress: function(event) {
+ if(this.active)
+ switch(event.keyCode) {
+ case Event.KEY_TAB:
+ case Event.KEY_RETURN:
+ this.selectEntry();
+ Event.stop(event);
+ case Event.KEY_ESC:
+ this.hide();
+ this.active = false;
+ Event.stop(event);
+ return;
+ case Event.KEY_LEFT:
+ case Event.KEY_RIGHT:
+ return;
+ case Event.KEY_UP:
+ this.markPrevious();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ case Event.KEY_DOWN:
+ this.markNext();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ }
+ else
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
+ return;
+
+ this.changed = true;
+ this.hasFocus = true;
+
+ if(this.observer) clearTimeout(this.observer);
+ this.observer =
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+ },
+
+ onHover: function(event) {
+ var element = Event.findElement(event, 'LI');
+ if(this.index != element.autocompleteIndex)
+ {
+ this.index = element.autocompleteIndex;
+ this.render();
+ }
+ Event.stop(event);
+ },
+
+ onClick: function(event) {
+ var element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.selectEntry();
+ this.hide();
+ },
+
+ onBlur: function(event) {
+ // needed to make click events working
+ setTimeout(this.hide.bind(this), 250);
+ this.hasFocus = false;
+ this.active = false;
+ },
+
+ render: function() {
+ if(this.entryCount > 0) {
+ for (var i = 0; i < this.entryCount; i++)
+ this.index==i ?
+ Element.addClassName(this.getEntry(i),"selected") :
+ Element.removeClassName(this.getEntry(i),"selected");
+
+ if(this.hasFocus) {
+ this.show();
+ this.active = true;
+ }
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ markPrevious: function() {
+ if(this.index > 0) this.index--
+ else this.index = this.entryCount-1;
+ },
+
+ markNext: function() {
+ if(this.index < this.entryCount-1) this.index++
+ else this.index = 0;
+ },
+
+ getEntry: function(index) {
+ return this.update.firstChild.childNodes[index];
+ },
+
+ getCurrentEntry: function() {
+ return this.getEntry(this.index);
+ },
+
+ selectEntry: function() {
+ this.active = false;
+ this.updateElement(this.getCurrentEntry());
+ },
+
+ updateElement: function(selectedElement) {
+ if (this.options.updateElement) {
+ this.options.updateElement(selectedElement);
+ return;
+ }
+
+ var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+ var lastTokenPos = this.findLastToken();
+ if (lastTokenPos != -1) {
+ var newValue = this.element.value.substr(0, lastTokenPos + 1);
+ var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+ if (whitespace)
+ newValue += whitespace[0];
+ this.element.value = newValue + value;
+ } else {
+ this.element.value = value;
+ }
+ this.element.focus();
+
+ if (this.options.afterUpdateElement)
+ this.options.afterUpdateElement(this.element, selectedElement);
+ },
+
+ updateChoices: function(choices) {
+ if(!this.changed && this.hasFocus) {
+ this.update.innerHTML = choices;
+ Element.cleanWhitespace(this.update);
+ Element.cleanWhitespace(this.update.firstChild);
+
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
+ this.entryCount =
+ this.update.firstChild.childNodes.length;
+ for (var i = 0; i < this.entryCount; i++) {
+ var entry = this.getEntry(i);
+ entry.autocompleteIndex = i;
+ this.addObservers(entry);
+ }
+ } else {
+ this.entryCount = 0;
+ }
+
+ this.stopIndicator();
+
+ this.index = 0;
+ this.render();
+ }
+ },
+
+ addObservers: function(element) {
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+ },
+
+ onObserverEvent: function() {
+ this.changed = false;
+ if(this.getToken().length>=this.options.minChars) {
+ this.startIndicator();
+ this.getUpdatedChoices();
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ getToken: function() {
+ var tokenPos = this.findLastToken();
+ if (tokenPos != -1)
+ var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+ else
+ var ret = this.element.value;
+
+ return /\n/.test(ret) ? '' : ret;
+ },
+
+ findLastToken: function() {
+ var lastTokenPos = -1;
+
+ for (var i=0; i<this.options.tokens.length; i++) {
+ var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+ if (thisTokenPos > lastTokenPos)
+ lastTokenPos = thisTokenPos;
+ }
+ return lastTokenPos;
+ }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+ initialize: function(element, update, url, options) {
+ this.baseInitialize(element, update, options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this);
+ this.options.defaultParams = this.options.parameters || null;
+ this.url = url;
+ },
+
+ getUpdatedChoices: function() {
+ entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, entry) : entry;
+
+ if(this.options.defaultParams)
+ this.options.parameters += '&' + this.options.defaultParams;
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ onComplete: function(request) {
+ this.updateChoices(request.responseText);
+ }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+// text only at the beginning of strings in the
+// autocomplete array. Defaults to true, which will
+// match text at the beginning of any *word* in the
+// strings in the autocomplete array. If you want to
+// search anywhere in the string, additionally set
+// the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
+// how many characters are required to do any match
+// at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+// Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+ initialize: function(element, update, array, options) {
+ this.baseInitialize(element, update, options);
+ this.options.array = array;
+ },
+
+ getUpdatedChoices: function() {
+ this.updateChoices(this.options.selector(this));
+ },
+
+ setOptions: function(options) {
+ this.options = Object.extend({
+ choices: 10,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
+ selector: function(instance) {
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
+ var count = 0;
+
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
+
+ var elem = instance.options.array[i];
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ elem.indexOf(entry);
+
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+ elem.substr(entry.length) + "</li>");
+ break;
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+ foundPos + entry.length) + "</li>");
+ break;
+ }
+ }
+
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
+
+ }
+ }
+ if (partial.length)
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+ return "<ul>" + ret.join('') + "</ul>";
+ }
+ }, options || {});
+ }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+ setTimeout(function() {
+ Field.activate(field);
+ }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = $(element);
+
+ this.options = Object.extend({
+ okText: "ok",
+ cancelText: "cancel",
+ savingText: "Saving...",
+ clickToEditText: "Click to edit",
+ okText: "ok",
+ rows: 1,
+ onComplete: function(transport, element) {
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+ },
+ onFailure: function(transport) {
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
+ },
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ handleLineBreaks: true,
+ loadingText: 'Loading...',
+ savingClassName: 'inplaceeditor-saving',
+ loadingClassName: 'inplaceeditor-loading',
+ formClassName: 'inplaceeditor-form',
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+ highlightendcolor: "#FFFFFF",
+ externalControl: null,
+ ajaxOptions: {}
+ }, options || {});
+
+ if(!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + "-inplaceeditor";
+ if ($(this.options.formId)) {
+ // there's already a form with that name, don't specify an id
+ this.options.formId = null;
+ }
+ }
+
+ if (this.options.externalControl) {
+ this.options.externalControl = $(this.options.externalControl);
+ }
+
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
+ if (!this.originalBackground) {
+ this.originalBackground = "transparent";
+ }
+
+ this.element.title = this.options.clickToEditText;
+
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+ Event.observe(this.element, 'click', this.onclickListener);
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ },
+ enterEditMode: function(evt) {
+ if (this.saving) return;
+ if (this.editing) return;
+ this.editing = true;
+ this.onEnterEditMode();
+ if (this.options.externalControl) {
+ Element.hide(this.options.externalControl);
+ }
+ Element.hide(this.element);
+ this.createForm();
+ this.element.parentNode.insertBefore(this.form, this.element);
+ Field.scrollFreeActivate(this.editField);
+ // stop the event to avoid a page refresh in Safari
+ if (evt) {
+ Event.stop(evt);
+ }
+ return false;
+ },
+ createForm: function() {
+ this.form = document.createElement("form");
+ this.form.id = this.options.formId;
+ Element.addClassName(this.form, this.options.formClassName)
+ this.form.onsubmit = this.onSubmit.bind(this);
+
+ this.createEditField();
+
+ if (this.options.textarea) {
+ var br = document.createElement("br");
+ this.form.appendChild(br);
+ }
+
+ okButton = document.createElement("input");
+ okButton.type = "submit";
+ okButton.value = this.options.okText;
+ this.form.appendChild(okButton);
+
+ cancelLink = document.createElement("a");
+ cancelLink.href = "#";
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+ cancelLink.onclick = this.onclickCancel.bind(this);
+ this.form.appendChild(cancelLink);
+ },
+ hasHTMLLineBreaks: function(string) {
+ if (!this.options.handleLineBreaks) return false;
+ return string.match(/<br/i) || string.match(/<p>/i);
+ },
+ convertHTMLLineBreaks: function(string) {
+ return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+ },
+ createEditField: function() {
+ var text;
+ if(this.options.loadTextURL) {
+ text = this.options.loadingText;
+ } else {
+ text = this.getText();
+ }
+
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+ this.options.textarea = false;
+ var textField = document.createElement("input");
+ textField.type = "text";
+ textField.name = "value";
+ textField.value = text;
+ textField.style.backgroundColor = this.options.highlightcolor;
+ var size = this.options.size || this.options.cols || 0;
+ if (size != 0) textField.size = size;
+ this.editField = textField;
+ } else {
+ this.options.textarea = true;
+ var textArea = document.createElement("textarea");
+ textArea.name = "value";
+ textArea.value = this.convertHTMLLineBreaks(text);
+ textArea.rows = this.options.rows;
+ textArea.cols = this.options.cols || 40;
+ this.editField = textArea;
+ }
+
+ if(this.options.loadTextURL) {
+ this.loadExternalText();
+ }
+ this.form.appendChild(this.editField);
+ },
+ getText: function() {
+ return this.element.innerHTML;
+ },
+ loadExternalText: function() {
+ Element.addClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = true;
+ new Ajax.Request(
+ this.options.loadTextURL,
+ Object.extend({
+ asynchronous: true,
+ onComplete: this.onLoadedExternalText.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ },
+ onLoadedExternalText: function(transport) {
+ Element.removeClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = false;
+ this.editField.value = transport.responseText.stripTags();
+ },
+ onclickCancel: function() {
+ this.onComplete();
+ this.leaveEditMode();
+ return false;
+ },
+ onFailure: function(transport) {
+ this.options.onFailure(transport);
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ this.oldInnerHTML = null;
+ }
+ return false;
+ },
+ onSubmit: function() {
+ // onLoading resets these so we need to save them away for the Ajax call
+ var form = this.form;
+ var value = this.editField.value;
+
+ // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+ // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+ // to be displayed indefinitely
+ this.onLoading();
+
+ new Ajax.Updater(
+ {
+ success: this.element,
+ // don't update on failure (this could be an option)
+ failure: null
+ },
+ this.url,
+ Object.extend({
+ parameters: this.options.callback(form, value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ // stop the event to avoid a page refresh in Safari
+ if (arguments.length > 1) {
+ Event.stop(arguments[0]);
+ }
+ return false;
+ },
+ onLoading: function() {
+ this.saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ showSaving: function() {
+ this.oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ Element.addClassName(this.element, this.options.savingClassName);
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ },
+ removeForm: function() {
+ if(this.form) {
+ if (this.form.parentNode) Element.remove(this.form);
+ this.form = null;
+ }
+ },
+ enterHover: function() {
+ if (this.saving) return;
+ this.element.style.backgroundColor = this.options.highlightcolor;
+ if (this.effect) {
+ this.effect.cancel();
+ }
+ Element.addClassName(this.element, this.options.hoverClassName)
+ },
+ leaveHover: function() {
+ if (this.options.backgroundColor) {
+ this.element.style.backgroundColor = this.oldBackground;
+ }
+ Element.removeClassName(this.element, this.options.hoverClassName)
+ if (this.saving) return;
+ this.effect = new Effect.Highlight(this.element, {
+ startcolor: this.options.highlightcolor,
+ endcolor: this.options.highlightendcolor,
+ restorecolor: this.originalBackground
+ });
+ },
+ leaveEditMode: function() {
+ Element.removeClassName(this.element, this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ if (this.options.externalControl) {
+ Element.show(this.options.externalControl);
+ }
+ this.editing = false;
+ this.saving = false;
+ this.oldInnerHTML = null;
+ this.onLeaveEditMode();
+ },
+ onComplete: function(transport) {
+ this.leaveEditMode();
+ this.options.onComplete.bind(this)(transport, this.element);
+ },
+ onEnterEditMode: function() {},
+ onLeaveEditMode: function() {},
+ dispose: function() {
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ }
+ this.leaveEditMode();
+ Event.stopObserving(this.element, 'click', this.onclickListener);
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ }
+};
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+ initialize: function(element, delay, callback) {
+ this.delay = delay || 0.5;
+ this.element = $(element);
+ this.callback = callback;
+ this.timer = null;
+ this.lastValue = $F(this.element);
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+ },
+ delayedListener: function(event) {
+ if(this.lastValue == $F(this.element)) return;
+ if(this.timer) clearTimeout(this.timer);
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+ this.lastValue = $F(this.element);
+ },
+ onTimerEvent: function() {
+ this.timer = null;
+ this.callback(this.element, $F(this.element));
+ }
+};
\ No newline at end of file
diff --git a/automation/admin/public/javascripts/dragdrop.js b/automation/admin/public/javascripts/dragdrop.js
new file mode 100644
index 0000000..92d1f73
--- /dev/null
+++ b/automation/admin/public/javascripts/dragdrop.js
@@ -0,0 +1,584 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+ drops: [],
+
+ remove: function(element) {
+ this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+ },
+
+ add: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ greedy: true,
+ hoverclass: null
+ }, arguments[1] || {});
+
+ // cache containers
+ if(options.containment) {
+ options._containers = [];
+ var containment = options.containment;
+ if((typeof containment == 'object') &&
+ (containment.constructor == Array)) {
+ containment.each( function(c) { options._containers.push($(c)) });
+ } else {
+ options._containers.push($(containment));
+ }
+ }
+
+ if(options.accept) options.accept = [options.accept].flatten();
+
+ Element.makePositioned(element); // fix IE
+ options.element = element;
+
+ this.drops.push(options);
+ },
+
+ isContained: function(element, drop) {
+ var parentNode = element.parentNode;
+ return drop._containers.detect(function(c) { return parentNode == c });
+ },
+
+ isAffected: function(point, element, drop) {
+ return (
+ (drop.element!=element) &&
+ ((!drop._containers) ||
+ this.isContained(element, drop)) &&
+ ((!drop.accept) ||
+ (Element.classNames(element).detect(
+ function(v) { return drop.accept.include(v) } ) )) &&
+ Position.within(drop.element, point[0], point[1]) );
+ },
+
+ deactivate: function(drop) {
+ if(drop.hoverclass)
+ Element.removeClassName(drop.element, drop.hoverclass);
+ this.last_active = null;
+ },
+
+ activate: function(drop) {
+ if(drop.hoverclass)
+ Element.addClassName(drop.element, drop.hoverclass);
+ this.last_active = drop;
+ },
+
+ show: function(point, element) {
+ if(!this.drops.length) return;
+
+ if(this.last_active) this.deactivate(this.last_active);
+ this.drops.each( function(drop) {
+ if(Droppables.isAffected(point, element, drop)) {
+ if(drop.onHover)
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+ if(drop.greedy) {
+ Droppables.activate(drop);
+ throw $break;
+ }
+ }
+ });
+ },
+
+ fire: function(event, element) {
+ if(!this.last_active) return;
+ Position.prepare();
+
+ if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+ if (this.last_active.onDrop)
+ this.last_active.onDrop(element, this.last_active.element, event);
+ },
+
+ reset: function() {
+ if(this.last_active)
+ this.deactivate(this.last_active);
+ }
+}
+
+var Draggables = {
+ drags: [],
+ observers: [],
+
+ register: function(draggable) {
+ if(this.drags.length == 0) {
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
+
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+ Event.observe(document, "keypress", this.eventKeypress);
+ }
+ this.drags.push(draggable);
+ },
+
+ unregister: function(draggable) {
+ this.drags = this.drags.reject(function(d) { return d==draggable });
+ if(this.drags.length == 0) {
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ Event.stopObserving(document, "keypress", this.eventKeypress);
+ }
+ },
+
+ activate: function(draggable) {
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+ this.activeDraggable = draggable;
+ },
+
+ deactivate: function(draggbale) {
+ this.activeDraggable = null;
+ },
+
+ updateDrag: function(event) {
+ if(!this.activeDraggable) return;
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ // Mozilla-based browsers fire successive mousemove events with
+ // the same coordinates, prevent needless redrawing (moz bug?)
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+ this._lastPointer = pointer;
+ this.activeDraggable.updateDrag(event, pointer);
+ },
+
+ endDrag: function(event) {
+ if(!this.activeDraggable) return;
+ this._lastPointer = null;
+ this.activeDraggable.endDrag(event);
+ },
+
+ keyPress: function(event) {
+ if(this.activeDraggable)
+ this.activeDraggable.keyPress(event);
+ },
+
+ addObserver: function(observer) {
+ this.observers.push(observer);
+ this._cacheObserverCallbacks();
+ },
+
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
+ this.observers = this.observers.reject( function(o) { return o.element==element });
+ this._cacheObserverCallbacks();
+ },
+
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
+ if(this[eventName+'Count'] > 0)
+ this.observers.each( function(o) {
+ if(o[eventName]) o[eventName](eventName, draggable, event);
+ });
+ },
+
+ _cacheObserverCallbacks: function() {
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
+ Draggables[eventName+'Count'] = Draggables.observers.select(
+ function(o) { return o[eventName]; }
+ ).length;
+ });
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+ initialize: function(element) {
+ var options = Object.extend({
+ handle: false,
+ starteffect: function(element) {
+ new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
+ },
+ reverteffect: function(element, top_offset, left_offset) {
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+ element._revert = new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
+ },
+ endeffect: function(element) {
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
+ },
+ zindex: 1000,
+ revert: false,
+ snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
+ }, arguments[1] || {});
+
+ this.element = $(element);
+
+ if(options.handle && (typeof options.handle == 'string'))
+ this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
+ if(!this.handle) this.handle = $(options.handle);
+ if(!this.handle) this.handle = this.element;
+
+ Element.makePositioned(this.element); // fix IE
+
+ this.delta = this.currentDelta();
+ this.options = options;
+ this.dragging = false;
+
+ this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+ Draggables.register(this);
+ },
+
+ destroy: function() {
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+ Draggables.unregister(this);
+ },
+
+ currentDelta: function() {
+ return([
+ parseInt(this.element.style.left || '0'),
+ parseInt(this.element.style.top || '0')]);
+ },
+
+ initDrag: function(event) {
+ if(Event.isLeftClick(event)) {
+ // abort on form elements, fixes a Firefox issue
+ var src = Event.element(event);
+ if(src.tagName && (
+ src.tagName=='INPUT' ||
+ src.tagName=='SELECT' ||
+ src.tagName=='BUTTON' ||
+ src.tagName=='TEXTAREA')) return;
+
+ if(this.element._revert) {
+ this.element._revert.cancel();
+ this.element._revert = null;
+ }
+
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var pos = Position.cumulativeOffset(this.element);
+ this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+
+ Draggables.activate(this);
+ Event.stop(event);
+ }
+ },
+
+ startDrag: function(event) {
+ this.dragging = true;
+
+ if(this.options.zindex) {
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+ this.element.style.zIndex = this.options.zindex;
+ }
+
+ if(this.options.ghosting) {
+ this._clone = this.element.cloneNode(true);
+ Position.absolutize(this.element);
+ this.element.parentNode.insertBefore(this._clone, this.element);
+ }
+
+ Draggables.notify('onStart', this, event);
+ if(this.options.starteffect) this.options.starteffect(this.element);
+ },
+
+ updateDrag: function(event, pointer) {
+ if(!this.dragging) this.startDrag(event);
+ Position.prepare();
+ Droppables.show(pointer, this.element);
+ Draggables.notify('onDrag', this, event);
+ this.draw(pointer);
+ if(this.options.change) this.options.change(this);
+
+ // fix AppleWebKit rendering
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ Event.stop(event);
+ },
+
+ finishDrag: function(event, success) {
+ this.dragging = false;
+
+ if(this.options.ghosting) {
+ Position.relativize(this.element);
+ Element.remove(this._clone);
+ this._clone = null;
+ }
+
+ if(success) Droppables.fire(event, this.element);
+ Draggables.notify('onEnd', this, event);
+
+ var revert = this.options.revert;
+ if(revert && typeof revert == 'function') revert = revert(this.element);
+
+ var d = this.currentDelta();
+ if(revert && this.options.reverteffect) {
+ this.options.reverteffect(this.element,
+ d[1]-this.delta[1], d[0]-this.delta[0]);
+ } else {
+ this.delta = d;
+ }
+
+ if(this.options.zindex)
+ this.element.style.zIndex = this.originalZ;
+
+ if(this.options.endeffect)
+ this.options.endeffect(this.element);
+
+ Draggables.deactivate(this);
+ Droppables.reset();
+ },
+
+ keyPress: function(event) {
+ if(!event.keyCode==Event.KEY_ESC) return;
+ this.finishDrag(event, false);
+ Event.stop(event);
+ },
+
+ endDrag: function(event) {
+ if(!this.dragging) return;
+ this.finishDrag(event, true);
+ Event.stop(event);
+ },
+
+ draw: function(point) {
+ var pos = Position.cumulativeOffset(this.element);
+ var d = this.currentDelta();
+ pos[0] -= d[0]; pos[1] -= d[1];
+
+ var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this));
+
+ if(this.options.snap) {
+ if(typeof this.options.snap == 'function') {
+ p = this.options.snap(p[0],p[1]);
+ } else {
+ if(this.options.snap instanceof Array) {
+ p = p.map( function(v, i) {
+ return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+ } else {
+ p = p.map( function(v) {
+ return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+ }
+ }}
+
+ var style = this.element.style;
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+ style.left = p[0] + "px";
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
+ style.top = p[1] + "px";
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+ initialize: function(element, observer) {
+ this.element = $(element);
+ this.observer = observer;
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onStart: function() {
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onEnd: function() {
+ Sortable.unmark();
+ if(this.lastValue != Sortable.serialize(this.element))
+ this.observer(this.element)
+ }
+}
+
+var Sortable = {
+ sortables: new Array(),
+
+ options: function(element){
+ element = $(element);
+ return this.sortables.detect(function(s) { return s.element == element });
+ },
+
+ destroy: function(element){
+ element = $(element);
+ this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
+ Draggables.removeObserver(s.element);
+ s.droppables.each(function(d){ Droppables.remove(d) });
+ s.draggables.invoke('destroy');
+ });
+ this.sortables = this.sortables.reject(function(s) { return s.element == element });
+ },
+
+ create: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ element: element,
+ tag: 'li', // assumes li children, override with tag: 'tagname'
+ dropOnEmpty: false,
+ tree: false, // fixme: unimplemented
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
+ containment: element, // also takes array of elements (or id's); or false
+ handle: false, // or a CSS class
+ only: false,
+ hoverclass: null,
+ ghosting: false,
+ format: null,
+ onChange: Prototype.emptyFunction,
+ onUpdate: Prototype.emptyFunction
+ }, arguments[1] || {});
+
+ // clear any old sortable with same element
+ this.destroy(element);
+
+ // build options for the draggables
+ var options_for_draggable = {
+ revert: true,
+ ghosting: options.ghosting,
+ constraint: options.constraint,
+ handle: options.handle };
+
+ if(options.starteffect)
+ options_for_draggable.starteffect = options.starteffect;
+
+ if(options.reverteffect)
+ options_for_draggable.reverteffect = options.reverteffect;
+ else
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+ element.style.top = 0;
+ element.style.left = 0;
+ };
+
+ if(options.endeffect)
+ options_for_draggable.endeffect = options.endeffect;
+
+ if(options.zindex)
+ options_for_draggable.zindex = options.zindex;
+
+ // build options for the droppables
+ var options_for_droppable = {
+ overlap: options.overlap,
+ containment: options.containment,
+ hoverclass: options.hoverclass,
+ onHover: Sortable.onHover,
+ greedy: !options.dropOnEmpty
+ }
+
+ // fix for gecko engine
+ Element.cleanWhitespace(element);
+
+ options.draggables = [];
+ options.droppables = [];
+
+ // make it so
+
+ // drop on empty handling
+ if(options.dropOnEmpty) {
+ Droppables.add(element,
+ {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
+ options.droppables.push(element);
+ }
+
+ (this.findElements(element, options) || []).each( function(e) {
+ // handles are per-draggable
+ var handle = options.handle ?
+ Element.childrenWithClassName(e, options.handle)[0] : e;
+ options.draggables.push(
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+ Droppables.add(e, options_for_droppable);
+ options.droppables.push(e);
+ });
+
+ // keep reference
+ this.sortables.push(options);
+
+ // for onupdate
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+ },
+
+ // return all suitable-for-sortable elements in a guaranteed order
+ findElements: function(element, options) {
+ if(!element.hasChildNodes()) return null;
+ var elements = [];
+ $A(element.childNodes).each( function(e) {
+ if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
+ (!options.only || (Element.hasClassName(e, options.only))))
+ elements.push(e);
+ if(options.tree) {
+ var grandchildren = this.findElements(e, options);
+ if(grandchildren) elements.push(grandchildren);
+ }
+ });
+
+ return (elements.length>0 ? elements.flatten() : null);
+ },
+
+ onHover: function(element, dropon, overlap) {
+ if(overlap>0.5) {
+ Sortable.mark(dropon, 'before');
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ } else {
+ Sortable.mark(dropon, 'after');
+ var nextElement = dropon.nextSibling || null;
+ if(nextElement != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, nextElement);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ }
+ },
+
+ onEmptyHover: function(element, dropon) {
+ if(element.parentNode!=dropon) {
+ var oldParentNode = element.parentNode;
+ dropon.appendChild(element);
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon).onChange(element);
+ }
+ },
+
+ unmark: function() {
+ if(Sortable._marker) Element.hide(Sortable._marker);
+ },
+
+ mark: function(dropon, position) {
+ // mark on ghosting only
+ var sortable = Sortable.options(dropon.parentNode);
+ if(sortable && !sortable.ghosting) return;
+
+ if(!Sortable._marker) {
+ Sortable._marker = $('dropmarker') || document.createElement('DIV');
+ Element.hide(Sortable._marker);
+ Element.addClassName(Sortable._marker, 'dropmarker');
+ Sortable._marker.style.position = 'absolute';
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+ }
+ var offsets = Position.cumulativeOffset(dropon);
+ Sortable._marker.style.left = offsets[0] + 'px';
+ Sortable._marker.style.top = offsets[1] + 'px';
+
+ if(position=='after')
+ if(sortable.overlap == 'horizontal')
+ Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+ else
+ Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+
+ Element.show(Sortable._marker);
+ },
+
+ serialize: function(element) {
+ element = $(element);
+ var sortableOptions = this.options(element);
+ var options = Object.extend({
+ tag: sortableOptions.tag,
+ only: sortableOptions.only,
+ name: element.id,
+ format: sortableOptions.format || /^[^_]*_(.*)$/
+ }, arguments[1] || {});
+ return $(this.findElements(element, options) || []).map( function(item) {
+ return (encodeURIComponent(options.name) + "[]=" +
+ encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
+ }).join("&");
+ }
+}
\ No newline at end of file
diff --git a/automation/admin/public/javascripts/effects.js b/automation/admin/public/javascripts/effects.js
new file mode 100644
index 0000000..414398c
--- /dev/null
+++ b/automation/admin/public/javascripts/effects.js
@@ -0,0 +1,854 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
+//
+// See scriptaculous.js for full license.
+
+/* ------------- element ext -------------- */
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+ var color = '#';
+ if(this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if(this.slice(0,1) == '#') {
+ if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if(this.length==7) color = this.toLowerCase();
+ }
+ }
+ return(color.length==7 ? color : (arguments[0] || this));
+}
+
+Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
+ var children = $(element).childNodes;
+ var text = '';
+ var classtest = new RegExp('^([^ ]+ )*' + ignoreclass+ '( [^ ]+)*$','i');
+
+ for (var i = 0; i < children.length; i++) {
+ if(children[i].nodeType==3) {
+ text+=children[i].nodeValue;
+ } else {
+ if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
+ text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
+ }
+ }
+
+ return text;
+}
+
+Element.setStyle = function(element, style) {
+ element = $(element);
+ for(k in style) element.style[k.camelize()] = style[k];
+}
+
+Element.setContentZoom = function(element, percent) {
+ Element.setStyle(element, {fontSize: (percent/100) + 'em'});
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
+
+Element.getOpacity = function(element){
+ var opacity;
+ if (opacity = Element.getStyle(element, 'opacity'))
+ return parseFloat(opacity);
+ if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if(opacity[1]) return parseFloat(opacity[1]) / 100;
+ return 1.0;
+}
+
+Element.setOpacity = function(element, value){
+ element= $(element);
+ if (value == 1){
+ Element.setStyle(element, { opacity:
+ (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
+ 0.999999 : null });
+ if(/MSIE/.test(navigator.userAgent))
+ Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
+ } else {
+ if(value < 0.00001) value = 0;
+ Element.setStyle(element, {opacity: value});
+ if(/MSIE/.test(navigator.userAgent))
+ Element.setStyle(element,
+ { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+ 'alpha(opacity='+value*100+')' });
+ }
+}
+
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
+}
+
+Element.childrenWithClassName = function(element, className) {
+ return $A($(element).getElementsByTagName('*')).select(
+ function(c) { return Element.hasClassName(c, className) });
+}
+
+Array.prototype.call = function() {
+ var args = arguments;
+ this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+ tagifyText: function(element) {
+ var tagifyStyle = 'position:relative';
+ if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if(child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ Builder.node('span',{style: tagifyStyle},
+ character == ' ' ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if(((typeof element == 'object') ||
+ (typeof element == 'function')) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || {});
+ var masterDelay = options.delay;
+
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+ });
+ }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+ return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse = function(pos) {
+ return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+ return (Math.floor(pos*10) % 2 == 0 ?
+ (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+ return 0;
+}
+Effect.Transitions.full = function(pos) {
+ return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.Queue = {
+ effects: [],
+ _each: function(iterator) {
+ this.effects._each(iterator);
+ },
+ interval: null,
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ switch(effect.options.queue) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+ this.effects.push(effect);
+ if(!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 40);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if(this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ this.effects.invoke('loop', timePos);
+ }
+}
+Object.extend(Effect.Queue, Enumerable);
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+ position: null,
+ setOptions: function(options) {
+ this.options = Object.extend({
+ transition: Effect.Transitions.sinoidal,
+ duration: 1.0, // seconds
+ fps: 25.0, // max. 25fps due to Effect.Queue implementation
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+ }, options || {});
+ },
+ start: function(options) {
+ this.setOptions(options || {});
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn + (this.options.duration*1000);
+ this.event('beforeStart');
+ if(!this.options.sync) Effect.Queue.add(this);
+ },
+ loop: function(timePos) {
+ if(timePos >= this.startOn) {
+ if(timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if(this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
+ var frame = Math.round(pos * this.options.fps * this.options.duration);
+ if(frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ render: function(pos) {
+ if(this.state == 'idle') {
+ this.state = 'running';
+ this.event('beforeSetup');
+ if(this.setup) this.setup();
+ this.event('afterSetup');
+ }
+ if(this.state == 'running') {
+ if(this.options.transition) pos = this.options.transition(pos);
+ pos *= (this.options.to-this.options.from);
+ pos += this.options.from;
+ this.position = pos;
+ this.event('beforeUpdate');
+ if(this.update) this.update(pos);
+ this.event('afterUpdate');
+ }
+ },
+ cancel: function() {
+ if(!this.options.sync) Effect.Queue.remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if(this.options[eventName]) this.options[eventName](this);
+ },
+ inspect: function() {
+ return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+ }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ this.effects.invoke('render', position);
+ },
+ finish: function(position) {
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if(effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
+ }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ // make this work on IE on elements without 'layout'
+ if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+ Element.setStyle(this.element, {zoom: 1});
+ var options = Object.extend({
+ from: Element.getOpacity(this.element) || 0.0,
+ to: 1.0
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ update: function(position) {
+ Element.setOpacity(this.element, position);
+ }
+});
+
+Effect.MoveBy = Class.create();
+Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
+ initialize: function(element, toTop, toLeft) {
+ this.element = $(element);
+ this.toTop = toTop;
+ this.toLeft = toLeft;
+ this.start(arguments[3]);
+ },
+ setup: function() {
+ // Bug in Opera: Opera returns the "real" position of a static element or
+ // relative element that does not have top/left explicitly set.
+ // ==> Always set top and left for position relative elements in your stylesheets
+ // (to 0 if you do not need them)
+ Element.makePositioned(this.element);
+ this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
+ this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
+ },
+ update: function(position) {
+ Element.setStyle(this.element, {
+ top: this.toTop * position + this.originalTop + 'px',
+ left: this.toLeft * position + this.originalLeft + 'px'
+ });
+ }
+});
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+ initialize: function(element, percent) {
+ this.element = $(element)
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or {} with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || {});
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = Element.getStyle(this.element,'position');
+
+ this.originalStyle = {};
+ ['top','left','width','height','fontSize'].each( function(k) {
+ this.originalStyle[k] = this.element.style[k];
+ }.bind(this));
+
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+
+ var fontSize = Element.getStyle(this.element,'font-size') || '100%';
+ ['em','px','%'].each( function(fontSizeType) {
+ if(fontSize.indexOf(fontSizeType)>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = fontSizeType;
+ }
+ }.bind(this));
+
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+ this.dims = null;
+ if(this.options.scaleMode=='box')
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+ if(/^content/.test(this.options.scaleMode))
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+ if(!this.dims)
+ this.dims = [this.options.scaleMode.originalHeight,
+ this.options.scaleMode.originalWidth];
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if(this.options.scaleContent && this.fontSize)
+ Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+ },
+ finish: function(position) {
+ if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
+ },
+ setDimensions: function(height, width) {
+ var d = {};
+ if(this.options.scaleX) d.width = width + 'px';
+ if(this.options.scaleY) d.height = height + 'px';
+ if(this.options.scaleFromCenter) {
+ var topd = (height - this.dims[0])/2;
+ var leftd = (width - this.dims[1])/2;
+ if(this.elementPositioning == 'absolute') {
+ if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+ if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+ } else {
+ if(this.options.scaleY) d.top = -topd + 'px';
+ if(this.options.scaleX) d.left = -leftd + 'px';
+ }
+ }
+ Element.setStyle(this.element, d);
+ }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // Prevent executing on elements not in the layout flow
+ if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
+ // Disable background image during the effect
+ this.oldStyle = {
+ backgroundImage: Element.getStyle(this.element, 'background-image') };
+ Element.setStyle(this.element, {backgroundImage: 'none'});
+ if(!this.options.endcolor)
+ this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
+ if(!this.options.restorecolor)
+ this.options.restorecolor = Element.getStyle(this.element, 'background-color');
+ // init color calculations
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+ },
+ update: function(position) {
+ Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
+ return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+ },
+ finish: function() {
+ Element.setStyle(this.element, Object.extend(this.oldStyle, {
+ backgroundColor: this.options.restorecolor
+ }));
+ }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ this.start(arguments[1] || {});
+ },
+ setup: function() {
+ Position.prepare();
+ var offsets = Position.cumulativeOffset(this.element);
+ if(this.options.offset) offsets[1] += this.options.offset;
+ var max = window.innerHeight ?
+ window.height - window.innerHeight :
+ document.body.scrollHeight -
+ (document.documentElement.clientHeight ?
+ document.documentElement.clientHeight : document.body.clientHeight);
+ this.scrollStart = Position.deltaY;
+ this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+ },
+ update: function(position) {
+ Position.prepare();
+ window.scrollTo(Position.deltaX,
+ this.scrollStart + (position*this.delta));
+ }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+ var oldOpacity = Element.getInlineOpacity(element);
+ var options = Object.extend({
+ from: Element.getOpacity(element) || 1.0,
+ to: 0.0,
+ afterFinishInternal: function(effect) { with(Element) {
+ if(effect.options.to!=0) return;
+ hide(effect.element);
+ setStyle(effect.element, {opacity: oldOpacity}); }}
+ }, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+ var options = Object.extend({
+ from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
+ to: 1.0,
+ beforeSetup: function(effect) { with(Element) {
+ setOpacity(effect.element, effect.options.from);
+ show(effect.element); }}
+ }, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+ element = $(element);
+ var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
+ return new Effect.Parallel(
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeSetupInternal: function(effect) { with(Element) {
+ setStyle(effect.effects[0].element, {position: 'absolute'}); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ hide(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindUp = function(element) {
+ element = $(element);
+ Element.makeClipping(element);
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ restoreAfterFinish: true,
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping].call(effect.element); }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindDown = function(element) {
+ element = $(element);
+ var oldHeight = Element.getStyle(element, 'height');
+ var elementDimensions = Element.getDimensions(element);
+ return new Effect.Scale(element, 100,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) { with(Element) {
+ makeClipping(effect.element);
+ setStyle(effect.element, {height: '0px'});
+ show(effect.element);
+ }},
+ afterFinishInternal: function(effect) { with(Element) {
+ undoClipping(effect.element);
+ setStyle(effect.element, {height: oldHeight});
+ }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.SwitchOff = function(element) {
+ element = $(element);
+ var oldOpacity = Element.getInlineOpacity(element);
+ return new Effect.Appear(element, {
+ duration: 0.4,
+ from: 0,
+ transition: Effect.Transitions.flicker,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(effect.element, 1, {
+ duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
+ beforeSetup: function(effect) { with(Element) {
+ [makePositioned,makeClipping].call(effect.element);
+ }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide,undoClipping,undoPositioned].call(effect.element);
+ setStyle(effect.element, {opacity: oldOpacity});
+ }}
+ })
+ }
+ });
+}
+
+Effect.DropOut = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: Element.getStyle(element, 'top'),
+ left: Element.getStyle(element, 'left'),
+ opacity: Element.getInlineOpacity(element) };
+ return new Effect.Parallel(
+ [ new Effect.MoveBy(element, 100, 0, { sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+ Object.extend(
+ { duration: 0.5,
+ beforeSetup: function(effect) { with(Element) {
+ makePositioned(effect.effects[0].element); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoPositioned].call(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: Element.getStyle(element, 'top'),
+ left: Element.getStyle(element, 'left') };
+ return new Effect.MoveBy(element, 0, 20,
+ { duration: 0.05, afterFinishInternal: function(effect) {
+ new Effect.MoveBy(effect.element, 0, -40,
+ { duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.MoveBy(effect.element, 0, 40,
+ { duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.MoveBy(effect.element, 0, -40,
+ { duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.MoveBy(effect.element, 0, 40,
+ { duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.MoveBy(effect.element, 0, -20,
+ { duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
+ undoPositioned(effect.element);
+ setStyle(effect.element, oldStyle);
+ }}}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+ element = $(element);
+ Element.cleanWhitespace(element);
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+ var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
+ var elementDimensions = Element.getDimensions(element);
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) { with(Element) {
+ makePositioned(effect.element);
+ makePositioned(effect.element.firstChild);
+ if(window.opera) setStyle(effect.element, {top: ''});
+ makeClipping(effect.element);
+ setStyle(effect.element, {height: '0px'});
+ show(element); }},
+ afterUpdateInternal: function(effect) { with(Element) {
+ setStyle(effect.element.firstChild, {bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ undoClipping(effect.element);
+ undoPositioned(effect.element.firstChild);
+ undoPositioned(effect.element);
+ setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.SlideUp = function(element) {
+ element = $(element);
+ Element.cleanWhitespace(element);
+ var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'box',
+ scaleFrom: 100,
+ restoreAfterFinish: true,
+ beforeStartInternal: function(effect) { with(Element) {
+ makePositioned(effect.element);
+ makePositioned(effect.element.firstChild);
+ if(window.opera) setStyle(effect.element, {top: ''});
+ makeClipping(effect.element);
+ show(element); }},
+ afterUpdateInternal: function(effect) { with(Element) {
+ setStyle(effect.element.firstChild, {bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping].call(effect.element);
+ undoPositioned(effect.element.firstChild);
+ undoPositioned(effect.element);
+ setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
+ }, arguments[1] || {})
+ );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+ return new Effect.Scale(element, window.opera ? 1 : 0,
+ { restoreAfterFinish: true,
+ beforeSetup: function(effect) { with(Element) {
+ makeClipping(effect.element); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ hide(effect.element);
+ undoClipping(effect.element); }}
+ });
+}
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransistion: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.full
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: Element.getInlineOpacity(element) };
+
+ var dims = Element.getDimensions(element);
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = dims.width;
+ initialMoveY = moveY = 0;
+ moveX = -dims.width;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = dims.height;
+ moveY = -dims.height;
+ break;
+ case 'bottom-right':
+ initialMoveX = dims.width;
+ initialMoveY = dims.height;
+ moveX = -dims.width;
+ moveY = -dims.height;
+ break;
+ case 'center':
+ initialMoveX = dims.width / 2;
+ initialMoveY = dims.height / 2;
+ moveX = -dims.width / 2;
+ moveY = -dims.height / 2;
+ break;
+ }
+
+ return new Effect.MoveBy(element, initialMoveY, initialMoveX, {
+ duration: 0.01,
+ beforeSetup: function(effect) { with(Element) {
+ hide(effect.element);
+ makeClipping(effect.element);
+ makePositioned(effect.element);
+ }},
+ afterFinishInternal: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+ new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: options.moveTransition }),
+ new Effect.Scale(effect.element, 100, {
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+ ], Object.extend({
+ beforeSetup: function(effect) { with(Element) {
+ setStyle(effect.effects[0].element, {height: '0px'});
+ show(effect.effects[0].element); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [undoClipping, undoPositioned].call(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, options)
+ )
+ }
+ });
+}
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransistion: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.none
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: Element.getInlineOpacity(element) };
+
+ var dims = Element.getDimensions(element);
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = dims.width;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = dims.height;
+ break;
+ case 'bottom-right':
+ moveX = dims.width;
+ moveY = dims.height;
+ break;
+ case 'center':
+ moveX = dims.width / 2;
+ moveY = dims.height / 2;
+ break;
+ }
+
+ return new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+ new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: options.moveTransition })
+ ], Object.extend({
+ beforeStartInternal: function(effect) { with(Element) {
+ [makePositioned, makeClipping].call(effect.effects[0].element) }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping, undoPositioned].call(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, options)
+ );
+}
+
+Effect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+ var oldOpacity = Element.getInlineOpacity(element);
+ var transition = options.transition || Effect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+ reverser.bind(transition);
+ return new Effect.Opacity(element,
+ Object.extend(Object.extend({ duration: 3.0, from: 0,
+ afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
+ }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height };
+ Element.makeClipping(element);
+ return new Effect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleY: false,
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping].call(effect.element);
+ setStyle(effect.element, oldStyle);
+ }} });
+ }}, arguments[1] || {}));
+}
diff --git a/automation/admin/public/javascripts/prototype.js b/automation/admin/public/javascripts/prototype.js
new file mode 100644
index 0000000..e9ccd3c
--- /dev/null
+++ b/automation/admin/public/javascripts/prototype.js
@@ -0,0 +1,1785 @@
+/* Prototype JavaScript framework, version 1.4.0
+ * (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
+ * against the source tree, available from the Prototype darcs repository.
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ *
+ * For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.4.0',
+ ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+ emptyFunction: function() {},
+ K: function(x) {return x}
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.inspect = function(object) {
+ try {
+ if (object == undefined) return 'undefined';
+ if (object == null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+}
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ var digits = this.toString(16);
+ if (this < 16) return '0' + digits;
+ return digits;
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ }
+});
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+Object.extend(String.prototype, {
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(eval);
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+ },
+
+ toQueryParams: function() {
+ var pairs = this.match(/^\??(.*)$/)[1].split('&');
+ return pairs.inject({}, function(params, pairString) {
+ var pair = pairString.split('=');
+ params[pair[0]] = pair[1];
+ return params;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ camelize: function() {
+ var oStringList = this.split('-');
+ if (oStringList.length == 1) return oStringList[0];
+
+ var camelizedString = this.indexOf('-') == 0
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+ : oStringList[0];
+
+ for (var i = 1, len = oStringList.length; i < len; i++) {
+ var s = oStringList[i];
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+ }
+
+ return camelizedString;
+ },
+
+ inspect: function() {
+ return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
+ }
+});
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != $continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function (iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value >= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value <= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ iterator(value = collections.pluck(index));
+ return value;
+ });
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0; i < this.length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != undefined || value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0; i < this.length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ shift: function() {
+ var result = this[0];
+ for (var i = 0; i < this.length - 1; i++)
+ this[i] = this[i + 1];
+ this.length--;
+ return result;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+});
+var Hash = {
+ _each: function(iterator) {
+ for (key in this) {
+ var value = this[key];
+ if (typeof value == 'function') continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject($H(this), function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ return pair.map(encodeURIComponent).join('=');
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ }
+}
+
+function $H(object) {
+ var hash = Object.extend({}, object || {});
+ Object.extend(hash, Enumerable);
+ Object.extend(hash, Hash);
+ return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ do {
+ iterator(value);
+ value = value.succ();
+ } while (this.include(value));
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+ function() {return new XMLHttpRequest()}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responderToAdd) {
+ if (!this.include(responderToAdd))
+ this.responders.push(responderToAdd);
+ },
+
+ unregister: function(responderToRemove) {
+ this.responders = this.responders.without(responderToRemove);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (responder[callback] && typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+ },
+
+ responseIsSuccess: function() {
+ return this.transport.status == undefined
+ || this.transport.status == 0
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ responseIsFailure: function() {
+ return !this.responseIsSuccess();
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ var parameters = this.options.parameters || '';
+ if (parameters.length > 0) parameters += '&_=';
+
+ try {
+ this.url = url;
+ if (this.options.method == 'get' && parameters.length > 0)
+ this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.options.method, this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+ }
+
+ this.setRequestHeaders();
+
+ var body = this.options.postBody ? this.options.postBody : parameters;
+ this.transport.send(this.options.method == 'post' ? body : null);
+
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ setRequestHeaders: function() {
+ var requestHeaders =
+ ['X-Requested-With', 'XMLHttpRequest',
+ 'X-Prototype-Version', Prototype.Version];
+
+ if (this.options.method == 'post') {
+ requestHeaders.push('Content-type',
+ 'application/x-www-form-urlencoded');
+
+ /* Force "Connection: close" for Mozilla browsers to work around
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
+ * header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType)
+ requestHeaders.push('Connection', 'close');
+ }
+
+ if (this.options.requestHeaders)
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+ for (var i = 0; i < requestHeaders.length; i += 2)
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState != 1)
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ header: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {}
+ },
+
+ evalJSON: function() {
+ try {
+ return eval(this.header('X-JSON'));
+ } catch (e) {}
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (event == 'Complete') {
+ try {
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.containers = {
+ success: container.success ? $(container.success) : $(container),
+ failure: container.failure ? $(container.failure) :
+ (container.success ? null : $(container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, object) {
+ this.updateContent();
+ onComplete(transport, object);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.responseIsSuccess() ?
+ this.containers.success : this.containers.failure;
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts)
+ response = response.stripScripts();
+
+ if (receiver) {
+ if (this.options.insertion) {
+ new this.options.insertion(receiver, response);
+ } else {
+ Element.update(receiver, response);
+ }
+ }
+
+ if (this.responseIsSuccess()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ return $A(children).inject([], function(elements, child) {
+ if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ elements.push(child);
+ return elements;
+ });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) {
+ var Element = new Object();
+}
+
+Object.extend(Element, {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ }
+ },
+
+ hide: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = 'none';
+ }
+ },
+
+ show: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = '';
+ }
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ },
+
+ update: function(element, html) {
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).include(className);
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).add(className);
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).remove(className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var node = element.childNodes[i];
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ Element.remove(node);
+ }
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.match(/^\s*$/);
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var x = element.x ? element.x : element.offsetLeft,
+ y = element.y ? element.y : element.offsetTop;
+ window.scrollTo(x, y);
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ var value = element.style[style.camelize()];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style.camelize()];
+ }
+ }
+
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+ return value == 'auto' ? null : value;
+ },
+
+ setStyle: function(element, style) {
+ element = $(element);
+ for (name in style)
+ element.style[name.camelize()] = style[name];
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'display') != 'none')
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = '';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = 'none';
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element._overflow = element.style.overflow;
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element.style.overflow = element._overflow;
+ element._overflow = undefined;
+ }
+});
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ if (this.element.tagName.toLowerCase() == 'tbody') {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set(this.toArray().concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set(this.select(function(className) {
+ return className != classNameToRemove;
+ }).join(' '));
+ },
+
+ toString: function() {
+ return this.toArray().join(' ');
+ }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Field = {
+ clear: function() {
+ for (var i = 0; i < arguments.length; i++)
+ $(arguments[i]).value = '';
+ },
+
+ focus: function(element) {
+ $(element).focus();
+ },
+
+ present: function() {
+ for (var i = 0; i < arguments.length; i++)
+ if ($(arguments[i]).value == '') return false;
+ return true;
+ },
+
+ select: function(element) {
+ $(element).select();
+ },
+
+ activate: function(element) {
+ element = $(element);
+ element.focus();
+ if (element.select)
+ element.select();
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+ serialize: function(form) {
+ var elements = Form.getElements($(form));
+ var queryComponents = new Array();
+
+ for (var i = 0; i < elements.length; i++) {
+ var queryComponent = Form.Element.serialize(elements[i]);
+ if (queryComponent)
+ queryComponents.push(queryComponent);
+ }
+
+ return queryComponents.join('&');
+ },
+
+ getElements: function(form) {
+ form = $(form);
+ var elements = new Array();
+
+ for (tagName in Form.Element.Serializers) {
+ var tagElements = form.getElementsByTagName(tagName);
+ for (var j = 0; j < tagElements.length; j++)
+ elements.push(tagElements[j]);
+ }
+ return elements;
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(input);
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.blur();
+ element.disabled = 'true';
+ }
+ },
+
+ enable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.disabled = '';
+ }
+ },
+
+ findFirstElement: function(form) {
+ return Form.getElements(form).find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ Field.activate(Form.findFirstElement(form));
+ },
+
+ reset: function(form) {
+ $(form).reset();
+ }
+}
+
+Form.Element = {
+ serialize: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter) {
+ var key = encodeURIComponent(parameter[0]);
+ if (key.length == 0) return;
+
+ if (parameter[1].constructor != Array)
+ parameter[1] = [parameter[1]];
+
+ return parameter[1].map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ }
+}
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'submit':
+ case 'hidden':
+ case 'password':
+ case 'text':
+ return Form.Element.Serializers.textarea(element);
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ }
+ return false;
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ return Form.Element.Serializers[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var value = '', opt, index = element.selectedIndex;
+ if (index >= 0) {
+ opt = element.options[index];
+ value = opt.value;
+ if (!value && !('value' in opt))
+ value = opt.text;
+ }
+ return [element.name, value];
+ },
+
+ selectMany: function(element) {
+ var value = new Array();
+ for (var i = 0; i < element.length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) {
+ var optValue = opt.value;
+ if (!optValue && !('value' in opt))
+ optValue = opt.text;
+ value.push(optValue);
+ }
+ }
+ return [element.name, value];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ var elements = Form.getElements(this.element);
+ for (var i = 0; i < elements.length; i++)
+ this.registerCallback(elements[i]);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0; i < Event.observers.length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ element.detachEvent('on' + name, observer);
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ clone: function(source, target) {
+ source = $(source);
+ target = $(target);
+ target.style.position = 'absolute';
+ var offsets = this.cumulativeOffset(source);
+ target.style.top = offsets[1] + 'px';
+ target.style.left = offsets[0] + 'px';
+ target.style.width = source.offsetWidth + 'px';
+ target.style.height = source.offsetHeight + 'px';
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+}
\ No newline at end of file
diff --git a/automation/admin/public/robots.txt b/automation/admin/public/robots.txt
new file mode 100644
index 0000000..4ab9e89
--- /dev/null
+++ b/automation/admin/public/robots.txt
@@ -0,0 +1 @@
+# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
\ No newline at end of file
diff --git a/automation/admin/public/stylesheets/admin.css b/automation/admin/public/stylesheets/admin.css
new file mode 100644
index 0000000..c1244bd
--- /dev/null
+++ b/automation/admin/public/stylesheets/admin.css
@@ -0,0 +1,106 @@
+body {
+ font-family: verdana, ariel, helvetica, sans-serif;
+ line-height: 120%;
+ background-color: #b7dcff;
+}
+
+.title {
+ font-size: 120%;
+}
+
+td {
+ background-color: #c9edff;
+ padding: 4px;
+ font-size: 80%;
+}
+
+th {
+ background-color: #eeffff;
+ padding: 4px;
+ font-size: 80%;
+}
+
+.actionlist {
+ background-color: #ffffee;
+}
+
+.footer {
+ background-color: #ffffee;
+ width: 30em;
+ text-align: center;
+ padding: 4px;
+}
+
+.fieldWithErrors {
+ padding: 0px;
+ background-color: red;
+ display: table;
+}
+
+#errorExplanation {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+#errorExplanation h2 {
+ font-size: 110%;
+}
+
+/* Set up the sidebar */
+div.link-list {
+ width:11em;
+ position:absolute;
+ top:0;
+ bottom:0;
+ font-size:100%;
+ padding-left:0.5em;
+ padding-right:0.5em;
+ padding-top:0.5em;
+ margin-left:0;
+ margin-right:0;
+ background-color: #c9edff;
+}
+
+div.menu-item {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ padding-left: 4px;
+ padding-right: 4px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #ffffff;
+}
+
+div.submenu {
+ background-color: #ddefff;
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+div.submenutitle {
+ text-align: center;
+}
+
+#main {
+ margin-left:11em;
+ padding-left:1em;
+}
+
+#sidebar {
+ left:0;
+ text-align: left;
+ line-height: 150%;
+}
+
+.notice {
+ font-size: 90%;
+ color: blue;
+}
+
+:link {
+ color: #3333aa;
+}
+
+:visited {
+ color: #7700aa;
+}
\ No newline at end of file
diff --git a/automation/admin/script/about b/automation/admin/script/about
new file mode 100755
index 0000000..7b07d46
--- /dev/null
+++ b/automation/admin/script/about
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/about'
\ No newline at end of file
diff --git a/automation/admin/script/benchmarker b/automation/admin/script/benchmarker
new file mode 100755
index 0000000..f5c6d7c
--- /dev/null
+++ b/automation/admin/script/benchmarker
@@ -0,0 +1,19 @@
+#!/usr/local/bin/ruby
+
+if ARGV.empty?
+ puts "Usage: benchmarker times 'Person.expensive_way' 'Person.another_expensive_way' ..."
+ exit
+end
+
+require File.dirname(__FILE__) + '/../config/environment'
+require 'benchmark'
+include Benchmark
+
+# Don't include compilation in the benchmark
+ARGV[1..-1].each { |expression| eval(expression) }
+
+bm(6) do |x|
+ ARGV[1..-1].each_with_index do |expression, idx|
+ x.report("##{idx + 1}") { ARGV[0].to_i.times { eval(expression) } }
+ end
+end
diff --git a/automation/admin/script/breakpointer b/automation/admin/script/breakpointer
new file mode 100755
index 0000000..64af76e
--- /dev/null
+++ b/automation/admin/script/breakpointer
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/breakpointer'
\ No newline at end of file
diff --git a/automation/admin/script/console b/automation/admin/script/console
new file mode 100755
index 0000000..42f28f7
--- /dev/null
+++ b/automation/admin/script/console
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/console'
\ No newline at end of file
diff --git a/automation/admin/script/destroy b/automation/admin/script/destroy
new file mode 100755
index 0000000..fa0e6fc
--- /dev/null
+++ b/automation/admin/script/destroy
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/destroy'
\ No newline at end of file
diff --git a/automation/admin/script/generate b/automation/admin/script/generate
new file mode 100755
index 0000000..ef976e0
--- /dev/null
+++ b/automation/admin/script/generate
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/generate'
\ No newline at end of file
diff --git a/automation/admin/script/performance/benchmarker b/automation/admin/script/performance/benchmarker
new file mode 100755
index 0000000..c842d35
--- /dev/null
+++ b/automation/admin/script/performance/benchmarker
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/benchmarker'
diff --git a/automation/admin/script/performance/profiler b/automation/admin/script/performance/profiler
new file mode 100755
index 0000000..d855ac8
--- /dev/null
+++ b/automation/admin/script/performance/profiler
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/performance/profiler'
diff --git a/automation/admin/script/plugin b/automation/admin/script/plugin
new file mode 100755
index 0000000..26ca64c
--- /dev/null
+++ b/automation/admin/script/plugin
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/plugin'
\ No newline at end of file
diff --git a/automation/admin/script/process/reaper b/automation/admin/script/process/reaper
new file mode 100755
index 0000000..c77f045
--- /dev/null
+++ b/automation/admin/script/process/reaper
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/reaper'
diff --git a/automation/admin/script/process/spawner b/automation/admin/script/process/spawner
new file mode 100755
index 0000000..7118f39
--- /dev/null
+++ b/automation/admin/script/process/spawner
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/spawner'
diff --git a/automation/admin/script/process/spinner b/automation/admin/script/process/spinner
new file mode 100755
index 0000000..6816b32
--- /dev/null
+++ b/automation/admin/script/process/spinner
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../../config/boot'
+require 'commands/process/spinner'
diff --git a/automation/admin/script/profiler b/automation/admin/script/profiler
new file mode 100755
index 0000000..d8927a1
--- /dev/null
+++ b/automation/admin/script/profiler
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ruby
+if ARGV.empty?
+ $stderr.puts "Usage: profiler 'Person.expensive_method(10)' [times]"
+ exit(1)
+end
+
+# Keep the expensive require out of the profile.
+$stderr.puts 'Loading Rails...'
+require File.dirname(__FILE__) + '/../config/environment'
+
+# Define a method to profile.
+if ARGV[1] and ARGV[1].to_i > 1
+ eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end"
+else
+ eval "def profile_me() #{ARGV[0]} end"
+end
+
+# Use the ruby-prof extension if available. Fall back to stdlib profiler.
+begin
+ require 'prof'
+ $stderr.puts 'Using the ruby-prof extension.'
+ Prof.clock_mode = Prof::GETTIMEOFDAY
+ Prof.start
+ profile_me
+ results = Prof.stop
+ require 'rubyprof_ext'
+ Prof.print_profile(results, $stderr)
+rescue LoadError
+ $stderr.puts 'Using the standard Ruby profiler.'
+ Profiler__.start_profile
+ profile_me
+ Profiler__.stop_profile
+ Profiler__.print_profile($stderr)
+end
diff --git a/automation/admin/script/runner b/automation/admin/script/runner
new file mode 100755
index 0000000..ccc30f9
--- /dev/null
+++ b/automation/admin/script/runner
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/runner'
\ No newline at end of file
diff --git a/automation/admin/script/server b/automation/admin/script/server
new file mode 100755
index 0000000..dfabcb8
--- /dev/null
+++ b/automation/admin/script/server
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/../config/boot'
+require 'commands/server'
\ No newline at end of file
diff --git a/automation/admin/test/fixtures/array_designs.yml b/automation/admin/test/fixtures/array_designs.yml
new file mode 100644
index 0000000..39c0490
--- /dev/null
+++ b/automation/admin/test/fixtures/array_designs.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ miamexpress_subid: 1001
+ accession: A-MEXP-1
+another:
+ id: 2
+ miamexpress_subid: 1002
+ accession: A-MEXP-2
diff --git a/automation/admin/test/fixtures/categories.yml b/automation/admin/test/fixtures/categories.yml
new file mode 100644
index 0000000..c605e24
--- /dev/null
+++ b/automation/admin/test/fixtures/categories.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first_category:
+ id: 1
+ ontology_term: Age
+ display_label: Age
+another_category:
+ id: 2
+ ontology_term: CellLine
+ display_label: Cell line
diff --git a/automation/admin/test/fixtures/data_files.yml b/automation/admin/test/fixtures/data_files.yml
new file mode 100644
index 0000000..8794d28
--- /dev/null
+++ b/automation/admin/test/fixtures/data_files.yml
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+another:
+ id: 2
diff --git a/automation/admin/test/fixtures/designs.yml b/automation/admin/test/fixtures/designs.yml
new file mode 100644
index 0000000..24d9203
--- /dev/null
+++ b/automation/admin/test/fixtures/designs.yml
@@ -0,0 +1,13 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ display_label: Cell type comparison
+ ontology_category: ExperimentalDesignType
+ ontology_value: cell_type_comparison_design
+ design_type: biological
+another:
+ id: 2
+ display_label: Dye swap
+ ontology_category: MethodologicalDesignType
+ ontology_value: dye_swap_design
+ design_type: methodological
diff --git a/automation/admin/test/fixtures/events.yml b/automation/admin/test/fixtures/events.yml
new file mode 100644
index 0000000..d1c4a63
--- /dev/null
+++ b/automation/admin/test/fixtures/events.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ event_type: MAGEvalidator
+ experiment_id: 2
+another:
+ id: 2
+ event_type: MAGEloader
+ experiment_id: 2
diff --git a/automation/admin/test/fixtures/experiments.yml b/automation/admin/test/fixtures/experiments.yml
new file mode 100644
index 0000000..4345b53
--- /dev/null
+++ b/automation/admin/test/fixtures/experiments.yml
@@ -0,0 +1,18 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first: # N.B. no accession for the first, otherwise test deprecation fails.
+ id: 1
+ experiment_type: Tab2MAGE
+ is_affymetrix: 0
+ in_curation: 0
+another:
+ id: 2
+ accession: E-TABM-1
+ experiment_type: Tab2MAGE
+ is_affymetrix: 1
+ in_curation: 1
+yetanother:
+ id: 3
+ accession: E-MEXP-1
+ experiment_type: MIAMExpress
+ is_affymetrix: 0
+ in_curation: 1
diff --git a/automation/admin/test/fixtures/factors.yml b/automation/admin/test/fixtures/factors.yml
new file mode 100644
index 0000000..6aef4f5
--- /dev/null
+++ b/automation/admin/test/fixtures/factors.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ name: cell_type
+another:
+ id: 2
+ name: compound
diff --git a/automation/admin/test/fixtures/materials.yml b/automation/admin/test/fixtures/materials.yml
new file mode 100644
index 0000000..38af1ac
--- /dev/null
+++ b/automation/admin/test/fixtures/materials.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ display_label: Whole organism
+ ontology_category: MaterialType
+ ontology_value: whole_organism
+another:
+ id: 2
+ display_label: Organism part
+ ontology_category: MaterialType
+ ontology_value: organism_part
diff --git a/automation/admin/test/fixtures/organisms.yml b/automation/admin/test/fixtures/organisms.yml
new file mode 100644
index 0000000..16528e0
--- /dev/null
+++ b/automation/admin/test/fixtures/organisms.yml
@@ -0,0 +1,13 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first_organism:
+ id: 1
+ accession: 562
+ common_name: E. coli
+ scientific_name: Escherichia coli
+ taxon_id: 1
+another_organism:
+ id: 2
+ accession: 4932
+ common_name: Baker's yeast
+ scientific_name: Saccharomyces cerevisiae
+ taxon_id: 2
\ No newline at end of file
diff --git a/automation/admin/test/fixtures/permissions.yml b/automation/admin/test/fixtures/permissions.yml
new file mode 100644
index 0000000..acaa96a
--- /dev/null
+++ b/automation/admin/test/fixtures/permissions.yml
@@ -0,0 +1,17 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+adminperms:
+ id: 1
+ name: '.*/.*'
+ info: 'Access All Areas'
+ is_deleted: 0
+exptperms:
+ id: 2
+ name: 'experiments/.*'
+ info: 'Full experiment access'
+ is_deleted: 0
+arrayperms:
+ id: 3
+ name: 'array_designs/.*'
+ info: 'Full array design access'
+ is_deleted: 0
+
diff --git a/automation/admin/test/fixtures/permissions_roles.yml b/automation/admin/test/fixtures/permissions_roles.yml
new file mode 100644
index 0000000..c80f72f
--- /dev/null
+++ b/automation/admin/test/fixtures/permissions_roles.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+adminrole:
+ role_id: 1
+ permission_id: 1
+exptrole:
+ role_id: 1
+ permission_id: 2
+arrayrole:
+ role_id: 1
+ permission_id: 3
diff --git a/automation/admin/test/fixtures/protocols.yml b/automation/admin/test/fixtures/protocols.yml
new file mode 100644
index 0000000..97ae777
--- /dev/null
+++ b/automation/admin/test/fixtures/protocols.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ accession: P-TABM-1
+ user_accession: my_acc
+ expt_accession: E-TABM-1
+another:
+ id: 2
+ accession: P-TABM-2
+ user_accession: my_other_acc
+ expt_accession: E-TABM-2
diff --git a/automation/admin/test/fixtures/quantitation_types.yml b/automation/admin/test/fixtures/quantitation_types.yml
new file mode 100644
index 0000000..ce9ff7d
--- /dev/null
+++ b/automation/admin/test/fixtures/quantitation_types.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ name: CHPSignal
+another:
+ id: 2
+ name: CHPDetection
diff --git a/automation/admin/test/fixtures/roles.yml b/automation/admin/test/fixtures/roles.yml
new file mode 100644
index 0000000..a6999e2
--- /dev/null
+++ b/automation/admin/test/fixtures/roles.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+ name: 'admin'
+ info: 'Superuser'
+ is_deleted: 0
+another:
+ id: 2
+ name: 'curator'
+ info: 'Curator'
+ is_deleted: 0
\ No newline at end of file
diff --git a/automation/admin/test/fixtures/roles_users.yml b/automation/admin/test/fixtures/roles_users.yml
new file mode 100644
index 0000000..f62f7d9
--- /dev/null
+++ b/automation/admin/test/fixtures/roles_users.yml
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+bobrole:
+ role_id: 1
+ user_id: 1000001
diff --git a/automation/admin/test/fixtures/spreadsheets.yml b/automation/admin/test/fixtures/spreadsheets.yml
new file mode 100644
index 0000000..8794d28
--- /dev/null
+++ b/automation/admin/test/fixtures/spreadsheets.yml
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+another:
+ id: 2
diff --git a/automation/admin/test/fixtures/taxons.yml b/automation/admin/test/fixtures/taxons.yml
new file mode 100644
index 0000000..0bd0e7a
--- /dev/null
+++ b/automation/admin/test/fixtures/taxons.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first_taxon:
+ id: 1
+ accession: 2
+ common_name: Eubacteria
+ scientific_name: Bacteria
+another_taxon:
+ id: 2
+ accession: 4890
+ common_name: Sac Fungi
+ scientific_name: Ascomycota
diff --git a/automation/admin/test/fixtures/users.yml b/automation/admin/test/fixtures/users.yml
new file mode 100644
index 0000000..5330c67
--- /dev/null
+++ b/automation/admin/test/fixtures/users.yml
@@ -0,0 +1,16 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+bob:
+ id: 1000001
+ login: bob
+ password: test # test
+
+existingbob:
+ id: 1000002
+ login: existingbob
+ password: test # test
+
+longbob:
+ id: 1000003
+ login: longbob
+ password: longtest # longtest
\ No newline at end of file
diff --git a/automation/admin/test/functional/account_controller_test.rb b/automation/admin/test/functional/account_controller_test.rb
new file mode 100644
index 0000000..f1d1fe6
--- /dev/null
+++ b/automation/admin/test/functional/account_controller_test.rb
@@ -0,0 +1,81 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'account_controller'
+
+# Raise errors beyond the default web-based presentation
+class AccountController; def rescue_action(e) raise e end; end
+
+class AccountControllerTest < Test::Unit::TestCase
+
+ fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+
+ def setup
+ @controller = AccountController.new
+ @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+ @request.host = "localhost"
+ end
+
+ def test_auth_bob
+ @request.session['return-to'] = "/account/welcome"
+
+ post :login, :user_login => "bob", :user_password => "test"
+ assert(@response.has_session_object?(:user))
+
+ assert_equal @bob, @response.session[:user]
+
+ assert(@response.redirect_url_match?(/\/account\/welcome$/))
+ end
+
+ def test_signup
+ @request.session['return-to'] = "/account/welcome"
+
+ post :signup, :user => { :login => "newbob",
+ :password => "newpassword",
+ :password_confirmation => "newpassword" }
+ assert(@response.has_session_object?(:user))
+
+ assert(@response.redirect_url_match?(/\/account\/welcome$/))
+ end
+
+ def test_bad_signup
+ @request.session['return-to'] = "/account/welcome"
+
+ post :signup, :user => { :login => "newbob",
+ :password => "newpassword",
+ :password_confirmation => "wrong" }
+
+ assert(@response.template_objects['user'].errors.invalid?(:password))
+ assert_response(:success)
+
+ post :signup, :user => { :login => "yo",
+ :password => "newpassword",
+ :password_confirmation => "newpassword" }
+ assert(@response.template_objects['user'].errors.invalid?(:login))
+ assert_response(:success)
+
+ post :signup, :user => { :login => "yo",
+ :password => "newpassword",
+ :password_confirmation => "wrong" }
+ assert(@response.template_objects['user'].errors.invalid?(:login))
+ assert(@response.template_objects['user'].errors.invalid?(:password))
+ assert_response(:success)
+ end
+
+ def test_invalid_login
+ post :login, :user_login => "bob", :user_password => "not_correct"
+
+ assert(! @response.has_session_object?("user"))
+ assert(@response.has_template_object?("message"))
+ assert(@response.has_template_object?("login"))
+ end
+
+ def test_login_logoff
+
+ post :login, :user_login => "bob", :user_password => "test"
+ assert(@response.has_session_object?(:user))
+
+ get :logout
+ assert(! @response.has_session_object?(:user))
+
+ end
+
+end
diff --git a/automation/admin/test/functional/array_designs_controller_test.rb b/automation/admin/test/functional/array_designs_controller_test.rb
new file mode 100644
index 0000000..d5a922c
--- /dev/null
+++ b/automation/admin/test/functional/array_designs_controller_test.rb
@@ -0,0 +1,88 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'array_designs_controller'
+
+# Re-raise errors caught by the controller.
+class ArrayDesignsController; def rescue_action(e) raise e end; end
+
+class ArrayDesignsControllerTest < Test::Unit::TestCase
+ fixtures :array_designs
+ fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+
+ def setup
+ @controller = ArrayDesignsController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @request.session[:user] = @bob
+ end
+
+ def test_index
+ get :index
+ assert_response :success
+ assert_template 'list'
+ end
+
+ def test_list
+ get :list
+
+ assert_response :success
+ assert_template 'list'
+
+ assert_not_nil assigns(:array_designs)
+ end
+
+ def test_show
+ get :show, :id => 1
+
+ assert_response :success
+ assert_template 'show'
+
+ assert_not_nil assigns(:array_design)
+ assert assigns(:array_design).valid?
+ end
+
+ def test_new
+ get :new
+
+ assert_response :success
+ assert_template 'new'
+
+ assert_not_nil assigns(:array_design)
+ end
+
+ def test_create
+ num_array_designs = ArrayDesign.count
+
+ post :create, :array_design => {:id => 3, :miamexpress_subid => 1003, :accession => "A-MEXP-3"}
+
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+
+ assert_equal num_array_designs + 1, ArrayDesign.count
+ end
+
+ def test_edit
+ get :edit, :id => 1
+
+ assert_response :success
+ assert_template 'edit'
+
+ assert_not_nil assigns(:array_design)
+ assert assigns(:array_design).valid?
+ end
+
+ def test_update
+ post :update, :id => 1
+ assert_response :redirect
+ assert_redirected_to :action => 'list_all'
+ end
+
+ def test_deprecate
+ assert_not_nil ArrayDesign.find(1)
+
+ post :deprecate, :id => 1
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+
+ assert_equal(1, ArrayDesign.find(1).is_deleted)
+ end
+end
diff --git a/automation/admin/test/functional/category_controller_test.rb b/automation/admin/test/functional/category_controller_test.rb
new file mode 100644
index 0000000..2b6558d
--- /dev/null
+++ b/automation/admin/test/functional/category_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'category_controller'
+
+# Re-raise errors caught by the controller.
+class CategoryController; def rescue_action(e) raise e end; end
+
+class CategoryControllerTest < Test::Unit::TestCase
+
+ fixtures :categories
+
+ def setup
+ @controller = CategoryController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/design_controller_test.rb b/automation/admin/test/functional/design_controller_test.rb
new file mode 100644
index 0000000..91eb1de
--- /dev/null
+++ b/automation/admin/test/functional/design_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'design_controller'
+
+# Re-raise errors caught by the controller.
+class DesignController; def rescue_action(e) raise e end; end
+
+class DesignControllerTest < Test::Unit::TestCase
+
+ fixtures :designs
+
+ def setup
+ @controller = DesignController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/event_controller_test.rb b/automation/admin/test/functional/event_controller_test.rb
new file mode 100644
index 0000000..71b596b
--- /dev/null
+++ b/automation/admin/test/functional/event_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'event_controller'
+
+# Re-raise errors caught by the controller.
+class EventController; def rescue_action(e) raise e end; end
+
+class EventControllerTest < Test::Unit::TestCase
+
+ fixtures :experiments, :events
+
+ def setup
+ @controller = EventController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/experiments_controller_test.rb b/automation/admin/test/functional/experiments_controller_test.rb
new file mode 100644
index 0000000..8415da8
--- /dev/null
+++ b/automation/admin/test/functional/experiments_controller_test.rb
@@ -0,0 +1,94 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'experiments_controller'
+
+# Re-raise errors caught by the controller.
+class ExperimentsController; def rescue_action(e) raise e end; end
+
+class ExperimentsControllerTest < Test::Unit::TestCase
+ fixtures :experiments
+ fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+
+ def setup
+ @controller = ExperimentsController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @request.session[:user] = @bob
+ end
+
+ def test_index
+ get :index
+ assert_response :success
+ assert_template 'list'
+ end
+
+ def test_list
+ get :list
+
+ assert_response :success
+ assert_template 'list'
+
+ assert_not_nil assigns(:experiments)
+ end
+
+ def test_show
+ get :show, :id => 1
+
+ assert_response :success
+ assert_template 'show'
+
+ assert_not_nil assigns(:experiment)
+ assert assigns(:experiment).valid?
+ end
+
+ def test_new
+ get :new
+
+ assert_response :success
+ assert_template 'new'
+
+ assert_not_nil assigns(:experiment)
+ end
+
+ def test_create
+ num_experiments = Experiment.count
+
+ post :create, :experiment => {:id => 4, :accession => 'E-GEOD-1',
+ :experiment_type => 'GEO',
+ :is_affymetrix => 0,
+ :in_curation => 0}
+
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+
+ assert_equal num_experiments + 1, Experiment.count
+ end
+
+ def test_edit
+ get :edit, :id => 1
+
+ # Experiments are redirected to their tab2mage or miamexp
+ # subclass. FIXME we should really add tests for these subclasses
+ # at some point.
+ assert_response :redirect
+ assert_redirected_to :controller => 'tab2mages', :action => 'edit'
+
+ assert_not_nil assigns(:experiment)
+ assert assigns(:experiment).valid?
+ end
+
+ def test_update
+ post :update, :id => 2
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+ end
+
+ def test_deprecate
+ assert_not_nil Experiment.find(1)
+
+ post :deprecate, :id => 1
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+
+ assert_equal(1, Experiment.find(1).is_deleted)
+ end
+end
diff --git a/automation/admin/test/functional/factor_controller_test.rb b/automation/admin/test/functional/factor_controller_test.rb
new file mode 100644
index 0000000..0a775bd
--- /dev/null
+++ b/automation/admin/test/functional/factor_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'factor_controller'
+
+# Re-raise errors caught by the controller.
+class FactorController; def rescue_action(e) raise e end; end
+
+class FactorControllerTest < Test::Unit::TestCase
+
+ fixtures :factors
+
+ def setup
+ @controller = FactorController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/material_controller_test.rb b/automation/admin/test/functional/material_controller_test.rb
new file mode 100644
index 0000000..d8f8cb7
--- /dev/null
+++ b/automation/admin/test/functional/material_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'material_controller'
+
+# Re-raise errors caught by the controller.
+class MaterialController; def rescue_action(e) raise e end; end
+
+class MaterialControllerTest < Test::Unit::TestCase
+
+ fixtures :materials
+
+ def setup
+ @controller = MaterialController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/miamexps_controller_test.rb b/automation/admin/test/functional/miamexps_controller_test.rb
new file mode 100644
index 0000000..56d7d48
--- /dev/null
+++ b/automation/admin/test/functional/miamexps_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'miamexps_controller'
+
+# Re-raise errors caught by the controller.
+class MiamexpsController; def rescue_action(e) raise e end; end
+
+class MiamexpsControllerTest < Test::Unit::TestCase
+ def setup
+ @controller = MiamexpsController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/organism_controller_test.rb b/automation/admin/test/functional/organism_controller_test.rb
new file mode 100644
index 0000000..dfe86b6
--- /dev/null
+++ b/automation/admin/test/functional/organism_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'organism_controller'
+
+# Re-raise errors caught by the controller.
+class OrganismController; def rescue_action(e) raise e end; end
+
+class OrganismControllerTest < Test::Unit::TestCase
+
+ fixtures :taxons, :organisms
+
+ def setup
+ @controller = OrganismController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/protocols_controller_test.rb b/automation/admin/test/functional/protocols_controller_test.rb
new file mode 100644
index 0000000..b5b1967
--- /dev/null
+++ b/automation/admin/test/functional/protocols_controller_test.rb
@@ -0,0 +1,88 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'protocols_controller'
+
+# Re-raise errors caught by the controller.
+class ProtocolsController; def rescue_action(e) raise e end; end
+
+class ProtocolsControllerTest < Test::Unit::TestCase
+ fixtures :protocols
+ fixtures :permissions, :roles, :users, :roles_users, :permissions_roles
+
+ def setup
+ @controller = ProtocolsController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @request.session[:user] = @bob
+ end
+
+ def test_index
+ get :index
+ assert_response :success
+ assert_template 'list'
+ end
+
+ def test_list
+ get :list
+
+ assert_response :success
+ assert_template 'list'
+
+ assert_not_nil assigns(:protocols)
+ end
+
+ def test_show
+ get :show, :id => 1
+
+ assert_response :success
+ assert_template 'show'
+
+ assert_not_nil assigns(:protocol)
+ assert assigns(:protocol).valid?
+ end
+
+ def test_new
+ get :new
+
+ assert_response :success
+ assert_template 'new'
+
+ assert_not_nil assigns(:protocol)
+ end
+
+ def test_create
+ num_protocols = Protocol.count
+
+ post :create, :protocol => {:id => 3, :accession => 3, :user_accession => 3, :expt_accession => 3}
+
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+
+ assert_equal num_protocols + 1, Protocol.count
+ end
+
+ def test_edit
+ get :edit, :id => 1
+
+ assert_response :success
+ assert_template 'edit'
+
+ assert_not_nil assigns(:protocol)
+ assert assigns(:protocol).valid?
+ end
+
+ def test_update
+ post :update, :id => 1
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+ end
+
+ def test_deprecate
+ assert_not_nil Protocol.find(1)
+
+ post :deprecate, :id => 1
+ assert_response :redirect
+ assert_redirected_to :action => 'list'
+
+ assert_equal(1, Protocol.find(1).is_deleted)
+ end
+end
diff --git a/automation/admin/test/functional/quantitation_type_controller_test.rb b/automation/admin/test/functional/quantitation_type_controller_test.rb
new file mode 100644
index 0000000..aff34e8
--- /dev/null
+++ b/automation/admin/test/functional/quantitation_type_controller_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'quantitation_type_controller'
+
+# Re-raise errors caught by the controller.
+class QuantitationTypeController; def rescue_action(e) raise e end; end
+
+class QuantitationTypeControllerTest < Test::Unit::TestCase
+
+ fixtures :quantitation_types
+
+ def setup
+ @controller = QuantitationTypeController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/tab2mages_controller_test.rb b/automation/admin/test/functional/tab2mages_controller_test.rb
new file mode 100644
index 0000000..6403635
--- /dev/null
+++ b/automation/admin/test/functional/tab2mages_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'tab2mages_controller'
+
+# Re-raise errors caught by the controller.
+class Tab2magesController; def rescue_action(e) raise e end; end
+
+class Tab2magesControllerTest < Test::Unit::TestCase
+ def setup
+ @controller = Tab2magesController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/taxon_controller_test.rb b/automation/admin/test/functional/taxon_controller_test.rb
new file mode 100644
index 0000000..0e4f5c3
--- /dev/null
+++ b/automation/admin/test/functional/taxon_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'taxon_controller'
+
+# Re-raise errors caught by the controller.
+class TaxonController; def rescue_action(e) raise e end; end
+
+class TaxonControllerTest < Test::Unit::TestCase
+ def setup
+ @controller = TaxonController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/functional/users_controller_test.rb b/automation/admin/test/functional/users_controller_test.rb
new file mode 100644
index 0000000..57a3fc8
--- /dev/null
+++ b/automation/admin/test/functional/users_controller_test.rb
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'users_controller'
+
+# Re-raise errors caught by the controller.
+class UsersController; def rescue_action(e) raise e end; end
+
+class UsersControllerTest < Test::Unit::TestCase
+ def setup
+ @controller = UsersController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/test_helper.rb b/automation/admin/test/test_helper.rb
new file mode 100644
index 0000000..40989bb
--- /dev/null
+++ b/automation/admin/test/test_helper.rb
@@ -0,0 +1,28 @@
+ENV["RAILS_ENV"] = "test"
+require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
+require 'test_help'
+
+class Test::Unit::TestCase
+ # Transactional fixtures accelerate your tests by wrapping each test method
+ # in a transaction that's rolled back on completion. This ensures that the
+ # test database remains unchanged so your fixtures don't have to be reloaded
+ # between every test method. Fewer database queries means faster tests.
+ #
+ # Read Mike Clark's excellent walkthrough at
+ # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
+ #
+ # Every Active Record database supports transactions except MyISAM tables
+ # in MySQL. Turn off transactional fixtures in this case; however, if you
+ # don't care one way or the other, switching from MyISAM to InnoDB tables
+ # is recommended.
+ self.use_transactional_fixtures = true
+
+ # Instantiated fixtures are slow, but give you @david where otherwise you
+ # would need people(:david). If you don't want to migrate your existing
+ # test cases which use the @david style and don't mind the speed hit (each
+ # instantiated fixtures translates to a database query per test method),
+ # then set this back to true.
+ self.use_instantiated_fixtures = true
+
+ # Add more helper methods to be used by all tests here...
+end
diff --git a/automation/admin/test/unit/array_design_test.rb b/automation/admin/test/unit/array_design_test.rb
new file mode 100644
index 0000000..c238037
--- /dev/null
+++ b/automation/admin/test/unit/array_design_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ArrayDesignTest < Test::Unit::TestCase
+ fixtures :array_designs
+
+ def setup
+ @array_design = ArrayDesign.find(1)
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert_kind_of ArrayDesign, @array_design
+ end
+end
diff --git a/automation/admin/test/unit/category_test.rb b/automation/admin/test/unit/category_test.rb
new file mode 100644
index 0000000..f58ea61
--- /dev/null
+++ b/automation/admin/test/unit/category_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class CategoryTest < Test::Unit::TestCase
+ fixtures :categories
+
+ def setup
+ @category = Category.find(1)
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert_kind_of Category, @category
+ end
+end
diff --git a/automation/admin/test/unit/data_file_test.rb b/automation/admin/test/unit/data_file_test.rb
new file mode 100644
index 0000000..261a725
--- /dev/null
+++ b/automation/admin/test/unit/data_file_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class DataFileTest < Test::Unit::TestCase
+ fixtures :data_files
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/design_test.rb b/automation/admin/test/unit/design_test.rb
new file mode 100644
index 0000000..03256e3
--- /dev/null
+++ b/automation/admin/test/unit/design_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class DesignTest < Test::Unit::TestCase
+ fixtures :designs
+
+ # Replace this with your real tests.
+ def test_truth
+ assert_kind_of Design, designs(:first)
+ end
+end
diff --git a/automation/admin/test/unit/event_test.rb b/automation/admin/test/unit/event_test.rb
new file mode 100644
index 0000000..d8925d8
--- /dev/null
+++ b/automation/admin/test/unit/event_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class EventTest < Test::Unit::TestCase
+ fixtures :experiments, :events
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/experiment_test.rb b/automation/admin/test/unit/experiment_test.rb
new file mode 100644
index 0000000..ae340c4
--- /dev/null
+++ b/automation/admin/test/unit/experiment_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ExperimentTest < Test::Unit::TestCase
+ fixtures :experiments
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/factor_test.rb b/automation/admin/test/unit/factor_test.rb
new file mode 100644
index 0000000..632cae1
--- /dev/null
+++ b/automation/admin/test/unit/factor_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class FactorTest < Test::Unit::TestCase
+ fixtures :factors
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/material_test.rb b/automation/admin/test/unit/material_test.rb
new file mode 100644
index 0000000..e4cf600
--- /dev/null
+++ b/automation/admin/test/unit/material_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class MaterialTest < Test::Unit::TestCase
+ fixtures :materials
+
+ # Replace this with your real tests.
+ def test_truth
+ assert_kind_of Material, materials(:first)
+ end
+end
diff --git a/automation/admin/test/unit/organism_test.rb b/automation/admin/test/unit/organism_test.rb
new file mode 100644
index 0000000..7086ba5
--- /dev/null
+++ b/automation/admin/test/unit/organism_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class OrganismTest < Test::Unit::TestCase
+ fixtures :taxons, :organisms
+
+ def setup
+ @organism = Organism.find(1)
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert_kind_of Organism, @organism
+ end
+end
diff --git a/automation/admin/test/unit/permission_test.rb b/automation/admin/test/unit/permission_test.rb
new file mode 100644
index 0000000..cab1ace
--- /dev/null
+++ b/automation/admin/test/unit/permission_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class PermissionTest < Test::Unit::TestCase
+ fixtures :permissions
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/protocol_test.rb b/automation/admin/test/unit/protocol_test.rb
new file mode 100644
index 0000000..b44d2da
--- /dev/null
+++ b/automation/admin/test/unit/protocol_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ProtocolTest < Test::Unit::TestCase
+ fixtures :protocols
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/quantitation_type_test.rb b/automation/admin/test/unit/quantitation_type_test.rb
new file mode 100644
index 0000000..48edaa8
--- /dev/null
+++ b/automation/admin/test/unit/quantitation_type_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class QuantitationTypeTest < Test::Unit::TestCase
+ fixtures :quantitation_types
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/role_test.rb b/automation/admin/test/unit/role_test.rb
new file mode 100644
index 0000000..05d6652
--- /dev/null
+++ b/automation/admin/test/unit/role_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class RoleTest < Test::Unit::TestCase
+ fixtures :roles
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/spreadsheet_test.rb b/automation/admin/test/unit/spreadsheet_test.rb
new file mode 100644
index 0000000..465ea8a
--- /dev/null
+++ b/automation/admin/test/unit/spreadsheet_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class SpreadsheetTest < Test::Unit::TestCase
+ fixtures :spreadsheets
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/automation/admin/test/unit/taxon_test.rb b/automation/admin/test/unit/taxon_test.rb
new file mode 100644
index 0000000..a8c311a
--- /dev/null
+++ b/automation/admin/test/unit/taxon_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TaxonTest < Test::Unit::TestCase
+ fixtures :taxons
+
+ def setup
+ @taxon = Taxon.find(1)
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert_kind_of Taxon, @taxon
+ end
+end
diff --git a/automation/admin/test/unit/user_test.rb b/automation/admin/test/unit/user_test.rb
new file mode 100644
index 0000000..0310e97
--- /dev/null
+++ b/automation/admin/test/unit/user_test.rb
@@ -0,0 +1,101 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class UserTest < Test::Unit::TestCase
+ self.use_instantiated_fixtures = true
+
+ fixtures :users
+
+ def test_auth
+ assert_equal @bob, User.authenticate("bob", "test")
+ assert_nil User.authenticate("nonbob", "test")
+
+ end
+
+
+ def test_passwordchange
+
+ @longbob.change_password("nonbobpasswd")
+ assert_equal @longbob, User.authenticate("longbob", "nonbobpasswd")
+ assert_nil User.authenticate("longbob", "longtest")
+ @longbob.change_password("longtest")
+ assert_equal @longbob, User.authenticate("longbob", "longtest")
+ assert_nil User.authenticate("longbob", "nonbobpasswd")
+
+ end
+
+ def test_disallowed_passwords
+
+ u = User.new( :is_deleted => 0 )
+ u.login = "nonbob"
+
+ u.password = u.password_confirmation = "tiny"
+ assert !u.save
+ assert u.errors.invalid?('password')
+
+ u.password = u.password_confirmation = "hugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehugehuge"
+ assert !u.save
+ assert u.errors.invalid?('password')
+
+ u.password = u.password_confirmation = ""
+ assert !u.save
+ assert u.errors.invalid?('password')
+
+ u.password = u.password_confirmation = "bobs_secure_password"
+ assert u.save
+ assert u.errors.empty?
+
+ end
+
+ def test_bad_logins
+
+ u = User.new( :is_deleted => 0 )
+ u.password = u.password_confirmation = "bobs_secure_password"
+
+ u.login = "x"
+ assert !u.save
+ assert u.errors.invalid?('login')
+
+ u.login = "hugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhugebobhug"
+ assert !u.save
+ assert u.errors.invalid?('login')
+
+ u.login = ""
+ assert !u.save
+ assert u.errors.invalid?('login')
+
+ u.login = "okbob"
+ assert u.save
+ assert u.errors.empty?
+
+ end
+
+
+ def test_collision
+ u = User.new
+ u.login = "existingbob"
+ u.password = u.password_confirmation = "bobs_secure_password"
+ assert !u.save
+ end
+
+
+ def test_create
+ u = User.new( :is_deleted => 0 )
+ u.login = "nonexistingbob"
+ u.password = u.password_confirmation = "bobs_secure_password"
+
+ assert u.save
+
+ end
+
+# def test_sha1
+# u = User.new
+# u.login = "nonexistingbob"
+# u.password = u.password_confirmation = "bobs_secure_password"
+# assert u.save
+#
+# assert_equal '98740ff87bade6d895010bceebbd9f718e7856bb', u.password
+#
+# end
+
+
+end
diff --git a/automation/autosubs_checkd.pl b/automation/autosubs_checkd.pl
new file mode 100755
index 0000000..9585f06
--- /dev/null
+++ b/automation/autosubs_checkd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate expt_check.pl running for newly-submitted tab2mage experiments
+#
+# $Id: autosubs_checkd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::T2MChecker;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start checking.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::T2MChecker->new({
+ polling_interval => 5,
+ checker_threshold => ($CONFIG->get_ERROR_INNOCENT()
+ | $CONFIG->get_ERROR_MIAME()),
+ experiment_type => 'Tab2MAGE',
+ accession_prefix => 'E-TABM-',
+ autosubs_admin => $CONFIG->get_AUTOSUBS_ADMIN(),
+});
+
+$daemon->run();
diff --git a/automation/autosubs_exportd.pl b/automation/autosubs_exportd.pl
new file mode 100755
index 0000000..9167e05
--- /dev/null
+++ b/automation/autosubs_exportd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate MAGE-ML export for Tab2MAGE experiments which
+# have passed automated checking.
+#
+# $Id: autosubs_exportd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::T2MExporter;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start exporting.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::T2MExporter->new({
+ polling_interval => 5,
+ experiment_type => 'Tab2MAGE',
+ autosubs_admin => $CONFIG->get_AUTOSUBS_ADMIN(),
+ pipeline_subdir => 'TABM',
+ accession_prefix => 'E-TABM-',
+});
+
+$daemon->run();
diff --git a/automation/autosubs_httpd.pl b/automation/autosubs_httpd.pl
new file mode 100755
index 0000000..10493a3
--- /dev/null
+++ b/automation/autosubs_httpd.pl
@@ -0,0 +1,298 @@
+#!/usr/bin/env perl
+#
+# Web server daemon script to provide views onto automated submissions
+# processing.
+#
+# Tim Rayner 2006 EBI Microarray Informatics Team
+#
+# $Id: autosubs_httpd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+#
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use HTTP::Daemon;
+use HTTP::Status;
+use HTTP::Response;
+use Proc::Daemon;
+use CGI;
+use Getopt::Long;
+
+require ArrayExpress::AutoSubmission::DB::Protocol;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+
+########
+# SUBS #
+########
+
+sub create_table {
+
+ my ( $methods, $records, $sort_key ) = @_;
+
+ my $cgi = CGI->new;
+
+ my $content = q{};
+
+ $content .= $cgi->start_html(
+ -title => 'Curator Submissions Tracking',
+ -style => { src => '/style.css' }
+ );
+ $content .= $cgi->h1('Curator Submissions Tracking (deprecated interface)');
+
+ $content .= $cgi->ul(
+ $cgi->li(
+ $cgi->a( { href => '/mx_array.html' }, "MIAMExpress Arrays" )
+ ),
+ $cgi->li(
+ $cgi->a(
+ { href => '/mx_experiment.html' },
+ "MIAMExpress Experiments"
+ )
+ ),
+ $cgi->li(
+ $cgi->a(
+ { href => '/t2m_experiment.html' },
+ "Tab2MAGE Experiments"
+ )
+ ),
+ $cgi->li(
+ $cgi->a( { href => '/t2m_protocol.html' }, "Tab2MAGE Protocols" )
+ )
+ );
+
+ if ( $methods && scalar @$methods ) {
+ my $header_data = q{};
+ foreach my $method (@$methods) {
+ my $formatted_heading = ucfirst($method);
+ $formatted_heading =~ s/_(\w)/q{ } . ucfirst($1)/ge;
+ $header_data .= $cgi->th($formatted_heading);
+ }
+ my $table_header = $cgi->Tr($header_data);
+
+ if ( $records && scalar @$records ) {
+
+ my @sorted =
+ map { $_->[0] }
+ reverse sort { $a->[1] <=> $b->[1] }
+ map { [ $_, $_->$sort_key ] }
+ @$records;
+
+ # Print out the whole table
+ my @table_rows;
+ foreach my $object (@sorted) {
+ my $row_data = q{};
+ foreach my $method (@$methods) {
+ my $cell = $object->$method;
+
+ # Colour up the status column values to highlight them.
+ if ( $method =~ /\bstatus\b/i ) {
+ my $color =
+ ( $cell =~ /\bfail(?:ed|ure|ing)?\b/i ) ? q{red}
+ : ( $cell =~ /\bpass(?:ed|ing)?\b/i ) ? q{green}
+ : ( $cell =~ /\bwarn(?:ed|ings?)?\b/i ) ? q{blue}
+ : q{};
+ $cell = $color
+ ? qq{<font color="$color">$cell</font>}
+ : $cell;
+ }
+ $row_data .= $cgi->td($cell);
+ }
+ push( @table_rows, $cgi->Tr($row_data) );
+ }
+ $content
+ .= $cgi->table( { border => 1 }, $table_header, @table_rows );
+ }
+
+ else {
+
+ # Headings only
+ $content .= $cgi->table( { border => 1 }, $table_header );
+ }
+ }
+
+ $content .= $cgi->end_html;
+
+ return $content;
+}
+
+sub mx_array_list {
+ my @records = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+ is_deleted => 0,
+ );
+ my @headings =
+ qw(
+ miamexpress_subid
+ accession
+ name
+ miamexpress_login
+ status
+ data_warehouse_ready
+ date_last_processed
+ comment
+ );
+ return ( 'text/html',
+ create_table( \@headings, \@records, 'miamexpress_subid' ) );
+}
+
+sub mx_experiment_list {
+ my @records = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ experiment_type => 'MIAMExpress',
+ is_deleted => 0,
+ );
+ my @headings =
+ qw(
+ miamexpress_subid
+ accession
+ name
+ miamexpress_login
+ status
+ data_warehouse_ready
+ date_last_processed
+ checker_score
+ software
+ comment
+ curator
+ );
+ return ( 'text/html',
+ create_table( \@headings, \@records, 'miamexpress_subid' ) );
+}
+
+sub t2m_experiment_list {
+ my @records = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ experiment_type => 'Tab2MAGE',
+ is_deleted => 0,
+ );
+ my @headings =
+ qw(
+ accession
+ name
+ user_id
+ status
+ data_warehouse_ready
+ date_last_processed
+ checker_score
+ software
+ in_curation
+ comment
+ curator
+ );
+ return ( 'text/html',
+ create_table( \@headings, \@records, 'id' ) );
+}
+
+sub t2m_protocol_list {
+ my @records = ArrayExpress::AutoSubmission::DB::Protocol->search(
+ is_deleted => 0,
+ );
+ my @headings =
+ qw(
+ accession
+ user_accession
+ expt_accession
+ name
+ date_last_processed
+ comment
+ );
+ return ( 'text/html',
+ create_table( \@headings, \@records, 'accession' ) );
+}
+
+########
+# MAIN #
+########
+
+my $DEBUG;
+GetOptions( "D|debug" => \$DEBUG );
+
+# Run in the background.
+unless ($DEBUG) {
+ print STDOUT "Starting HTTP Daemon...\n";
+ Proc::Daemon::Init;
+}
+
+# Set up the http daemon process.
+my $port = 8086;
+my $httpd = HTTP::Daemon->new( LocalPort => $port )
+ or die("Error: cannot bind to port $port: $!");
+print STDOUT ( "Started HTTP at " . $httpd->url() . "\n" ) if $DEBUG;
+
+# Ignore child process exits. This should cut down on zombie
+# processes.
+$SIG{CHLD} = 'IGNORE';
+
+# Hold the stylesheet in memory.
+my $stylesheet = join( "\n", <DATA> );
+
+# Supported page links should be enumerated here.
+my %dispatch = (
+ '/mx_array.html' => \&mx_array_list,
+ '/mx_experiment.html' => \&mx_experiment_list,
+ '/t2m_protocol.html' => \&t2m_protocol_list,
+ '/t2m_experiment.html' => \&t2m_experiment_list,
+ '/style.css' => sub { return ( 'text/css', $stylesheet ) },
+);
+
+# Get HTTP requests and respond to them in a forked process.
+while ( my $client = $httpd->accept() ) {
+ my $pid = fork();
+ if ( !$pid ) {
+
+ #### Child process ####
+ while ( my $request = $client->get_request() ) {
+
+ my $response = HTTP::Response->new(RC_OK);
+
+ if ( $request->method() eq 'GET' ) {
+ if ( my $url = $request->url()->path() ) {
+
+ # See if the request is in the dispatch table.
+ my ( $type, $content );
+ if ( my $code_ref = $dispatch{$url} ) {
+ ( $type, $content ) = $code_ref->();
+ }
+
+ # If everything worked, serve up the desired content.
+ if ( $type && $content ) {
+ $response->header( 'Content-Type' => $type );
+ $response->content($content);
+ }
+
+ # Otherwise, fall back to a basic menu.
+ else {
+ $response->header( 'Content-Type' => 'text/html' );
+ $response->content( create_table() );
+ }
+ }
+ }
+ $client->send_response($response);
+
+ # Log the request.
+ print STDOUT (
+ $request->method() . "\n" . $request->url()->path() . "\n" )
+ if $DEBUG;
+ }
+
+ # Clean up.
+ $client->close();
+ undef($client);
+ exit();
+ #### End of Child process ####
+ }
+ #### Parent process ####
+}
+
+__DATA__
+body {
+ font-family: ariel, helvetica, sans-serif;
+ line-height: 120%;
+ background-color: #ccddff;
+}
+
+td, th {
+ background-color: #ddeeff;
+}
+
diff --git a/automation/calculate_celqc.pl b/automation/calculate_celqc.pl
new file mode 100755
index 0000000..d014fc5
--- /dev/null
+++ b/automation/calculate_celqc.pl
@@ -0,0 +1,52 @@
+#!/usr/bin/env perl
+#
+# Script to automate CEL QC checks run across the whole AE database,
+# processed in parallel using the LSF batch job submission system.
+#
+# $Id: calculate_celqc.pl 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+# NOTE: no "use lib" placeholder for this script, because it uses a
+# PATH specific to the LSF cluster which is maintained in the shell
+# wrapper for this script.
+
+use ArrayExpress::Tracking::QCJobManager;
+use ArrayExpress::Curator::Database qw(get_ae_dbh);
+
+# Create AE statement handle, start JobManager.
+my $dbh = get_ae_dbh();
+
+my $sth = $dbh->prepare(<<'QUERY', { ora_auto_lob => 0 });
+select di.identifier as identifier,
+ di.id as dbid,
+ bdc.dataformat as dataformat,
+ bdc.netcdf as lob
+from tt_identifiable i,
+ tt_experiment e,
+ tt_bioassaydata_t_experimen de,
+ tt_bioassaydata d,
+ tt_identifiable di,
+ tt_biodatacube bdc,
+ pl_label l
+where i.id=e.id
+and i.id=l.mainobj_id
+and e.id=de.t_experiment_id
+and de.bioassaydata_id=d.id
+and d.id=di.id
+and d.biodatavalues_id=bdc.id
+and bdc.dataformat in ('CELv3','CELv4')
+and i.identifier!='E-TABM-185'
+QUERY
+
+$sth->execute() or die($sth->errstr());
+
+# num_procs is the maximum number of concurrent processes.
+my $manager = ArrayExpress::Tracking::QCJobManager->new({
+ ae_sth => $sth,
+ ae_dbh => $dbh,
+ num_procs => 20,
+});
+
+$manager->run();
diff --git a/automation/calculate_datahashes.pl b/automation/calculate_datahashes.pl
new file mode 100755
index 0000000..cad2b13
--- /dev/null
+++ b/automation/calculate_datahashes.pl
@@ -0,0 +1,289 @@
+#!/usr/bin/env perl
+#
+# Script to populate the tracking database with MD5 hashes of data
+# BLOBs loaded into ArrayExpress (tt_biodatacube.netcdf) for the
+# purposes of quality control. This script is designed to be run as a
+# cron job, around once per day.
+#
+# $Id: calculate_datahashes.pl 2069 2008-06-04 14:33:52Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Digest::MD5;
+use Getopt::Long;
+
+use ArrayExpress::AutoSubmission::DB::LoadedData;
+use ArrayExpress::AutoSubmission::DB::DataFormat;
+use ArrayExpress::AutoSubmission::DB::Experiment;
+use ArrayExpress::Curator::Database qw(get_ae_dbh);
+use ArrayExpress::Curator::Common qw(date_now);
+
+########
+# SUBS #
+########
+
+sub delete_unused_data {
+
+ my ( $ae_dbh ) = @_;
+
+ print qq{Scanning for recently deleted data objects...\n};
+
+ my $records = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+ is_deleted => 0,
+ );
+
+ # FIXME modify this to confirm that there's a "valid" experiment
+ # accession; i.e. not E-XXXX-n_bad or whatever. Will probably need
+ # caching a big AE query.
+ my $ae_sth = $ae_dbh->prepare(<<'QUERY');
+select identifier
+from tt_identifiable
+where identifier=?
+QUERY
+
+ while ( my $data = $records->next() ) {
+ $ae_sth->execute( $data->identifier() ) or die( $ae_sth->errstr() );
+ my $results = $ae_sth->fetchall_arrayref();
+ unless ( scalar @{ $results } ) {
+ printf( q{Deleting obsolete record "%s"...}, $data->identifier() );
+ $data->set('is_deleted' => 1);
+ $data->update();
+ print qq{ done.\n};
+ }
+ }
+
+ $ae_sth->finish();
+
+ return;
+}
+
+sub get_old_hashed_data {
+
+ print( q{Caching experiment accessions and data identifiers...} );
+
+ my $hashed_data = {};
+
+ my $dbh = ArrayExpress::AutoSubmission::DB->db_Main();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select e.accession, d.identifier
+from experiments e, loaded_data d, experiments_loaded_data ed
+where e.id=ed.experiment_id
+and d.id=ed.loaded_data_id
+and d.is_deleted=0
+QUERY
+
+ $sth->execute() or die( $sth->errstr() );
+
+ while ( my $row = $sth->fetchrow_hashref( 'NAME_lc' ) ) {
+ $hashed_data->{ $row->{'accession'} }{ $row->{'identifier'} }++;
+ }
+
+ $sth->finish();
+
+ print( qq{ done.\n} );
+
+ return $hashed_data;
+}
+
+{
+
+ my $cached_query;
+
+sub needs_hashing {
+
+ my ( $row ) = @_;
+
+ # Quick look to see if the data has been already hashed.
+ $cached_query ||= get_old_hashed_data();
+ return if $cached_query->{ $row->{'accession'} }{ $row->{'identifier'} };
+
+ # More detailed look to check linkage etc.
+ my $experiment = ArrayExpress::AutoSubmission::DB::Experiment->find_or_create({
+ accession => $row->{'accession'},
+ is_deleted => 0,
+ });
+ my %attached = map { $_->identifier() => 1 } $experiment->loaded_data();
+ if ( $attached{ $row->{'identifier'} } ) {
+
+ # Data file has been processed and is atteched.
+ return;
+ }
+
+ my @data = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+ identifier => $row->{'identifier'},
+ is_deleted => 0,
+ );
+ if ( scalar @data ) {
+ foreach my $datum ( @data ) { # Should be only one, but just in case...
+ print qq{Attaching hash "$row->{identifier}"}
+ . qq{ to experiment "$row->{accession}"...\n};
+ $experiment->add_to_loaded_data_instances({
+ loaded_data_id => $datum,
+ });
+ }
+ return;
+ }
+
+ return $experiment;
+}
+
+}
+
+sub hash_row_data {
+
+ my ( $row, $experiment, $ae_dbh ) = @_;
+
+ print( qq{Hashing data for "$row->{identifier}"...} );
+
+ my $md5 = Digest::MD5->new();
+
+ my $lob_locator = $row->{'lob'};
+ my $length = $ae_dbh->ora_lob_length( $lob_locator );
+ my $chunksize = 65536;
+
+ # Read in the LOB and add it to a Digest::MD5 object.
+ for ( my $i = 1; $i <= $length; $i += $chunksize ) {
+ my $chunk = $ae_dbh->ora_lob_read( $lob_locator, $i, $chunksize );
+ $md5->add( $chunk );
+ }
+
+ # Create records in the SQLite DB for this data file.
+ my $format = ArrayExpress::AutoSubmission::DB::DataFormat->find_or_create({
+ name => $row->{'dataformat'},
+ });
+ my $datafile = ArrayExpress::AutoSubmission::DB::LoadedData->create({
+ identifier => $row->{'identifier'},
+ data_format_id => $format,
+ md5_hash => $md5->hexdigest(),
+ is_deleted => 0,
+ needs_metrics_calculation => 1,
+ date_hashed => date_now(),
+ });
+ $datafile->add_to_loaded_data_instances({
+ experiment_id => $experiment,
+ });
+
+ print( qq{ done.\n} );
+
+ return;
+}
+
+sub import_data_hashes {
+
+ my ( $dataformat, $ae_dbh ) = @_;
+
+ my $ae_sth = $ae_dbh->prepare(<<'QUERY', { ora_auto_lob => 0 });
+select i.identifier as accession,
+ di.identifier as identifier,
+ bdc.dataformat as dataformat,
+ bdc.netcdf as lob
+from tt_identifiable i,
+ tt_experiment e,
+ tt_bioassaydata_t_experimen de,
+ tt_bioassaydata d,
+ tt_identifiable di,
+ tt_biodatacube bdc,
+ pl_label l
+where i.id=e.id
+and i.id=l.mainobj_id
+and e.id=de.t_experiment_id
+and de.bioassaydata_id=d.id
+and d.id=di.id
+and d.biodatavalues_id=bdc.id
+and bdc.dataformat=?
+and i.identifier!='E-TABM-185'
+QUERY
+
+ $ae_sth->execute( $dataformat ) or die( $ae_sth->errstr() );
+
+ ROW:
+ while ( my $row = $ae_sth->fetchrow_hashref( 'NAME_lc' ) ) {
+
+ # Skip obviously bad accessions.
+ next ROW unless ( $row->{'accession'} =~ m/\A E-[A-Z]{4}-\d+[a-zA-Z]* \z/xms );
+
+ # Skip data files we've already processed.
+
+ # First, check this experiment to see if the file has already
+ # been processed and atteched.
+ my $experiment = needs_hashing( $row );
+ next ROW unless ( $experiment );
+
+ hash_row_data( $row, $experiment, $ae_dbh );
+ }
+
+ $ae_sth->finish();
+
+ return;
+}
+
+sub parse_args {
+
+ my ( $dataformat, $updatemode );
+
+ GetOptions(
+ "f|format=s" => \$dataformat,
+ "u|update" => \$updatemode,
+ );
+
+ unless ( defined $dataformat || defined $updatemode ) {
+
+ print <<"USAGE";
+ Usage: $0 -f <data format>
+
+ This hashes all new data of the specified format.
+
+ or: $0 -u
+
+ This hashes all new data of the formats already
+ loaded into the tracking database. This will also
+ delete data hashes from the tracking database where
+ they have been deleted from ArrayExpress.
+
+ Example: $0 -f CELv3
+
+USAGE
+
+ exit 255;
+ }
+
+ # Quick sanity check - some things aren't really supported yet.
+ if ( $dataformat
+ && $dataformat =~ /\A \b (tab|whitespace) [ ]* (delimited)? \z/ixms ) {
+ die("\n I'm sorry Dave, I can't do that.\n\n (Unsupported format: $dataformat)\n\n");
+ }
+
+ return ( $dataformat, $updatemode );
+}
+
+########
+# MAIN #
+########
+
+# Autoflush STDOUT.
+$| = 1;
+
+my ( $dataformat, $updatemode ) = parse_args();
+
+my $ae_dbh = get_ae_dbh();
+
+# Run the hashing function.
+if ( defined $dataformat ) {
+ import_data_hashes( $dataformat, $ae_dbh );
+}
+elsif ( defined $updatemode ) {
+
+ # Clean the table to remove data objects which no longer exist in AE.
+ delete_unused_data( $ae_dbh );
+
+ my $format = ArrayExpress::AutoSubmission::DB::DataFormat->retrieve_all();
+ while ( defined( my $df = $format->next() ) ) {
+ printf ( qq{Looking for new data in the "%s" format...\n}, $df->name() );
+ import_data_hashes( $df->name(), $ae_dbh );
+ }
+}
diff --git a/automation/cel_qcstats.pl b/automation/cel_qcstats.pl
new file mode 100755
index 0000000..a902203
--- /dev/null
+++ b/automation/cel_qcstats.pl
@@ -0,0 +1,181 @@
+#!/usr/bin/perl --
+#
+# Script to generate QC stats for Affy CEL files. Note that the CelQC
+# module requires the R executable to be on your path; you may also
+# need to set $R_LIBS to point to a suitable library directory, since
+# Affy annotation libraries will be automatically downloaded by R as
+# appropriate.
+#
+# N.B. the first line of this script needs to point to the perl interpreter.
+#
+# $Id: cel_qcstats.pl 2087 2008-06-25 10:57:21Z tfrayner $
+
+use strict;
+use warnings;
+
+# NOTE: no "use lib" placeholder for this script, because it uses a
+# PATH specific to the LSF cluster which is maintained in the shell
+# wrapper for this script.
+
+use Getopt::Long;
+use Benchmark;
+
+use ArrayExpress::Tracking::CelQC;
+use ArrayExpress::Curator::Common qw(date_now);
+
+########
+# SUBS #
+########
+
+sub parse_args {
+
+ my ( $identifier, $celfile, $quiet, $delete_cel, $want_help );
+
+ GetOptions(
+ "i|identifier=s" => \$identifier,
+ "f|file=s" => \$celfile,
+ "q|quiet" => \$quiet,
+ "d|delete" => \$delete_cel,
+ "h|help" => \$want_help,
+ );
+
+ if ( $want_help || ! defined $celfile ) {
+ print <<"USAGE";
+
+ Usage: $0 -f <cel file>
+
+ Options: -i BioAssayData identifier, used to update the record in
+ the tracking database.
+
+ -q Suppress output, e.g. as part of a cron job.
+
+ -d Delete CEL file upon completion (also used as part of a cron job).
+
+USAGE
+
+ exit 255;
+ }
+ unless ( -r $celfile ) {
+ die(qq{Error: CEL file "$celfile" does not exist or is unreadable.\n});
+ }
+
+ return ( $identifier, $celfile, $quiet, $delete_cel );
+}
+
+sub create_record {
+
+ my ( $loaded_data, $key, $value ) = @_;
+
+ require ArrayExpress::AutoSubmission::DB::QualityMetric;
+ require ArrayExpress::AutoSubmission::DB::Platform;
+
+ if ( lc( $key ) eq 'platform' ) {
+
+ # Platform is a special case.
+ my $platform = ArrayExpress::AutoSubmission::DB::Platform->find_or_create({
+ name => $value,
+ });
+ $loaded_data->set('platform_id', $platform);
+ $loaded_data->update();
+ }
+ else {
+
+ # Everything else goes in quality_metrics. At the moment this
+ # is constrained to decimal type.
+ my $qm = ArrayExpress::AutoSubmission::DB::QualityMetric->find_or_create({
+ type => $key,
+ });
+
+ my $qm_instances = $loaded_data->quality_metric_instances(
+ quality_metric_id => $qm,
+ );
+ return if ( scalar $qm_instances );
+
+ $loaded_data->add_to_quality_metrics({
+ loaded_data_id => $loaded_data,
+ quality_metric_id => $qm,
+ value => $value,
+ date_calculated => date_now(),
+ });
+ }
+
+ return;
+}
+
+########
+# MAIN #
+########
+
+my ( $identifier, $celfile, $quiet, $delete_cel ) = parse_args();
+
+my $loaded_data;
+if ( $identifier ) {
+
+ require ArrayExpress::AutoSubmission::DB::LoadedData;
+
+ my @data = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+ identifier => $identifier,
+ is_deleted => 0,
+ );
+ unless ( scalar @data ) {
+ die(qq{Error: identifier "$identifier" not found in tracking DB});
+ }
+
+ $loaded_data = $data[0];
+
+ unless ( $loaded_data->needs_metrics_calculation() ) {
+ die(qq{Loaded data "$identifier" needs no metrics calculation.\n});
+ }
+}
+
+my $starttime = new Benchmark;
+
+my $qc = ArrayExpress::Tracking::CelQC->new({
+ input => $celfile,
+ quiet => $quiet,
+});
+
+# This may fail, in which case it will croak().
+my $result;
+eval { $result = $qc->run_metrics() };
+if ( $@ ) {
+
+ # Failure.
+ print("Error calculating metrics: $@");
+}
+else {
+
+ # Success.
+ my $endtime = new Benchmark;
+ my $timediff = timediff( $endtime, $starttime );
+ print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+ if ( $loaded_data ) {
+
+ # Write the results to the database.
+ RESULT:
+ while ( my ( $key, $value ) = each %{ $result } ) {
+ create_record( $loaded_data, $key, $value );
+ }
+ }
+ else {
+
+ foreach my $key (sort keys %$result) {
+ print STDOUT (join("\t", $key, $result->{$key}), "\n");
+ }
+ }
+}
+
+if ( $loaded_data ) {
+
+ # Set our loaded_data as having had metrics calculation (or at
+ # least an attempt therof).
+ $loaded_data->set('needs_metrics_calculation', 0);
+ $loaded_data->update();
+}
+
+# Delete the cel file if we've been told to.
+if ( $delete_cel ) {
+ unlink($celfile) or die("Error deleting CEL file: $!");
+}
+
diff --git a/automation/database_overseer.pl b/automation/database_overseer.pl
new file mode 100755
index 0000000..b9f482a
--- /dev/null
+++ b/automation/database_overseer.pl
@@ -0,0 +1,170 @@
+#!/usr/bin/env perl
+#
+# Script to perform periodic checks and maintenance on the
+# autosubmissions database. Tasks include:
+#
+# 1. Query database for all Tab2MAGE experiments which have data
+# uploaded but appear to be abandoned, email submitter and ask them to
+# complete.
+#
+# [more to be added as necessary]
+#
+# $Id: database_overseer.pl 1960 2008-02-21 12:03:19Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use MIME::Lite;
+use Date::Manip qw(ParseDate DateCalc Delta_Format);
+use Getopt::Long;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+sub email_reminder {
+
+ my $submission = shift;
+
+ my $user = $submission->user_id()->name();
+ my $type = $submission->experiment_type();
+ my $expt = $submission->name();
+ my $mailbody = (<<"MAILBODY");
+Dear $user,
+
+We are emailing you to query the status of your ongoing $type
+data submission, "$expt".
+
+Our records indicate that it has now been longer than a week since you
+last edited your experiment, and that you have successfully uploaded
+both a spreadsheet and accompanying data files. If your submission is
+now complete, please return to it and click on the final "submit" button
+to indicate to us that it is ready for processing. Until this is done,
+we will assume that your submission is not yet finished to your
+satisfaction.
+
+Best regards,
+
+The ArrayExpress Curation Team
+MAILBODY
+
+ my $mail = MIME::Lite->new(
+ From => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+ To => $submission->user_id()->email(),
+ Subject => "$type submission follow-up: $expt",
+ Encoding => 'quoted-printable',
+ Data => $mailbody,
+ Type => 'text/plain',
+ );
+
+ # Use the CONFIG SMTP server, if set.
+ if ( my $server = $CONFIG->get_AUTOSUBS_SMTP_SERVER() ) {
+ $mail->send('smtp', $server)
+ or die("Error sending reminder email: $!");
+ }
+ else {
+ $mail->send()
+ or die("Error sending reminder email: $!");
+ }
+
+ return;
+
+}
+
+########
+# MAIN #
+########
+
+my $help_wanted;
+GetOptions("h|help" => \$help_wanted,);
+
+if ($help_wanted) {
+
+ print (<<"USAGE");
+
+ Usage: $0
+
+This script is designed to be run as a cron job; it performs various periodic
+maintenance tasks on the autosubmissions database.
+
+USAGE
+
+ exit 255;
+}
+
+# Check for submissions that are (a) over a week since last edited,
+# (b) have both data files and spreadsheet uploaded, (c) are not
+# in_curation, and (d) have no date_last_processed or
+# date_last_processed is over a month ago. Email submitter to check
+# they haven't forgotten something, and set date_last_processed to
+# now.
+
+my $expt_iterator
+ = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ is_deleted => 0,
+ in_curation => 0,
+ experiment_type => 'Tab2MAGE',
+ );
+
+SUBMISSION:
+while ( my $submission = $expt_iterator->next() ) {
+
+ # Skip clearly unfinished submissions.
+ unless ( scalar $submission->spreadsheets(is_deleted => 0)
+ && scalar $submission->data_files(is_deleted => 0) ) {
+ next SUBMISSION;
+ }
+
+ # Skip test submissions, and subs without users.
+ my $user = $submission->user_id();
+ if ( ! $user || ( $user->login() eq 'test' ) ) {
+ next SUBMISSION;
+ }
+
+ my $then = ParseDate( $submission->date_last_edited() );
+ next SUBMISSION unless $then;
+
+ my $now = ParseDate( date_now() );
+ die("Error parsing date today!") unless $now;
+
+ my $delta = DateCalc( $then, $now, 1 );
+
+ # Time elapsed in weeks.
+ my $elapsed_weeks = Delta_Format( $delta, 'approx', 2, '%wt' );
+
+ # Over a week since last edit.
+ if ( $elapsed_weeks > 1 ) {
+ my $last = ParseDate( $submission->date_last_processed() );
+
+ my $elapsed_months;
+ if ($last) {
+ my $procdelta = DateCalc( $last, $now, 1 );
+ $elapsed_months = Delta_Format( $procdelta, 'approx', 2, '%Mt' );
+ }
+
+ # Either never processed, or over a month since last done.
+ if ( ! $last || ( $elapsed_months > 1 ) ) {
+
+ # Make a note for cron feedback to admin.
+ print STDOUT (
+ "Emailing ",
+ $user->login(),
+ " concerning submission ",
+ $submission->name(),
+ "\n",
+ );
+
+ # Send the email, note the date.
+ email_reminder($submission);
+ my $date = date_now();
+ $submission->set(
+ 'date_last_processed' => $date,
+ 'comment' => "Emailed reminder on $date\n\n"
+ . ($submission->comment() || q{}),
+ );
+ $submission->update();
+ }
+ }
+}
diff --git a/automation/magetab.cgi b/automation/magetab.cgi
new file mode 100755
index 0000000..f1e2dbe
--- /dev/null
+++ b/automation/magetab.cgi
@@ -0,0 +1,76 @@
+#!/usr/bin/env perl -wT
+#
+# Example instance script for tab2mage web submissions.
+#
+# $Id: submit.cgi 1595 2007-06-08 13:24:25Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::WebForm;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+ PARAMS => {
+ experiment_type => 'MAGE-TAB',
+ spreadsheet_type => 'MAGE-TAB',
+ cgi_base => '/cgi-bin/microarray/magetab.cgi',
+ stylesheet => '/microarray/tab2mage.css',
+ intro_html_template => 'magetab_intro.html',
+ upload_html_template => 'magetab_upload.html',
+ template_html_template => 'magetab_template.html',
+ sidebar_icons => [
+ {
+ image => '/microarray/aelogo.png',
+ destination => 'http://www.ebi.ac.uk/arrayexpress/',
+ alt => 'ArrayExpress',
+ },
+ {
+ image => '/microarray/MAGETAB_logo_small.png',
+ destination => 'http://tab2mage.sourceforge.net/docs/magetab_subs.html',
+ alt => 'MAGE-TAB',
+ width => 100,
+ },
+ ],
+ sidebar_links => [
+ {
+ text => 'Submitting MAGE-TAB to ArrayExpress',
+ destination => 'http://tab2mage.sourceforge.net/docs/magetab_subs.html',
+ },
+ {
+ text => 'MAGE-TAB overview',
+ destination => 'http://tab2mage.sourceforge.net/docs/magetab_docs.html',
+ },
+ {
+ text => 'IDF detailed notes',
+ destination => 'http://tab2mage.sourceforge.net/docs/idf.html',
+ },
+ {
+ text => 'SDRF detailed notes',
+ destination => 'http://tab2mage.sourceforge.net/docs/sdrf.html',
+ },
+ {
+ text => 'MAGE-TAB specification',
+ destination => 'http://www.mged.org/mage-tab/',
+ },
+ {
+ text => 'Microarray data submissions',
+ destination => 'http://www.ebi.ac.uk/microarray/submissions.html',
+ },
+ {
+ text => 'Query ArrayExpress',
+ destination => 'http://www.ebi.ac.uk/arrayexpress/',
+ },
+ {
+ text => 'Microarray group',
+ destination => 'http://www.ebi.ac.uk/microarray/',
+ },
+ ],
+ }
+);
+
+# Start the CGI script.
+$webapp->run();
diff --git a/automation/magetab_checkd.pl b/automation/magetab_checkd.pl
new file mode 100755
index 0000000..9d33dc6
--- /dev/null
+++ b/automation/magetab_checkd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate expt_check.pl running for newly-submitted MAGE-TAB experiments
+#
+# $Id: magetab_checkd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MAGETABChecker;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start checking.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MAGETABChecker->new({
+ polling_interval => 5,
+ checker_threshold => ($CONFIG->get_ERROR_INNOCENT()
+ | $CONFIG->get_ERROR_MIAME()),
+ experiment_type => 'MAGE-TAB',
+ accession_prefix => 'E-MTAB-',
+ autosubs_admin => $CONFIG->get_AUTOSUBS_ADMIN(),
+});
+
+$daemon->run();
diff --git a/automation/magetab_exportd.pl b/automation/magetab_exportd.pl
new file mode 100755
index 0000000..ffcf160
--- /dev/null
+++ b/automation/magetab_exportd.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate MAGE-ML export for MAGE-TAB experiments which
+# have passed automated checking.
+#
+# $Id: magetab_exportd.pl 1853 2007-12-13 17:53:43Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MAGETABExporter;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start exporting.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MAGETABExporter->new({
+ polling_interval => 5,
+ experiment_type => 'MAGE-TAB',
+ autosubs_admin => $CONFIG->get_AUTOSUBS_ADMIN(),
+ pipeline_subdir => 'MTAB',
+ accession_prefix => 'E-MTAB-',
+});
+
+$daemon->run();
diff --git a/automation/magetab_insert_sub.pl b/automation/magetab_insert_sub.pl
new file mode 100755
index 0000000..05e0451
--- /dev/null
+++ b/automation/magetab_insert_sub.pl
@@ -0,0 +1,121 @@
+#!/usr/bin/env perl
+#
+# Script to add a new tab2mage submission to the autosubmissions
+# system (used e.g. for FTP uploaded submissions).
+#
+# $Id: magetab_insert_sub.pl 1843 2007-12-09 21:04:53Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use ArrayExpress::AutoSubmission::DB;
+use ArrayExpress::AutoSubmission::Creator;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::MAGETAB::IDF;
+
+########
+# MAIN #
+########
+
+my ($idf, $sdrf_arg, $magetab_doc, $login, $accession);
+
+GetOptions(
+ "i|idf=s" => \$idf,
+ "s|sdrf=s" => \$sdrf_arg,
+ "m|magetab=s" => \$magetab_doc,
+ "l|login=s" => \$login,
+ "A|accession=s" => \$accession,
+);
+
+unless ( ($idf || $magetab_doc) && $login) {
+
+ print STDERR (<<"USAGE");
+Usage: $0 -i <IDF file> -l <login> <list of data file archives>
+
+ Optional arguments:
+
+ -A <accession number to assign to this submission>
+
+ -s <SDRF file>
+
+If the IDF is insufficiently well formatted for the parser to retrieve
+the SDRF filename(s), then use -s to indicate the SDRF filename.
+
+ -m <Combined IDF+SDRF document>
+
+For combined MAGE-TAB documents such as those created by the template
+generation system, use the -m option to insert.
+
+USAGE
+
+ exit 255;
+}
+
+my @sdrfs;
+if ( defined($sdrf_arg) ) {
+ push @sdrfs, $sdrf_arg;
+}
+
+# Figure out the SDRF name if not given
+if ( $idf && ! scalar @sdrfs ) {
+ my $parser = ArrayExpress::MAGETAB::IDF->new({
+ idf => $idf,
+ expt_accession => 'DUMMY',
+ in_relaxed_mode => 1,
+ });
+
+ eval {
+ my $sdrf_ref;
+ (undef, undef, $sdrf_ref) = $parser->parse();
+ @sdrfs = @{ $sdrf_ref };
+ };
+ if ($@) {
+ die( "Error parsing IDF file to retrieve SDRF filename. "
+ . "Please try using the -s option. Error was as follows:\n\n" . $@);
+ }
+ else {
+ unless ( scalar @sdrfs ) {
+ die("Error: No SDRFs found in IDF. Please use the -s option to include SDRFs.\n");
+ }
+ }
+}
+
+my $startfile = ($idf || $magetab_doc);
+
+# Quick sanity check on the file list.
+foreach my $file ($startfile, @sdrfs, @ARGV) {
+ die("Error: file not found: $file\n") unless ( defined($file) && -f $file && -r $file );
+}
+
+# Instantiate our Creator object.
+my $creator = ArrayExpress::AutoSubmission::Creator->new({
+ login => $login,
+ name => $startfile,
+ spreadsheet => $startfile,
+ data_files => [ @sdrfs, @ARGV ],
+ accession => $accession,
+ experiment_type => 'MAGE-TAB',
+ comment => 'Submission inserted manually',
+ clobber => 0,
+});
+
+# Create the experiment and insert the spreadsheet.
+my $expt = $creator->get_experiment();
+
+# Copy the files to the submissions directory.
+print STDERR ("Copying files...\n");
+$creator->insert_spreadsheet();
+$creator->insert_data_files();
+
+# Now we're all set, release the hounds.
+$expt->set(
+ status => $CONFIG->get_STATUS_PENDING(),
+ in_curation => 1,
+ num_submissions => ( $expt->num_submissions() + 1 ),
+);
+$expt->update();
diff --git a/automation/mx_autocheck_daemon.pl b/automation/mx_autocheck_daemon.pl
new file mode 100755
index 0000000..c8f1f6f
--- /dev/null
+++ b/automation/mx_autocheck_daemon.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+#
+# Script to automate expt_check.pl running for newly-submitted MX experiments
+#
+# $Id: mx_autocheck_daemon.pl 1728 2007-08-31 16:09:26Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MXChecker;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start checking.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MXChecker->new({
+ polling_interval => 5,
+ checker_threshold => ($CONFIG->get_ERROR_INNOCENT()
+ | $CONFIG->get_ERROR_MIAME()),
+ experiment_type => 'MIAMExpress',
+ accession_prefix => 'E-MEXP-',
+ autosubs_admin => $CONFIG->get_MX_AUTOSUBS_ADMIN(),
+ mx_dsn => $CONFIG->get_MX_DSN(),
+ mx_username => $CONFIG->get_MX_USERNAME(),
+ mx_password => $CONFIG->get_MX_PASSWORD(),
+ mx_dbparams => $CONFIG->get_MX_DBPARAMS(),
+});
+
+$daemon->run();
diff --git a/automation/mx_export_daemon.pl b/automation/mx_export_daemon.pl
new file mode 100755
index 0000000..21580c8
--- /dev/null
+++ b/automation/mx_export_daemon.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+# Script to automate MAGE-ML export for MIAMExpress experiments which
+# have passed automated checking.
+#
+# $Id: mx_export_daemon.pl 1728 2007-08-31 16:09:26Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::Daemon::MXExporter;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# Create the daemon object and start exporting.
+my $daemon = ArrayExpress::AutoSubmission::Daemon::MXExporter->new({
+ polling_interval => 5,
+ experiment_type => 'MIAMExpress',
+ accession_prefix => 'E-MEXP-',
+ mx_export_command => $CONFIG->get_MX_MAGEML_EXPORT_COMMAND(),
+ autosubs_admin => $CONFIG->get_MX_AUTOSUBS_ADMIN(),
+});
+
+$daemon->run();
diff --git a/automation/mx_reset_experiment.pl b/automation/mx_reset_experiment.pl
new file mode 100755
index 0000000..e59ca02
--- /dev/null
+++ b/automation/mx_reset_experiment.pl
@@ -0,0 +1,235 @@
+#!/usr/bin/env perl
+#
+# Script to reset a MIAMExpress experiment submission to pending, and
+# marking it for checking upon resubmission.
+#
+# $Id: mx_reset_experiment.pl 2013 2008-03-31 17:23:14Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use DBI;
+use Getopt::Long;
+use English qw( -no_match_vars );
+use Readonly;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now mx2tab);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+
+Readonly my $EXPT_TYPE => 'MIAMExpress';
+Readonly my $CURATION => 1;
+Readonly my $PENDING => 0;
+
+########
+# SUBS #
+########
+
+sub reset_mx_tsubmis {
+
+ my ( $subid, $new_status ) = @_;
+
+ unless ( $CONFIG->get_MX_DSN() ) {
+ warn( "Warning: MX_DSN constant not set. "
+ . "No connection made to MIAMExpress database." );
+ return;
+ }
+
+ my $dbh = DBI->connect(
+ $CONFIG->get_MX_DSN(), $CONFIG->get_MX_USERNAME(),
+ $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS()
+ )
+ or die("Database connection error: $DBI::errstr\n");
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_SYSUID, TSUBMIS_PROC_STATUS
+from TSUBMIS, TEXPRMNT
+where TSUBMIS_SYSUID=?
+and TSUBMIS_SYSUID=TEXPRMNT_SUBID
+and TSUBMIS_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+QUERY
+
+ $sth->execute($subid) or die("$sth->errstr\n");
+ my $results = $sth->fetchall_hashref('TSUBMIS_SYSUID');
+ $sth->finish();
+
+ my $count = scalar( grep { defined $_ } values %$results );
+
+ unless ( $count == 1 ) {
+ die( "Error: SubID $subid returns invalid "
+ . "number of MIAMExpress experiments: $count\n" );
+ }
+
+ my $old_status = $results->{$subid}{'TSUBMIS_PROC_STATUS'}
+ or warn("Warning: MIAMExpress TSUBMIS_PROC_STATUS is empty.\n");
+
+ $sth = $dbh->prepare(<<'QUERY');
+update TSUBMIS set TSUBMIS_PROC_STATUS=?
+where TSUBMIS_SYSUID=?
+and TSUBMIS_DEL_STATUS='U'
+QUERY
+
+ $sth->execute( $new_status, $subid ) or die("$sth->errstr\n");
+ $sth->finish();
+
+ $dbh->disconnect()
+ or die( "Database disconnection error: " . $dbh->errstr() . "\n" );
+
+ print STDOUT ( "MIAMExpress TSUBMIS table successfully updated "
+ . "($subid: changed from $old_status to $new_status).\n" );
+
+ return;
+}
+
+sub reset_accession_cache {
+
+ my ( $subid, $status, $in_curation, $experiment_type ) = @_;
+
+ # In future this will be a MAGE-TAB experiment that we reset to MIAMExpress.
+ my @experiments
+ = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ miamexpress_subid => $subid,
+ is_deleted => 0,
+ );
+
+ if ( scalar @experiments == 1 ) {
+
+ my $expt = $experiments[0];
+
+ # Sometimes submissions are forced to export; we handle the
+ # requisite MX to MAGE-TAX export here.
+ if ( $status eq $CONFIG->get_STATUS_PASSED()
+ && $expt->experiment_type() eq 'MIAMExpress' ) {
+ mx2tab($expt);
+ }
+
+ $experiment_type ||= $expt->experiment_type();
+
+ $expt->set(
+ status => $status,
+ date_last_processed => date_now(),
+ curator => getlogin,
+ in_curation => $in_curation,
+ experiment_type => $experiment_type,
+ comment => (
+ $expt->status() eq $CONFIG->get_STATUS_CRASHED()
+ ? q{}
+ : $expt->comment()
+ ),
+ );
+ $expt->update();
+ print STDOUT (
+ qq{Accession table successfully updated ($subid set to "$status").\n}
+ );
+ }
+ elsif ( scalar @experiments > 1 ) {
+ warn (
+ "Warning: Duplicate records found for SubID $subid in"
+ . " experiments table. Please fix and re-run this script.\n"
+ );
+ }
+ else {
+ warn (
+ "Warning: No submission with SubID $subid found in experiments table.\n"
+ );
+ }
+
+ return;
+}
+
+sub parse_args {
+
+ my %args;
+
+ GetOptions(
+ "p|pending" => \$args{pending},
+ "c|check" => \$args{check},
+ "q|quick" => \$args{quick},
+ "e|export" => \$args{export},
+ );
+
+ unless ( ( $args{pending} || $args{check} || $args{quick} || $args{export} ) && @ARGV) {
+ print <<"NOINPUT";
+Usage: $PROGRAM_NAME <option> <list of submission ids>
+
+Options: -c set submission for full re-checking
+ (sets experiment back to "MIAMExpress", checks database annotation and data files).
+
+ -q set submission for quick re-checking
+ (checks experiment as indicated by its current type;
+ typically this will run annotation-only checks on MX MAGE-TAB experiments).
+
+ -e set submission for MAGE-ML export without re-checking
+ (experiment is exported according to its current type).
+
+ -p set submission to pending status for user editing
+ (sets experiment back to "MIAMExpress").
+
+NOINPUT
+
+ exit 255;
+ }
+
+ return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+my $args = parse_args();
+
+foreach my $subid (@ARGV) {
+
+ if ( $args->{export} ) {
+
+ reset_mx_tsubmis( $subid, 'C' );
+ reset_accession_cache(
+ $subid,
+ $CONFIG->get_STATUS_PASSED(),
+ $CURATION,
+ );
+ }
+ elsif ( $args->{quick} ) {
+
+ reset_mx_tsubmis( $subid, 'C' );
+ reset_accession_cache(
+ $subid,
+ $CONFIG->get_STATUS_PENDING(),
+ $CURATION,
+ );
+ }
+ elsif ( $args->{check} ) {
+
+ reset_mx_tsubmis( $subid, 'C' );
+ reset_accession_cache(
+ $subid,
+ $CONFIG->get_STATUS_PENDING(),
+ $CURATION,
+ $EXPT_TYPE,
+ );
+ }
+ elsif ( $args->{pending} ) {
+
+ reset_mx_tsubmis( $subid, 'P' );
+ reset_accession_cache(
+ $subid,
+ $CONFIG->get_STATUS_PENDING(),
+ $PENDING,
+ $EXPT_TYPE,
+ );
+ }
+ else {
+ die("Error: Unrecognised user option.");
+ }
+}
+
+print STDOUT ("Done.\n\n");
+
+exit 0;
+
diff --git a/automation/populate_tracking_info.pl b/automation/populate_tracking_info.pl
new file mode 100755
index 0000000..5608948
--- /dev/null
+++ b/automation/populate_tracking_info.pl
@@ -0,0 +1,599 @@
+#!/usr/bin/env perl
+#
+# Script to populate the tracking database with information from the
+# ArrayExpress repository and data warehouse DB instances. The plan is
+# to incorporate elements of this code into a regular update
+# mechanism, running every 30 minutes or so. We also want a mechanism
+# for wiping all the imported data indiscriminately and repopulating.
+#
+# $Id: populate_tracking_info.pl 1898 2008-01-18 20:13:25Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Getopt::Long;
+use Carp;
+use List::Util qw(first);
+
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+require ArrayExpress::AutoSubmission::DB::Organism;
+require ArrayExpress::AutoSubmission::DB::Factor;
+require ArrayExpress::AutoSubmission::DB::QuantitationType;
+require ArrayExpress::AutoSubmission::DB::Event;
+
+require ArrayExpress::Tracking::Event;
+require ArrayExpress::Tracking::QueryHandler;
+
+sub update_toplevel_objects {
+
+ my ( $aedb ) = @_;
+
+ print STDOUT "Inserting new experiment records...\n";
+ my $ae_experiments = $aedb->get_experiments();
+ my @prev_expts = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ is_deleted => 0,
+ );
+ my @prev_expt_accns = map { $_->accession() } @prev_expts;
+
+ EXPT:
+ foreach my $accession ( @$ae_experiments ) {
+
+ # Don't load obviously bad accessions.
+ next EXPT unless ( $accession =~ m/\A E-[A-Z]{4}-\d+[a-zA-Z]* \z/xms );
+
+ unless ( first { defined($_) && $_ eq $accession } @prev_expt_accns ) {
+ my $expt = ArrayExpress::AutoSubmission::DB::Experiment->find_or_create({
+ accession => $accession,
+ is_deleted => 0,
+ });
+ unless ( $expt->experiment_type() ) {
+ $expt->set(
+ experiment_type => 'Unknown',
+ );
+ $expt->update();
+ }
+ }
+ }
+ print STDOUT "Inserting new array design records...\n";
+ my $ae_arrays = $aedb->get_array_designs();
+ my @prev_arrays = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+ is_deleted => 0,
+ );
+ my @prev_array_accns = map { $_->accession() } @prev_arrays;
+
+ ARRAY:
+ foreach my $accession ( @$ae_arrays ) {
+
+ # Don't load obviously bad accessions.
+ next ARRAY unless ( $accession =~ m/\A A-[A-Z]{4}-\d+[a-zA-Z]* \z/xms );
+
+ unless ( first { defined($_) && $_ eq $accession } @prev_array_accns ) {
+ ArrayExpress::AutoSubmission::DB::ArrayDesign->find_or_create({
+ accession => $accession,
+ is_deleted => 0,
+ });
+ }
+ }
+
+ return;
+}
+
+sub delete_cached_data {
+
+ my ( $aedb, $todo ) = @_;
+
+ # The aedb query object contains information on what *can* be
+ # repopulated - and therefore deleted here.
+
+ # Delete all events associated with the database instances of interest.
+ if ( $todo->{events} ) {
+ foreach my $instance ( @{ $aedb->get_instances() } ) {
+ print STDOUT ("Deleting old event data imported from $instance...\n");
+
+ # FIXME consider trying this without using the iterator
+ # approach? It might be faster.
+ ArrayExpress::AutoSubmission::DB::Event->search(
+ target_db => $instance,
+ )->delete_all();
+ }
+ }
+
+ # Delete metadata associated with experiments, array designs.
+ if ( $todo->{metadata} ) {
+ delete_cached_metadata( $aedb );
+ }
+
+ return;
+}
+
+sub delete_cached_metadata {
+
+ my ( $aedb ) = @_;
+
+ # Don't delete expt species (or anything else) for experiments not
+ # loaded... but then how to mark these as being updateable upon
+ # loading?
+
+ # Experiments.
+ my $ae_experiments = $aedb->get_experiments();
+
+ my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ is_deleted => 0,
+ );
+
+ EXPT:
+ while ( my $expt = $expt_iterator->next() ) {
+ next EXPT unless ( first { $_ eq $expt->accession() } @$ae_experiments );
+
+ printf STDOUT ("Deleting metadata for %s...\n", $expt->accession());
+
+ # Setting release_date = undef is particularly important, sinc
+ # it is used to flag objects needing update.
+ $expt->set(
+ submitter_description => undef,
+ curated_name => undef,
+ num_samples => undef,
+ num_hybridizations => undef,
+ has_raw_data => undef,
+ has_processed_data => undef,
+ release_date => undef,
+ is_released => undef,
+ ae_miame_score => undef,
+ ae_data_warehouse_score => undef,
+ );
+
+ $expt->array_design_instances()->delete_all();
+ $expt->organism_instances()->delete_all();
+ $expt->factor_instances()->delete_all();
+ $expt->quantitation_type_instances()->delete_all();
+
+ $expt->update();
+ }
+
+ # Array Designs.
+ my $ae_arrays = $aedb->get_array_designs();
+
+ my $array_iterator = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+ is_deleted => 0,
+ );
+
+ EXPT:
+ while ( my $array = $array_iterator->next() ) {
+ next EXPT unless ( first { $_ eq $array->accession() } @$ae_arrays );
+
+ printf STDOUT ("Deleting metadata for %s...\n", $array->accession());
+
+ # Setting release_date = undef is particularly important, sinc
+ # it is used to flag objects needing update.
+ $array->set(
+ release_date => undef,
+ is_released => undef,
+ );
+
+ $array->organism_instances()->delete_all();
+
+ $array->update();
+ }
+
+ return;
+}
+
+sub update_events {
+
+ my ( $object, $aedb ) = @_;
+
+ # $object can be an experiment or array_design.
+
+ unless ( $object->accession() ) {
+ croak("Error: update_events called with an invalid experiment object (no accession).");
+ }
+
+ # N.B. we *do* process unloaded objects here.
+
+ my $events = $aedb->get_events( $object->accession() );
+
+ foreach my $event ( @{ $events } ) {
+ $object->add_to_events({
+ event_type => $event->get_event_type(),
+ was_successful => $event->get_success(),
+ source_db => $event->get_source_db(),
+ target_db => $event->get_target_db(),
+ start_time => $event->get_starttime(),
+ end_time => $event->get_endtime(),
+ machine => $event->get_machine(),
+ operator => $event->get_operator(),
+ log_file => $event->get_log_file(),
+ jobregister_dbid => $event->get_jobregister_dbid(),
+ comment => $event->get_comment(),
+ is_deleted => 0,
+ });
+ }
+
+ return;
+}
+
+sub update_expt_metadata {
+
+ my ( $expt, $aedb ) = @_;
+
+ # FIXME not implemented: ae_data_warehouse_score.
+
+ my $acc = $expt->accession();
+
+ unless ( $acc ) {
+ croak("Error: update_expt_metadata called with an invalid experiment object (no accession).");
+ }
+
+ # Don't process unloaded experiments.
+ return unless ( $aedb->get_is_loaded( $acc ) );
+
+ my $has_metadata = $expt->release_date();
+
+ # Always update the date info, as it will change.
+ $expt->set(
+ release_date => ( $aedb->get_release_date($acc) || 0 ),
+ );
+ $expt->set(
+ is_released => $aedb->get_is_released($acc),
+ );
+ $expt->set(
+ in_data_warehouse => $aedb->get_expt_in_data_warehouse($acc),
+ );
+
+ unless ( $expt->curated_name() ) {
+ $expt->set(
+ curated_name => $aedb->get_curated_name($acc),
+ );
+ }
+
+ # Skip metadata update for experiments having a release date (this
+ # is a fairly arbitrary shortcut to reduce processing time).
+ unless ( defined $has_metadata ) {
+ update_heavy_expt_queries( $expt, $aedb );
+ }
+
+ # Save any changes.
+ $expt->update();
+
+ return;
+}
+
+sub update_heavy_expt_queries {
+
+ my ( $expt, $aedb ) = @_;
+
+ my $acc = $expt->accession();
+
+ unless ( $acc ) {
+ croak("Error: update_heavy_expt_queries called with an invalid experiment object (no accession).");
+ }
+
+ # Only update the following if the metadatum is not present.
+ unless ( defined ($expt->submitter_description()) ) {
+ $expt->set(
+ submitter_description => $aedb->get_submitter_description($acc),
+ );
+ }
+ unless ( defined ($expt->num_samples()) ) {
+ $expt->set(
+ num_samples => $aedb->get_num_samples($acc),
+ );
+ }
+ unless ( defined ($expt->num_hybridizations()) ) {
+ $expt->set(
+ num_hybridizations => $aedb->get_num_hybridizations($acc),
+ );
+ }
+ unless ( defined ($expt->has_raw_data()) ) {
+ $expt->set(
+ has_raw_data => $aedb->get_has_raw_data($acc),
+ );
+ }
+ unless ( defined ($expt->has_processed_data()) ) {
+ $expt->set(
+ has_processed_data => $aedb->get_has_processed_data($acc),
+ );
+ }
+ unless ( defined ($expt->ae_miame_score()) ) {
+ $expt->set(
+ ae_miame_score => $aedb->get_ae_miame_score($acc),
+ );
+ }
+
+ # FIXME where do we even get this from?
+# unless ( defined ($expt->ae_data_warehouse_score()) ) {
+# $expt->set(
+# ae_data_warehouse_score => $aedb->get_ae_data_warehouse_score($acc),
+# );
+# }
+ unless ( scalar ($expt->organisms()) ) {
+ my $species_list = $aedb->get_expt_species($acc);
+ foreach my $species ( @$species_list ) {
+ next unless defined ( $species );
+ my $organism = ArrayExpress::AutoSubmission::DB::Organism->find_or_create({
+ scientific_name => $species,
+ is_deleted => 0,
+ });
+ $expt->add_to_organism_instances({
+ experiment_id => $expt,
+ organism_id => $organism,
+ });
+ }
+ }
+ unless ( scalar ($expt->factors()) ) {
+ my $factor_list = $aedb->get_expt_factors($acc);
+ foreach my $factor_name ( @$factor_list ) {
+ next unless defined ( $factor_name );
+ my $factor = ArrayExpress::AutoSubmission::DB::Factor->find_or_create({
+ name => $factor_name,
+ is_deleted => 0,
+ });
+ $expt->add_to_factor_instances({
+ experiment_id => $expt,
+ factor_id => $factor,
+ });
+ }
+ }
+ unless ( scalar ($expt->quantitation_types()) ) {
+ my $qt_list = $aedb->get_expt_qts($acc);
+ foreach my $qt_name ( @$qt_list ) {
+ next unless defined ( $qt_name );
+ my $qt = ArrayExpress::AutoSubmission::DB::QuantitationType->find_or_create({
+ name => $qt_name,
+ is_deleted => 0,
+ });
+ $expt->add_to_quantitation_type_instances({
+ experiment_id => $expt,
+ quantitation_type_id => $qt,
+ });
+ }
+ }
+ unless ( scalar ($expt->array_designs()) ) {
+ my $array_list = $aedb->get_expt_arrays($acc);
+ foreach my $array_acc ( @$array_list ) {
+ next unless defined ( $array_acc );
+ my $array_design = ArrayExpress::AutoSubmission::DB::ArrayDesign->find_or_create({
+ accession => $array_acc,
+ is_deleted => 0,
+ });
+ $expt->add_to_array_design_instances({
+ experiment_id => $expt,
+ array_design_id => $array_design,
+ });
+ }
+ }
+
+ # Save any changes.
+ $expt->update();
+
+ return;
+}
+
+sub update_array_metadata {
+
+ my ( $array, $aedb ) = @_;
+
+ # FIXME not implemented: annotation_source, annotation_version, biomart_table_name.
+
+ my $acc = $array->accession();
+
+ unless ( $acc ) {
+ croak("Error: update_array_metadata called with an invalid array design object (no accession).");
+ }
+
+ # Don't process unloaded array_designs.
+ return unless ( $aedb->get_is_loaded( $acc ) );
+
+ my $has_metadata = $array->release_date();
+
+ $array->set(
+ release_date => ( $aedb->get_release_date($acc) || 0 ),
+ );
+ $array->set(
+ is_released => $aedb->get_is_released($acc),
+ );
+ $array->set(
+ in_data_warehouse => $aedb->get_array_in_data_warehouse($acc),
+ );
+
+ # Skip metadata update for array designs having a release date
+ # (this is a fairly arbitrary shortcut to reduce processing time).
+ unless ( defined $has_metadata ) {
+ update_heavy_array_queries( $array, $aedb );
+ }
+
+ # Save any changes.
+ $array->update();
+
+ return;
+}
+
+sub update_heavy_array_queries {
+
+ my ( $array, $aedb ) = @_;
+
+ my $acc = $array->accession();
+
+ unless ( $acc ) {
+ croak("Error: update_heavy_array_queries called with an invalid array design object (no accession).");
+ }
+
+ unless ( scalar ($array->organisms()) ) {
+ my $species_list = $aedb->get_array_species($acc);
+ foreach my $species ( @$species_list ) {
+ my $organism = ArrayExpress::AutoSubmission::DB::Organism->find_or_create({
+ scientific_name => $species,
+ is_deleted => 0,
+ });
+ $array->add_to_organism_instances({
+ array_design_id => $array,
+ organism_id => $organism,
+ });
+ }
+ }
+
+ # Save any changes.
+ $array->update();
+
+ return;
+}
+
+sub max_job_dbids {
+
+ my ( $aedb ) = @_;
+
+ my %last_jobid;
+ foreach my $instance ( @{ $aedb->get_instances() } ) {
+ $last_jobid{$instance} =
+ ArrayExpress::AutoSubmission::DB::Event
+ ->sql_last_jobid('target_db')->select_val($instance);
+ }
+
+ return \%last_jobid;
+}
+
+sub update_unfinished_events {
+
+ my ( $aedb ) = @_;
+
+ my $event_iterator = ArrayExpress::AutoSubmission::DB::Event->search(
+ was_successful => undef,
+ end_time => undef,
+ is_deleted => 0,
+ );
+
+ while ( my $event = $event_iterator->next() ) {
+ my ( $endtime, $success ) = $aedb->get_updated_event_data(
+ $event->jobregister_dbid(),
+ $event->target_db(),
+ );
+
+ if ( defined( $endtime ) || defined( $success ) ) {
+ printf STDOUT ("Updating event %s...\n", $event->jobregister_dbid());
+ $event->set(
+ end_time => $endtime,
+ was_successful => $success,
+ );
+ $event->update();
+ }
+ }
+
+ return;
+}
+
+# Separate autosubs DB queries from AE/AEDW queries. Transparently
+# cache data (e.g. jobregister) retrieved from the AE databases.
+
+# Optional autosubs clearing step; don't delete qt, factor or
+# array_design instances, just the links to experiment.
+
+# Iterate over experiments, array designs and query the cached AE data
+# for results.
+
+my ( $repopulating, $event_only, $metadata_only, $want_help );
+
+GetOptions(
+ "R|repopulate" => \$repopulating,
+ "E|event-only" => \$event_only,
+ "M|metadata-only" => \$metadata_only,
+ "h|help" => \$want_help,
+);
+
+if ( $want_help ) {
+
+ print <<"USAGE";
+
+ Usage: populate_tracking_info.pl
+
+ Optional arguments:
+
+ -R Wipe the old tracking data and repopulate.
+ -E Only update Event data.
+ -M Only update Experiment and Array metadata.
+ -h Print this help text.
+
+USAGE
+
+ exit 255;
+}
+
+# This hash is inspected to figure out which operations need to be
+# performed.
+my %todo = ( # N.B. careful about how you add entries to this hash.
+ events => (not $metadata_only),
+ metadata => (not $event_only),
+);
+
+# One single ArrayExpress::Tracking::QueryHandler query object used
+# throughout. We want to use the largest jobregister_dbid as
+# last_jobid here so that updates work correctly.
+my $aedb = ArrayExpress::Tracking::QueryHandler->new();
+unless ( $repopulating ) {
+ my $last_jobid = max_job_dbids( $aedb );
+ $aedb->set_last_jobid( $last_jobid );
+}
+
+# The first thing to do is to make sure we have a full list of
+# every experiment and array design in the AE databases.
+update_toplevel_objects( $aedb );
+
+# FIXME we should consider wiping the release_date and is_released
+# data every time we re-run the metadata updates.
+
+# If we want a full repopulation we delete the old cached data here.
+if ( $repopulating ) {
+
+ print("WARNING: You have chosen to delete the following information, and repopulate from the AE databases:\n\n");
+ print(" " . join("; ", grep { $todo{$_} } keys %todo) . "\n\nProceed (Y/N)? ");
+
+ chomp( my $choice = lc <STDIN> );
+
+ unless( $choice eq 'y' ) {
+ print("User terminated script execution.\n");
+ exit 255;
+ }
+
+ delete_cached_data( $aedb, \%todo );
+}
+
+# Update experiments here.
+my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ is_deleted => 0,
+);
+
+EXPT:
+while ( my $expt = $expt_iterator->next() ) {
+
+ # Skip experiments without assigned accessions.
+ next EXPT unless $expt->accession();
+
+ printf STDOUT ("Updating experiment %s...\n", $expt->accession());
+
+ update_events( $expt, $aedb ) if $todo{events};
+ update_expt_metadata( $expt, $aedb ) if $todo{metadata};
+}
+
+# Update array designs here.
+my $array_iterator = ArrayExpress::AutoSubmission::DB::ArrayDesign->search(
+ is_deleted => 0,
+);
+
+ARRAY_DESIGN:
+while ( my $array_design = $array_iterator->next() ) {
+
+ # Skip array designs without assigned accessions.
+ next ARRAY_DESIGN unless $array_design->accession();
+
+ printf STDOUT ("Updating array design %s...\n", $array_design->accession());
+
+ update_events( $array_design, $aedb ) if $todo{events};
+ update_array_metadata( $array_design, $aedb ) if $todo{metadata};
+}
+
+# Finally, update any previously-unfinished events.
+update_unfinished_events( $aedb ) if $todo{events};
+
diff --git a/automation/reset_experiment.pl b/automation/reset_experiment.pl
new file mode 100755
index 0000000..be57721
--- /dev/null
+++ b/automation/reset_experiment.pl
@@ -0,0 +1,168 @@
+#!/usr/bin/env perl
+#
+# Script to reset a Tab2MAGE experiment submission to pending, and
+# marking it for checking upon resubmission.
+#
+# $Id: reset_experiment.pl 2014 2008-04-01 15:03:56Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use English qw( -no_match_vars );
+use Readonly;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+
+# These values are assumed to be numeric later.
+Readonly my $CURATION => 1;
+Readonly my $PENDING => 0;
+
+########
+# SUBS #
+########
+
+sub reset_accession_cache {
+
+ my ( $id, $expt_type, $status, $in_curation ) = @_;
+
+ my @experiments
+ = ArrayExpress::AutoSubmission::DB::Experiment->search_like(
+ id => $id,
+ experiment_type => $expt_type,
+ is_deleted => 0,
+ );
+
+ if ( scalar @experiments == 1 ) {
+
+ my $experiment = $experiments[0];
+
+ # We can't set MX experiments to pending, so we don't attempt it.
+ if ( $experiment->miamexpress_subid() && $in_curation == $PENDING ) {
+ die("Error: This script is unable to set MIAMExpress experiments"
+ . " back to pending. Please use the mx_reset_experiment.pl script.\n");
+ }
+
+ $experiment->set(
+ status => $status,
+ date_last_processed => date_now(),
+ curator => getlogin,
+ in_curation => $in_curation,
+ comment => (
+ $experiment->status() eq $CONFIG->get_STATUS_CRASHED()
+ ? q{}
+ : undef
+ ),
+ );
+ $experiment->update();
+ printf STDOUT (
+ qq{Accession table successfully updated (%s_%i set to "%s").\n},
+ $experiment->experiment_type(),
+ $experiment->id(),
+ $experiment->status(),
+ );
+ if ( $in_curation ) {
+ print STDOUT (qq{Experiment is in curation.\n});
+ }
+ else {
+ print STDOUT (qq{Experiment is pending.\n});
+ }
+ }
+ elsif ( scalar @experiments > 1 ) {
+ print STDERR (
+ "Error: Multiple $expt_type submissions with ID $id found in accession table. Skipping.\n"
+ );
+ }
+ else {
+ print STDERR (
+ "Error: No $expt_type submission with ID $id found in accession table. Skipping.\n"
+ );
+ }
+
+ return;
+}
+
+sub parse_args {
+
+ my %args;
+
+ GetOptions(
+ "p|pending" => \$args{pending},
+ "c|check" => \$args{check},
+ "e|export" => \$args{export},
+ );
+
+ unless ( ( $args{pending} || $args{check} || $args{export} ) && @ARGV) {
+ print <<"NOINPUT";
+Usage: $PROGRAM_NAME <option> <list of submission directories>
+
+Options: -c set submission for immediate re-checking
+ -e set submission for MAGE-ML export without re-checking
+ -p set submission to pending status for user editing
+
+ Note that -p will not work for MIAMExpress submissions exported to MAGE-TAB;
+ for such submissions use the mx_reset_experiment.pl script.
+
+NOINPUT
+
+ exit 255;
+ }
+
+ return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+my $args = parse_args();
+
+SUBMISSION:
+foreach my $dirname (@ARGV) {
+
+ my ( $expt_type, $id ) = ($dirname =~ m/\A (.+) \_ (\d+) \z/xms);
+
+ unless ( $expt_type && $id ) {
+ warn("Error: cannot parse directory name $dirname. Skipping.\n");
+ next SUBMISSION;
+ }
+
+ if ( $args->{export} ) {
+ reset_accession_cache(
+ $id,
+ $expt_type,
+ $CONFIG->get_STATUS_PASSED(),
+ $CURATION,
+ );
+ }
+ elsif ( $args->{check} ) {
+ reset_accession_cache(
+ $id,
+ $expt_type,
+ $CONFIG->get_STATUS_PENDING(),
+ $CURATION,
+ );
+ }
+ elsif ( $args->{pending} ) {
+ reset_accession_cache(
+ $id,
+ $expt_type,
+ $CONFIG->get_STATUS_PENDING(),
+ $PENDING,
+ );
+ }
+ else {
+ die("Error: Unrecognised user option.");
+ }
+}
+
+print STDOUT ("Done.\n\n");
+
+exit 0;
+
diff --git a/automation/t2m_insert_sub.pl b/automation/t2m_insert_sub.pl
new file mode 100755
index 0000000..782bbe8
--- /dev/null
+++ b/automation/t2m_insert_sub.pl
@@ -0,0 +1,77 @@
+#!/usr/bin/env perl
+#
+# Script to add a new tab2mage submission to the autosubmissions
+# system (used e.g. for FTP uploaded submissions).
+#
+# $Id: t2m_insert_sub.pl 1843 2007-12-09 21:04:53Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use ArrayExpress::AutoSubmission::DB;
+use ArrayExpress::AutoSubmission::Creator;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+
+########
+# MAIN #
+########
+
+my ($spreadsheet, $login, $accession);
+
+GetOptions(
+ "e|spreadsheet=s" => \$spreadsheet,
+ "l|login=s" => \$login,
+ "A|accession=s" => \$accession,
+);
+
+unless ($spreadsheet && $login) {
+
+ print STDERR (<<"USAGE");
+Usage: $0 -e <spreadsheet> -l <login> <list of data file archives>
+
+ Optional argument:
+
+ -A <accession number to assign to this submission>
+
+USAGE
+
+ exit 255;
+}
+
+# Quick sanity check on the file list.
+foreach my $file ($spreadsheet, @ARGV) {
+ die("Error: file not found: $file\n") unless ( -f $file && -r $file );
+}
+
+# Instantiate our Creator object.
+my $creator = ArrayExpress::AutoSubmission::Creator->new({
+ login => $login,
+ name => $spreadsheet,
+ spreadsheet => $spreadsheet,
+ data_files => \@ARGV,
+ accession => $accession,
+ experiment_type => 'Tab2MAGE',
+ comment => 'Submission inserted manually',
+ clobber => 0,
+});
+
+# Create the experiment and insert the spreadsheet.
+my $expt = $creator->get_experiment();
+
+# Copy the files to the submissions directory.
+print STDERR ("Copying files...\n");
+$creator->insert_spreadsheet();
+$creator->insert_data_files();
+
+# Now we're all set, release the hounds.
+$expt->set(
+ status => $CONFIG->get_STATUS_PENDING(),
+ in_curation => 1,
+ num_submissions => ( $expt->num_submissions() + 1 ),
+);
+$expt->update();
diff --git a/automation/tab2mage.cgi b/automation/tab2mage.cgi
new file mode 100755
index 0000000..f4b0f6a
--- /dev/null
+++ b/automation/tab2mage.cgi
@@ -0,0 +1,61 @@
+#!/usr/bin/env perl -wT
+#
+# Example instance script for tab2mage web submissions.
+#
+# $Id: tab2mage.cgi 1606 2007-06-15 15:08:56Z tfrayner $
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use ArrayExpress::AutoSubmission::WebForm;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+ PARAMS => {
+ experiment_type => 'Tab2MAGE',
+ spreadsheet_type => 'Tab2MAGE',
+ cgi_base => '/cgi-bin/microarray/tab2mage.cgi',
+ stylesheet => '/microarray/tab2mage.css',
+ sidebar_icons => [
+ {
+ image => '/microarray/aelogo.png',
+ destination => 'http://www.ebi.ac.uk/arrayexpress/',
+ alt => 'ArrayExpress',
+ },
+ {
+ image => '/microarray/T2M_logo_small.png',
+ destination => 'http://tab2mage.sourceforge.net/',
+ alt => 'Tab2MAGE',
+ width => 100,
+ },
+ ],
+ sidebar_links => [
+ {
+ text => 'Introduction',
+ destination => 'http://www.ebi.ac.uk/miamexpress/help/tab2mage_help.html',
+ },
+ {
+ text => 'Creating a spreadsheet',
+ destination => 'http://tab2mage.sourceforge.net/docs/spreadsheet.html',
+ },
+ {
+ text => 'Microarray data submissions',
+ destination => 'http://www.ebi.ac.uk/microarray/submissions.html',
+ },
+ {
+ text => 'Query ArrayExpress',
+ destination => 'http://www.ebi.ac.uk/arrayexpress/',
+ },
+ {
+ text => 'Microarray group',
+ destination => 'http://www.ebi.ac.uk/microarray/',
+ },
+ ],
+ }
+);
+
+# Start the CGI script.
+$webapp->run();
diff --git a/bin/convert_to_mcmr.pl b/bin/convert_to_mcmr.pl
new file mode 100755
index 0000000..525aa92
--- /dev/null
+++ b/bin/convert_to_mcmr.pl
@@ -0,0 +1,116 @@
+#!/usr/bin/env perl
+#
+# Script to use Tab2MAGE routines to convert data files to MC/MR/C/R format.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: convert_to_mcmr.pl 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+# Added to circumvent weird Exporter bug in 5.8.3 which caused
+# crashing when requiring ArrayExpress::Datafile (which in turn uses
+# ArrayExpress::Curator::Common).
+use ArrayExpress::Curator::Common;
+
+require ArrayExpress::Datafile;
+
+unless (@ARGV) {
+
+ print STDERR <<USAGE;
+Usage:
+
+ convert_to_mcmr.pl <list of files to convert>
+
+Converted files are named with a ".fixed" suffix.
+
+Supported formats:
+
+ GenePix
+ Agilent
+ ScanAlyze
+ ArrayVision
+ ScanArray
+ QuantArray
+ Spotfinder
+ BlueFuse
+ UCSFSpot
+ Nimblegen (use at your own risk)
+ CodeLink
+ AppliedBiosystems
+
+USAGE
+
+ exit;
+
+}
+
+FILENAME:
+foreach my $filename (@ARGV) {
+
+ my $file = ArrayExpress::Datafile->new({
+ name => $filename,
+ data_type => 'raw',
+ array_design_id => 'UNKNOWN',
+ });
+
+ unless ( -T $filename ) {
+ warn("ERROR: File $filename appears to be binary. Skipping.\n");
+ next FILENAME;
+ }
+
+ # Figure out what kind of file we have, get column headings and indices
+ $file->parse_header();
+
+ print STDOUT (
+ "File $filename is " . $file->get_format_type() . " format.\n" );
+
+ unless ( @{ $file->get_column_headings() } ) {
+ print STDERR (
+ "Error: No column headings for file " . $file->get_name() . "\n" );
+ }
+
+ # Convert the file to MC/MR/C/R coordinates. The file continues to
+ # be referred to by the $input_fh filehandle.
+ ( $file->get_format_type() eq 'Generic' ) && do {
+ print STDOUT (" File needs no conversion. Skipping.\n");
+ next FILENAME;
+ };
+
+ my $rc = $file->fix_known_text_format();
+
+ # Success
+ if ($rc) {
+
+ my $input_fh = $file->get_filehandle();
+
+ # Output the column headings and data
+ open (my $output_fh, '>', "$filename.fixed")
+ or die("Error opening output file $filename.fixed: $!\n");
+
+ print $output_fh ( join( "\t", @{ $file->get_column_headings() } ),
+ "\n" );
+
+ while ( my $line = <$input_fh> ) { print $output_fh $line; }
+
+ close ($output_fh) or die("Error closing filehandle: $!\n");
+
+ }
+
+ # Failure
+ else {
+
+ # Fallback error report
+ warn( "ERROR: Unrecognized file format "
+ . $file->get_format_type()
+ . ". Skipping.\n" );
+ next FILENAME;
+
+ }
+
+}
diff --git a/bin/expt_check.pl b/bin/expt_check.pl
new file mode 100755
index 0000000..126a1c2
--- /dev/null
+++ b/bin/expt_check.pl
@@ -0,0 +1,454 @@
+#!/usr/bin/env perl
+#
+# expt_check.pl - Tim Rayner 2006 ArrayExpress team, EBI
+# See "expt_check.pl -h" for usage information.
+#
+# $Id: expt_check.pl 1709 2007-08-22 12:52:09Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../index.html">
+ <img src="T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Script detail: expt_check.pl</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+expt_check.pl - a script to check experiment data files submitted to MIAMExpress
+
+=head1 SYNOPSIS
+
+=over 2
+
+=item B<Tab2MAGE mode:>
+
+ expt_check.pl -e <Tab2MAGE spreadsheet>
+
+=item B<MAGE-TAB mode:>
+
+ expt_check.pl -i <IDF file>
+
+=item B<MIAMExpress mode:>
+
+ expt_check.pl -l <login name> -t <experiment title>
+
+=item B<Standalone mode:>
+
+ expt_check.pl -s <list of data files>
+
+ (use -A or -a to check files against an array design in standalone mode).
+
+=back
+
+=head1 DESCRIPTION
+
+This script can be used to check experimental data for submission to
+ArrayExpress in a few different ways. For Tab2MAGE submissions it
+parses the Tab2MAGE spreadsheet format and reports on problems with
+data files and MIAME metadata. Used in conjunction with a local
+MIAMExpress installation the script takes the experiment title and the
+submitter login name, and checks the submitted data files for
+errors. Several log files are written to the relevant MIAMExpress
+submission directory unless the B<-p> option is used to redirect them
+to the current directory. Normally the script will be able to figure
+out which array design to use from the Tab2MAGE spreadsheet or by
+querying the MIAMExpress database. An optional ADF filename argument
+may also be provided using the B<-a> option, and ArrayExpress
+accession numbers may be specified using the B<-A> option. Specifying
+your own ADFs/accession numbers will make the script ignore any array
+designs pointed to by the spreadsheet or database entry for the
+experiment.
+
+=head1 QUANTITATION TYPES
+
+Known QuantitationTypes are listed in a separate file or files,
+created with a simple tab-delimited format. The layout of
+these files is described in L<ArrayExpress::Datafile::QT_list>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-e> C<spreadsheet filename>
+
+The Tab2MAGE spreadsheet to be checked.
+
+=item B<-i> C<IDF filename>
+
+The MAGE-TAB IDF file to be checked.
+
+=item B<-l> C<login name>
+
+The MIAMExpress login name of the experiment submitter
+
+=item B<-t> C<experiment title>
+
+The MIAMExpress title of the experiment, surrounded by quotes if the title contains spaces.
+
+=item B<-a> C<ADF filename>
+
+The B<-a> switch designates the ADF filename to be used for all the
+hybridizations in the experiment. This option overrides any database
+links between hybridizations and array designs, and is provided
+initially as a convenience.
+
+=item B<-A> C<Array accession number (ArrayExpress)>
+
+Use the B<-A> switch to indicate the accession number of an
+ArrayExpress array design to be used for checking the data
+files. Ordinarily this should not be needed, as the script should be
+able to link the MIAMExpress submission with ArrayExpress array
+designs automatically.
+
+=item B<-c>
+
+Forces overwriting of existing files ("clobber"). If this switch is
+omitted the user will be asked whether to overwrite already existing
+files.
+
+=item B<-p>
+
+Write to files in present working directory ("pwd"). The default is to
+write to files in the submission directory.
+
+=item B<-s>
+
+Standalone option. The script will check the files listed on the
+command line rather than connecting to MIAMExpress and
+ArrayExpress. When used with the B<-e> or B<-i> options, the Tab2MAGE
+or MAGE-TAB document is checked but no connection is made to ArrayExpress to
+retrieve array information. To check features and reporter identifiers
+with this option, an ADF must be specified with the B<-a> option or an
+ArrayExpress accession number can be used with the B<-A> option. In
+the latter case a connection is made to ArrayExpress without
+connecting to MIAMExpress.
+
+=item B<-d> C<directory>
+
+Source directory. This indicates the directory to search for data
+files. This option is only used for Tab2MAGE submissions checks, as
+MIAMExpress defines its own directory structure which is automatically
+searched by this script. If this option is omitted, only the current
+working directory is searched for Tab2MAGE submission data files.
+
+=item B<-x>
+
+Skip data file checking. This option can be used to quickly check
+experiment annotation without having to wait for the script to
+validate all the data files.
+
+=item B<-q> C<QT filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined in the
+ArrayExpress::Curator::Config module. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-Q> C<QT filename>
+
+QuantitationType file. This option will add the new QuantitationType
+definitions to those included with the Tab2MAGE package. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-R> C<namespace>
+
+Prefix of the Reporter identifier to use when checking data files
+against array designs. This prefix is added to each "Reporter
+Identifier" in the data files prior to comparison with the actual
+identifiers in the ADF. The default is to assume MIAMExpress-like
+identifiers.
+
+=item B<-C> C<namespace>
+
+Prefix of the CompositeSequence identifier to use when checking data
+files against array designs. This prefix is added to each
+"CompositeSequence Identifier" in the data files prior to comparison
+with the actual identifiers in the ADF. The default is to assume
+MIAMExpress-like identifiers.
+
+=item B<-L>
+
+Ignore the data file size limit as configured in Config.yml (i.e.,
+MAX_DATAFILE_SIZE).
+
+=item B<-m>
+
+The checker will support MAGE-TAB documents in which a single IDF and
+SDRF have been combined (in that order), with the start of each
+section marked by [IDF] and [SDRF] respectively. Note that such
+documents are not compliant with the MAGE-TAB format specification;
+this format is used by ArrayExpress to simplify data submissions.
+
+=item B<-v>
+
+Prints the version number of the script.
+
+=item B<-h>
+
+Prints a short help text.
+
+=back
+
+=head1 TESTS
+
+There are numerous tests performed by this script. Listed below are
+the tests which are performed on each data file. There is also a
+series of checks which are made on any MIAME metadata supplied to the
+script. This metadata may be in the form of a Tab2MAGE spreadsheet, or
+an experiment submission in a local MIAMExpress database. Please see
+L<ArrayExpress::Curator::ExperimentChecker/"TESTS"> for a list of
+general tests performed on all submissions. See also
+L<ArrayExpress::Curator::Validate/"TESTS"> for the Tab2MAGE
+spreadsheet tests, L<ArrayExpress::MAGETAB::Checker/"TESTS"> for
+MAGE-TAB document checking, and
+L<ArrayExpress::Curator::MIAMExpress/"TESTS"> for more information on
+the MIAMExpress tests.
+
+=head1 LOCAL MIAMEXPRESS INSTALLATIONS
+
+Users wishing to use this script with local installations of
+MIAMExpress should check the ArrayExpress::Curator::Config module
+and change whatever parameters are necessary.
+
+=head1 SEE ALSO
+
+=over 1
+
+=item L<ArrayExpress::Curator::ExperimentChecker>
+
+=item L<ArrayExpress::Curator::Validate>
+
+=item L<ArrayExpress::MAGETAB::Checker>
+
+=item L<ArrayExpress::Curator::MIAMExpress>
+
+=item L<ArrayExpress::Datafile::QT_list>
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=head1 BUGS
+
+Affymetrix CHP files are not currently supported. Moreover, the script
+will not check the features referred to in CEL files against an array
+design until I can figure out how to retrieve a list of valid features
+in a timely fashion. CompositeSequence IDs in final data matrix
+files from Affymetrix submissions are fully supported and checked
+against the array design. Basic data quality calculations (percent
+null, Benfords law) are made on Affymetrix CEL and FGEM data.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use strict;
+use warnings;
+
+use Benchmark;
+use Pod::Usage;
+use Getopt::Long qw(:config no_ignore_case);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+###############
+# Subroutines #
+###############
+
+sub parse_args {
+
+ my ( %args, $want_help, $want_version );
+ GetOptions(
+ "a|adf=s" => \$args{adf_filename},
+ "e|edf=s" => \$args{edf_filename},
+ "i|idf=s" => \$args{idf_filename},
+ "A|accession=s" => \$args{array_accession},
+ "l|login=s" => \$args{login},
+ "t|title=s" => \$args{title},
+ "p|pwd:s" => \$args{pwd_log},
+ "c|clobber" => \$args{clobber},
+ "x|skip" => \$args{skip_data_checks},
+ "s|standalone" => \$args{standalone},
+ "d|source=s" => \$args{source_directory},
+ "q|qtfile=s" => \$args{qtfile},
+ "Q|QTFILE=s" => \$args{QTFILE},
+ "R|reporter=s" => \$args{reporter_prefix},
+ "C|compseq=s" => \$args{compseq_prefix},
+ "m|magetab=s" => \$args{magetab_doc},
+ "L|large-files" => \$args{ignore_size_limits},
+ "h|help" => \$want_help,
+ "v|version" => \$want_version,
+ );
+
+ if ($want_version) {
+ print STDERR (
+ "This is ", $CONFIG->get_EXPTCHECK_PROGNAME(),
+ " v", $CONFIG->get_EXPTCHECK_VERSION(), "\n\n"
+ );
+ exit 255;
+ }
+
+ if ($want_help) {
+ pod2usage(
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 1,
+ );
+ }
+
+ unless ( ( $args{login} && $args{title} )
+ || $args{standalone}
+ || $args{idf_filename}
+ || $args{edf_filename}
+ || $args{magetab_doc} ) {
+ pod2usage(
+ -message => 'Please see "expt_check.pl -h" for further help notes.',
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 0,
+ );
+ }
+
+ # FIXME this could be better.
+ if ( ( $args{idf_filename} || $args{edf_filename} || $args{magetab_doc} )
+ && ( $args{login} || $args{title} ) ) {
+ die("Error: ambiguous options used (mixed MIAMExpress and Tab2MAGE/MAGE-TAB).\n\n");
+ }
+
+ # Clean up this path a bit, make sure it's absolute
+ if ( $args{adf_filename} ) {
+ $args{adf_filename} = File::Spec->rel2abs( $args{adf_filename} );
+ die("Error: ADF file $args{adf_filename} is not readable")
+ unless ( -f $args{adf_filename} );
+ }
+
+ return ( \%args );
+}
+
+# End subroutines
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args();
+
+# Initialise some stuff.
+my $starttime = new Benchmark;
+
+# Arguments common to all ExperimentChecker objects.
+my %common_args = (
+ is_standalone => $args->{standalone},
+ adf_filename => $args->{adf_filename},
+ array_accession => $args->{array_accession},
+ log_to_current_dir => $args->{pwd_log},
+ clobber => $args->{clobber},
+ qt_filename => ( $args->{qtfile} || $args->{QTFILE} ),
+ include_default_qts => defined( $args->{QTFILE} ),
+ skip_data_checks => $args->{skip_data_checks},
+ source_directory => $args->{source_directory},
+ reporter_prefix => $args->{reporter_prefix},
+ compseq_prefix => $args->{compseq_prefix},
+ ignore_size_limits => $args->{ignore_size_limits},
+);
+
+my $checker;
+unless ( $args->{standalone}
+ || $args->{edf_filename}
+ || $args->{idf_filename}
+ || $args->{magetab_doc} ) {
+
+ # MIAMExpress checking.
+ require ArrayExpress::Curator::MIAMExpress;
+ $checker = ArrayExpress::Curator::MIAMExpress->new({
+ mx_login => $args->{login},
+ mx_title => $args->{title},
+ %common_args,
+ });
+}
+else {
+
+ if ( $args->{standalone}
+ && ! $args->{edf_filename}
+ && ! $args->{idf_filename}
+ && ! $args->{magetab_doc} ) {
+
+ # Standalone checking.
+ require ArrayExpress::Curator::Standalone;
+ $checker = ArrayExpress::Curator::Standalone->new({
+ data_files => \@ARGV,
+ %common_args,
+ });
+ }
+ elsif ( $args->{edf_filename} ) {
+
+ # Tab2MAGE checking.
+ require ArrayExpress::Curator::Validate;
+ $checker = ArrayExpress::Curator::Validate->new({
+ spreadsheet_filename => $args->{edf_filename},
+ %common_args,
+ });
+ }
+ elsif ( $args->{idf_filename} ) {
+
+ # MAGE-TAB checking.
+ require ArrayExpress::MAGETAB::Checker;
+ $checker = ArrayExpress::MAGETAB::Checker->new({
+ idf => $args->{idf_filename},
+ %common_args,
+ });
+ }
+ elsif ( $args->{magetab_doc} ) {
+
+ # MAGE-TAB (combined IDF+SDRF) checking.
+ require ArrayExpress::MAGETAB::Checker;
+ $checker = ArrayExpress::MAGETAB::Checker->new({
+ magetab_doc => $args->{magetab_doc},
+ %common_args,
+ });
+ }
+ else {
+ die("Error: Ambiguous command-line options used.\n");
+ }
+}
+
+$checker->check();
+
+my $endtime = new Benchmark;
+my $timediff = timediff( $endtime, $starttime );
+print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+exit $checker->get_error();
diff --git a/bin/magetab.pl b/bin/magetab.pl
new file mode 100755
index 0000000..ef793b3
--- /dev/null
+++ b/bin/magetab.pl
@@ -0,0 +1,361 @@
+#!/usr/bin/env perl
+#
+# magetab.pl
+#
+# A script to generate MAGE-ML from a MAGE-TAB document and set of
+# datafiles.
+#
+# Tim Rayner 2007 ArrayExpress Team, EBI
+#
+# $Id: magetab.pl 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use English qw( -no_match_vars );
+use Getopt::Long qw(:config no_ignore_case);
+use File::Spec;
+use Readonly;
+use Pod::Usage;
+use Benchmark;
+
+use ArrayExpress::MAGETAB;
+use ArrayExpress::Curator::Visualize qw( dot_and_png );
+
+Readonly my $APP_NAME => 'MAGETabulator';
+
+sub parse_args {
+
+ my %args;
+
+ my ( $want_version, $want_help );
+
+ GetOptions(
+ "i|idf=s" => \$args{idf},
+ "n|accession=s" => \$args{expt_accession},
+ "t|target=s" => \$args{target_directory},
+ "d|source=s" => \$args{source_directory},
+ "q|qtfile=s" => \$args{QT_file},
+ "Q|QTFILE=s" => \$args{included_QTs},
+ "k|all-qts" => \$args{keep_all_qts},
+ "K|keep-accns" => \$args{keep_protocol_accns},
+ "R|reporter=s" => \$args{reporter_prefix},
+ "C|compseq=s" => \$args{compseq_prefix},
+ "s|standalone" => \$args{is_standalone},
+ "P|plain-text" => \$args{text_datafiles},
+ "L|large-files" => \$args{ignore_size_limits},
+ "x|skip-data" => \$args{skip_datafiles},
+ "m|magetab=s" => \$args{magetab_doc},
+ "f|font=s" => \$args{font},
+ "v|version" => \$want_version,
+ "h|help" => \$want_help,
+ "c|clobber" => \$args{clobber},
+ );
+
+ if ($want_version) {
+ print STDERR "This is $APP_NAME v$ArrayExpress::MAGETAB::VERSION\n\n";
+ exit 255;
+ }
+
+ if ($want_help) {
+ pod2usage(
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 1,
+ );
+ }
+
+ unless ( ( $args{idf} || $args{magetab_doc} )
+ && $args{target_directory} && $args{expt_accession} ) {
+ pod2usage(
+ -message => 'Please see "magetab.pl -h" for further help notes.',
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 0,
+ );
+ }
+
+ return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+# Get our command-line options.
+my $args = parse_args();
+
+# Note the starting time.
+my $starttime = new Benchmark;
+
+# Initialise our parser object.
+my %parser_opts = (
+ authority => 'ebi.ac.uk',
+ namespace => 'MAGETabulator',
+ expt_accession => $args->{expt_accession},
+ output_file => "$args->{expt_accession}.xml",
+ target_directory => $args->{target_directory},
+ source_directory => $args->{source_directory},
+ is_standalone => $args->{is_standalone},
+ qt_filename => ( $args->{QT_file} || $args->{included_QTs} ),
+ include_default_qts => defined( $args->{included_QTs} ),
+ keep_all_qts => $args->{keep_all_qts},
+ keep_protocol_accns => $args->{keep_protocol_accns},
+ reporter_prefix => $args->{reporter_prefix},
+ compseq_prefix => $args->{compseq_prefix},
+ use_plain_text => $args->{text_datafiles},
+ ignore_size_limits => $args->{ignore_size_limits},
+ skip_datafiles => $args->{skip_datafiles},
+ clobber => $args->{clobber},
+ protocol_accession_prefix => 'P-MTAB-',
+);
+
+my $magetab;
+if ( $args->{idf} ) {
+ $magetab = ArrayExpress::MAGETAB->new({
+ idf => $args->{idf},
+ %parser_opts,
+ });
+}
+elsif ( $args->{magetab_doc} ) {
+ $magetab = ArrayExpress::MAGETAB->new({
+ magetab_doc => $args->{magetab_doc},
+ %parser_opts,
+ });
+ $magetab->parse_magetab_doc();
+}
+else {
+ die("Error: Either IDF or MAGETAB doc required by script.");
+}
+
+# Parse the files and write out the MAGE-ML
+$magetab->write_mageml();
+
+# Create graphs
+my $graphfile_base = File::Spec->catfile(
+ $args->{target_directory},
+ $args->{expt_accession},
+);
+
+my $classes = {
+ 'source' => 'Source',
+ 'sample' => 'Sample',
+ 'extract' => 'Extract',
+ 'labeledextract' => 'Labeled Extract',
+ 'pba' => 'Hybridization',
+ 'mba' => 'Feature Extraction',
+ 'datamatrix_mba' => 'Array Data',
+ 'dba' => 'Normalization',
+ 'datamatrix_dba' => 'Array Data',
+};
+
+$args->{clobber} = dot_and_png(
+ $magetab->get_bags(),
+ $graphfile_base,
+ $args->{font},
+ $magetab->get_clobber(),
+ $classes,
+);
+
+# We're done. Write out our benchmark info and exit.
+print STDOUT ("Finished.\n");
+
+my $endtime = new Benchmark;
+my $timediff = timediff( $endtime, $starttime );
+print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../index.html">
+ <img src="MAGETAB_logo.png"
+ border="0" height="50" alt="MAGE-TAB logo"></td>
+ </a>
+ <td class="pagetitle">Script detail: magetab.pl</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+magetab.pl - a script to generate valid MAGE-ML from an input MAGE-TAB
+document.
+
+=head1 SYNOPSIS
+
+ magetab.pl -i <IDF filename> -n <experiment accession> -t <target directory>
+
+=head1 DESCRIPTION
+
+This script will process a MAGE-TAB document and a set of data files
+to generate valid MAGE-ML. The supported data file formats are listed
+in the accompanying documentation. The MAGE-TAB format specification
+may be downloaded from this link:
+
+ http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz
+
+There is one auxilliary file which can be supplied alongside the data
+files. This file simply defines the QuantitationTypes which are to be
+extracted from the data files. This QuantitationType file is optional,
+however, as the script is supplied with a set of defaults determined
+by a survey of incoming QuantitationTypes by ArrayExpress
+curators. The script generates a log file in the target directory
+which details which columns have been ignored. To keep all
+unrecognized QuantitationTypes, invoke the script with the B<-k>
+option.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-i> C<IDF>
+
+The MAGE-TAB IDF file to be parsed.
+
+=item B<-n> C<accession>
+
+The experiment accession to be used as the top-level Experiment
+identifier in the generated MAGE-ML.
+
+=item B<-t> C<directory>
+
+The target directory to be created. This directory will contain the
+MAGE-ML file and external data files ready for validation.
+
+=item B<-q> C<QT filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined as part of
+the Tab2MAGE package. See L<ArrayExpress::Datafile::QT_list> for more
+information.
+
+=item B<-Q> C<QT filename>
+
+QuantitationType file. This option will add the new QuantitationType
+definitions to those included with the Tab2MAGE package. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-k>
+
+Keep all columns in the data files, regardless of whether they are
+recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.
+
+=item B<-K>
+
+If the autosubmissions system is configured, magetab.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.
+
+=item B<-s>
+
+Standalone option. This prevents the script from attempting to connect
+to ArrayExpress to retrieve array information.
+
+=item B<-x>
+
+Skip data file processing during MAGE-ML generation. The script will
+attempt to generate MAGE-ML for the metadata contained in the IDF and
+SDRF only.
+
+=item B<-R> C<namespace>
+
+Reporter identifier prefix. By default the script uses the MIAMExpress
+convention for generating reporter identifiers. This option allows you
+to override this behaviour by supplying an alternate prefix for
+identifiers.
+
+=item B<-C> C<namespace>
+
+CompositeSequence identifier prefix. By default the script uses the
+MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.
+
+=item B<-d> C<directory>
+
+Source directory containing all the data files referenced in the
+SDRF. If this is omitted, the current working directory will be
+searched for data files.
+
+=item B<-P>
+
+By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used for
+ArrayExpress submissions, this option allows for such files to be
+directly downloadable from the ArrayExpress web interface. However, in
+unusual circumstances when you might wish to use plain-text
+encoding for these data files, use this option.
+
+=item B<-L>
+
+Ignore the data file size limit as configured in Config.yml (i.e.,
+MAX_DATAFILE_SIZE).
+
+=item B<-m>
+
+The parser will support MAGE-TAB documents in which a single IDF and
+SDRF have been combined (in that order), with the start of each
+section marked by [IDF] and [SDRF] respectively. Note that such
+documents are not compliant with the MAGE-TAB format specification;
+this format is used by ArrayExpress to simplify data submissions.
+
+=item B<-f> C<font name>
+
+Name of the font to be used for Graphviz-generated PNGs.
+
+=item B<-c>
+
+Overwrite preexisting files ("clobber" option).
+
+=item B<-v>
+
+Prints the version number of the script.
+
+=item B<-h>
+
+Print a short help text summarizing these options.
+
+=back
+
+=head1 QUANTITATIONTYPE FILE
+
+Please see L<ArrayExpress::Datafile::QT_list> for a description of
+the format of this file.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2007.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
diff --git a/bin/parse_affy.pl b/bin/parse_affy.pl
new file mode 100755
index 0000000..840b3dd
--- /dev/null
+++ b/bin/parse_affy.pl
@@ -0,0 +1,249 @@
+#!/usr/bin/env perl
+#
+# Script to parse Affymetrix data files.
+# Shows example usage of ArrayExpress::Datafile::Affymetrix modules.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: parse_affy.pl 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+=pod
+
+=head1 NAME
+
+parse_affy.pl - an example Affymetrix data file parsing script.
+
+=head1 SYNOPSIS
+
+ parse_affy.pl -i <Affymetrix data file (CEL or CHP)>
+ -o <tab-delimited data matrix output file>
+
+=head1 DESCRIPTION
+
+This script is a 'toy' script to illustrate the use of the
+ArrayExpress::Datafile::Affymetrix parsing module supplied with the
+Tab2MAGE distribution. It may nonetheless be useful in circumstances
+where other Affymetrix parsers fail. The script reads data files (CEL
+and CHP) in both old (version 3, GDAC) and new (version 4, XDA/GCOS)
+formats. The script then outputs any combination of the following: (a)
+a tab-delimited data matrix, (b) a set of MAGE identifiers making up
+the DesignElementDimension of the matrix, and/or (c) a set of
+identifiers comprising the QuantitationTypeDimension of the data
+matrix. Please see L<ArrayExpress::Datafile::Affymetrix> for more
+information on the Affymetrix parsing API.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-i> C<input filename>
+
+Affymetrix CEL or CHP data file to parse.
+
+=item B<-o> C<output filename>
+
+File to be used for output (tab-delimited data).
+
+=item B<-d> C<DED filename>
+
+File to which the DesignElementDimension is to be written (optional).
+
+=item B<-q> C<QTD filename>
+
+File to which the QuantitationTypeDimension is to be written
+(optional).
+
+=item B<-c> C<input CDF filename>
+
+Affymetrix library CDF file to use for CHP file parsing.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Getopt::Long;
+use IO::File;
+use Pod::Usage;
+use ArrayExpress::Datafile::Affymetrix;
+use ArrayExpress::Datafile::Binary qw(get_integer);
+
+sub parse_args {
+
+ my (%args, $want_help);
+ GetOptions(
+ "i|infile=s" => \$args{infile},
+ "o|outfile=s" => \$args{outfile},
+ "d|dedfile=s" => \$args{dedfile},
+ "q|qtdfile=s" => \$args{qtdfile},
+ "c|cdf=s" => \$args{cdf},
+ "h|help" => \$want_help,
+ );
+
+ if ($want_help) {
+ pod2usage(
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 1,
+ );
+ }
+
+ unless ( $args{infile}
+ && ( $args{outfile} || $args{dedfile} || $args{qtdfile} ) ) {
+ pod2usage(
+ -message => 'Please see "parse_affy.pl -h" for further help notes.',
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 0,
+ );
+ }
+ return \%args;
+}
+
+########
+# MAIN #
+########
+
+# Get our arguments
+my $args = parse_args();
+my $affy_factory = ArrayExpress::Datafile::Affymetrix->new();
+
+# Deal with CDF if supplied (XDA only for now)
+my $cdf;
+if ( $args->{cdf} ) {
+ $cdf = $affy_factory->make_parser($args->{cdf});
+ $cdf->parse();
+}
+
+# Open our file for input
+#my $infile = IO::File->new( $args->{infile}, '<' )
+# or die("Error: cannot open input file $args->{infile}: $!\n");
+#binmode($infile);
+
+SWITCH: {
+ ( $args->{infile} =~ m/\.CEL$/i ) && do { # CEL file
+
+ my $cel = $affy_factory->make_parser( $args->{infile} );
+
+ $cel->parse();
+ print( "Algorithm: ", $cel->get_algorithm, "\n" );
+ print( "NumCells: ", $cel->get_num_cells, "\n" );
+
+ while ( my ( $key, $value ) = each %{ $cel->get_parameters() } ) {
+ print("$key: $value\n");
+ }
+
+ # Output
+ $args->{outfile} && do {
+ my $outfile = IO::File->new( $args->{outfile}, '>' )
+ or die(
+ "Error: cannot open data output file $args->{outfile}: $!\n");
+ $cel->export($outfile);
+ };
+
+ $args->{dedfile} && do {
+ my $dedfile = IO::File->new( $args->{dedfile}, '>' )
+ or die(
+ "Error: cannot open DED output file $args->{dedfile}: $!\n");
+ foreach my $identifier ( @{ $cel->get_ded() } ) {
+ print $dedfile ("$identifier\n");
+ }
+ };
+
+ $args->{qtdfile} && do {
+ my $qtdfile = IO::File->new( $args->{qtdfile}, '>' )
+ or die(
+ "Error: cannot open QTD output file $args->{qtdfile}: $!\n");
+ foreach my $identifier ( @{ $cel->get_qtd() } ) {
+ print $qtdfile ("$identifier\n");
+ }
+ };
+
+ last SWITCH;
+ };
+
+ ( $args->{infile} =~ m/\.CHP$/i ) && do { # CHP file
+
+ my $chp = $affy_factory->make_parser( $args->{infile} );
+
+ unless ($cdf) {
+ die( "Unable to export CHP data: no CDF supplied. "
+ . "Please use the -c option to specify a CDF filename.\n"
+ );
+ }
+
+ $chp->parse();
+
+ # Output
+ $args->{outfile} && do {
+ my $outfile = IO::File->new( $args->{outfile}, '>' )
+ or die(
+ "Error: cannot open data output file $args->{outfile}: $!\n");
+ $chp->export( $outfile, $cdf );
+ };
+
+ $args->{dedfile} && do {
+ my $dedfile = IO::File->new( $args->{dedfile}, '>' )
+ or die(
+ "Error: cannot open DED output file $args->{dedfile}: $!\n");
+ foreach my $identifier ( @{ $chp->get_ded($cdf) } ) {
+ print $dedfile ("$identifier\n");
+ }
+ };
+
+ $args->{qtdfile} && do {
+ my $qtdfile = IO::File->new( $args->{qtdfile}, '>' )
+ or die(
+ "Error: cannot open QTD output file $args->{qtdfile}: $!\n");
+ foreach my $identifier ( @{ $chp->get_qtd() } ) {
+ print $qtdfile ("$identifier\n");
+ }
+ };
+
+ last SWITCH;
+ };
+
+ ( $args->{infile} =~ m/\.EXP$/i ) && do { # EXP file
+
+ my $exp = $affy_factory->make_parser( $args->{infile} );
+
+ $exp->parse();
+
+ # Open our file for output
+ my $outfile = IO::File->new( $args->{outfile}, '>' )
+ or die("Error: cannot open file $args->{outfile}: $!\n");
+
+ $exp->export($outfile);
+
+ last SWITCH;
+ };
+
+ die("Error: Unknown file extension.\n");
+}
+
diff --git a/bin/t2m_visualize.pl b/bin/t2m_visualize.pl
new file mode 100755
index 0000000..aaca75b
--- /dev/null
+++ b/bin/t2m_visualize.pl
@@ -0,0 +1,181 @@
+#!/usr/bin/env perl
+#
+# t2m_vizualizer.pl
+#
+# A script to create a graph from a Tab2MAGE spreadsheet, allowing the
+# user to visualize the relationships between biomaterials and
+# hybridizations.
+#
+# Tim Rayner 2005 ArrayExpress Team, EBI
+#
+# $Id: t2m_visualize.pl 1519 2007-04-19 13:39:57Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../index.html">
+ <img src="T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Script detail: t2m_visualize.pl</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+t2m_visualize.pl - a rapid visualization script for Tab2MAGE
+spreadsheets.
+
+=head1 SYNOPSIS
+
+ t2m_visualize.pl -e <Tab2MAGE spreadsheet file>
+
+=head1 DESCRIPTION
+
+This script simply parses a Tab2MAGE spreadsheet into a format
+readable by the Graphviz "dot" application. If Graphviz has been
+installed the script then attempts to generate a PNG file. This PNG
+file contains a graph which represents the experiment which has been
+coded. Both the tab2mage.pl and expt_check.pl scripts include this
+functionality, but t2m_visualize.pl has two advantages: it does not
+need the data files to be present, and it works much faster.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-e> C<spreadsheet filename>
+
+The Tab2MAGE spreadsheet to visualize.
+
+=item B<-f> C<font name>
+
+Font to use in graph generation (optional).
+
+=item B<-v>
+
+Print the version of the string.
+
+=item B<-c>
+
+Overwrite ("clobber") existing files.
+
+=item B<-h>
+
+Prints a short help text.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Getopt::Long;
+use Pod::Usage;
+
+use ArrayExpress::Curator::Tab2MAGE;
+use ArrayExpress::Curator::MAGE qw(unique_identifier);
+use ArrayExpress::Curator::Visualize qw(dot_and_png);
+
+###########
+# GLOBALS #
+###########
+#
+# Used to track the creation of unique objects.
+#
+
+my $PROGRAMNAME = "T2M_Visualizer";
+my $VERSION = "1.2";
+
+my $logfile = lc($PROGRAMNAME) . ".log";
+
+########
+# MAIN #
+########
+
+my ( $spreadsheet, $want_version, $font, $clobber, $want_help );
+
+GetOptions(
+ "e|edf=s" => \$spreadsheet,
+ "f|font=s" => \$font,
+ "v|version" => \$want_version,
+ "c|clobber" => \$clobber,
+ "h|help" => \$want_help,
+);
+
+if ($want_version) {
+ print STDERR "This is $PROGRAMNAME v$VERSION\n\n";
+ exit 255;
+}
+
+if ($want_help) {
+ pod2usage(
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 1,
+ );
+}
+
+unless ($spreadsheet) {
+ pod2usage(
+ -message => 'Please see "t2m_visualize.pl -h" for further help notes.',
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 0,
+ );
+}
+
+my $tab2mage = ArrayExpress::Curator::Tab2MAGE->new({
+ spreadsheet_filename => $spreadsheet,
+ target_directory => q{.},
+});
+
+# Parse the EDF
+$tab2mage->read_edf( \*STDOUT );
+
+# Create and link the mage objects
+foreach my $datarow (@{ $tab2mage->get_hyb_section() } ) {
+ $tab2mage->create_mage_objects( $datarow, unique_identifier );
+}
+
+# Create the dotfile
+my $output_base = $spreadsheet;
+$output_base =~ s/\.\w{3}$//;
+$output_base ||= lc($PROGRAMNAME); # Just in case
+
+$clobber = dot_and_png( $tab2mage->get_bags(), $output_base, $font, $clobber );
+
+exit $tab2mage->get_error();
diff --git a/bin/tab2mage.pl b/bin/tab2mage.pl
new file mode 100755
index 0000000..0e9968e
--- /dev/null
+++ b/bin/tab2mage.pl
@@ -0,0 +1,388 @@
+#!/usr/bin/env perl
+#
+# tab2mage.pl
+#
+# A script to generate MAGE-ML from a set of datafiles. A template
+# spreadsheet file (tab-delimited text) is required.
+#
+# Tim Rayner 2004 ArrayExpress Team, EBI
+#
+# $Id: tab2mage.pl 1734 2007-09-05 15:41:17Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use English qw( -no_match_vars );
+use Benchmark;
+use File::Spec;
+use Getopt::Long qw(:config no_ignore_case);
+use Pod::Usage;
+
+use ArrayExpress::Curator::Tab2MAGE;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Visualize qw(dot_and_png);
+
+########
+# SUBS #
+########
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../index.html">
+ <img src="T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Script detail: tab2mage.pl</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+tab2mage.pl - a script to produce valid MAGE-ML from a set of
+raw datafiles and a summary spreadsheet of defined format.
+
+=head1 SYNOPSIS
+
+ tab2mage.pl -e <spreadsheet_filename> -t <target_directory>
+
+=head1 DESCRIPTION
+
+This script is designed to take a set of unprocessed raw datafiles and
+a spreadsheet providing the experiment metadata and generate valid
+MAGE-ML. The supported data file formats are listed in the
+accompanying documentation.
+
+Important: the data files must each contain a column heading line
+immediately preceding the start of the data. Typically, raw
+unprocessed data files have this as standard. Do not remove this line,
+as the script will use it to determine which QuantitationTypes to keep
+and which to discard.
+
+There are two auxilliary files which can be supplied alongside the
+data files. The first, experiment summary spreadsheet describes the
+way in which the experiment was performed. The second simply defines
+the QuantitationTypes which are to be extracted from the data
+files. This QuantitationType file is optional, however, as the script
+is supplied with a set of defaults determined by a survey of incoming
+QuantitationTypes by ArrayExpress curators. The script generates a log
+file in the target directory which details which columns have been
+ignored.
+
+=head1 EXPERIMENT SUMMARY FILE
+
+The experiment summary file has a flexible format based around a set
+of predefined column headers. Comments may be inserted using the '#'
+character at the start of any line. The file consists of three
+sections, each of which ends with a blank line:
+
+=head2 Experiment section
+
+This section contains top-level information about the experiment, such
+as the title, description and accession number. The section is
+constructed in two columns, with row names as described in
+L<ArrayExpress::Curator::MAGE::Definitions/"SUPPORTED HEADINGS">.
+
+=head2 Protocol section
+
+Protocols are defined as needed in this section. The section is
+organized into rows, with column headings as described in
+L<ArrayExpress::Curator::MAGE::Definitions/"SUPPORTED HEADINGS">. If all of the protocols
+used in the experiment have previously been loaded into ArrayExpress
+and given accession numbers, this whole section can be omitted.
+
+=head2 Hybridization section
+
+This section contains the bulk of the experiment information. At its
+simplest, each row describes the route taken from BioSource to output
+data file.
+
+Column headings for BioMaterialCharacteristics and FactorValue should
+be provided in the following form:
+
+=over 2
+
+=item BioMaterialCharacteristics[<MGED Ontology Category>]
+
+=item FactorValue[<MGED Ontology Category>]
+
+=back
+
+Each of these headings should contain a valid Category subclass from
+the MGED ontology. The values in the columns must likewise be valid
+ontology entry values for these subclasses.
+
+Multichannel (e.g., two-colour) data can be described by
+entering each channel as a separate line. Pooling can be described at
+multiple levels by using as many lines as necessary to describe all
+the relationships between upstream and downstream samples.
+
+In a sense the Hybridization table can be compared to an SQL database
+table in the way that it provides links between MAGE objects. Again,
+the recognized column headings for this section are described in
+L<ArrayExpress::Curator::MAGE::Definitions/"SUPPORTED HEADINGS">.
+
+=head1 OPTIONS
+
+=over 2
+
+=item B<-e> C<filename>
+
+The Tab2MAGE spreadsheet to be checked.
+
+=item B<-t> C<directory>
+
+The target directory to be created. This directory will contain the
+MAGE-ML file and external data files ready for validation.
+
+=item B<-n> C<accession>
+
+Normally the script uses the experiment accession number from the
+spreadsheet to be parsed. In cases where no accession has been entered
+in the spreadsheet, or you wish to override that accession, use this
+option.
+
+=item B<-q> C<QT filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined as part of
+the Tab2MAGE package. See L<ArrayExpress::Datafile::QT_list> for more
+information.
+
+=item B<-Q> C<QT filename>
+
+QuantitationType file. This option will add the new QuantitationType
+definitions to those included with the Tab2MAGE package. See
+L<ArrayExpress::Datafile::QT_list> for more information.
+
+=item B<-k>
+
+Keep all columns in the data files, regardless of whether they are
+recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.
+
+=item B<-K>
+
+If the autosubmissions system is configured, tab2mage.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.
+
+=item B<-s>
+
+Standalone option. This prevents the script from attempting to connect
+to ArrayExpress to retrieve array information.
+
+=item B<-R> C<namespace>
+
+Reporter identifier prefix. By default the script uses the MIAMExpress
+convention for generating reporter identifiers. This option allows you
+to override this behaviour by supplying an alternate prefix for
+identifiers.
+
+=item B<-C> C<namespace>
+
+CompositeSequence identifier prefix. By default the script uses the
+MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.
+
+=item B<-d> C<directory>
+
+Source directory containing all the data files referenced in the tab2mage
+spreadsheet. If this is omitted, the current working directory will be
+searched for data files.
+
+=item B<-P>
+
+By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used for
+ArrayExpress submissions, this option allows for such files to be
+directly downloadable from the ArrayExpress web interface. However, in
+unusual circumstances when you might wish to use plain-text
+encoding for these datafile, use this option.
+
+=item B<-L>
+
+Ignore the data file size limit as configured in Config.yml (i.e.,
+MAX_DATAFILE_SIZE).
+
+=item B<-f> C<font name>
+
+Name of the font to be used for Graphviz-generated PNGs.
+
+=item B<-c>
+
+Overwrite preexisting files ("clobber" option).
+
+=item B<-v>
+
+Prints the version number of the script.
+
+=item B<-h>
+
+Print a short help text summarizing these options.
+
+=back
+
+=head1 QUANTITATIONTYPE FILE
+
+Please see L<ArrayExpress::Datafile::QT_list> for a description of
+the format of this file.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. Particular credit
+goes to Ele Holloway, who was responsible for curating the lists of
+QuantitationTypes included with this script.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+###########
+# GLOBALS #
+###########
+#
+# Used to track the creation of unique objects.
+#
+
+my $APP_NAME = $CONFIG->get_TAB2MAGE_PROGNAME();
+my $VERSION = $CONFIG->get_TAB2MAGE_VERSION();
+
+###############
+# SUBROUTINES #
+###############
+
+sub parse_args {
+
+ my %args;
+
+ my ( $want_version, $want_help );
+
+ GetOptions(
+ "e|edf=s" => \$args{spreadsheet_filename},
+ "t|target=s" => \$args{target_directory},
+ "n|accession=s" => \$args{expt_accession},
+ "d|source=s" => \$args{source_directory},
+ "q|qtfile=s" => \$args{QT_file},
+ "Q|QTFILE=s" => \$args{included_QTs},
+ "k|all-qts" => \$args{keep_all_qts},
+ "s|standalone" => \$args{is_standalone},
+ "R|reporter=s" => \$args{reporter_prefix},
+ "C|compseq=s" => \$args{compseq_prefix},
+ "P|plain-text" => \$args{text_datafiles},
+ "L|large-files" => \$args{ignore_size_limits},
+ "f|font=s" => \$args{font},
+ "v|version" => \$want_version,
+ "c|clobber" => \$args{clobber},
+ "h|help" => \$want_help,
+ "K|keep-accns" => \$args{keep_protocol_accns},
+ );
+
+ if ($want_version) {
+ print STDERR "This is $APP_NAME v$VERSION\n\n";
+ exit 255;
+ }
+
+ if ($want_help) {
+ pod2usage(
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 1,
+ );
+ }
+
+ unless ( $args{spreadsheet_filename} && $args{target_directory} ) {
+ pod2usage(
+ -message => 'Please see "tab2mage.pl -h" for further help notes.',
+ -exitval => 255,
+ -output => \*STDERR,
+ -verbose => 0,
+ );
+ }
+
+ return \%args;
+
+}
+
+########
+# MAIN #
+########
+
+# Get our command-line options.
+my $args = parse_args();
+
+# Initialise some stuff.
+my $starttime = new Benchmark;
+
+# Generate the MAGE objects, output the MAGE-ML, and return the hash
+# of MAGE container objects and the experiment accession for further
+# processing (e.g. visualisation).
+my $exporter = ArrayExpress::Curator::Tab2MAGE->new({
+ spreadsheet_filename => $args->{spreadsheet_filename},
+ target_directory => $args->{target_directory},
+ source_directory => $args->{source_directory},
+ is_standalone => $args->{is_standalone},
+ clobber => $args->{clobber},
+ qt_filename => ( $args->{QT_file} || $args->{included_QTs} ),
+ include_default_qts => defined( $args->{included_QTs} ),
+ keep_all_qts => $args->{keep_all_qts},
+ use_plain_text => $args->{text_datafiles},
+ keep_protocol_accns => $args->{keep_protocol_accns},
+ reporter_prefix => $args->{reporter_prefix},
+ compseq_prefix => $args->{compseq_prefix},
+ ignore_size_limits => $args->{ignore_size_limits},
+ external_accession => $args->{expt_accession},
+});
+
+my ($mage, $accession, $error) = $exporter->write_mageml();
+
+# Create graphs
+my $graphfile_base
+ = File::Spec->catfile( $args->{target_directory}, $accession );
+
+$args->{clobber} = dot_and_png(
+ $exporter->get_bags(),
+ $graphfile_base,
+ $args->{font},
+ $exporter->get_clobber(),
+);
+
+print STDOUT ("Finished.\n");
+
+my $endtime = new Benchmark;
+my $timediff = timediff( $endtime, $starttime );
+print STDOUT ( "\nTotal run time = ", timestr($timediff), "\n\n", );
+
+# If we get here, we must assume that the parser ran more-or-less
+# okay, so we return zero to the shell.
+exit 0;
+
diff --git a/debian/TODO b/debian/TODO
deleted file mode 100644
index 19e2b58..0000000
--- a/debian/TODO
+++ /dev/null
@@ -1,41 +0,0 @@
-t/creator...............ok
- 1/1 skipped: Class::DBI not installed
-t/daemon................ok
- 1/1 skipped: MIME::Lite, Archive::Any or Class::DBI not installed
-t/daemon_checker........ok
- 3/3 skipped: Parent class ArrayExpress::AutoSubmission::Daemon not available.
-t/daemon_exporter.......ok
- 3/3 skipped: Parent class ArrayExpress::AutoSubmission::Daemon not available.
-t/daemon_mxchecker......ok
- 1/1 skipped: DBD::mysql not installed, or ArrayExpress::Curator::Daemon superclass unavailable.
-t/daemon_mxexporter.....ok
- 1/1 skipped: DBD::mysql not installed, or ArrayExpress::Curator::Daemon superclass unavailable.
-t/data_metrics..........ok
-t/database..............ok
-t/datafile..............ok
- 15/142 skipped: Test::Exception not installed
-t/datafile_parser.......ok
-t/db....................ok
- 31/31 skipped: Class::DBI not installed
-t/entrez_list...........ok
-t/experiment_checker....ok
-t/fgem..................ok
-t/logger................ok
-t/mage..................ok
-t/mage_defs.............ok
-t/magetab...............ok
-t/magetab_checker.......ok
-t/magetab_datamatrix....ok
-t/magetab_idf...........ok
-t/magetab_sdrf..........ok
-t/magetab_tabfile.......ok
-t/miamexpress...........ok
- 1/1 skipped: DBI and/or DBD::mysql not installed
-t/qt_list...............ok
-t/report................ok
-t/spreadsheet...........ok
- 1/1 skipped: Class::DBI not installed
-t/standalone............ok
-t/tab2mage..............ok
-t/tracking..............ok
- 2/2 skipped: Date::Manip not installed
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index d948d7e..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,12 +0,0 @@
-tab2mage (20080626-1) UNRELEASED; urgency=low
-
- * Initial release (Closes: #nnnnnn)
-
- [ Thorsten Alteholz ]
- * new upstream version
- * debian/rules: target get-orig-source added
- * debian/control: dh 8
- * debian/control: Standards=3.9.2
- * debian/control: VCS fields added
-
- -- David Paleino <d.paleino at gmail.com> Sat, 31 May 2008 19:39:45 +0200
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index f599e28..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-10
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 415314a..0000000
--- a/debian/control
+++ /dev/null
@@ -1,40 +0,0 @@
-Source: tab2mage
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Charles Plessy <plessy at debian.org>
-Section: science
-Priority: optional
-Build-Depends: debhelper (>= 10)
-Standards-Version: 3.9.8
-Vcs-Browser: https://anonscm.debian.org/viewvc/debian-med/trunk/packages/tab2mage/trunk/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/tab2mage/trunk
-Homepage: http://tab2mage.sourceforge.net/
-
-Package: tab2mage
-Architecture: all
-Depends: ${misc:Depends},
- ${perl:Depends},
- libbio-mage-perl,
- libbio-mage-utils-perl,
- libwww-perl,
- libtie-ixhash-perl,
- liblist-moreutils-perl,
- libtext-csv-perl,
- libreadonly-xs-perl,
- libconfig-yaml-perl,
- libclass-std-perl,
- libparse-recdescent-perl,
- libxml-xerces-perl
-Recommends: libdbi-perl,
- libdbd-mysql-perl
-Suggests: graphviz
-Description: submitting large microarray experiment datasets to public repository database
- Tab2MAGE is a software package written and supported by the ArrayExpress
- curation team, which aims to ease the process of submitting large
- microarray experiment datasets to our public repository database. To
- this end, Tab2MAGE currently includes two tools, the tab2mage.pl script
- itself, and a data file checking script, expt_check.pl. With these
- scripts it is possible to perform an initial data file validation
- against an array design (e.g., in the form of an "Array Description
- File" or ADF), and then to generate MAGE-ML using these data files
- alongside a separate spreadsheet providing MIAME-compliant sample
- annotation.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 0cdf2ac..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,24 +0,0 @@
-This package was debianized by Charles Plessy <charles-debian-nospam at plessy.org> on
-Fri, 11 Apr 2008 10:55:53 +0900.
-
-It was downloaded from <url://example.com>
-
-Upstream Author(s):
-
- <put author's name and email here>
- <likewise for another author>
-
-Copyright:
-
- <Copyright (C) YYYY Name OfAuthor>
- <likewise for another author>
-
-License:
-
- <Put the license of the package here indented by 4 spaces>
-
-The Debian packaging is (C) 2008, Charles Plessy <charles-debian-nospam at plessy.org> and
-is licensed under the GPL, see `/usr/share/common-licenses/GPL'.
-
-# Please also look if there are files or directories which have a
-# different copyright/license attached and list them here.
diff --git a/debian/dirs b/debian/dirs
deleted file mode 100644
index ca882bb..0000000
--- a/debian/dirs
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/bin
-usr/sbin
diff --git a/debian/docs b/debian/docs
deleted file mode 100644
index 68eda78..0000000
--- a/debian/docs
+++ /dev/null
@@ -1,3 +0,0 @@
-docs/*
-MAGETAB.txt
-README
diff --git a/debian/examples b/debian/examples
deleted file mode 100644
index e39721e..0000000
--- a/debian/examples
+++ /dev/null
@@ -1 +0,0 @@
-examples/*
diff --git a/debian/patches/add-binaries-to-make-install.patch b/debian/patches/add-binaries-to-make-install.patch
deleted file mode 100644
index 527371a..0000000
--- a/debian/patches/add-binaries-to-make-install.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-Index: tab2mage-20080110/Makefile.PL
-===================================================================
---- tab2mage-20080110.orig/Makefile.PL 2008-04-14 10:22:36.000000000 +0900
-+++ tab2mage-20080110/Makefile.PL 2008-04-14 10:23:23.000000000 +0900
-@@ -40,6 +40,9 @@
- bin/magetab.pl
- bin/expt_check.pl
- bin/convert_to_mcmr.pl
-+ bin/mx_add_affy.pl
-+ bin/parse_affy.pl
-+ bin/t2m_visualize.pl
- );
- my @LIBS = qw(xerces-c);
-
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 68a745e..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1 +0,0 @@
-add-binaries-to-make-install.patch
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 912872f..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/make -f
-
-%:
- dh $@
-
-get-orig-source:
- mkdir -p ../tarballs
- uscan --verbose --force-download --destdir=../tarballs
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index b578f7c..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,2 +0,0 @@
-version=3
-http://sf.net/tab2mage/Tab2MAGE-(.*)\.tar\.gz
diff --git a/docs/ArrayExpress/ArrayMAGE.html b/docs/ArrayExpress/ArrayMAGE.html
new file mode 100644
index 0000000..f93a400
--- /dev/null
+++ b/docs/ArrayExpress/ArrayMAGE.html
@@ -0,0 +1,476 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::ArrayMAGE.pm - an OO module providing methods
+for writing MAGE-ML for array designs.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#public_methods">PUBLIC METHODS</a></li>
+ <li><a href="#subclasses">SUBCLASSES</a></li>
+ <ul>
+
+ <li><a href="#arrayexpress__arraymage__arraydesign">ArrayExpress::ArrayMAGE::ArrayDesign</a></li>
+ <li><a href="#arrayexpress__arraymage__biosequence">ArrayExpress::ArrayMAGE::BioSequence</a></li>
+ <li><a href="#arrayexpress__arraymage__compositegroup">ArrayExpress::ArrayMAGE::CompositeGroup</a></li>
+ <li><a href="#arrayexpress__arraymage__compositesequence">ArrayExpress::ArrayMAGE::CompositeSequence</a></li>
+ <li><a href="#arrayexpress__arraymage__database">ArrayExpress::ArrayMAGE::Database</a></li>
+ <li><a href="#arrayexpress__arraymage__feature">ArrayExpress::ArrayMAGE::Feature</a></li>
+ <li><a href="#arrayexpress__arraymage__featurereportermap">ArrayExpress::ArrayMAGE::FeatureReporterMap</a></li>
+ <li><a href="#arrayexpress__arraymage__reporter">ArrayExpress::ArrayMAGE::Reporter</a></li>
+ <li><a href="#arrayexpress__arraymage__reportercompositemap">ArrayExpress::ArrayMAGE::ReporterCompositeMap</a></li>
+ <li><a href="#arrayexpress__arraymage__reportergroup">ArrayExpress::ArrayMAGE::ReporterGroup</a></li>
+ <li><a href="#arrayexpress__arraymage__zone">ArrayExpress::ArrayMAGE::Zone</a></li>
+ </ul>
+
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ArrayMAGE.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::ArrayMAGE.pm - an OO module providing methods for
+writing MAGE-ML for array designs.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ # Import all the ArrayMAGE classes.
+ use ArrayExpress::ArrayMAGE qw(:ALL);
+
+ # Instantiate the objects we require.
+ my @classes = qw(
+ Feature
+ Reporter
+ BioSequence
+ FeatureReporterMap
+ ArrayDesign
+ Zone
+ );
+ my %object;
+ foreach my $type (@classes) {
+ if ( $type eq 'ArrayDesign' ) {
+
+ # ArrayDesign requires an accession.
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+ accession => $acc,
+ });
+ }
+ else {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+ }
+ }
+
+ # ReporterGroup and CompositeGroup are typically handled
+ # slightly differently, because multiple groups may be
+ # instantiated during a run (see below).
+ my ( %reporter_group, %composite_group );
+
+ # Add objects, e.g. while scanning through an ADF or CDF file.
+ foreach my $row_hashref ( @list_of_lines ) {
+ $object{Feature}->add({
+ metacolumn => $row->{'MetaColumn'},
+ metarow => $row->{'MetaRow'},
+ column => $row->{'Column'},
+ row => $row->{'Row'},
+ });
+
+ # ReporterGroups and CompositeGroups are handled in similar
+ # ways (only ReporterGroup is shown here):
+ while ( my ( $rname, $rvalue ) = each %$row ) {
+ if ( my ($type) = ( $rname =~ m!ReporterGroup\[(.*?)\]!i ) ) {
+ my $key = "$type.$rvalue";
+ $reporter_group{$key}
+ ||= ArrayExpress::ArrayMAGE::ReporterGroup->new({
+ identifier => $key,
+ tag => $rvalue,
+ is_species => ( $type =~ m!species!i ) || 0,
+ });
+ $reporter_group{$key}->add({
+ identifier => $row->{'Reporter Identifier'},
+ });
+ }
+ }
+ }
+
+ # Write out the MAGE-ML to STDOUT.
+ ArrayExpress::ArrayMAGE->combine_parts(
+ \%objects,
+ \%reporter_group,
+ \%composite_group,
+ \*STDOUT,
+ );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a module designed to allow the creation of MAGE-ML for
+array designs in a memory-efficient fashion. This module is the
+abstract superclass for a series of MAGE-like classes representing
+parts of the MAGE-ML document to be written. These classes, when
+instantiated, all point to temporary filehandles into which is
+written the XML describing MAGE object instances. Each subclass
+implements an 'add' method in addition to those inherited from this
+base class. Instantiating the object with <a href=
+"#item_new"><code>new()</code></a> opens up the XML tags for a list
+of objects, the 'add' adds objects based on the values it is passed
+(no surprise there) and finally the XML tags are closed and the
+MAGE-ML written out by calling the class method <a href=
+"#item_combine_parts"><code>combine_parts()</code></a> in this
+top-level class.</p>
+<hr />
+<h1><a name="public_methods" id="public_methods">PUBLIC
+METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_combine_parts" id=
+"item_combine_parts"><code>combine_parts( \%obj, \%rg, \%cg, $fh
+)</code></a></strong></dt>
+<dd>
+<p>Class method. Takes: a hashref of ArrayMAGE objects, a hashref
+of ReporterGroups, a hashref of CompositeGroups, and an open output
+filehandle, and writes the completed MAGE-ML to that
+filehandle.</p>
+</dd>
+<dt><strong><a name="item_set_namespace" id=
+"item_set_namespace"><code>set_namespace("namespace")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the identifier namespace
+for the output MAGE objects.</p>
+</dd>
+<dt><strong><a name="item_set_design" id=
+"item_set_design"><code>set_design("design")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the design name used in
+identifiers for the output MAGE objects. Typically this will be the
+same as the array accession, but e.g. for Affymetrix arrays will
+correspond to the design name.</p>
+</dd>
+<dt><strong><a name="item_set_separator" id=
+"item_set_separator"><code>set_separator("separator")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the identifier separator
+for the output MAGE objects. Usually this will be q{:} or q{.}.</p>
+</dd>
+<dt><strong><a name="item_set_programname" id=
+"item_set_programname"><code>set_programname("programname")</code></a></strong></dt>
+<dd>
+<p>Class method. Allows the user to set the name of the program
+(this is not particularly useful).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="subclasses" id="subclasses">SUBCLASSES</a></h1>
+<h2><a name="arrayexpress__arraymage__arraydesign" id=
+"arrayexpress__arraymage__arraydesign">ArrayExpress::ArrayMAGE::ArrayDesign</a></h2>
+<dl>
+<dt><strong><a name="item_new" id="item_new"><code>new()</code>
+required attribute: <code>accession</code></a></strong></dt>
+<dd>
+<p>The accession attribute becomes the MAGE PhysicalArrayDesign
+identifier.</p>
+</dd>
+<dt><strong><a name="item_add" id="item_add"><code>add()</code> is
+not implemented for this class.</a></strong></dt>
+</dl>
+<h2><a name="arrayexpress__arraymage__biosequence" id=
+"arrayexpress__arraymage__biosequence">ArrayExpress::ArrayMAGE::BioSequence</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create
+the BioSequence identifier.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>dbrefs</code></strong></dt>
+<dd>
+<p>A hashref with db accessions as keys, and db tags as values
+(e.g. <code>{ A12345 => 'embl' }</code> ). Typically used for
+Reporter BioSequence Database Entry ADF columns.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__compositegroup" id=
+"arrayexpress__arraymage__compositegroup">ArrayExpress::ArrayMAGE::CompositeGroup</a></h2>
+<dl>
+<dt><strong><code>new()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+CompositeGroup identifier.</p>
+</dd>
+<dt><strong><code>new()</code> required attribute:
+<code>tag</code></strong></dt>
+<dd>
+<p>This tag is used as the CompositeGroup name. If
+<code>is_species</code> is set, then it will also be used in a
+Species_assn element as the species name.</p>
+</dd>
+<dt><strong><code>new()</code> optional attribute:
+<code>is_species</code></strong></dt>
+<dd>
+<p>A flag indicating whether this is a grouping based on species or
+not.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>id_ref</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+CompositeSequence_ref identifier.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__compositesequence" id=
+"arrayexpress__arraymage__compositesequence">ArrayExpress::ArrayMAGE::CompositeSequence</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create
+the CompositeSequence identifier.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>name</code></strong></dt>
+<dd>
+<p>The name of the CompositeSequence.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>biosequences</code></strong></dt>
+<dd>
+<p>An arrayref of BioSequence identifiers to be attached to this
+CompositeSequence.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>dbrefs</code></strong></dt>
+<dd>
+<p>A hashref with db accessions as keys, and db tags as values
+(e.g. <code>{ A12345 => 'embl' }</code> ). Typically used for
+CompositeSequence Description Database Entry ADF columns.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>controltype</code></strong></dt>
+<dd>
+<p>A ControlType ontology term to be attached to this
+CompositeSequence.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>comment</code></strong></dt>
+<dd>
+<p>A comment to be added to this CompositeSequence as a
+Description_assn.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>cs_only</code></strong></dt>
+<dd>
+<p>A flag indicating that we are not creating Reporters (and can
+therefore skip ReporterCompositeMap creation).</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__database" id=
+"arrayexpress__arraymage__database">ArrayExpress::ArrayMAGE::Database</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>tag</code></strong></dt>
+<dd>
+<p>This tag is combined with the class namespace to create the
+Database identifier. Typically this will be a standard database
+tag, e.g. <code>embl</code>.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__feature" id=
+"arrayexpress__arraymage__feature">ArrayExpress::ArrayMAGE::Feature</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>metacolumn</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>metarow</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>column</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>row</code></strong></dt>
+<dd>
+<p>Feature coordinates, used in the Feature and Zone_ref
+identifiers and in FeatureLocation.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__featurereportermap" id=
+"arrayexpress__arraymage__featurereportermap">ArrayExpress::ArrayMAGE::FeatureReporterMap</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>The Reporter identifier to which Features should be mapped.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>features</code></strong></dt>
+<dd>
+<p>An arrayref of Feature identifiers mapped to the Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>mismatch_info</code></strong></dt>
+<dd>
+<p>A hashref of hashrefs, keyed by Feature identifier and then by
+<code>start_coord</code>, <code>new_sequence</code> and
+<code>replaced_length</code>, used to create a
+MismatchInformation_assnlist (useful for Affymetrix array
+designs).</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__reporter" id=
+"arrayexpress__arraymage__reporter">ArrayExpress::ArrayMAGE::Reporter</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create
+the Reporter identifier.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>name</code></strong></dt>
+<dd>
+<p>The name of the Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>biosequences</code></strong></dt>
+<dd>
+<p>An arrayref of BioSequence identifiers to be attached to this
+Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>dbrefs</code></strong></dt>
+<dd>
+<p>A hashref with db accessions as keys, and db tags as values
+(e.g. <code>{ A12345 => 'embl' }</code> ). Typically used for
+Reporter Description Database Entry ADF columns.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>controltype</code></strong></dt>
+<dd>
+<p>A ControlType ontology term to be attached to this Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>warningtype</code></strong></dt>
+<dd>
+<p>A WarningType ontology term to be attached to this Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>failtype</code></strong></dt>
+<dd>
+<p>A FailType ontology term to be attached to this Reporter.</p>
+</dd>
+<dt><strong><code>add()</code> optional attribute:
+<code>comment</code></strong></dt>
+<dd>
+<p>A comment to be added to this Reporter as a
+Description_assn.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__reportercompositemap" id=
+"arrayexpress__arraymage__reportercompositemap">ArrayExpress::ArrayMAGE::ReporterCompositeMap</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>The CompositeSequence identifier to which Reporters should be
+mapped.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>reporters</code></strong></dt>
+<dd>
+<p>An arrayref of Reporter identifiers mapped to the
+CompositeSequence.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__reportergroup" id=
+"arrayexpress__arraymage__reportergroup">ArrayExpress::ArrayMAGE::ReporterGroup</a></h2>
+<dl>
+<dt><strong><code>new()</code> required attribute:
+<code>identifier</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+ReporterGroup identifier.</p>
+</dd>
+<dt><strong><code>new()</code> required attribute:
+<code>tag</code></strong></dt>
+<dd>
+<p>This tag is used as the ReporterGroup name. If
+<code>is_species</code> is set, then it will also be used in a
+Species_assn element as the species name.</p>
+</dd>
+<dt><strong><code>new()</code> optional attribute:
+<code>is_species</code></strong></dt>
+<dd>
+<p>A flag indicating whether this is a grouping based on species or
+not.</p>
+</dd>
+<dt><strong><code>add()</code> required attribute:
+<code>id_ref</code></strong></dt>
+<dd>
+<p>This identifier is combined with the class namespace to create a
+Reporter_ref identifier.</p>
+</dd>
+</dl>
+<h2><a name="arrayexpress__arraymage__zone" id=
+"arrayexpress__arraymage__zone">ArrayExpress::ArrayMAGE::Zone</a></h2>
+<dl>
+<dt><strong><code>new()</code> requires no
+attributes.</strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>column</code></strong></dt>
+<dt><strong><code>add()</code> required attribute:
+<code>row</code></strong></dt>
+<dd>
+<p>Zone coordinates, used in the Zone identifier and in its row and
+column attributes.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/Creator.html b/docs/ArrayExpress/AutoSubmission/Creator.html
new file mode 100644
index 0000000..5ce7ff9
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/Creator.html
@@ -0,0 +1,185 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::Creator - A class providing
+convenience methods for experiment object creation and insertion
+into the autosubmissions tracking database.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Creator.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::Creator - A class providing
+convenience methods for experiment object creation and insertion
+into the autosubmissions tracking database.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::Creator;
+ my $creator = ArrayExpress::AutoSubmission::Creator->new({
+ login => $login,
+ name => $expt_name,
+ spreadsheet => $startfile,
+ data_files => \@datafiles,
+ accession => $accession,
+ experiment_type => 'MAGE-TAB',
+ comment => 'Submission inserted manually',
+ clobber => 0,
+ });
+</pre>
+<pre>
+ # Create the experiment and insert the spreadsheet.
+ my $expt = $creator->get_experiment();
+</pre>
+<pre>
+ # Copy the files to the submissions directory.
+ print STDERR ("Copying files...\n");
+ $creator->insert_spreadsheet();
+ $creator->insert_data_files();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides some basic convenience methods to automate
+error handling when inserting new experiments into the tracking
+database.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>Object constructor. Takes a hashref of options, including the
+following:</p>
+</dd>
+<dd>
+<dl>
+<dt><strong><a name="item_login" id=
+"item_login"><code>login</code></a></strong></dt>
+<dd>
+<p>The login name of the user to which the experiment should be
+assigned.</p>
+</dd>
+<dt><strong><a name="item_name" id=
+"item_name"><code>name</code></a></strong></dt>
+<dd>
+<p>The name of the experiment</p>
+</dd>
+<dt><strong><a name="item_spreadsheet" id=
+"item_spreadsheet"><code>spreadsheet</code></a></strong></dt>
+<dd>
+<p>The path to spreadsheet file to be inserted.</p>
+</dd>
+<dt><strong><a name="item_data_files" id=
+"item_data_files"><code>data_files</code></a></strong></dt>
+<dd>
+<p>An arrayref listing the paths to the data files to be
+inserted.</p>
+</dd>
+<dt><strong><a name="item_accession" id=
+"item_accession"><code>accession</code></a></strong></dt>
+<dd>
+<p>The accession number for the experiment.</p>
+</dd>
+<dt><strong><a name="item_comment" id=
+"item_comment"><code>comment</code></a></strong></dt>
+<dd>
+<p>Any comments to be attached to the experiment.</p>
+</dd>
+<dt><strong><a name="item_experiment_type" id=
+"item_experiment_type"><code>experiment_type</code></a></strong></dt>
+<dd>
+<p>The experiment type. This should typically be one of the
+following: MAGE-TAB, Tab2MAGE, MIAMExpress, GEO, MUGEN,
+Unknown.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to overwrite files without prompting
+the user.</p>
+</dd>
+<dt><strong><a name="item_organisms" id=
+"item_organisms"><code>organisms</code></a></strong></dt>
+<dd>
+<p>An arrayref of organism scientific names to associate with this
+experiment.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="item_mk_passwd" id=
+"item_mk_passwd"><code>mk_passwd</code></a></strong></dt>
+<dd>
+<p>Returns a random string suitable for use as an account password.
+Can pass an optional length argument, otherwise defaults to eight
+characters.</p>
+</dd>
+<dt><strong><a name="item_insert_spreadsheet" id=
+"item_insert_spreadsheet"><code>insert_spreadsheet</code></a></strong></dt>
+<dd>
+<p>Copy the spreadsheet file into the appropriate filesystem
+location and insert a record into the database.</p>
+</dd>
+<dt><strong><a name="item_insert_data_files" id=
+"item_insert_data_files"><code>insert_data_files</code></a></strong></dt>
+<dd>
+<p>Copy the data files into the appropriate filesystem location and
+insert records into the database.</p>
+</dd>
+<dt><strong><a name="item_get_user" id=
+"item_get_user"><code>get_user</code></a></strong></dt>
+<dd>
+<p>Returns the appropriate Class::DBI user object as retrieved from
+(or inserted into) the database.</p>
+</dd>
+<dt><strong><a name="item_get_experiment" id=
+"item_get_experiment"><code>get_experiment</code></a></strong></dt>
+<dd>
+<p>Returns the appropriate Class::DBI experiment object as
+retrieved from (or inserted into) the database.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/DB.html b/docs/ArrayExpress/AutoSubmission/DB.html
new file mode 100644
index 0000000..4276dc9
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/DB.html
@@ -0,0 +1,2905 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::DB - Class::DBI based
+interface to the autosubmissions tracking database.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#tables">TABLES</a></li>
+ <ul>
+
+ <li><a href="#array_designs">array_designs</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#miamexpress_subid">miamexpress_subid</a></li>
+ <li><a href="#accession">accession</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#miamexpress_login">miamexpress_login</a></li>
+ <li><a href="#status">status</a></li>
+ <li><a href="#data_warehouse_ready">data_warehouse_ready</a></li>
+ <li><a href="#date_last_processed">date_last_processed</a></li>
+ <li><a href="#comment">comment</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ <li><a href="#miame_score">miame_score</a></li>
+ <li><a href="#in_data_warehouse">in_data_warehouse</a></li>
+ <li><a href="#annotation_source">annotation_source</a></li>
+ <li><a href="#annotation_version">annotation_version</a></li>
+ <li><a href="#biomart_table_name">biomart_table_name</a></li>
+ <li><a href="#release_date">release_date</a></li>
+ <li><a href="#is_released">is_released</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#unique">UNIQUE</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#array_designs_experiments">array_designs_experiments</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#array_design_id">array_design_id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#array_designs_organisms">array_designs_organisms</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#organism_id">organism_id</a></li>
+ <li><a href="#array_design_id">array_design_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#categories">categories</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#ontology_term">ontology_term</a></li>
+ <li><a href="#display_label">display_label</a></li>
+ <li><a href="#is_common">is_common</a></li>
+ <li><a href="#is_bmc">is_bmc</a></li>
+ <li><a href="#is_fv">is_fv</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#categories_designs">categories_designs</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#category_id">category_id</a></li>
+ <li><a href="#design_id">design_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#categories_materials">categories_materials</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#category_id">category_id</a></li>
+ <li><a href="#material_id">material_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#categories_taxons">categories_taxons</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#category_id">category_id</a></li>
+ <li><a href="#taxon_id">taxon_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#data_files">data_files</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#is_unpacked">is_unpacked</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#data_formats">data_formats</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#name">name</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#unique">UNIQUE</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#design_instances">design_instances</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#design_id">design_id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#designs">designs</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#display_label">display_label</a></li>
+ <li><a href="#ontology_category">ontology_category</a></li>
+ <li><a href="#ontology_value">ontology_value</a></li>
+ <li><a href="#design_type">design_type</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#events">events</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#array_design_id">array_design_id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ <li><a href="#event_type">event_type</a></li>
+ <li><a href="#was_successful">was_successful</a></li>
+ <li><a href="#source_db">source_db</a></li>
+ <li><a href="#target_db">target_db</a></li>
+ <li><a href="#start_time">start_time</a></li>
+ <li><a href="#end_time">end_time</a></li>
+ <li><a href="#machine">machine</a></li>
+ <li><a href="#operator">operator</a></li>
+ <li><a href="#log_file">log_file</a></li>
+ <li><a href="#jobregister_dbid">jobregister_dbid</a></li>
+ <li><a href="#comment">comment</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#experiments">experiments</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#accession">accession</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#user_id">user_id</a></li>
+ <li><a href="#checker_score">checker_score</a></li>
+ <li><a href="#software">software</a></li>
+ <li><a href="#status">status</a></li>
+ <li><a href="#data_warehouse_ready">data_warehouse_ready</a></li>
+ <li><a href="#date_last_edited">date_last_edited</a></li>
+ <li><a href="#date_submitted">date_submitted</a></li>
+ <li><a href="#date_last_processed">date_last_processed</a></li>
+ <li><a href="#in_curation">in_curation</a></li>
+ <li><a href="#curator">curator</a></li>
+ <li><a href="#comment">comment</a></li>
+ <li><a href="#experiment_type">experiment_type</a></li>
+ <li><a href="#miamexpress_login">miamexpress_login</a></li>
+ <li><a href="#miamexpress_subid">miamexpress_subid</a></li>
+ <li><a href="#is_affymetrix">is_affymetrix</a></li>
+ <li><a href="#is_mx_batchloader">is_mx_batchloader</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ <li><a href="#miame_score">miame_score</a></li>
+ <li><a href="#in_data_warehouse">in_data_warehouse</a></li>
+ <li><a href="#num_submissions">num_submissions</a></li>
+ <li><a href="#submitter_description">submitter_description</a></li>
+ <li><a href="#curated_name">curated_name</a></li>
+ <li><a href="#num_samples">num_samples</a></li>
+ <li><a href="#num_hybridizations">num_hybridizations</a></li>
+ <li><a href="#has_raw_data">has_raw_data</a></li>
+ <li><a href="#has_processed_data">has_processed_data</a></li>
+ <li><a href="#release_date">release_date</a></li>
+ <li><a href="#is_released">is_released</a></li>
+ <li><a href="#ae_miame_score">ae_miame_score</a></li>
+ <li><a href="#ae_data_warehouse_score">ae_data_warehouse_score</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#experiments_factors">experiments_factors</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ <li><a href="#factor_id">factor_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#experiments_loaded_data">experiments_loaded_data</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#experiment_id">experiment_id</a></li>
+ <li><a href="#loaded_data_id">loaded_data_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#experiments_quantitation_types">experiments_quantitation_types</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#quantitation_type_id">quantitation_type_id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#factors">factors</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#loaded_data">loaded_data</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#identifier">identifier</a></li>
+ <li><a href="#md5_hash">md5_hash</a></li>
+ <li><a href="#data_format_id">data_format_id</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ <li><a href="#platform_id">platform_id</a></li>
+ <li><a href="#needs_metrics_calculation">needs_metrics_calculation</a></li>
+ <li><a href="#date_hashed">date_hashed</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#loaded_data_quality_metrics">loaded_data_quality_metrics</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#value">value</a></li>
+ <li><a href="#quality_metric_id">quality_metric_id</a></li>
+ <li><a href="#loaded_data_id">loaded_data_id</a></li>
+ <li><a href="#date_calculated">date_calculated</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#material_instances">material_instances</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#material_id">material_id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#materials">materials</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#display_label">display_label</a></li>
+ <li><a href="#ontology_category">ontology_category</a></li>
+ <li><a href="#ontology_value">ontology_value</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#meta">meta</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#name">name</a></li>
+ <li><a href="#value">value</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#organism_instances">organism_instances</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#organism_id">organism_id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#organisms">organisms</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#scientific_name">scientific_name</a></li>
+ <li><a href="#common_name">common_name</a></li>
+ <li><a href="#accession">accession</a></li>
+ <li><a href="#taxon_id">taxon_id</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#permissions">permissions</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#info">info</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#permissions_roles">permissions_roles</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#role_id">role_id</a></li>
+ <li><a href="#permission_id">permission_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#platforms">platforms</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#name">name</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#unique">UNIQUE</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#protocols">protocols</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#accession">accession</a></li>
+ <li><a href="#user_accession">user_accession</a></li>
+ <li><a href="#expt_accession">expt_accession</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#date_last_processed">date_last_processed</a></li>
+ <li><a href="#comment">comment</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#unique">UNIQUE</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#quality_metrics">quality_metrics</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#type">type</a></li>
+ <li><a href="#description">description</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#unique">UNIQUE</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#quantitation_types">quantitation_types</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#roles">roles</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#info">info</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#roles_users">roles_users</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#user_id">user_id</a></li>
+ <li><a href="#role_id">role_id</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#spreadsheets">spreadsheets</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#experiment_id">experiment_id</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#indices">INDICES</a></li>
+ <ul>
+
+ <li><a href="#normal">NORMAL</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#foreign_key">FOREIGN KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#taxons">taxons</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#scientific_name">scientific_name</a></li>
+ <li><a href="#common_name">common_name</a></li>
+ <li><a href="#accession">accession</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ </ul>
+
+ </ul>
+
+ <li><a href="#users">users</a></li>
+ <ul>
+
+ <li><a href="#fields">FIELDS</a></li>
+ <ul>
+
+ <li><a href="#id">id</a></li>
+ <li><a href="#login">login</a></li>
+ <li><a href="#name">name</a></li>
+ <li><a href="#password">password</a></li>
+ <li><a href="#email">email</a></li>
+ <li><a href="#modified_at">modified_at</a></li>
+ <li><a href="#created_at">created_at</a></li>
+ <li><a href="#access">access</a></li>
+ <li><a href="#is_deleted">is_deleted</a></li>
+ </ul>
+
+ <li><a href="#constraints">CONSTRAINTS</a></li>
+ <ul>
+
+ <li><a href="#primary_key">PRIMARY KEY</a></li>
+ <li><a href="#unique">UNIQUE</a></li>
+ </ul>
+
+ </ul>
+
+ </ul>
+
+ <li><a href="#produced_by">PRODUCED BY</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: DB.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::DB - Class::DBI based interface to
+the autosubmissions tracking database.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::DB::Experiment;
+ my $expt = ArrayExpress::AutoSubmission::DB::Experiment->retrieve( $id );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is the abstract superclass for a set of Class::DBI -
+based modules, which are used as an object-relational mapping to
+the underlying submissions tracking database (currently implemented
+using MySQL). You should not use this class directly; rather, you
+should use the relevant table subclasses to query the database (see
+<a href="#synopsis">SYNOPSIS</a> for an example). Please refer to
+the Class::DBI documentation for information on query syntax.</p>
+<hr />
+<h1><a name="tables" id="tables">TABLES</a></h1>
+<p>Included below are auto-generated descriptions of the MySQL
+tables. This listing was generated using the
+SQL::Translator::Producer::POD module.</p>
+<h2><a name="array_designs" id=
+"array_designs">array_designs</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><a name="item_int" id=
+"item_int"><code>int(11)</code></a></strong></li>
+<li><strong><a name="item_primary_key" id=
+"item_primary_key">PRIMARY KEY</a></strong></li>
+<li><strong><a name="item_nullable__27no_27" id=
+"item_nullable__27no_27">Nullable 'No'</a></strong></li>
+</ul>
+<h4><a name="miamexpress_subid" id=
+"miamexpress_subid">miamexpress_subid</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong><a name="item_default__27null_27" id=
+"item_default__27null_27">Default 'NULL'</a></strong></li>
+<li><strong><a name="item_nullable__27yes_27" id=
+"item_nullable__27yes_27">Nullable 'Yes'</a></strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><a name="item_varchar" id=
+"item_varchar"><code>varchar(255)</code></a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="miamexpress_login" id=
+"miamexpress_login">miamexpress_login</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="status" id="status">status</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="data_warehouse_ready" id=
+"data_warehouse_ready">data_warehouse_ready</a></h4>
+<ul>
+<li><strong><a name="item_char" id=
+"item_char"><code>char(15)</code></a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_processed" id=
+"date_last_processed">date_last_processed</a></h4>
+<ul>
+<li><strong><a name="item_datetime" id=
+"item_datetime">datetime</a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><a name="item_text" id=
+"item_text"><code>text(65535)</code></a></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="miame_score" id="miame_score">miame_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="in_data_warehouse" id=
+"in_data_warehouse">in_data_warehouse</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="annotation_source" id=
+"annotation_source">annotation_source</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="annotation_version" id=
+"annotation_version">annotation_version</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="biomart_table_name" id=
+"biomart_table_name">biomart_table_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="release_date" id="release_date">release_date</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_released" id="is_released">is_released</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_id" id=
+"item_fields__3d_id">Fields = id</a></strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_miamexpress_subid" id=
+"item_fields__3d_miamexpress_subid">Fields =
+miamexpress_subid</a></strong></li>
+</ul>
+<h2><a name="array_designs_experiments" id=
+"array_designs_experiments">array_designs_experiments</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="array_design_id" id=
+"array_design_id">array_design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_array_design_id" id=
+"item_fields__3d_array_design_id">Fields =
+array_design_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_experiment_id" id=
+"item_fields__3d_experiment_id">Fields =
+experiment_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2farray_designs" id=
+"item_reference_table__3d__2farray_designs">Reference Table =</a>
+<a href="#array_designs">array_designs</a></strong></li>
+<li><strong><a name="item_reference_fields__3d__2fid" id=
+"item_reference_fields__3d__2fid">Reference Fields =</a> <a href=
+"#id">id</a></strong></li>
+<li><strong><a name="item_on_delete__3d_cascade" id=
+"item_on_delete__3d_cascade">On delete = CASCADE</a></strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fexperiments" id=
+"item_reference_table__3d__2fexperiments">Reference Table =</a>
+<a href="#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="array_designs_organisms" id=
+"array_designs_organisms">array_designs_organisms</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="organism_id" id="organism_id">organism_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="array_design_id" id=
+"array_design_id">array_design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_organism_id" id=
+"item_fields__3d_organism_id">Fields =
+organism_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = organism_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2forganisms" id=
+"item_reference_table__3d__2forganisms">Reference Table =</a>
+<a href="#organisms">organisms</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#array_designs">array_designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="categories" id="categories">categories</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="ontology_term" id=
+"ontology_term">ontology_term</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="display_label" id=
+"display_label">display_label</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_common" id="is_common">is_common</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_bmc" id="is_bmc">is_bmc</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_fv" id="is_fv">is_fv</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="categories_designs" id=
+"categories_designs">categories_designs</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="category_id" id="category_id">category_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="design_id" id="design_id">design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_category_id" id=
+"item_fields__3d_category_id">Fields =
+category_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_design_id" id=
+"item_fields__3d_design_id">Fields = design_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fcategories" id=
+"item_reference_table__3d__2fcategories">Reference Table =</a>
+<a href="#categories">categories</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = design_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fdesigns" id=
+"item_reference_table__3d__2fdesigns">Reference Table =</a>
+<a href="#designs">designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="categories_materials" id=
+"categories_materials">categories_materials</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="category_id" id="category_id">category_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="material_id" id="material_id">material_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_material_id" id=
+"item_fields__3d_material_id">Fields =
+material_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#categories">categories</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = material_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fmaterials" id=
+"item_reference_table__3d__2fmaterials">Reference Table =</a>
+<a href="#materials">materials</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="categories_taxons" id=
+"categories_taxons">categories_taxons</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="category_id" id="category_id">category_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="taxon_id" id="taxon_id">taxon_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_taxon_id" id=
+"item_fields__3d_taxon_id">Fields = taxon_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = category_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#categories">categories</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = taxon_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2ftaxons" id=
+"item_reference_table__3d__2ftaxons">Reference Table =</a> <a href=
+"#taxons">taxons</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="data_files" id="data_files">data_files</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_unpacked" id="is_unpacked">is_unpacked</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="data_formats" id="data_formats">data_formats</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_name" id=
+"item_fields__3d_name">Fields = name</a></strong></li>
+</ul>
+<h2><a name="design_instances" id=
+"design_instances">design_instances</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="design_id" id="design_id">design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = design_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = design_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#designs">designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="designs" id="designs">designs</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="display_label" id=
+"display_label">display_label</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_category" id=
+"ontology_category">ontology_category</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_value" id=
+"ontology_value">ontology_value</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="design_type" id="design_type">design_type</a></h4>
+<ul>
+<li><strong><code>char(15)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="events" id="events">events</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="array_design_id" id=
+"array_design_id">array_design_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="event_type" id="event_type">event_type</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="was_successful" id=
+"was_successful">was_successful</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="source_db" id="source_db">source_db</a></h4>
+<ul>
+<li><strong><code>varchar(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="target_db" id="target_db">target_db</a></h4>
+<ul>
+<li><strong><code>varchar(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="start_time" id="start_time">start_time</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="end_time" id="end_time">end_time</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="machine" id="machine">machine</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="operator" id="operator">operator</a></h4>
+<ul>
+<li><strong><code>varchar(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="log_file" id="log_file">log_file</a></h4>
+<ul>
+<li><strong><code>varchar(511)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="jobregister_dbid" id=
+"jobregister_dbid">jobregister_dbid</a></h4>
+<ul>
+<li><strong><code>int(15)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = array_design_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#array_designs">array_designs</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="experiments" id="experiments">experiments</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="user_id" id="user_id">user_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="checker_score" id=
+"checker_score">checker_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="software" id="software">software</a></h4>
+<ul>
+<li><strong><code>varchar(100)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="status" id="status">status</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="data_warehouse_ready" id=
+"data_warehouse_ready">data_warehouse_ready</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_edited" id=
+"date_last_edited">date_last_edited</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_submitted" id=
+"date_submitted">date_submitted</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_processed" id=
+"date_last_processed">date_last_processed</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="in_curation" id="in_curation">in_curation</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="curator" id="curator">curator</a></h4>
+<ul>
+<li><strong><code>char(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="experiment_type" id=
+"experiment_type">experiment_type</a></h4>
+<ul>
+<li><strong><code>char(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="miamexpress_login" id=
+"miamexpress_login">miamexpress_login</a></h4>
+<ul>
+<li><strong><code>char(30)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="miamexpress_subid" id=
+"miamexpress_subid">miamexpress_subid</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_affymetrix" id=
+"is_affymetrix">is_affymetrix</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_mx_batchloader" id=
+"is_mx_batchloader">is_mx_batchloader</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="miame_score" id="miame_score">miame_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="in_data_warehouse" id=
+"in_data_warehouse">in_data_warehouse</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="num_submissions" id=
+"num_submissions">num_submissions</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="submitter_description" id=
+"submitter_description">submitter_description</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="curated_name" id="curated_name">curated_name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="num_samples" id="num_samples">num_samples</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="num_hybridizations" id=
+"num_hybridizations">num_hybridizations</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="has_raw_data" id="has_raw_data">has_raw_data</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="has_processed_data" id=
+"has_processed_data">has_processed_data</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="release_date" id="release_date">release_date</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_released" id="is_released">is_released</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ae_miame_score" id=
+"ae_miame_score">ae_miame_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ae_data_warehouse_score" id=
+"ae_data_warehouse_score">ae_data_warehouse_score</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_user_id" id=
+"item_fields__3d_user_id">Fields = user_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = user_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fusers" id=
+"item_reference_table__3d__2fusers">Reference Table =</a> <a href=
+"#users">users</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="experiments_factors" id=
+"experiments_factors">experiments_factors</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="factor_id" id="factor_id">factor_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_factor_id" id=
+"item_fields__3d_factor_id">Fields = factor_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = factor_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2ffactors" id=
+"item_reference_table__3d__2ffactors">Reference Table =</a>
+<a href="#factors">factors</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="experiments_loaded_data" id=
+"experiments_loaded_data">experiments_loaded_data</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="loaded_data_id" id=
+"loaded_data_id">loaded_data_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_loaded_data_id" id=
+"item_fields__3d_loaded_data_id">Fields =
+loaded_data_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = loaded_data_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2floaded_data" id=
+"item_reference_table__3d__2floaded_data">Reference Table =</a>
+<a href="#loaded_data">loaded_data</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="experiments_quantitation_types" id=
+"experiments_quantitation_types">experiments_quantitation_types</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="quantitation_type_id" id=
+"quantitation_type_id">quantitation_type_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_quantitation_type_id" id=
+"item_fields__3d_quantitation_type_id">Fields =
+quantitation_type_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = quantitation_type_id</strong></li>
+<li><strong><a name=
+"item_reference_table__3d__2fquantitation_types" id=
+"item_reference_table__3d__2fquantitation_types">Reference Table
+=</a> <a href=
+"#quantitation_types">quantitation_types</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="factors" id="factors">factors</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="loaded_data" id="loaded_data">loaded_data</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="identifier" id="identifier">identifier</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="md5_hash" id="md5_hash">md5_hash</a></h4>
+<ul>
+<li><strong><code>char(35)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="data_format_id" id=
+"data_format_id">data_format_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="platform_id" id="platform_id">platform_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="needs_metrics_calculation" id=
+"needs_metrics_calculation">needs_metrics_calculation</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="date_hashed" id="date_hashed">date_hashed</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_data_format_id" id=
+"item_fields__3d_data_format_id">Fields =
+data_format_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_platform_id" id=
+"item_fields__3d_platform_id">Fields =
+platform_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = platform_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fplatforms" id=
+"item_reference_table__3d__2fplatforms">Reference Table =</a>
+<a href="#platforms">platforms</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = data_format_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fdata_formats" id=
+"item_reference_table__3d__2fdata_formats">Reference Table =</a>
+<a href="#data_formats">data_formats</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="loaded_data_quality_metrics" id=
+"loaded_data_quality_metrics">loaded_data_quality_metrics</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="value" id="value">value</a></h4>
+<ul>
+<li><strong><a name="item_decimal" id=
+"item_decimal"><code>decimal(12,5)</code></a></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="quality_metric_id" id=
+"quality_metric_id">quality_metric_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="loaded_data_id" id=
+"loaded_data_id">loaded_data_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="date_calculated" id=
+"date_calculated">date_calculated</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_quality_metric_id" id=
+"item_fields__3d_quality_metric_id">Fields =
+quality_metric_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = loaded_data_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = loaded_data_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#loaded_data">loaded_data</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = quality_metric_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fquality_metrics"
+id="item_reference_table__3d__2fquality_metrics">Reference Table
+=</a> <a href="#quality_metrics">quality_metrics</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="material_instances" id=
+"material_instances">material_instances</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="material_id" id="material_id">material_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = material_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = material_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#materials">materials</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="materials" id="materials">materials</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="display_label" id=
+"display_label">display_label</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_category" id=
+"ontology_category">ontology_category</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="ontology_value" id=
+"ontology_value">ontology_value</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="meta" id="meta">meta</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong><a name="item_default__27_27" id=
+"item_default__27_27">Default ''</a></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="value" id="value">value</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>Default ''</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = name</strong></li>
+</ul>
+<h2><a name="organism_instances" id=
+"organism_instances">organism_instances</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="organism_id" id="organism_id">organism_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = organism_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = organism_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#organisms">organisms</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="organisms" id="organisms">organisms</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="scientific_name" id=
+"scientific_name">scientific_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="common_name" id="common_name">common_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="taxon_id" id="taxon_id">taxon_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = taxon_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = taxon_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#taxons">taxons</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="permissions" id="permissions">permissions</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="info" id="info">info</a></h4>
+<ul>
+<li><strong><code>varchar(80)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="permissions_roles" id=
+"permissions_roles">permissions_roles</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="role_id" id="role_id">role_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="permission_id" id=
+"permission_id">permission_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_role_id" id=
+"item_fields__3d_role_id">Fields = role_id</a></strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_permission_id" id=
+"item_fields__3d_permission_id">Fields =
+permission_id</a></strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = role_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2froles" id=
+"item_reference_table__3d__2froles">Reference Table =</a> <a href=
+"#roles">roles</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = permission_id</strong></li>
+<li><strong><a name="item_reference_table__3d__2fpermissions" id=
+"item_reference_table__3d__2fpermissions">Reference Table =</a>
+<a href="#permissions">permissions</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="platforms" id="platforms">platforms</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong>Fields = name</strong></li>
+</ul>
+<h2><a name="protocols" id="protocols">protocols</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>char(15)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="user_accession" id=
+"user_accession">user_accession</a></h4>
+<ul>
+<li><strong><code>varchar(100)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="expt_accession" id=
+"expt_accession">expt_accession</a></h4>
+<ul>
+<li><strong><code>char(15)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="date_last_processed" id=
+"date_last_processed">date_last_processed</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="comment" id="comment">comment</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_accession" id=
+"item_fields__3d_accession">Fields = accession</a></strong></li>
+</ul>
+<h2><a name="quality_metrics" id=
+"quality_metrics">quality_metrics</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="type" id="type">type</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="description" id="description">description</a></h4>
+<ul>
+<li><strong><code>text(65535)</code></strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_type" id=
+"item_fields__3d_type">Fields = type</a></strong></li>
+</ul>
+<h2><a name="quantitation_types" id=
+"quantitation_types">quantitation_types</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(128)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="roles" id="roles">roles</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="info" id="info">info</a></h4>
+<ul>
+<li><strong><code>varchar(80)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="roles_users" id="roles_users">roles_users</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="user_id" id="user_id">user_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="role_id" id="role_id">role_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = user_id</strong></li>
+</ul>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = role_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = user_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#users">users</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = role_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#roles">roles</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+<li><strong>On delete = CASCADE</strong></li>
+</ul>
+<h2><a name="spreadsheets" id="spreadsheets">spreadsheets</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="experiment_id" id=
+"experiment_id">experiment_id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(255)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="indices" id="indices">INDICES</a></h3>
+<h4><a name="normal" id="normal">NORMAL</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="foreign_key" id="foreign_key">FOREIGN KEY</a></h4>
+<ul>
+<li><strong>Fields = experiment_id</strong></li>
+<li><strong>Reference Table = <a href=
+"#experiments">experiments</a></strong></li>
+<li><strong>Reference Fields = <a href="#id">id</a></strong></li>
+</ul>
+<h2><a name="taxons" id="taxons">taxons</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="scientific_name" id=
+"scientific_name">scientific_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="common_name" id="common_name">common_name</a></h4>
+<ul>
+<li><strong><code>varchar(50)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="accession" id="accession">accession</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h2><a name="users" id="users">users</a></h2>
+<h3><a name="fields" id="fields">FIELDS</a></h3>
+<h4><a name="id" id="id">id</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>PRIMARY KEY</strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="login" id="login">login</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="name" id="name">name</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="password" id="password">password</a></h4>
+<ul>
+<li><strong><code>varchar(40)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h4><a name="email" id="email">email</a></h4>
+<ul>
+<li><strong><code>varchar(100)</code></strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="modified_at" id="modified_at">modified_at</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="created_at" id="created_at">created_at</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="access" id="access">access</a></h4>
+<ul>
+<li><strong>datetime</strong></li>
+<li><strong>Default 'NULL'</strong></li>
+<li><strong>Nullable 'Yes'</strong></li>
+</ul>
+<h4><a name="is_deleted" id="is_deleted">is_deleted</a></h4>
+<ul>
+<li><strong><code>int(11)</code></strong></li>
+<li><strong>Nullable 'No'</strong></li>
+</ul>
+<h3><a name="constraints" id="constraints">CONSTRAINTS</a></h3>
+<h4><a name="primary_key" id="primary_key">PRIMARY KEY</a></h4>
+<ul>
+<li><strong>Fields = id</strong></li>
+</ul>
+<h4><a name="unique" id="unique">UNIQUE</a></h4>
+<ul>
+<li><strong><a name="item_fields__3d_login" id=
+"item_fields__3d_login">Fields = login</a></strong></li>
+</ul>
+<hr />
+<h1><a name="produced_by" id="produced_by">PRODUCED BY</a></h1>
+<p>SQL::Translator::Producer::POD</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/Spreadsheet.html b/docs/ArrayExpress/AutoSubmission/Spreadsheet.html
new file mode 100644
index 0000000..8344337
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/Spreadsheet.html
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::Spreadsheet - A
+Tab2MAGE/MAGE-TAB document templating class.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Spreadsheet.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::Spreadsheet - A Tab2MAGE/MAGE-TAB
+document templating class.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::Spreadsheet;
+ my $ss = ArrayExpress::AutoSubmission::Spreadsheet->new({
+ experiment => $expt,
+ url => $self->query()->url(),
+ date => date_now(),
+ });
+</pre>
+<pre>
+ $ss_content = $spreadsheet->magetab();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides Tab2MAGE and MAGE-TAB document templating
+functions. The functions take Class::DBI experiment objects from
+the tracking database and generate the appropriate output strings
+which can then be printed to file.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new</code></a></strong></dt>
+<dd>
+<p>Object constructor, taking the following arguments:</p>
+</dd>
+<dd>
+<dl>
+<dt><strong><a name="item_experiment" id=
+"item_experiment"><code>experiment</code></a></strong></dt>
+<dd>
+<p>The Class::DBI experiment object to use in template
+generation.</p>
+</dd>
+<dt><strong><a name="item_url" id=
+"item_url"><code>url</code></a></strong></dt>
+<dd>
+<p>The URL of the page to which completed templates should be
+submitted (this is embedded in the template for convenience).</p>
+</dd>
+<dt><strong><a name="item_date" id=
+"item_date"><code>date</code></a></strong></dt>
+<dd>
+<p>The date of template generation.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="item_magetab" id=
+"item_magetab"><code>magetab</code></a></strong></dt>
+<dd>
+<p>Return a string representing the MAGE-TAB template for this
+experiment.</p>
+</dd>
+<dt><strong><a name="item_tab2mage" id=
+"item_tab2mage"><code>tab2mage</code></a></strong></dt>
+<dd>
+<p>Return a string representing the Tab2MAGE template for this
+experiment.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/AutoSubmission/WebForm.html b/docs/ArrayExpress/AutoSubmission/WebForm.html
new file mode 100644
index 0000000..871f4c0
--- /dev/null
+++ b/docs/ArrayExpress/AutoSubmission/WebForm.html
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::AutoSubmission::WebForm - A CGI::Application
+web form for Tab2MAGE/MAGE-TAB data submissions.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#todo">TODO</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: WebForm.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::AutoSubmission::WebForm - A CGI::Application web
+form for Tab2MAGE/MAGE-TAB data submissions.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::AutoSubmission::WebForm;
+ my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+ PARAMS => {
+ experiment_type => 'MAGE-TAB',
+ spreadsheet_type => 'MAGE-TAB',
+ cgi_base => '/cgi-bin/microarray/magetab.cgi',
+ stylesheet => '/microarray/tab2mage.css',
+ intro_html_template => 'magetab_intro.html',
+ upload_html_template => 'magetab_upload.html',
+ template_html_template => 'magetab_template.html',
+ sidebar_icons => [
+ {
+ image => '/microarray/aelogo.png',
+ destination => '<a href=
+"http://www.ebi.ac.uk/arrayexpress/">http://www.ebi.ac.uk/arrayexpress/</a>',
+ alt => 'ArrayExpress',
+ },
+ ],
+ sidebar_links => [
+ {
+ text => 'Microarray group',
+ destination => '<a href=
+"http://www.ebi.ac.uk/microarray/">http://www.ebi.ac.uk/microarray/</a>',
+ },
+ ],
+ }
+ );
+</pre>
+<pre>
+ # Start the CGI script.
+ $webapp->run();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is the core of the web application used in the
+Tab2MAGE and MAGE-TAB data submission web forms. Objects are
+instantiated with a PARAMS attribute which can be used to customise
+various aspects of the web form behaviour.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new</code></a></strong></dt>
+<dd>
+<p>Object constructor. See the CGI::Application documentation for
+details.</p>
+</dd>
+<dt><strong><a name="item_run" id=
+"item_run"><code>run</code></a></strong></dt>
+<dd>
+<p>The method used to start the web application.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="todo" id="todo">TODO</a></h1>
+<p>Documentation of the options which can be customised using the
+PARAMS attribute.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Common.html b/docs/ArrayExpress/Curator/Common.html
new file mode 100644
index 0000000..891bb65
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Common.html
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Common.pm - a module providing some
+simple utility functions and regexps.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#functions">FUNCTIONS</a></li>
+ <li><a href="#regexps">REGEXPS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Common.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Common.pm - a module providing some
+simple utility functions and regexps.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Common qw(date_now $RE_EMPTY_STRING);
+
+ if ( $test =~ $RE_EMPTY_STRING ) {
+ print date_now();
+ }
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_strip_discards" id=
+"item_strip_discards"><code>strip_discards( $indexcols, $line_array
+)</code></a></strong></dt>
+<dd>
+<p>Sub to strip values out of an arrayref ($line_array) based on an
+arrayref of unwanted array indices ($indexcols). Does not modify
+the input lists, but instead returns a suitably stripped
+arrayref.</p>
+</dd>
+<dt><strong><a name="item_round" id="item_round"><code>round(
+$number, $precision )</code></a></strong></dt>
+<dd>
+<p>A simple rounding function that returns $number rounded to
+$precision decimal places.</p>
+</dd>
+<dt><strong><a name="item_get_indexcol" id=
+"item_get_indexcol"><code>get_indexcol( $list, $name
+)</code></a></strong></dt>
+<dd>
+<p>Function to return the first index within @$list matching the
+string or regexp passed as $name. Returns -1 on failure.</p>
+</dd>
+<dt><strong><a name="item_check_linebreaks" id=
+"item_check_linebreaks"><code>check_linebreaks( $path
+)</code></a></strong></dt>
+<dd>
+<p>Takes a filename as an argument, checks for Mac, Unix or DOS
+line endings by reading the whole file in chunks, and regexp
+matching the various linebreak types. Returns the appropriate
+linebreak for acceptable line breaks (line breaks must be
+unanimous), undef if a consensus is unreachable.</p>
+</dd>
+<dt><strong><a name="item_clean_hash" id=
+"item_clean_hash"><code>clean_hash( $hashref
+)</code></a></strong></dt>
+<dd>
+<p>Strip out undef or empty string values from $hashref.</p>
+</dd>
+<dt><strong><a name="item_date_now" id=
+"item_date_now"><code>date_now()</code></a></strong></dt>
+<dd>
+<p>Return current date and time as a MAGE best practice date-time
+string (uses mage_date(), below).</p>
+</dd>
+<dt><strong><a name="item_mage_date" id=
+"item_mage_date"><code>mage_date( $time )</code></a></strong></dt>
+<dd>
+<p>When passed the return value from e.g. perl's
+<code>time()</code> built-in, returns a MAGE best practice
+date-time string. This string will always be from the UTC/GMT time
+zone.</p>
+</dd>
+<dt><strong><a name="item_untaint" id="item_untaint"><code>untaint(
+$string )</code></a></strong></dt>
+<dd>
+<p>A convenient data untainting function which replaces any run of
+non-whitelisted characters with a single underscore.</p>
+</dd>
+<dt><strong><a name="item_find_cdf" id=
+"item_find_cdf"><code>find_cdf( $name, $dir
+)</code></a></strong></dt>
+<dd>
+<p>Given a filename and an optional directory, returns an actual
+filename found in a case-insensitive fashion. Preferentially checks
+the main Affy library directory if set in $CONFIG (see <a href=
+"./Config.html">the ArrayExpress::Curator::Config manpage</a>).</p>
+</dd>
+<dt><strong><a name="item_decamelize" id=
+"item_decamelize"><code>decamelize( $string
+)</code></a></strong></dt>
+<dd>
+<p>Function to convert CamelCase strings to
+underscore_delimited.</p>
+</dd>
+<dt><strong><a name="item_get_filepath_from_uri" id=
+"item_get_filepath_from_uri"><code>get_filepath_from_uri( $string,
+$dir )</code></a></strong></dt>
+<dd>
+<p>Given a string and an optional directory argument, this function
+determines which URI scheme is in use (default is ``file://''), and
+downloads ``http://'' and ``ftp://'' URIs to either the indicated
+filesystem directory or the current working directory. Returns the
+filesystem path to the file.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="regexps" id="regexps">REGEXPS</a></h1>
+<dl>
+<dt><strong><a name="item__re_empty_string" id=
+"item__re_empty_string"><code>$RE_EMPTY_STRING</code></a></strong></dt>
+<dd>
+<p>Matches an empty string (whitespace ignored).</p>
+</dd>
+<dt><strong><a name="item__re_commented_string" id=
+"item__re_commented_string"><code>$RE_COMMENTED_STRING</code></a></strong></dt>
+<dd>
+<p>Matches a string beginning with #.</p>
+</dd>
+<dt><strong><a name="item__re_surrounded_by_whitespace" id=
+"item__re_surrounded_by_whitespace"><code>$RE_SURROUNDED_BY_WHITESPACE</code></a></strong></dt>
+<dd>
+<p>Matches a string with whitespace on either side; $1 contains the
+string minus the whitespace.</p>
+</dd>
+<dt><strong><a name="item__re_within_parentheses" id=
+"item__re_within_parentheses"><code>$RE_WITHIN_PARENTHESES</code></a></strong></dt>
+<dd>
+<p>Matches a string with parentheses on either side; $1 contains
+the string minus the parentheses.</p>
+</dd>
+<dt><strong><a name="item__re_within_brackets" id=
+"item__re_within_brackets"><code>$RE_WITHIN_BRACKETS</code></a></strong></dt>
+<dd>
+<p>Matches a string with brackets on either side; $1 contains the
+string minus the brackets.</p>
+</dd>
+<dt><strong><a name="item__re_square_brackets" id=
+"item__re_square_brackets"><code>$RE_SQUARE_BRACKETS</code></a></strong></dt>
+<dd>
+<p>Matches either [ or ]; $1 contains the matched character.</p>
+</dd>
+<dt><strong><a name="item__re_line_break" id=
+"item__re_line_break"><code>$RE_LINE_BREAK</code></a></strong></dt>
+<dd>
+<p>Matches a linebreak (DOS, Unix or MacOS).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Config.html b/docs/ArrayExpress/Curator/Config.html
new file mode 100644
index 0000000..cf1c2a2
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Config.html
@@ -0,0 +1,462 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Config.pm - a module defining general
+config options.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <ul>
+
+ <li><a href="#configuration_options">Configuration options</a></li>
+ </ul>
+
+ <li><a href="#private_options">Private options</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Config.pm - a module defining general
+config options.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Config qw($CONFIG);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides definition of some configuration options
+used throughout the code. These options may be changed by editing
+the ArrayExpress/Curator/Config.yml file. If you wish to point your
+Tab2MAGE installation to an alternate site config YAML file, please
+edit the $siteconf variable in the ArrayExpress/Curator/Config.pm
+file. For user-specific configuration, this module will also read
+any .tab2mage.conf file located in your home directory (i.e.,
+$HOME/.tab2mage.conf).</p>
+<p>See <a href="MAGE/Definitions.html">the
+ArrayExpress::Curator::MAGE::Definitions manpage</a> for other
+MAGE-specific constants.</p>
+<h2><a name="configuration_options" id=
+"configuration_options">Configuration options</a></h2>
+<dl>
+<dt><strong><a name="item_mx_dsn" id=
+"item_mx_dsn">MX_DSN</a></strong></dt>
+<dd>
+<p>MIAMExpress data source name or DSN, e.g.,
+``DBI:mysql:db_name:host:port''.</p>
+</dd>
+<dt><strong><a name="item_mx_username" id=
+"item_mx_username">MX_USERNAME</a></strong></dt>
+<dd>
+<p>The username used to connect to the MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_mx_password" id=
+"item_mx_password">MX_PASSWORD</a></strong></dt>
+<dd>
+<p>The password used to connect to the MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_ae_dsn" id=
+"item_ae_dsn">AE_DSN</a></strong></dt>
+<dd>
+<p>For local ArrayExpress installations only. This is the
+ArrayExpress data source name or DSN, e.g.,
+``DBI:Oracle:db_name:host:port'' used in direct connection to the
+database. Note that the appropriate DBD::Oracle module must be
+installed for this to work.</p>
+</dd>
+<dt><strong><a name="item_ae_username" id=
+"item_ae_username">AE_USERNAME</a></strong></dt>
+<dd>
+<p>The username used to connect to the ArrayExpress database.</p>
+</dd>
+<dt><strong><a name="item_ae_password" id=
+"item_ae_password">AE_PASSWORD</a></strong></dt>
+<dd>
+<p>The password used to connect to the ArrayExpress database.</p>
+</dd>
+<dt><strong><a name="item_aedw_dsn" id=
+"item_aedw_dsn">AEDW_DSN</a></strong></dt>
+<dd>
+<p>For checking the data warehouse readiness of an experiment
+submission. This database connection is used in checking that any
+required array designs have been loaded into the data warehouse.
+This config value should be the ArrayExpress DW data source name or
+DSN, e.g., ``DBI:Oracle:db_name:host:port'' used in direct
+connection to the data warehouse. Note that the appropriate
+DBD::Oracle module must be installed for this to work.</p>
+</dd>
+<dt><strong><a name="item_aedw_username" id=
+"item_aedw_username">AEDW_USERNAME</a></strong></dt>
+<dd>
+<p>The username used to connect to the AEDW database.</p>
+</dd>
+<dt><strong><a name="item_aedw_password" id=
+"item_aedw_password">AEDW_PASSWORD</a></strong></dt>
+<dd>
+<p>The password used to connect to the AEDW database.</p>
+</dd>
+<dt><strong><a name="item_ae_arraydesign_list" id=
+"item_ae_arraydesign_list">AE_ARRAYDESIGN_LIST</a></strong></dt>
+<dd>
+<p>Remote ArrayExpress array design list web page. Used by
+Tab2MAGE, MIAMExpress and the experiment checker. This is currently
+accessible from outside EBI, and so the setting can be left as it
+is.</p>
+</dd>
+<dt><strong><a name="item_ae_retrieve_featurelist" id=
+"item_ae_retrieve_featurelist">AE_RETRIEVE_FEATURELIST</a></strong></dt>
+<dd>
+<p>The ArrayExpress web page root for retrieving array feature
+lists. In use, the accession number for the PhysicalArrayDesign is
+appended to AE_RETRIEVE_FEATURELIST before it is used in an HTTP
+GET.</p>
+</dd>
+<dt><strong><a name="item_ae_retrieve_adf" id=
+"item_ae_retrieve_adf">AE_RETRIEVE_ADF</a></strong></dt>
+<dd>
+<p>The ArrayExpress web page root for retrieving ADFs. In use, the
+database identifier for the PhysicalArrayDesign is appended to
+AE_RETRIEVE_ADF before it is used in an HTTP GET.</p>
+</dd>
+<dt><strong><a name="item_affymetrix_librarypath" id=
+"item_affymetrix_librarypath">AFFYMETRIX_LIBRARYPATH</a></strong></dt>
+<dd>
+<p>The path to a directory containing CDF files needed for parsing
+Affymetrix CHP files. The default value is an empty string, which
+indicates to the scripts that the CDF files are in the current
+working directory.</p>
+</dd>
+<dt><strong><a name="item_t2m_protocol_prefix" id=
+"item_t2m_protocol_prefix">T2M_PROTOCOL_PREFIX</a></strong></dt>
+<dd>
+<p>The prefix used to autogenerate reassigned protocol accessions.
+Default value is ``P-TABM-''. Please note that you should change
+this to prevent conflicts if you intend to use protocol accession
+reassignment and submit the resulting MAGE-ML to ArrayExpress.</p>
+</dd>
+<dt><strong><a name="item_t2m_experiment_prefix" id=
+"item_t2m_experiment_prefix">T2M_EXPERIMENT_PREFIX</a></strong></dt>
+<dd>
+<p>The prefix used in creating experiment accessions. This is used
+to check that a valid experiment accession number has been used in
+conjunction with the protocol reassignment mechanism.</p>
+</dd>
+<dt><strong><a name="item_max_lwp_download" id=
+"item_max_lwp_download">MAX_LWP_DOWNLOAD</a></strong></dt>
+<dd>
+<p>The maximum size of LWP::UserAgent downloads. This applies to
+ArrayExpress ADF and feature list downloads. Currently set to
+40MB.</p>
+</dd>
+<dt><strong><a name="item_max_datafile_size" id=
+"item_max_datafile_size">MAX_DATAFILE_SIZE</a></strong></dt>
+<dd>
+<p>The maximum size of data file which these scripts will attempt
+to parse. Currently set to 100MB.</p>
+</dd>
+<dt><strong><a name="item_visualize_font" id=
+"item_visualize_font">VISUALIZE_FONT</a></strong></dt>
+<dd>
+<p>The name of the default font to use in visualization graph
+creation. This gets passed to the ``dot'' program. Currently set to
+``Courier''.</p>
+</dd>
+<dt><strong><a name="item_autosubs_pidfile" id=
+"item_autosubs_pidfile">AUTOSUBS_PIDFILE</a></strong></dt>
+<dd>
+<p>Full path to the file used by autosubs_checkd.pl to track
+running instances of itself.</p>
+</dd>
+<dt><strong><a name="item_autosubs_admin" id=
+"item_autosubs_admin">AUTOSUBS_ADMIN</a></strong></dt>
+<dd>
+<p>Email address of the administrator responsible for managing the
+checker and exporter daemon processes. Emails will be sent to this
+address on abnormal termination of the process (e.g. on
+crashes).</p>
+</dd>
+<dt><strong><a name="item_autosubs_admin_username" id=
+"item_autosubs_admin_username">AUTOSUBS_ADMIN_USERNAME</a></strong></dt>
+<dd>
+<p>Login for the administrator responsible for managing the checker
+and exporter daemon processes. Other users are restricted from
+launching these daemons to aid in process management.</p>
+</dd>
+<dt><strong><a name="item_autosubs_domain" id=
+"item_autosubs_domain">AUTOSUBS_DOMAIN</a></strong></dt>
+<dd>
+<p>The default domain used in creating MAGE-ML identifiers when
+exporting experiments using the autosubmissions system. This is set
+by default to 'ebi.ac.uk'. Note that this does not affect
+submissions exported from a MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_autosubs_curator_email" id=
+"item_autosubs_curator_email">AUTOSUBS_CURATOR_EMAIL</a></strong></dt>
+<dd>
+<p>The email to which enquiries will be directed from the
+autosubmissions web form.</p>
+</dd>
+<dt><strong><a name="item_autosubs_smtp_server" id=
+"item_autosubs_smtp_server">AUTOSUBS_SMTP_SERVER</a></strong></dt>
+<dd>
+<p>The SMTP server used to send notification email from the
+autosubmissions system.</p>
+</dd>
+<dt><strong><a name="item_autosubs_dsn" id=
+"item_autosubs_dsn">AUTOSUBS_DSN</a></strong></dt>
+<dd>
+<p>The DSN to use when connecting to the autosubmissions database
+system. Typically this will be of the form
+``DBI:mysql:dbname:host:port''.</p>
+</dd>
+<dt><strong><a name="item_autosubs_username" id=
+"item_autosubs_username">AUTOSUBS_USERNAME</a></strong></dt>
+<dd>
+<p>The username to use when connecting to the autosubmissions
+database.</p>
+</dd>
+<dt><strong><a name="item_autosubs_password" id=
+"item_autosubs_password">AUTOSUBS_PASSWORD</a></strong></dt>
+<dd>
+<p>The password to use when connecting to the autosubmissions
+database.</p>
+</dd>
+<dt><strong><a name="item_autosubmissions_filebase" id=
+"item_autosubmissions_filebase">AUTOSUBMISSIONS_FILEBASE</a></strong></dt>
+<dd>
+<p>The filesystem path to the top-level directory where the
+autosubmissions system should store uploaded spreadsheets and data
+files.</p>
+</dd>
+<dt><strong><a name="item_autosubmissions_target" id=
+"item_autosubmissions_target">AUTOSUBMISSIONS_TARGET</a></strong></dt>
+<dd>
+<p>The filesystem directory into which new submissions are exported
+as MAGE-ML. A new directory, named using the automatically assigned
+experiment accession, will be created and populated.</p>
+</dd>
+<dt><strong><a name="item_webform_template_dir" id=
+"item_webform_template_dir">WEBFORM_TEMPLATE_DIR</a></strong></dt>
+<dd>
+<p>The filesystem directory where the web submissions form may
+write temporary files. This is principally used for temporary
+storage of spreadsheet template files. It is recommended that you
+run a regular script from e.g. your crontab to delete old
+files.</p>
+</dd>
+<dt><strong><a name="item_webform_template_url" id=
+"item_webform_template_url">WEBFORM_TEMPLATE_URL</a></strong></dt>
+<dd>
+<p>The URL pointing to the WEBFORM_TEMPLATE_DIR, used to create
+links to the temporary template files from the web submissions
+form.</p>
+</dd>
+<dt><strong><a name="item_mx_autosubs_pidfile" id=
+"item_mx_autosubs_pidfile">MX_AUTOSUBS_PIDFILE</a></strong></dt>
+<dd>
+<p>Full path to the file used by mx_autocheck_daemon.pl to track
+running instances of itself.</p>
+</dd>
+<dt><strong><a name="item_mx_autosubs_admin" id=
+"item_mx_autosubs_admin">MX_AUTOSUBS_ADMIN</a></strong></dt>
+<dd>
+<p>Email address of the administrator responsible for managing the
+mx_autocheck_daemon.pl process. See notes for AUTOSUBS_ADMIN.</p>
+</dd>
+<dt><strong><a name="item_mx_mageml_export_command" id=
+"item_mx_mageml_export_command">MX_MAGEML_EXPORT_COMMAND</a></strong></dt>
+<dd>
+<p>Full command (minus arguments) used to export MAGE-ML from a
+local MIAMExpress database.</p>
+</dd>
+<dt><strong><a name="item_aedw_design_types" id=
+"item_aedw_design_types">AEDW_DESIGN_TYPES</a></strong></dt>
+<dd>
+<p>A list of MO ExperimentDesignTypes that indicate an experiment
+is suitable for the ArrayExpress Data Warehouse.</p>
+</dd>
+<dt><strong><a name="item_aedw_unwanted_design_types" id=
+"item_aedw_unwanted_design_types">AEDW_UNWANTED_DESIGN_TYPES</a></strong></dt>
+<dd>
+<p>A list of MO ExperimentDesignTypes that indicate an experiment
+is not suitable for the ArrayExpress Data Warehouse. This is used
+to differentiate between experiments known to be of the wrong type
+from those which are merely under-annotated.</p>
+</dd>
+<dt><strong><a name="item_aedw_minimum_hybs" id=
+"item_aedw_minimum_hybs">AEDW_MINIMUM_HYBS</a></strong></dt>
+<dd>
+<p>The minimum number of hybridizations required in an experiment
+for it to be considered for the AE Data Warehouse.</p>
+</dd>
+<dt><strong><a name="item_miame_compliant_array_pipelines" id=
+"item_miame_compliant_array_pipelines">MIAME_COMPLIANT_ARRAY_PIPELINES</a></strong></dt>
+<dd>
+<p>A list of array design accession prefixes (e.g. ``A-AFFY-'')
+where every design can be assumed to be MIAME compliant, for the
+purposes of experiment MIAME checking.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="private_options" id="private_options">Private
+options</a></h1>
+<p>These are options we recommend you don't change, unless you know
+what you're doing.</p>
+<dl>
+<dt><strong><a name="item_t2m_indices" id=
+"item_t2m_indices">T2M_INDICES</a></strong></dt>
+<dd>
+<p>A hashref, with keys representing data file format type and
+values as arrayrefs listing the coordinate index columns to be used
+for parsing those formats. See also <a href="../Datafile.html">the
+ArrayExpress::Datafile manpage</a>. Current format types are:
+Generic, GenePix, Affymetrix, ArrayVision, Agilent, Scanalyze,
+ScanArray, QuantArray, Spotfinder, BlueFuse, UCSF Spot, Illumina,
+CodeLink, Applied Biosystems, NimbleScan.</p>
+</dd>
+<dt><strong><a name="item_t2m_file_types" id=
+"item_t2m_file_types">T2M_FILE_TYPES</a></strong></dt>
+<dd>
+<p>Supported data file types for per-hyb parsing and MAGE-ML
+creation. These tags can appear in the Tab2MAGE spreadsheet as part
+of a File[] column heading. Currently supported: ``raw'' and
+``normalized''.</p>
+</dd>
+<dt><strong><a name="item_fgem_file_type" id=
+"item_fgem_file_type">FGEM_FILE_TYPE</a></strong></dt>
+<dd>
+<p>Supported data file type for FGEM parsing and MAGE-ML creation.
+These tags can appear in the Tab2MAGE spreadsheet as part of a
+File[] column heading. Currently supported: ``transformed''.</p>
+</dd>
+<dt><strong><a name="item_ignored_qts" id=
+"item_ignored_qts">IGNORED_QTS</a></strong></dt>
+<dd>
+<p>A list of regular expression which match QTs which are omitted
+from analyses or MAGE-ML output from non-data matrix files.</p>
+</dd>
+<dt><strong><a name=
+"item_error_innocent_2c_error_miame_2c_error_parsebad_2c" id=
+"item_error_innocent_2c_error_miame_2c_error_parsebad_2c">ERROR_INNOCENT,
+ERROR_MIAME, ERROR_PARSEBAD, ERROR_PARSEFAIL</a></strong></dt>
+<dd>
+<p>Errors returned to the shell by each of the scripts are
+represented by 8-bit integers; here we map them to the constants
+used.</p>
+</dd>
+<dt><strong><a name="item_error_checkercrash" id=
+"item_error_checkercrash">ERROR_CHECKERCRASH</a></strong></dt>
+<dd>
+<p>Similar to the errors above, this error indicates that the
+checking process crashed for some reason.</p>
+</dd>
+<dt><strong><a name=
+"item_error_message_args_2c_error_message_private" id=
+"item_error_message_args_2c_error_message_private">ERROR_MESSAGE_ARGS,
+ERROR_MESSAGE_PRIVATE</a></strong></dt>
+<dd>
+<p>A selection of internal error message texts.</p>
+</dd>
+<dt><strong><a name="item_mx_extended_report" id=
+"item_mx_extended_report">MX_EXTENDED_REPORT</a></strong></dt>
+<dd>
+<p>Controls whether extended reporting of sample annotation and
+factor values is available (only supported for generic MIAMExpress
+installations).</p>
+</dd>
+<dt><strong><a name="item_default_qt_filename" id=
+"item_default_qt_filename">DEFAULT_QT_FILENAME</a></strong></dt>
+<dd>
+<p>Name of the file to use as the default source of QT information.
+This is used to point to a file installed alongside the perl
+modules, and should not be changed unless you know what you're
+doing.</p>
+</dd>
+<dt><strong><a name="item_default_entrez_filename" id=
+"item_default_entrez_filename">DEFAULT_ENTREZ_FILENAME</a></strong></dt>
+<dd>
+<p>Name of the file containing a list of Entrez-approved
+publication abbreviations. Again, this value should not be changed
+unless strictly necessary.</p>
+</dd>
+<dt><strong><a name="item_file_permissions" id=
+"item_file_permissions">FILE_PERMISSIONS</a></strong></dt>
+<dd>
+<p>Octal number indicating the default permissions to use when
+creating files using the autosubmission system. This is useful if,
+for example, your webserver process is in a different group from
+that of your users. The default is 0555.</p>
+</dd>
+<dt><strong><a name="item_dir_permissions" id=
+"item_dir_permissions">DIR_PERMISSIONS</a></strong></dt>
+<dd>
+<p>Octal number indicating the default directory permissions for
+the autosubmission system. The default is 0777.</p>
+</dd>
+<dt><strong><a name="item_mx_dbparams" id=
+"item_mx_dbparams">MX_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the MIAMExpress database
+connection.</p>
+</dd>
+<dt><strong><a name="item_ae_dbparams" id=
+"item_ae_dbparams">AE_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the ArrayExpress database
+connection.</p>
+</dd>
+<dt><strong><a name="item_aedw_dbparams" id=
+"item_aedw_dbparams">AEDW_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the AEDW database
+connection.</p>
+</dd>
+<dt><strong><a name="item_autosubs_dbparams" id=
+"item_autosubs_dbparams">AUTOSUBS_DBPARAMS</a></strong></dt>
+<dd>
+<p>A hashref of parameters used in the autosubmissions database
+connection.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Database.html b/docs/ArrayExpress/Curator/Database.html
new file mode 100644
index 0000000..7bcd15b
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Database.html
@@ -0,0 +1,137 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Database - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#functions">FUNCTIONS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Database.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Database - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Database qw(retrieve_AE_adf parse_adf);
+</pre>
+<pre>
+ my $db_id = 123456789;
+ my ( $fh, $accession ) = retrieve_AE_adf( $db_id, undef, \*STDERR, 0 );
+ my $array_design = parse_adf( $fh, $accession );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to interact with the ArrayExpress databases. Queries
+may be redirected to local ArrayExpress instances by editing values
+in the <a href="./Config.html">the ArrayExpress::Curator::Config
+manpage</a> package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_get_ae_dbh" id=
+"item_get_ae_dbh"><code>get_ae_dbh()</code></a></strong></dt>
+<dd>
+<p>Returns a singleton database handle for the currently-configured
+ArrayExpress repository database instance.</p>
+</dd>
+<dt><strong><a name="item_get_aedw_dbh" id=
+"item_get_aedw_dbh"><code>get_aedw_dbh()</code></a></strong></dt>
+<dd>
+<p>Returns a singleton database handle for the currently-configured
+ArrayExpress warehouse database instance.</p>
+</dd>
+<dt><strong><a name="item_retrieve_ae_adf" id=
+"item_retrieve_ae_adf"><code>retrieve_AE_adf( $db_id, $accession,
+$error_fh, $skip_adf_download )</code></a></strong></dt>
+<dd>
+<p>Given either AE database table row identifier or AE accession,
+an open filehandle for error reporting and a flag indicating
+whether the ADF should be downloaded, check for the existence of
+said ADF (by database id or accession), download it and return an
+opened filehandle and the accession number (the latter is useful
+when querying by database id).</p>
+</dd>
+<dt><strong><a name="item_retrieve_ae_featurelist" id=
+"item_retrieve_ae_featurelist"><code>retrieve_AE_featurelist(
+$accession )</code></a></strong></dt>
+<dd>
+<p>Given an array accession number, return a hashref where the keys
+are q{.}-delimited lists of MetaColumn.MetaRow.Column.Row feature
+coordinates, and the values are Reporter identifiers.</p>
+</dd>
+<dt><strong><a name="item_parse_adf" id=
+"item_parse_adf"><code>parse_adf( $adf_fh, $accno,
+$reporter_prefix, $compseq_prefix )</code></a></strong></dt>
+<dd>
+<p>Passed an ADF filehandle and an accession number (see
+retrieve_AE_adf()), and optional Reporter or Composite Sequence
+identifier prefixes, parse the ADF and return an ArrayDesign object
+(see <a href="../Datafile/ArrayDesign.html">the
+ArrayExpress::Datafile::ArrayDesign manpage</a>).</p>
+</dd>
+<dt><strong><a name="item_arrayaccession_in_aedw" id=
+"item_arrayaccession_in_aedw"><code>arrayaccession_in_aedw(
+$accession )</code></a></strong></dt>
+<dd>
+<p>Given an array design accession number, return true if the
+design is loaded into the currently-configured AE data warehouse,
+false otherwise.</p>
+</dd>
+<dt><strong><a name="item_map_affy_accno_to_name" id=
+"item_map_affy_accno_to_name"><code>map_affy_accno_to_name(
+$accession )</code></a></strong></dt>
+<dd>
+<p>Given an array design accession number, attempt to match it to
+an Affymetrix design name. This relies on the names of loaded Affy
+array designs including the design name (e.g. HG-U133A) in square
+brackets (e.g. ``Affymetrix GeneChip Human Genome HG-U133A
+[HG-U133A]'').</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Entrez_list.html b/docs/ArrayExpress/Curator/Entrez_list.html
new file mode 100644
index 0000000..e070fd3
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Entrez_list.html
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Entrez_list - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#functions">FUNCTIONS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Entrez_list.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Entrez_list - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Entrez_list qw(parse_entrez_names);
+
+ my $is_recognized = parse_entrez_names();
+ die("Error: Unknown journal: $pub") unless $is_recognized->{ $pub };
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a very basic interface to the listing of
+approved Entrez publication names included with the Tab2MAGE
+package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_entrez_names" id=
+"item_parse_entrez_names"><code>parse_entrez_names( $fh
+)</code></a></strong></dt>
+<dd>
+<p>Parses the contents of either the optional passed filehandle, or
+the internally-defined Entrez_list.txt filehandle, to generate a
+hashref of publication name keys and true values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/ExperimentChecker.html b/docs/ArrayExpress/Curator/ExperimentChecker.html
new file mode 100644
index 0000000..71fbac8
--- /dev/null
+++ b/docs/ArrayExpress/Curator/ExperimentChecker.html
@@ -0,0 +1,392 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::ExperimentChecker - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#files">FILES</a></li>
+ <li><a href="#tests">TESTS</a></li>
+ <li><a href="#see_also">SEE ALSO</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::ExperimentChecker - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw/ArrayExpress::Curator::ExperimentChecker/;
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module represents an abstract parent class providing
+methods for data file and experiment annotation checks to its child
+classes. See the following documentation for concrete checker
+classes: <a href="./Validate.html">the
+ArrayExpress::Curator::Validate manpage</a> (Tab2MAGE), <a href=
+"../MAGETAB/Checker.html">the ArrayExpress::MAGETAB::Checker
+manpage</a> (MAGE-TAB), <a href="./MIAMExpress.html">the
+ArrayExpress::Curator::MIAMExpress manpage</a> (MIAMExpress) and
+<a href="./Standalone.html">the ArrayExpress::Curator::Standalone
+manpage</a> (standalone).</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The objects created by this module can be instantiated with the
+following options, common to all submission routes.</p>
+<dl>
+<dt><strong><a name="item_log_to_current_dir" id=
+"item_log_to_current_dir"><code>log_to_current_dir</code></a></strong></dt>
+<dd>
+<p>Write the log files to the current working directory, rather
+than in the submissions directory.</p>
+</dd>
+<dt><strong><a name="item_is_standalone" id=
+"item_is_standalone"><code>is_standalone</code></a></strong></dt>
+<dd>
+<p>Run checker in standalone mode.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>Overwrite existing log and graph files without asking for
+confirmation.</p>
+</dd>
+<dt><strong><a name="item_adf_filename" id=
+"item_adf_filename"><code>adf_filename</code></a></strong></dt>
+<dd>
+<p>The name of the file to use as ADF in feature/reporter checks.
+This overrides any array designs specified in the submission.</p>
+</dd>
+<dt><strong><a name="item_array_accession" id=
+"item_array_accession"><code>array_accession</code></a></strong></dt>
+<dd>
+<p>The ArrayExpress accession number of the array design to use for
+feature/reporter checks. This overrides any array accessions
+specified in the submission.</p>
+</dd>
+<dt><strong><a name="item_qt_filename" id=
+"item_qt_filename"><code>qt_filename</code></a></strong></dt>
+<dd>
+<p>The name of the file to be used for QT definitions. See <a href=
+"../Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a> for information on the format of this file. See also
+<a href=
+"#item_include_default_qts"><code>include_default_qts</code></a>.</p>
+</dd>
+<dt><strong><a name="item_include_default_qts" id=
+"item_include_default_qts"><code>include_default_qts</code></a></strong></dt>
+<dd>
+<p>Use the QT definitions supplied with these scripts alongside any
+new QT definitions provided by the <a href=
+"#item_qt_filename"><code>qt_filename</code></a> option.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_check" id=
+"item_check"><code>check()</code></a></strong></dt>
+<dd>
+<p>Starts the checks, based on the options specified in the
+constructor.</p>
+</dd>
+<dt><strong><a name="item_get_miamexpress_software_type" id=
+"item_get_miamexpress_software_type"><code>get_miamexpress_software_type()</code></a></strong></dt>
+<dd>
+<p>Returns the software term to use for a MIAMExpress export, if a
+unanimous verdict can be reached. Otherwise returns undef. This is
+also used for information purposes with Tab2MAGE and MAGE-TAB
+submissions, which is why it's in this superclass.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="files" id="files">FILES</a></h1>
+<p>There are currently five log files written out by the
+script:</p>
+<dl>
+<dt><strong><a name="item_expt_report_2elog" id=
+"item_expt_report_2elog"><code>expt_report.log</code></a></strong></dt>
+<dd>
+<p>Contains some summary information and data quality statistics
+for each data file.</p>
+</dd>
+<dt><strong><a name="item_expt_errors_2elog" id=
+"item_expt_errors_2elog"><code>expt_errors.log</code></a></strong></dt>
+<dd>
+<p>Lists the errors that were encountered in parsing the data
+files. Details on Feature and QuantitationType errors are given in
+separate log files. Qualifier Value Source usage is also logged in
+this error log file.</p>
+</dd>
+<dt><strong><a name="item_expt_biomaterials_2elog" id=
+"item_expt_biomaterials_2elog"><code>expt_biomaterials.log</code></a></strong></dt>
+<dd>
+<p>Provides a basic sanity check of the flow of BioMaterials
+through the experiment (i.e. Sample -> Extract -> Labeled
+Extract -> Hybridization). For Tab2MAGE checking the majority of
+the information in this file has been moved to an output PNG file,
+since it is a much clearer and more flexible visualization
+format.</p>
+</dd>
+<dt><strong><a name="item_expt_feature_2elog" id=
+"item_expt_feature_2elog"><code>expt_feature.log</code></a></strong></dt>
+<dd>
+<p>Lists feature coordinates and/or reporter identifiers (FGEM
+only) missing from the array <code>design(s)</code> used in the
+experiment. Entries here will typically mean that a dummy array has
+to be used. Update: This file now also lists duplicate features in
+a separate list.</p>
+</dd>
+<dt><strong><a name="item_expt_columnheadings_2elog" id=
+"item_expt_columnheadings_2elog"><code>expt_columnheadings.log</code></a></strong></dt>
+<dd>
+<p>Lists unrecognized QuantitationTypes or hybridization IDs (FGEM
+only) appearing in the data column headings.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report filehandles:</p>
+<dl>
+<dt><strong><a name="item_file_existence" id=
+"item_file_existence"><strong>File
+existence</strong></a></strong></dt>
+<dd>
+<p>Checks that the data files referred to in the Tab2MAGE
+spreadsheet/MAGE-TAB SDRF/MIAMExpress database actually exist on
+the filesystem (error log).</p>
+</dd>
+<dt><strong><a name="item_affymetrix_chp_file_as_normalized_data"
+id="item_affymetrix_chp_file_as_normalized_data"><strong>Affymetrix
+CHP file as normalized data</strong></a></strong></dt>
+<dd>
+<p>Checks that CHP files have been submitted as normalized rather
+than raw data (error log).</p>
+</dd>
+<dt><strong><a name="item_text_file_check" id=
+"item_text_file_check"><strong>Text file
+check</strong></a></strong></dt>
+<dd>
+<p>Confirms that submitted data files are text, not binary. This
+test is not applied to Affymetrix CHP files.</p>
+</dd>
+<dt><strong><a name="item_file_line_endings_check" id=
+"item_file_line_endings_check"><strong>File line endings
+check</strong></a></strong></dt>
+<dd>
+<p>Checks for Unix/DOS/Mac line endings (report log).</p>
+</dd>
+<dt><strong><a name="item_affymetrix_exp_file_check" id=
+"item_affymetrix_exp_file_check"><strong>Affymetrix EXP file
+check</strong></a></strong></dt>
+<dd>
+<p>Checks that EXP files are submitted as raw data and that they
+all have Protocol, Station and Module information (error log).</p>
+</dd>
+<dt><strong><a name="item_basic_data_file_summary" id=
+"item_basic_data_file_summary"><strong>Basic data file
+summary</strong></a></strong></dt>
+<dd>
+<p>Prints out the file format (Affymetrix, GenePix or Generic), the
+type (raw, normalized or transformed), row and column counts
+(report log).</p>
+</dd>
+<dt><strong><a name="item_duplicate_columns" id=
+"item_duplicate_columns"><strong>Duplicate
+columns</strong></a></strong></dt>
+<dd>
+<p>Checks for repeated column headings in each data file (error
+log).</p>
+</dd>
+<dt><strong><a name="item_excel_truncated_files" id=
+"item_excel_truncated_files"><strong>Excel truncated
+files</strong></a></strong></dt>
+<dd>
+<p>Checks for possible data corruption by Excel truncation of the
+file (error log).</p>
+</dd>
+<dt><strong><a name="item_quantitationtypes" id=
+"item_quantitationtypes"><strong>QuantitationTypes</strong></a></strong></dt>
+<dd>
+<p>Checks column headings against a list of known QTs. Reports on
+unrecognized QTs (error log, column headings log). This is a work
+in progress.</p>
+</dd>
+<dt><strong><a name="item_fgem_hybridization_ids" id=
+"item_fgem_hybridization_ids"><strong>FGEM hybridization
+IDs</strong></a></strong></dt>
+<dd>
+<p>Checks FGEM column headings against the hybridization IDs for
+the submission, reports on those which are not recognized. This
+incorporates a check on the QTs for FGEM files (error log, column
+headings log).</p>
+</dd>
+<dt><strong><a name="item_fgem_biodatacube_order" id=
+"item_fgem_biodatacube_order"><strong>FGEM BioDataCube
+order</strong></a></strong></dt>
+<dd>
+<p>Checks that the included final data matrix is laid out in DBQ
+order, rather than DQB. The ArrayExpress MAGE-ML loader software
+does not support the DQB order.</p>
+</dd>
+<dt><strong><a name="item_data_checks" id=
+"item_data_checks"><strong>Data checks</strong></a></strong></dt>
+<dd>
+<p>Data checks are only performed on recognized QT columns. Checks
+are for:</p>
+</dd>
+<dd>
+<pre>
+ Text in numeric columns
+ Null values in numeric columns
+ Floats in integer columns
+ Inappropriate boolean values (i.e., not 0 or 1)
+ Log ratios outside reasonable range
+ Basic check on saturation indicators (primarily GenePix files)
+</pre></dd>
+<dd>
+<p>(error log).</p>
+</dd>
+<dt><strong><a name="item_benford_27s_law" id=
+"item_benford_27s_law"><strong>Benford's
+law</strong></a></strong></dt>
+<dd>
+<p>Calculates Benford's law across dimensioned float data. In
+theory this should be approximately 30% for good data (report
+log).</p>
+</dd>
+<dt><strong><a name="item_percent_null" id=
+"item_percent_null"><strong>Percent null</strong></a></strong></dt>
+<dd>
+<p>Calculates overall percent null across the whole data set. Zero
+values are also counted as null. In practice under 10% null values
+seems to be a reasonable expectation (report log).</p>
+</dd>
+<dt><strong><a name=
+"item_feature_2freporter_check_vs_2e_array_design" id=
+"item_feature_2freporter_check_vs_2e_array_design"><strong>Feature/Reporter
+check vs. array design</strong></a></strong></dt>
+<dd>
+<p>Checks the feature coordinates (raw, normalized data) or the
+reporter identifiers (FGEM) against either the array designs linked
+to the hybridization in MIAMExpress, or against a user-supplied
+ADF. Prints out a list of features not found in the array design
+(error log, features log). Also alerts the curator when
+significantly fewer features are found in the data file compared to
+the array design (error log). Note that this will give false errors
+on array designs associated with dummy array designs (e.g., some
+Affy arrays).</p>
+</dd>
+<dt><strong><a name="item_duplicate_features_2freporters" id=
+"item_duplicate_features_2freporters"><strong>Duplicate
+Features/Reporters</strong></a></strong></dt>
+<dd>
+<p>Checks that Features or Reporter identifiers are not repeated
+within the same file. Prints out a list of duplicate
+features/reporters (error log, features log).</p>
+</dd>
+<dt><strong><a name="item_duplicate_filenames" id=
+"item_duplicate_filenames"><strong>Duplicate
+filenames</strong></a></strong></dt>
+<dd>
+<p>Checks that there are no duplicate files associated with the
+submission (error log).</p>
+</dd>
+<dt><strong><a name="item_row_2fcolumn_count_consistency" id=
+"item_row_2fcolumn_count_consistency"><strong>Row/column count
+consistency</strong></a></strong></dt>
+<dd>
+<p>Checks that all the files of a given type (raw, normalized) have
+consistent numbers of rows and columns (error log).</p>
+</dd>
+<dt><strong><a name="item_affy_exp_file_consistency" id=
+"item_affy_exp_file_consistency"><strong>Affy EXP file
+consistency</strong></a></strong></dt>
+<dd>
+<p>Checks that the parameters in each EXP file which should be the
+same are the same (error log).</p>
+</dd>
+<dt><strong><a name=
+"item_potentially_duplicated_files_with_different_names" id=
+"item_potentially_duplicated_files_with_different_names"><strong>Potentially
+duplicated files with different names</strong></a></strong></dt>
+<dd>
+<p>Checks a single line from each file against the same line in
+every other file, and reports on any matching pairs of files (error
+log).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="see_also" id="see_also">SEE ALSO</a></h1>
+<dl>
+<dt><strong><a name="item_arrayexpress_3a_3acurator_3a_3avalidate"
+id="item_arrayexpress_3a_3acurator_3a_3avalidate"></a><a href=
+"./Validate.html">the ArrayExpress::Curator::Validate
+manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3amagetab_3a_3achecker"
+id="item_arrayexpress_3a_3amagetab_3a_3achecker"></a><a href=
+"../MAGETAB/Checker.html">the ArrayExpress::MAGETAB::Checker
+manpage</a></strong></dt>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress" id=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress"></a><a href=
+"./MIAMExpress.html">the ArrayExpress::Curator::MIAMExpress
+manpage</a></strong></dt>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3astandalone" id=
+"item_arrayexpress_3a_3acurator_3a_3astandalone"></a><a href=
+"./Standalone.html">the ArrayExpress::Curator::Standalone
+manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3adatafile_3a_3aqt_list"
+id="item_arrayexpress_3a_3adatafile_3a_3aqt_list"></a><a href=
+"../Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Logger.html b/docs/ArrayExpress/Curator/Logger.html
new file mode 100644
index 0000000..1fca5af
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Logger.html
@@ -0,0 +1,204 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>lib/ArrayExpress/Curator/Logger.pm</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#public_methods">PUBLIC METHODS</a></li>
+ <li><a href="#available_log_types_and_templates">AVAILABLE LOG TYPES AND TEMPLATES</a></li>
+ <li><a href="#see_also">SEE ALSO</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Logger</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw/ArrayExpress::Curator::Logger/;
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is subclassed by ExperimentChecker and Tab2MAGE
+classes to provide them both with logging and error recording
+facilities.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The objects created by this module can be instantiated with the
+following options.</p>
+<dl>
+<dt><strong><a name="item_log_to_current_dir" id=
+"item_log_to_current_dir"><code>log_to_current_dir</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to write log files to the current
+directory or not. Note that this has been overloaded such that any
+true value is now interpreted as the name of a directory into which
+to write.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to silently overwrite old logs or
+not.</p>
+</dd>
+<dt><strong><a name="item_progname" id=
+"item_progname"><code>progname</code></a></strong></dt>
+<dd>
+<p>A program name string, used in log headers.</p>
+</dd>
+<dt><strong><a name="item_version" id=
+"item_version"><code>version</code></a></strong></dt>
+<dd>
+<p>A version string, used in log headers.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="public_methods" id="public_methods">PUBLIC
+METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_add_error" id=
+"item_add_error"><code>add_error( $error )</code></a></strong></dt>
+<dd>
+<p>Takes an integer, bitwise ORs it with the object's
+internally-stored error, records and returns the result. Used to
+store the overall error code determining suitability for MAGE-ML
+export. The final value is accessible using get_error().</p>
+</dd>
+<dt><strong><a name="item_logfiles" id=
+"item_logfiles"><code>logfiles( $type )</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type, returns the desired filename
+template. Otherwise, returns a list of log file types.</p>
+</dd>
+<dt><strong><a name="item_log_fh" id="item_log_fh"><code>log_fh(
+$type )</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type, returns the opened
+filehandle.</p>
+</dd>
+<dt><strong><a name="item_logprint" id=
+"item_logprint"><code>logprint( $type, $message
+)</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type and a message, prints the
+message to that log file. Linebreaks are not added to the
+output.</p>
+</dd>
+<dt><strong><a name="item_logprint_line" id=
+"item_logprint_line"><code>logprint_line( $type, $message
+)</code></a></strong></dt>
+<dd>
+<p>Given the desired log file type and a message, prints the
+message as part of a section-delimiting line in the log file. In
+this case linebreaks are added to the output.</p>
+</dd>
+<dt><strong><a name="item_localize_logfiles" id=
+"item_localize_logfiles"><code>localize_logfiles( $args
+)</code></a></strong></dt>
+<dd>
+<p>Given an optional hashref argument (keys: ``volume'',
+``directory'', ``name''), attempts to localize the logfiles to the
+directory structure given. Overridden by the log_to_current_dir
+attribute when setting log file location.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="available_log_types_and_templates" id=
+"available_log_types_and_templates">AVAILABLE LOG TYPES AND
+TEMPLATES</a></h1>
+<dl>
+<dt><strong><a name="item_error__3d_3e__27expt_error_2elog_27_2c"
+id="item_error__3d_3e__27expt_error_2elog_27_2c">error =>
+'expt_error.log',</a></strong></dt>
+<dt><strong><a name="item_report__3d_3e__27expt_report_2elog_27_2c"
+id="item_report__3d_3e__27expt_report_2elog_27_2c">report =>
+'expt_report.log',</a></strong></dt>
+<dt><strong><a name=
+"item_workflow__3d_3e__27expt_biomaterials_2elog_27_2c" id=
+"item_workflow__3d_3e__27expt_biomaterials_2elog_27_2c">workflow
+=> 'expt_biomaterials.log',</a></strong></dt>
+<dt><strong><a name=
+"item_feature__3d_3e__27expt_feature_2elog_27_2c" id=
+"item_feature__3d_3e__27expt_feature_2elog_27_2c">feature =>
+'expt_feature.log',</a></strong></dt>
+<dt><strong><a name=
+"item_columns__3d_3e__27expt_columnheadings_2elog_27_2c" id=
+"item_columns__3d_3e__27expt_columnheadings_2elog_27_2c">columns
+=> 'expt_columnheadings.log',</a></strong></dt>
+<dt><strong><a name=
+"item_sample__3d_3e__27expt_samples_2elog_27_2c" id=
+"item_sample__3d_3e__27expt_samples_2elog_27_2c">sample =>
+'expt_samples.log',</a></strong></dt>
+<dt><strong><a name=
+"item_protocol__3d_3e__27expt_protocols_2elog_27_2c" id=
+"item_protocol__3d_3e__27expt_protocols_2elog_27_2c">protocol =>
+'expt_protocols.log',</a></strong></dt>
+<dt><strong><a name="item_miame__3d_3e__27expt_miame_2elog_27_2c"
+id="item_miame__3d_3e__27expt_miame_2elog_27_2c">miame =>
+'expt_miame.log',</a></strong></dt>
+<dt><strong><a name="item_aedw__3d_3e__27expt_aedw_2elog_27_2c" id=
+"item_aedw__3d_3e__27expt_aedw_2elog_27_2c">aedw =>
+'expt_aedw.log',</a></strong></dt>
+<dt><strong><a name="item_tab2mage__3d_3e__27tab2mage_2elog_27_2c"
+id="item_tab2mage__3d_3e__27tab2mage_2elog_27_2c">tab2mage =>
+'tab2mage.log',</a></strong></dt>
+<dt><strong><a name="item_magetab__3d_3e__27magetab_2elog_27_2c"
+id="item_magetab__3d_3e__27magetab_2elog_27_2c">magetab =>
+'magetab.log',</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="see_also" id="see_also">SEE ALSO</a></h1>
+<dl>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker" id=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker"></a><a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3acurator_3a_3atab2mage"
+id="item_arrayexpress_3a_3acurator_3a_3atab2mage"></a><a href=
+"./Tab2MAGE.html">the ArrayExpress::Curator::Tab2MAGE
+manpage</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/MAGE/Definitions.html b/docs/ArrayExpress/Curator/MAGE/Definitions.html
new file mode 100644
index 0000000..7a07945
--- /dev/null
+++ b/docs/ArrayExpress/Curator/MAGE/Definitions.html
@@ -0,0 +1,712 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::MAGE::Definitions.pm - a module
+providing a central location for managing EDF column names,
+OntologyEntry categories and values.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#supported_headings">SUPPORTED HEADINGS</a></li>
+ <ul>
+
+ <li><a href="#experiment_section__row_names">Experiment section: row names</a></li>
+ <li><a href="#protocol_section__column_names">Protocol section: column names</a></li>
+ <li><a href="#hybridization_section__column_names">Hybridization section: column names</a></li>
+ </ul>
+
+ <li><a href="#validation_subroutines">VALIDATION SUBROUTINES</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Definitions.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::MAGE::Definitions.pm - a module providing
+a central location for managing EDF column names, OntologyEntry
+categories and values.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::MAGE::Definitions
+ qw($EDF_EXPTACCESSION
+ validate_hybridization_section
+ $OE_CAT_PROTOCOLTYPE
+ $OE_VAL_GROW);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a central location for various ontology
+terms and column heading definitions. In other words, if you want
+to change the parsing of your EDF or the output OntologyEntries,
+edit this module. Also provided is a series of optional validation
+subroutines for checking column or row names against the expected
+EDF headers.</p>
+<hr />
+<h1><a name="supported_headings" id="supported_headings">SUPPORTED
+HEADINGS</a></h1>
+<h2><a name="experiment_section__row_names" id=
+"experiment_section__row_names">Experiment section: row
+names</a></h2>
+<dl>
+<dt><strong><a name="item_accession" id=
+"item_accession">accession</a></strong></dt>
+<dd>
+<p>A string unique to the experiment. Used in all identifiers, and
+as a top-level Experiment identifier. Synonymous with the
+ArrayExpress accession number for an experiment. Example:
+E-MEXP-100</p>
+</dd>
+<dt><strong><a name="item_domain" id=
+"item_domain">domain</a></strong></dt>
+<dd>
+<p>A string identifying the origin of the information provided.
+Typically this will be an internet domain name such as
+``ebi.ac.uk''. This string is used to define the namespace of the
+MAGE identifiers created.</p>
+</dd>
+<dt><strong><a name="item_name" id=
+"item_name">name</a></strong></dt>
+<dd>
+<p>The name of the experiment. The forms the name attribute of the
+top-level Experiment object.</p>
+</dd>
+<dt><strong><a name="item_description" id=
+"item_description">description</a></strong></dt>
+<dd>
+<p>A short description of the experiment. This is inserted into a
+Description text attribute attached to the top-level Experiment
+object.</p>
+</dd>
+<dt><strong><a name="item_release_date" id=
+"item_release_date">release_date</a></strong></dt>
+<dd>
+<p>In the form
+YYYY-MM-DD<strong>T</strong>hh:mm:ss<strong>Z</strong>. This is
+currently used in a NameValueType (name ArrayExpressReleaseDate) in
+the top-level Experiment object.</p>
+</dd>
+<dt><strong><a name="item_submission_date" id=
+"item_submission_date">submission_date</a></strong></dt>
+<dd>
+<p>In the form
+YYYY-MM-DD<strong>T</strong>hh:mm:ss<strong>Z</strong>. This is
+currently used in a NameValueType (name ArrayExpressSubmissionDate)
+in the top-level Experiment object.</p>
+</dd>
+<dt><strong><a name="item_experiment_design_type" id=
+"item_experiment_design_type">experiment_design_type</a></strong></dt>
+<dd>
+<p>A comma-separated list of MO ExperimentDesignType terms used in
+the top-level ExperimentDesign object.</p>
+</dd>
+<dt><strong><a name="item_quality_control" id=
+"item_quality_control">quality_control</a></strong></dt>
+<dd>
+<p>A comma-separated list of MO QualityControlDescriptionType used
+in the top-level ExperimentDesign object.</p>
+</dd>
+<dt><strong><a name="item_submitter" id=
+"item_submitter">submitter</a></strong></dt>
+<dd>
+<p>The name of the person submitting the experiment. This is used
+to create a Person object in the AuditAndSecurity package with a
+Roles:submitter OntologyEntry. The Person object is referenced in
+the Experiment package.</p>
+</dd>
+<dt><strong><a name="item_submitter_email" id=
+"item_submitter_email">submitter_email</a></strong></dt>
+<dd>
+<p>The email address of the person submitting the experiment.</p>
+</dd>
+<dt><strong><a name="item_data_coder" id=
+"item_data_coder">data_coder</a></strong></dt>
+<dd>
+<p>The name of the person responsible for coding the experiment
+into MAGE-ML. This is used to create a Person object in the
+AuditAndSecurity package with a Roles:data_coder OntologyEntry. The
+Person object is referenced in the Experiment package.</p>
+</dd>
+<dt><strong><a name="item_data_coder_email" id=
+"item_data_coder_email">data_coder_email</a></strong></dt>
+<dd>
+<p>The email address of the person who coded the experiment in
+MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item_curator" id=
+"item_curator">curator</a></strong></dt>
+<dd>
+<p>The name of the person who curated the experiment. This is used
+to create a Person object in the AuditAndSecurity package with a
+Roles:curator OntologyEntry. The Person object is referenced in the
+Experiment package.</p>
+</dd>
+<dt><strong><a name="item_curator_email" id=
+"item_curator_email">curator_email</a></strong></dt>
+<dd>
+<p>The email address of the person who curated the experiment.</p>
+</dd>
+<dt><strong><a name="item_investigator" id=
+"item_investigator">investigator</a></strong></dt>
+<dd>
+<p>The name of the primary investigator on the experiment. This is
+used to create a Person object in the AuditAndSecurity package with
+a Roles:curator OntologyEntry. The Person object is referenced in
+the Experiment package.</p>
+</dd>
+<dt><strong><a name="item_investigator_email" id=
+"item_investigator_email">investigator_email</a></strong></dt>
+<dd>
+<p>The email address of the primary investigator.</p>
+</dd>
+<dt><strong><a name="item_organization" id=
+"item_organization">organization</a></strong></dt>
+<dd>
+<p>The name of the organisation to which the submitter is
+affiliated. This is used to create an Organization MAGE object,
+which is then associated with each Person object.</p>
+</dd>
+<dt><strong><a name="item_address" id=
+"item_address">address</a></strong></dt>
+<dd>
+<p>The address of the organisation to which the submitter is
+affiliated.</p>
+</dd>
+<dt><strong><a name="item_ae_display_name" id=
+"item_ae_display_name">ae_display_name</a></strong></dt>
+<dd>
+<p>The name of the experiment to be displayed in the ArrayExpress
+repository interface. This will typically be added by the
+ArrayExpress curators after data submission.</p>
+</dd>
+<dt><strong><a name="item_uri" id="item_uri">URI</a></strong></dt>
+<dd>
+<p>A URI pointing to an alternative location for the experimental
+data, e.g. in a public repository database.</p>
+</dd>
+<dt><strong><a name="item_geo_release_date" id=
+"item_geo_release_date">geo_release_date</a></strong></dt>
+<dd>
+<p>For processing of GEO experiments; this value is used to create
+a ``GEOReleaseDate'' NameValueType object associated with the
+top-level Experiment; this is used internally by the ArrayExpress
+database.</p>
+</dd>
+<dt><strong><a name="item_secondary_accession" id=
+"item_secondary_accession">secondary_accession</a></strong></dt>
+<dd>
+<p>This value is used to populate the ``SecondaryAcession''
+NameValueType object associated with the top-level Experiment. This
+is used internally by ArrayExpress to represent e.g. GEO accession
+numbers.</p>
+</dd>
+</dl>
+<p>The following are used in a BibliographicReference associated
+with a second Description object in the top-level Experiment
+object:</p>
+<dl>
+<dt><strong><a name="item_publication_title" id=
+"item_publication_title">publication_title</a></strong></dt>
+<dd>
+<p>The free-text title of any associated publication. This is
+currently assumed to have PublicationType journal_article (MO),
+although more control over this may be added in future. Used in</p>
+</dd>
+<dt><strong><a name="item_authors" id=
+"item_authors">authors</a></strong></dt>
+<dd>
+<p>A free-text list of publication authors.</p>
+</dd>
+<dt><strong><a name="item_journal" id=
+"item_journal">journal</a></strong></dt>
+<dd>
+<p>The name of the journal. This should be a standard Pubmed
+abbreviation.</p>
+</dd>
+<dt><strong><a name="item_year" id=
+"item_year">year</a></strong></dt>
+<dd>
+<p>Year of publication.</p>
+</dd>
+<dt><strong><a name="item_volume" id=
+"item_volume">volume</a></strong></dt>
+<dd>
+<p>Journal volume.</p>
+</dd>
+<dt><strong><a name="item_issue" id=
+"item_issue">issue</a></strong></dt>
+<dd>
+<p>Journal issue.</p>
+</dd>
+<dt><strong><a name="item_pages" id=
+"item_pages">pages</a></strong></dt>
+<dd>
+<p>Page range of journal article.</p>
+</dd>
+<dt><strong><a name="item_publication_uri" id=
+"item_publication_uri">publication_URI</a></strong></dt>
+<dd>
+<p>Any URI associated with the publication.</p>
+</dd>
+<dt><strong><a name="item_pubmed_id" id=
+"item_pubmed_id">pubmed_id</a></strong></dt>
+<dd>
+<p>The Pubmed ID associated with the publication.</p>
+</dd>
+</dl>
+<h2><a name="protocol_section__column_names" id=
+"protocol_section__column_names">Protocol section: column
+names</a></h2>
+<p>The following are all attached to individual protocols defined
+by successive lines in this section:</p>
+<dl>
+<dt><strong>accession</strong></dt>
+<dd>
+<p>Database (e.g. ArrayExpress) accession no.</p>
+</dd>
+<dt><strong>name</strong></dt>
+<dd>
+<p>Protocol name.</p>
+</dd>
+<dt><strong><a name="item_type" id=
+"item_type">type</a></strong></dt>
+<dd>
+<p>MO ProtocolType term; this is an optional field. In its absence,
+Tab2MAGE will use default ProtocolType terms based on how the
+protocol is used within the Hybridization section.</p>
+</dd>
+<dt><strong><a name="item_text" id=
+"item_text">text</a></strong></dt>
+<dd>
+<p>Protocol text.</p>
+</dd>
+<dt><strong><a name="item_parameters" id=
+"item_parameters">parameters</a></strong></dt>
+<dd>
+<p>Protocol parameters, listed in the following form:
+name1(unit1);name2(unit2);...</p>
+</dd>
+</dl>
+<h2><a name="hybridization_section__column_names" id=
+"hybridization_section__column_names">Hybridization section: column
+names</a></h2>
+<dl>
+<dt><strong><a name="item_biosource" id=
+"item_biosource">BioSource</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSource. This term is used as a unique
+identifier within a Tab2MAGE run to determine correct linking
+between objects. It may however be omitted, in favour of using the
+set of BioMaterialCharacteristics associated with a BioMaterial as
+the sole indicator of BioSource identity.</p>
+</dd>
+<dt><strong><a name="item_sample" id=
+"item_sample">Sample</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSample (associated with a
+BioSampleType:not_extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a
+BioSample name constructed from the raw data filename is used
+instead.</p>
+</dd>
+<dt><strong><a name="item_extract" id=
+"item_extract">Extract</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a Extract
+name constructed from the raw data filename is used instead.</p>
+</dd>
+<dt><strong><a name="item_immunoprecipitate" id=
+"item_immunoprecipitate">Immunoprecipitate</a></strong></dt>
+<dd>
+<p>Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case an
+Immunoprecipitate name constructed from the raw data filename is
+used instead. These objects are used in ChIP experiments and may be
+ignored for expression or CGH studies.</p>
+</dd>
+<dt><strong><a name="item_labeledextract" id=
+"item_labeledextract">LabeledExtract</a></strong></dt>
+<dd>
+<p>Arbitrary name for a LabeledExtract. Used to control linking
+between objects; may be omitted if desired, in which case a
+LabeledExtract name constructed from the raw data filename and the
+label dye name is used instead.</p>
+</dd>
+<dt><strong><a name="item_dye" id="item_dye">Dye</a></strong></dt>
+<dd>
+<p>Name of the dye linked to the labeled extract (e.g., Cy3, Cy5,
+biotin).</p>
+</dd>
+<dt><strong><a name="item_biosourcematerial" id=
+"item_biosourcematerial">BioSourceMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the BioSource. Default:
+whole_organism</p>
+</dd>
+<dt><strong><a name="item_samplematerial" id=
+"item_samplematerial">SampleMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the BioSample. Default:
+organism_part</p>
+</dd>
+<dt><strong><a name="item_extractmaterial" id=
+"item_extractmaterial">ExtractMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the Extract. Default:
+total_RNA</p>
+</dd>
+<dt><strong><a name="item_immunoprecipitatematerial" id=
+"item_immunoprecipitatematerial">ImmunoprecipitateMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the Immunoprecipitate.
+Default: genomic_DNA</p>
+</dd>
+<dt><strong><a name="item_labeledextractmaterial" id=
+"item_labeledextractmaterial">LabeledExtractMaterial</a></strong></dt>
+<dd>
+<p>MO MaterialType term to be attached to the LabeledExtract.
+Default: synthetic_DNA</p>
+</dd>
+<dt><strong><a name="item_biosourcedescription" id=
+"item_biosourcedescription">BioSourceDescription</a></strong></dt>
+<dd>
+<p>Free-text description to attached to the BioSource (this should
+be used sparingly, if at all).</p>
+</dd>
+<dt><strong><a name="item_hybridization" id=
+"item_hybridization">Hybridization</a></strong></dt>
+<dd>
+<p>Arbitrary name for a hybridization. Used to control linking
+between objects; may be omitted if desired, in which case a
+Hybridization name constructed from the raw data filename is used
+instead.</p>
+</dd>
+<dt><strong><a name="item_scan" id=
+"item_scan">Scan</a></strong></dt>
+<dd>
+<p>Arbitrary name for a scanning event. Used to control linking
+between objects; may be omitted if desired, in which case a Scan
+name constructed from the raw data filename is used instead.</p>
+</dd>
+<dt><strong><a name="item_normalization" id=
+"item_normalization">Normalization</a></strong></dt>
+<dd>
+<p>Arbitrary name for a normalization procedure. Used to control
+linking between objects; may be omitted if desired, in which case a
+Normalization name constructed from the normalized data filename is
+used instead.</p>
+</dd>
+<dt><strong><a name="item_normalizationtype" id=
+"item_normalizationtype">NormalizationType</a></strong></dt>
+<dd>
+<p>MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.</p>
+</dd>
+<dt><strong><a name="item_transformation" id=
+"item_transformation">Transformation</a></strong></dt>
+<dd>
+<p>Arbitrary name for a data transformation procedure. Used to
+control linking between objects; may be omitted if desired.</p>
+</dd>
+<dt><strong><a name="item_transformationtype" id=
+"item_transformationtype">TransformationType</a></strong></dt>
+<dd>
+<p>MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.</p>
+</dd>
+<dt><strong><a name="item_imageformat" id=
+"item_imageformat">ImageFormat</a></strong></dt>
+<dd>
+<p>MO ImageFormat term. Only used if File[image] columns have been
+included in the spreadsheet. Used to create Image objects.</p>
+</dd>
+</dl>
+<dl>
+<dt><strong><a name="item_protocol_5bgrow_5d" id=
+"item_protocol_5bgrow_5d">Protocol[grow]</a></strong></dt>
+<dd>
+<p>Accession number for the ``growth'' protocol
+(BioSource->BioSample Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: grow</p>
+</dd>
+<dt><strong><a name="item_protocol_5btreatment_5d" id=
+"item_protocol_5btreatment_5d">Protocol[treatment]</a></strong></dt>
+<dd>
+<p>Accession number for the ``treatment'' protocol
+(BioSource->BioSample Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+specified_biomaterial_action</p>
+</dd>
+<dt><strong><a name="item_protocol_5bextraction_5d" id=
+"item_protocol_5bextraction_5d">Protocol[extraction]</a></strong></dt>
+<dd>
+<p>Accession number for the ``extraction'' protocol
+(BioSample->Extract Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+nucleic_acid_extraction</p>
+</dd>
+<dt><strong><a name="item_protocol_5bpool_5d" id=
+"item_protocol_5bpool_5d">Protocol[pool]</a></strong></dt>
+<dd>
+<p>Accession number for the ``pooling'' protocol
+(BioSample->Extract Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: pool</p>
+</dd>
+<dt><strong><a name="item_protocol_5blabeling_5d" id=
+"item_protocol_5blabeling_5d">Protocol[labeling]</a></strong></dt>
+<dd>
+<p>Accession number for the ``labeling'' protocol
+(Extract->LabeledExtract Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: labeling</p>
+</dd>
+<dt><strong><a name="item_protocol_5bimmunoprecipitate_5d" id=
+"item_protocol_5bimmunoprecipitate_5d">Protocol[immunoprecipitate]</a></strong></dt>
+<dd>
+<p>Accession number for the ``immunoprecipitation'' protocol
+(Extract->Immunoprecipitate Treatment). The accession number
+should either be present in the protocol section of the
+spreadsheet, or pre-existing in ArrayExpress. Omit if
+Immunoprecipitates are not used in the experiment. Default
+ProtocolType: immunoprecipitate</p>
+</dd>
+<dt><strong><a name="item_protocol_5bhybridization_5d" id=
+"item_protocol_5bhybridization_5d">Protocol[hybridization]</a></strong></dt>
+<dd>
+<p>Accession number for the ``hybridization'' protocol
+(PhysicalBioAssay BioAssayCreation). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+hybridization</p>
+</dd>
+<dt><strong><a name="item_protocol_5bscanning_5d" id=
+"item_protocol_5bscanning_5d">Protocol[scanning]</a></strong></dt>
+<dd>
+<p>Accession number for the ``image acquisition'' protocol
+(PhysicalBioAssay BioAssayTreatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+image_acquisition; note however that if Protocol[image_analysis] is
+not specified then the scanning protocol defaults to
+feature_extraction. This is an ArrayExpress-specific behaviour and
+relates to the appearance of the experiment in the ArrayExpress web
+interface.</p>
+</dd>
+<dt><strong><a name="item_protocol_5bimage_analysis_5d" id=
+"item_protocol_5bimage_analysis_5d">Protocol[image_analysis]</a></strong></dt>
+<dd>
+<p>Accession number for the ``feature extraction'' protocol
+(MeasuredBioAssay FeatureExtraction). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType:
+feature_extraction</p>
+</dd>
+<dt><strong><a name="item_protocol_5bnormalization_5d" id=
+"item_protocol_5bnormalization_5d">Protocol[normalization]</a></strong></dt>
+<dd>
+<p>Accession number for the ``normalization'' protocol
+(DerivedBioAssayData ProducerTransformation). The accession number
+should either be present in the protocol section of the
+spreadsheet, or pre-existing in ArrayExpress. Default ProtocolType:
+bioassay_data_transformation</p>
+</dd>
+<dt><strong><a name="item_protocol_5btransformation_5d" id=
+"item_protocol_5btransformation_5d">Protocol[transformation]</a></strong></dt>
+<dd>
+<p>Accession number for the ``transformation'' protocol
+(DerivedBioAssayData ProducerTransformation). The accession number
+should either be present in the protocol section of the
+spreadsheet, or pre-existing in ArrayExpress. Default ProtocolType:
+bioassay_data_transformation</p>
+</dd>
+</dl>
+<p>Software is created in the Protocol package, and referenced as
+SoftwareApplication in the appropriate ProtocolApplication (see
+above).</p>
+<dl>
+<dt><strong><a name="item_software_5bscanning_5d" id=
+"item_software_5bscanning_5d">Software[scanning]</a></strong></dt>
+<dd>
+<p>Name of the scanning software, followed by its version in
+parentheses. The Software name is used to create a Software object
+in the Protocol package, and the version is inserted into the
+relevant SoftwareApplication objects.</p>
+</dd>
+<dt><strong><a name="item_software_5bimage_analysis_5d" id=
+"item_software_5bimage_analysis_5d">Software[image_analysis]</a></strong></dt>
+<dd>
+<p>Name of the feature extraction software, followed by its version
+in parentheses. The Software name is used to create a Software
+object in the Protocol package, and the version is inserted into
+the relevant SoftwareApplication objects.</p>
+</dd>
+<dt><strong><a name="item_software_5bnormalization_5d" id=
+"item_software_5bnormalization_5d">Software[normalization]</a></strong></dt>
+<dd>
+<p>Name of the normalization software, followed by its version in
+parentheses. The Software name is used to create a Software object
+in the Protocol package, and the version is inserted into the
+relevant SoftwareApplication objects.</p>
+</dd>
+<dt><strong><a name="item_software_5btransformation_5d" id=
+"item_software_5btransformation_5d">Software[transformation]</a></strong></dt>
+<dd>
+<p>Name of the data transformation software, followed by its
+version in parentheses. The Software name is used to create a
+Software object in the Protocol package, and the version is
+inserted into the relevant SoftwareApplication objects.</p>
+</dd>
+</dl>
+<dl>
+<dt><strong><a name="item_array_5baccession_5d" id=
+"item_array_5baccession_5d">Array[accession]</a></strong></dt>
+<dd>
+<p>ArrayExpress array accession number (e.g., A-MEXP-1). This is
+used in the Array package, and also to create Feature and Reporter
+identifiers for the DesignElementDimensions.</p>
+</dd>
+<dt><strong><a name="item_array_5bserial_5d" id=
+"item_array_5bserial_5d">Array[serial]</a></strong></dt>
+<dd>
+<p>Serial or lot number of the array. This is used to define
+ArrayManufacture objects within the Array package.</p>
+</dd>
+</dl>
+<dl>
+<dt><strong><a name="item_file_5braw_5d" id=
+"item_file_5braw_5d">File[raw]</a></strong></dt>
+<dd>
+<p>Name of the raw data file for a given hybridization.</p>
+</dd>
+<dt><strong><a name="item_file_5bnormalized_5d" id=
+"item_file_5bnormalized_5d">File[normalized]</a></strong></dt>
+<dd>
+<p>Name of the normalized data file for a given
+hybridization/normalization.</p>
+</dd>
+<dt><strong><a name="item_file_5btransformed_5d" id=
+"item_file_5btransformed_5d">File[transformed]</a></strong></dt>
+<dd>
+<p>Name of the transformed final data matrix file for the
+experiment.</p>
+</dd>
+<dt><strong><a name="item_file_5bimage_5d" id=
+"item_file_5bimage_5d">File[image]</a></strong></dt>
+<dd>
+<p>URI locating the Image object associated with an
+ImageAcquisition (scanning) event. ArrayExpress does not store
+images, although we can link to images stored on external web
+sites, where desired.</p>
+</dd>
+<dt><strong><a name="item_file_5bcdf_5d" id=
+"item_file_5bcdf_5d">File[cdf]</a></strong></dt>
+<dd>
+<p>Affymetrix array library file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data
+files, although for standard Affymetrix arrays the actual CDF file
+does not need to be supplied.</p>
+</dd>
+<dt><strong><a name="item_file_5bexp_5d" id=
+"item_file_5bexp_5d">File[exp]</a></strong></dt>
+<dd>
+<p>Affymetrix experiment description file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data
+files.</p>
+</dd>
+<dt><strong><a name=
+"item_biomaterialcharacteristics_5b_3ccategory_3e_5d" id=
+"item_biomaterialcharacteristics_5b_3ccategory_3e_5d">BioMaterialCharacteristics[<<em>category</em>>]</a></strong></dt>
+<dd>
+<p>MO term describing some feature of the BioSource (e.g.,
+Genotype, Sex, DiseaseState, etc.). As many
+BioMaterialCharacteristics columns may be used as are desired. Each
+column should contain values from a different
+<strong>category</strong> within the MGED Ontology.</p>
+</dd>
+<dt><strong><a name="item_factorvalue_5b_3ccategory_3e_5d" id=
+"item_factorvalue_5b_3ccategory_3e_5d">FactorValue[<<em>category</em>>]</a></strong></dt>
+<dd>
+<p>MO term describing a FactorValue associated with a given
+hybridization (e.g., Genotype, Sex, DiseaseState, etc.). As many
+FactorValue columns may be used as are desired. Each column should
+contain values from a different <strong>category</strong> within
+the MGED Ontology.</p>
+</dd>
+<dt><strong><a name="item_parameter_5b_3cparameter_name_3e_5d" id=
+"item_parameter_5b_3cparameter_name_3e_5d">Parameter[<<em>parameter
+name</em>>]</a></strong></dt>
+<dd>
+<p>Parameter values for the parameters declared in the Protocol
+section of the spreadsheet. Note that a protocol must be declared
+in the spreadsheet in order to be able to use parameters with
+it.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="validation_subroutines" id=
+"validation_subroutines">VALIDATION SUBROUTINES</a></h1>
+<dl>
+<dt><strong><a name="item_validate_expriment_section" id=
+"item_validate_expriment_section">validate_expriment_section($test_arrayref,
+$error_fh)</a></strong></dt>
+<dt><strong><a name="item_validate_protocol_section" id=
+"item_validate_protocol_section">validate_protocol_section($test_arrayref,
+$error_fh)</a></strong></dt>
+<dt><strong><a name="item_validate_hybridization_section" id=
+"item_validate_hybridization_section">validate_hybridization_section($test_arrayref,
+$error_fh)</a></strong></dt>
+<dd>
+<p>Each of these subroutines simply takes a reference to an array
+containing the list of headings to be checked, and prints warnings
+on STDERR (and on the optional error filehandle) if it finds
+unrecognized headings. No further action is taken; these
+subroutines merely serve as a warning to the user.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/MIAMExpress.html b/docs/ArrayExpress/Curator/MIAMExpress.html
new file mode 100644
index 0000000..06e5191
--- /dev/null
+++ b/docs/ArrayExpress/Curator/MIAMExpress.html
@@ -0,0 +1,193 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::MIAMExpress - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#tests">TESTS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: MIAMExpress.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::MIAMExpress - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::MIAMExpress;
+
+ my $checker = ArrayExpress::Curator::MIAMExpress->new({
+ mx_login => $login,
+ mx_title => $title,
+ });
+
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to interact with a local MIAMExpress installation.
+It optionally exports just two (fairly heavyweight) subroutines.
+Please see the end of this document for the tests carried out by
+this module.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_mx_login" id=
+"item_mx_login"><code>mx_login</code></a></strong></dt>
+<dd>
+<p>The submitter login name associated with a MIAMExpress
+submission. See <a href=
+"#item_mx_title"><code>mx_title</code></a>.</p>
+</dd>
+<dt><strong><a name="item_mx_title" id=
+"item_mx_title"><code>mx_title</code></a></strong></dt>
+<dd>
+<p>The title of a MIAMExpress submission. See <a href=
+"#item_mx_login"><code>mx_login</code></a>.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report filehandles (see <a href=
+"./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a> for a list of
+more general tests which are also run):</p>
+<dl>
+<dt><strong><a name="item_biomaterials" id=
+"item_biomaterials"><strong>BioMaterials</strong></a></strong></dt>
+<dd>
+<p>Checks the linking between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file which attempts to
+portray these links in ASCII. Highlights BioMaterials which have
+not been properly linked through to hybridizations (biomaterials
+log).</p>
+</dd>
+<dt><strong><a name="item_experimental_factor_values" id=
+"item_experimental_factor_values"><strong>Experimental factor
+values</strong></a></strong></dt>
+<dd>
+<p>Generates a report showing how hybridizations have been linked
+to experimental factor values, and vice versa (biomaterials
+log).</p>
+</dd>
+<dt><strong><a name="item_sample_annotation" id=
+"item_sample_annotation"><strong>Sample
+annotation</strong></a></strong></dt>
+<dd>
+<p>Generates a report on all the annotation attached to the samples
+in the experiment (sample log).</p>
+</dd>
+<dt><strong><a name="item_publication" id=
+"item_publication"><strong>Publication</strong></a></strong></dt>
+<dd>
+<p>Checks whether the publication details include standard Pubmed
+abbreviations; also alerts the curator if a new publication
+(``other'') has been indicated (error log).</p>
+</dd>
+<dt><strong><a name="item_qualifiervaluesource" id=
+"item_qualifiervaluesource"><strong>QualifierValueSource</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if QVS entries have been used under
+Experiment or Sample annotation (error log).</p>
+</dd>
+<dt><strong><a name="item_other" id=
+"item_other"><strong>Other</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if ``Other'' entries have been used under
+Experiment, Protocol or Sample annotation (error log).</p>
+</dd>
+<dt><strong><a name="item_transformation_protocol" id=
+"item_transformation_protocol"><strong>Transformation
+protocol</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if a final data matrix file has been supplied
+without a transformation protocol (error log).</p>
+</dd>
+<dt><strong><a name="item_protocol_description_length" id=
+"item_protocol_description_length"><strong>Protocol description
+length</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if protocols with fewer than 50 characters
+are submitted (error log).</p>
+</dd>
+<dt><strong><a name="item_pooling_protocol" id=
+"item_pooling_protocol"><strong>Pooling
+protocol</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if a pooling protocol has been linked to a
+submission (error log).</p>
+</dd>
+<dt><strong><a name="item_normalization_protocol" id=
+"item_normalization_protocol"><strong>Normalization
+protocol</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if normalized data files have been supplied
+without a normalization protocol (error log).</p>
+</dd>
+<dt><strong><a name="item_other_unbound_protocols" id=
+"item_other_unbound_protocols"><strong>Other unbound
+protocols</strong></a></strong></dt>
+<dd>
+<p>Alerts the curator if protocols other than pooling,
+normalization or transformation have not been bound to their
+appropriate objects (error log).</p>
+</dd>
+<dt><strong><a name=
+"item_number_of_files_submitted_per_hybridization" id=
+"item_number_of_files_submitted_per_hybridization"><strong>Number
+of files submitted per hybridization</strong></a></strong></dt>
+<dd>
+<p>Prints a table indicating which kinds of raw and normalized data
+file have been submitted for each hybridization (report log).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Report.html b/docs/ArrayExpress/Curator/Report.html
new file mode 100644
index 0000000..ce2a54c
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Report.html
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Reporter.pm - a module providing some
+functions useful in generating reports.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#functions">FUNCTIONS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../index.html"><img src="../T2M_logo.png" border="0" height=
+"50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Reporter.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Reporter.pm - a module providing some
+functions useful in generating reports.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Report qw(datafile_consistency_table);
+
+ print datafile_consistency_table( \@filelist );
+
+=head1 DESCRIPTION
+</pre>
+<p>This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_datafile_consistency_table" id=
+"item_datafile_consistency_table"><code>datafile_consistency_table(
+\@filelist )</code></a></strong></dt>
+<dd>
+<p>Given an arrayref of Datafile objects (see <a href=
+"../Datafile.html">the ArrayExpress::Datafile manpage</a>), returns
+a string representing a summary of files tabulated by
+hyb_identifier, reporting on presence or absence in the
+filesystem.</p>
+</dd>
+<dt><strong><a name="item_biomaterials_report" id=
+"item_biomaterials_report"><code>biomaterials_report( $HoH, $title,
+$fh )</code></a></strong></dt>
+<dd>
+<p>Given a nested hashref of hashrefs (of indeterminate depth), a
+report title and an output filehandle, prints a report giving links
+between nodes in the hashref structure (recursively).</p>
+</dd>
+<dt><strong><a name="item_report_accumulated_errors" id=
+"item_report_accumulated_errors"><code>report_accumulated_errors(
+$HoH, $fh, $string )</code></a></strong></dt>
+<dd>
+<p>Given a hashref of hashrefs keyed by filename and then by
+elements (e.g. duplicate feature identifiers), an output filehandle
+and an explanatory string, print a report.</p>
+</dd>
+<dt><strong><a name="item_format_description" id=
+"item_format_description"><code>format_description( $text, $fh
+)</code></a></strong></dt>
+<dd>
+<p>Takes a text string and an output filehandle, and prints the
+string in lines of less than 80 chars, splitting words on
+whitespace.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Standalone.html b/docs/ArrayExpress/Curator/Standalone.html
new file mode 100644
index 0000000..26f4d97
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Standalone.html
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Standalone - Standalone experiment
+checking.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Standalone.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Standalone - Standalone experiment
+checking.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Standalone;
+
+ my $checker =
+ ArrayExpress::Curator::Standalone->new({
+ data_files => \@ARGV,
+ });
+
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides the basic operations needed for checking a
+set of data files in full standalone mode. For MIAMExpress and
+Tab2MAGE experiment checking, see <a href="./MIAMExpress.html">the
+ArrayExpress::Curator::MIAMExpress manpage</a> and <a href=
+"./Validate.html">the ArrayExpress::Curator::Validate manpage</a>,
+respectively.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_data_files" id=
+"item_data_files"><code>data_files</code></a></strong></dt>
+<dd>
+<p>An array reference containing a list of data filenames to be
+checked.</p>
+</dd>
+</dl>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2007.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Validate.html b/docs/ArrayExpress/Curator/Validate.html
new file mode 100644
index 0000000..718e9ec
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Validate.html
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Validate - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#tests">TESTS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Validate.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Validate - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Validate;
+
+ my $checker = ArrayExpress::Curator::Validate->new({
+ spreadsheet_filename => $tab2mage_file,
+ });
+
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href="./ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_spreadsheet_filename" id=
+"item_spreadsheet_filename"><code>spreadsheet_filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to check.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report filehandles:</p>
+<dl>
+<dt><strong><a name="item_hybridizations" id=
+"item_hybridizations"><strong>Hybridizations</strong></a></strong></dt>
+<dd>
+<p>Confirms that each hybridization is associated with an array
+design (i.e., ArrayExpress accession number), and at least one
+experimental factor value.</p>
+</dd>
+<dt><strong><a name="item_biomaterialcharacteristics" id=
+"item_biomaterialcharacteristics"><strong>BioMaterialCharacteristics</strong></a></strong></dt>
+<dd>
+<p>Checks that each BioSource has at least some annotation. This is
+not, however, a full test of MIAME compliance.</p>
+</dd>
+<dt><strong><a name="item_undeclared_experimental_factors" id=
+"item_undeclared_experimental_factors"><strong>Undeclared
+Experimental Factors</strong></a></strong></dt>
+<dd>
+<p>Checks all material characteristics against the factors
+described by the document, alerting the user if any such
+characteristics vary during the experiment without having been
+declared as an experimental factor.</p>
+</dd>
+<dt><strong><a name="item_protocols" id=
+"item_protocols"><strong>Protocols</strong></a></strong></dt>
+<dd>
+<p>Checks that all protocols referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_protocol_accessions" id=
+"item_protocol_accessions"><strong>Protocol
+accessions</strong></a></strong></dt>
+<dd>
+<p>Confirms that all declared protocols in the Protocol section
+have accession numbers.</p>
+</dd>
+<dt><strong><a name="item_protocol_text_length" id=
+"item_protocol_text_length"><strong>Protocol text
+length</strong></a></strong></dt>
+<dd>
+<p>Warns if any of the protocol texts seem too brief.</p>
+</dd>
+<dt><strong><a name="item_protocol_presence_and_usage" id=
+"item_protocol_presence_and_usage"><strong>Protocol presence and
+usage</strong></a></strong></dt>
+<dd>
+<p>Checks that a protocol has been attached to each step of the
+experiment (biomaterial treatments, hybridization, scanning and
+normalization).</p>
+</dd>
+<dt><strong><a name="item_parameters" id=
+"item_parameters"><strong>Parameters</strong></a></strong></dt>
+<dd>
+<p>Checks that all parameters referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_biomaterials" id=
+"item_biomaterials"><strong>BioMaterials</strong></a></strong></dt>
+<dd>
+<p>Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the
+numbers of each. If the Graphviz software is installed
+(http://www.graphviz.org) then the script will use the 'dot'
+program to produce a PNG format graph showing how the various
+components relate to each other.</p>
+</dd>
+<dt><strong><a name="item_publication_journal" id=
+"item_publication_journal"><strong>Publication
+journal</strong></a></strong></dt>
+<dd>
+<p>Warns the user if the specified publication journal name is not
+in the included list of standard Entrez journal abbreviations.</p>
+</dd>
+<dt><strong><a name="item_pubmed_id" id=
+"item_pubmed_id"><strong>PubMed ID</strong></a></strong></dt>
+<dd>
+<p>Warns the user if a non-numeric PubMed ID has been entered.</p>
+</dd>
+<dt><strong><a name="item_submission_and_release_dates" id=
+"item_submission_and_release_dates"><strong>Submission and release
+dates</strong></a></strong></dt>
+<dd>
+<p>Warns the user if submission and/or release dates have not been
+specified.</p>
+</dd>
+<dt><strong><a name="item_submitter" id=
+"item_submitter"><strong>Submitter</strong></a></strong></dt>
+<dd>
+<p>Checks that both submitter name and email have been
+provided.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Curator/Visualize.html b/docs/ArrayExpress/Curator/Visualize.html
new file mode 100644
index 0000000..b496f37
--- /dev/null
+++ b/docs/ArrayExpress/Curator/Visualize.html
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Curator::Visualize - a module used by
+expt_check.pl</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#functions">FUNCTIONS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Visualize.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Curator::Visualize - a module used by
+expt_check.pl</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Curator::Visualize qw(dot_and_png);
+
+ dot_and_png( $mage_bags, 'my_expt', 'luxisr', 1 );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.</p>
+<hr />
+<h1><a name="functions" id="functions">FUNCTIONS</a></h1>
+<dl>
+<dt><strong><a name="item_dot_and_png" id=
+"item_dot_and_png"><code>dot_and_png( $mage_bags, $output_base,
+$font, $clobber, $classes )</code></a></strong></dt>
+<dd>
+<p>Takes: a hashref of MAGE containers, as generated by Tab2MAGE,
+MAGE and MAGE-TAB classes; a string for the output filenames (minus
+extension, so e.g. ``out'' becomes ``out.dot'' and ``out.png''); an
+optional font name; an optional flag indicating whether files
+should be silently overwritten; an optional hashref of classes to
+draw. This last argument will usually be omitted. Generates the dot
+file and passes it to the Graphviz dot for drawing. Returns a new
+$clobber argument.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile.html b/docs/ArrayExpress/Datafile.html
new file mode 100644
index 0000000..e0c5b84
--- /dev/null
+++ b/docs/ArrayExpress/Datafile.html
@@ -0,0 +1,478 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile.pm - an OO module providing methods
+for parsing data files.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <ul>
+
+ <li><a href="#accessor_methods">Accessor methods</a></li>
+ </ul>
+
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Datafile.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile.pm - an OO module providing methods for
+parsing data files.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile;
+ my $file = ArrayExpress::Datafile->new({
+ name => 'data.txt',
+ data_type => 'raw',
+ array_design_id => 'A-MEXP-123',
+ });
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a module providing methods for data file parsing. See
+also <a href="Datafile/Parser.html">the
+ArrayExpress::Datafile::Parser manpage</a> for Datafile handler
+objects.</p>
+<h2><a name="accessor_methods" id="accessor_methods">Accessor
+methods</a></h2>
+<dl>
+<dt><strong><a name="item_set_row_count" id=
+"item_set_row_count">set_row_count</a></strong></dt>
+<dd>
+<p>Setter method for the total row count.</p>
+</dd>
+<dt><strong><a name="item_get_row_count" id=
+"item_get_row_count">get_row_count</a></strong></dt>
+<dd>
+<p>Getter method for the total row count.</p>
+</dd>
+<dt><strong><a name="item_increment_row_count" id=
+"item_increment_row_count">increment_row_count</a></strong></dt>
+<dd>
+<p>Adds one (or the passed value) to the overall row count. Returns
+the new row count.</p>
+</dd>
+<dt><strong><a name="item_get_parse_errors" id=
+"item_get_parse_errors">get_parse_errors</a></strong></dt>
+<dd>
+<p>Getter method for the total number of parse errors.</p>
+</dd>
+<dt><strong><a name="item_increment_parse_errors" id=
+"item_increment_parse_errors">increment_parse_errors</a></strong></dt>
+<dd>
+<p>Adds one (or the passed value) to the overall parsing error
+count. Returns the new error count.</p>
+</dd>
+<dt><strong><a name="item_get_not_null" id=
+"item_get_not_null">get_not_null</a></strong></dt>
+<dd>
+<p>The number of data file cells which are not null relating to
+known QTs.</p>
+</dd>
+<dt><strong><a name="item_increment_not_null" id=
+"item_increment_not_null">increment_not_null</a></strong></dt>
+<dd>
+<p>This is an incremental counter for the number of data file cells
+which are not null relating to known QTs. Returns the new ``not
+null'' count.</p>
+</dd>
+<dt><strong><a name="item_set_hyb_identifier" id=
+"item_set_hyb_identifier">set_hyb_identifier</a></strong></dt>
+<dd>
+<p>Setter method for the Tab2MAGE or MIAMExpress hybridization
+identifier associated with a raw or normalized data file. Does not
+apply to FGEM files.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_identifier" id=
+"item_get_hyb_identifier">get_hyb_identifier</a></strong></dt>
+<dd>
+<p>Getter method for the Tab2MAGE or MIAMExpress hybridization
+identifier associated with a raw or normalized data file. Does not
+apply to FGEM files.</p>
+</dd>
+<dt><strong><a name="item_set_hyb_sysuid" id=
+"item_set_hyb_sysuid">set_hyb_sysuid</a></strong></dt>
+<dd>
+<p>Setter method for the internal MIAMExpress SYSUID value
+associated with a raw or normalized data file. Does not apply to
+FGEM files.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_sysuid" id=
+"item_get_hyb_sysuid">get_hyb_sysuid</a></strong></dt>
+<dd>
+<p>Getter method for the internal MIAMExpress SYSUID value
+associated with a raw or normalized data file. Does not apply to
+FGEM files.</p>
+</dd>
+<dt><strong><a name="item_set_array_design_id" id=
+"item_set_array_design_id">set_array_design_id</a></strong></dt>
+<dd>
+<p>Setter method for an identifier linking the data file to an
+array design. For Tab2MAGE this identifier is the ArrayExpress
+array accession number. For MIAMExpress this identifier is the
+internal ArrayExpress Oracle database identifier for the array
+design.</p>
+</dd>
+<dt><strong><a name="item_get_array_design_id" id=
+"item_get_array_design_id">get_array_design_id</a></strong></dt>
+<dd>
+<p>Getter method for an identifier linking the data file to an
+array design. For Tab2MAGE this identifier is the ArrayExpress
+array accession number. For MIAMExpress this identifier is the
+internal ArrayExpress Oracle database identifier for the array
+design.</p>
+</dd>
+<dt><strong><a name="item_set_ded_identifier" id=
+"item_set_ded_identifier">set_ded_identifier</a></strong></dt>
+<dd>
+<p>Setter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_ded_identifier" id=
+"item_get_ded_identifier">get_ded_identifier</a></strong></dt>
+<dd>
+<p>Getter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_array_design" id=
+"item_set_array_design">set_array_design</a></strong></dt>
+<dd>
+<p>Setter method for the ArrayDesign object associated with this
+data file. See <a href="Datafile/ArrayDesign.html">the
+ArrayExpress::Datafile::ArrayDesign manpage</a> for information on
+this class.</p>
+</dd>
+<dt><strong><a name="item_get_array_design" id=
+"item_get_array_design">get_array_design</a></strong></dt>
+<dd>
+<p>Getter method for the Array Design object associated with this
+data file.</p>
+</dd>
+<dt><strong><a name="item_set_data_metrics" id=
+"item_set_data_metrics">set_data_metrics</a></strong></dt>
+<dd>
+<p>The set_data_metrics method is a setter method for a hashref
+which relates actual column heading to datatype, scale and
+subclass. The keys are QT names as defined in <a href=
+"Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a>. Note that the returned hashref should only have
+daughter hashrefs as values, and so if no datatype,scale or
+subclass info is available for a column heading (e.g. MetaRow,
+MetaColumn) then that coumn should not be represented. In practice,
+this method should only return information on the QTs for a single
+software type (see $self->check_column_headings).</p>
+</dd>
+<dt><strong><a name="item_get_data_metrics" id=
+"item_get_data_metrics">get_data_metrics</a></strong></dt>
+<dd>
+<p>Getter method for data metrics hashref (see
+set_data_metrics).</p>
+</dd>
+<dt><strong><a name="item_set_intensity_vector" id=
+"item_set_intensity_vector">set_intensity_vector</a></strong></dt>
+<dd>
+<p>Setter method for a hashref linking a datafile row identifier
+(e.g. ``1.1.4.1'') to a measured data value. Used in Pearson
+correlation coefficient calculation.</p>
+</dd>
+<dt><strong><a name="item_get_intensity_vector" id=
+"item_get_intensity_vector">get_intensity_vector</a></strong></dt>
+<dd>
+<p>Getter method for a hashref linking a datafile row identifier
+(e.g. ``1.1.4.1'') to a measured data value. Used in Pearson
+correlation coefficient calculation.</p>
+</dd>
+<dt><strong><a name="item_set_index_columns" id=
+"item_set_index_columns">set_index_columns</a></strong></dt>
+<dd>
+<p>Setter method for an arrayref describing the array indices of
+the coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.</p>
+</dd>
+<dt><strong><a name="item_get_index_columns" id=
+"item_get_index_columns">get_index_columns</a></strong></dt>
+<dd>
+<p>Getter method for an arrayref describing the array indices of
+the coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.</p>
+</dd>
+<dt><strong><a name="item_set_column_headings" id=
+"item_set_column_headings">set_column_headings</a></strong></dt>
+<dd>
+<p>Setter method for the actual column headings found in the data
+file (arrayref).</p>
+</dd>
+<dt><strong><a name="item_get_column_headings" id=
+"item_get_column_headings">get_column_headings</a></strong></dt>
+<dd>
+<p>Getter method for the actual column headings found in the data
+file (arrayref).</p>
+</dd>
+<dt><strong><a name="item_set_heading_qts" id=
+"item_set_heading_qts">set_heading_qts</a></strong></dt>
+<dd>
+<p>Setter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated
+QT will appear multiple times (as for example, in a FGEM data
+file).</p>
+</dd>
+<dt><strong><a name="item_get_heading_qts" id=
+"item_get_heading_qts">get_heading_qts</a></strong></dt>
+<dd>
+<p>Getter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated
+QT will appear multiple times (as for example, in a FGEM data
+file).</p>
+</dd>
+<dt><strong><a name="item_set_heading_hybs" id=
+"item_set_heading_hybs">set_heading_hybs</a></strong></dt>
+<dd>
+<p>Setter method for an arrayref containing a list of (potential)
+hyb ids derived from the column headings of a FGEM file. These are
+checked elsewhere.</p>
+</dd>
+<dt><strong><a name="item_get_heading_hybs" id=
+"item_get_heading_hybs">get_heading_hybs</a></strong></dt>
+<dd>
+<p>Getter method for an arrayref containing a list of (potential)
+hyb ids derived from the column headings of a FGEM file. These are
+checked elsewhere.</p>
+</dd>
+<dt><strong><a name="item_add_fail_columns" id=
+"item_add_fail_columns">add_fail_columns</a></strong></dt>
+<dd>
+<p>Method which adds the passed argument to a list of column
+headings which are unrecognized.</p>
+</dd>
+<dt><strong><a name="item_get_fail_columns" id=
+"item_get_fail_columns">get_fail_columns</a></strong></dt>
+<dd>
+<p>Returns an arrayref listing the unrecognized column headings
+(uniqued and sorted).</p>
+</dd>
+<dt><strong><a name="item_add_fail_hybs" id=
+"item_add_fail_hybs">add_fail_hybs</a></strong></dt>
+<dd>
+<p>Method which adds the passed argument to a list of unrecognized
+hybridization identifiers parsed from FGEM column headings which
+are unrecognized. Hybridization identifiers are either the Tab2MAGE
+or MIAMExpress user-supplied names for the hybridizations.</p>
+</dd>
+<dt><strong><a name="item_get_fail_hybs" id=
+"item_get_fail_hybs">get_fail_hybs</a></strong></dt>
+<dd>
+<p>Returns an arrayref listing the unrecognised hyb identifiers
+(uniqued and sorted).</p>
+</dd>
+<dt><strong><a name="item_set_is_exp" id=
+"item_set_is_exp">set_is_exp</a></strong></dt>
+<dd>
+<p>Setter method for boolean flag indicating whether the file is an
+EXP file or not.</p>
+</dd>
+<dt><strong><a name="item_get_is_exp" id=
+"item_get_is_exp">get_is_exp</a></strong></dt>
+<dd>
+<p>Getter method for EXP file flag.</p>
+</dd>
+<dt><strong><a name="item_get_is_binary" id=
+"item_get_is_binary">get_is_binary</a></strong></dt>
+<dd>
+<p>Getter method for binary file flag.</p>
+</dd>
+<dt><strong><a name="item_set_is_miamexpress" id=
+"item_set_is_miamexpress">set_is_miamexpress</a></strong></dt>
+<dd>
+<p>Setter method for boolean flag indicating whether the file is
+part of a MIAMExpress submission, for which slightly different
+validation rules are used. More typically the is_miamexpress
+argument to <code>new()</code> is used.</p>
+</dd>
+<dt><strong><a name="item_get_is_miamexpress" id=
+"item_get_is_miamexpress">get_is_miamexpress</a></strong></dt>
+<dd>
+<p>Getter method for MIAMExpress file flag.</p>
+</dd>
+<dt><strong><a name="item_add_factor_value" id=
+"item_add_factor_value">add_factor_value</a></strong></dt>
+<dd>
+<p>Mutator method for the experimental factor values associated
+with the file. Takes (category, value) as an argument, adds them to
+the list.</p>
+</dd>
+<dt><strong><a name="item_get_factor_value" id=
+"item_get_factor_value">get_factor_value</a></strong></dt>
+<dd>
+<p>Getter method for factor values associated with the file;
+returns a hashref in the form:</p>
+</dd>
+<dd>
+<pre>
+ {category => [value1, value2, ...], ...}
+</pre></dd>
+<dt><strong><a name="item_set_ded_type" id=
+"item_set_ded_type">set_ded_type</a></strong></dt>
+<dd>
+<p>Setter method for the type of DesignElementDimension associated
+with the file (Feature, Reporter or CompositeSequence).</p>
+</dd>
+<dt><strong><a name="item_get_ded_type" id=
+"item_get_ded_type">get_ded_type</a></strong></dt>
+<dd>
+<p>Getter method for the type of DesignElementDimension associated
+with the file.</p>
+</dd>
+<dt><strong><a name="item_set_format_type" id=
+"item_set_format_type">set_format_type</a></strong></dt>
+<dd>
+<p>Setter method for the format type (e.g., Affymetrix, GenePix,
+BlueFuse etc.). See <a href="Curator/Config.html">the
+ArrayExpress::Curator::Config manpage</a> for the enumerated
+types.</p>
+</dd>
+<dt><strong><a name="item_get_format_type" id=
+"item_get_format_type">get_format_type</a></strong></dt>
+<dd>
+<p>Getter method for the format type.</p>
+</dd>
+<dt><strong><a name="item_set_data_type" id=
+"item_set_data_type">set_data_type</a></strong></dt>
+<dd>
+<p>Setter method for the data type (e.g., raw, normalized,
+transformed). See <a href="Curator/Config.html">the
+ArrayExpress::Curator::Config manpage</a> for the enumerated types.
+Also allowed is the 'EXP' file type (Affymetrix).</p>
+</dd>
+<dt><strong><a name="item_get_data_type" id=
+"item_get_data_type">get_data_type</a></strong></dt>
+<dd>
+<p>Getter method for the data type.</p>
+</dd>
+<dt><strong><a name="item_set_qt_type" id=
+"item_set_qt_type">set_qt_type</a></strong></dt>
+<dd>
+<p>Setter method for the QT type associated with the file. This is
+derived from the software names defined in <a href=
+"Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a>.</p>
+</dd>
+<dt><strong><a name="item_get_qt_type" id=
+"item_get_qt_type">get_qt_type</a></strong></dt>
+<dd>
+<p>Getter method for the QT type associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_path" id=
+"item_set_path">set_path</a></strong></dt>
+<dd>
+<p>Setter method for the full filesystem path of the data file.</p>
+</dd>
+<dt><strong><a name="item_get_path" id=
+"item_get_path">get_path</a></strong></dt>
+<dd>
+<p>Getter method for the full filesystem path of the data file.</p>
+</dd>
+<dt><strong><a name="item_set_name" id=
+"item_set_name">set_name</a></strong></dt>
+<dd>
+<p>Setter method for the name of the file. Also sets
+$self->set_path if it has not been otherwise set. Note that this
+is not the same behaviour as the ``name'' argument to the object
+constructor, which does not change the path at all.</p>
+</dd>
+<dt><strong><a name="item_get_name" id=
+"item_get_name">get_name</a></strong></dt>
+<dd>
+<p>Getter method for the name of the file.</p>
+</dd>
+<dt><strong><a name="item_set_target_filename" id=
+"item_set_target_filename">set_target_filename</a></strong></dt>
+<dd>
+<p>Setter method for the name of the output stripped data file.</p>
+</dd>
+<dt><strong><a name="item_get_target_filename" id=
+"item_get_target_filename">get_target_filename</a></strong></dt>
+<dd>
+<p>Getter method for the name of the output stripped data file.</p>
+</dd>
+<dt><strong><a name="item_get_linebreak_type" id=
+"item_get_linebreak_type">get_linebreak_type</a></strong></dt>
+<dd>
+<p>Getter method for the line-ending character(s). Can be
+<code>\n</code>, <code>\r\n</code>, <code>\r</code> or
+<code>Unknown</code>.</p>
+</dd>
+<dt><strong><a name="item_get_line_format" id=
+"item_get_line_format">get_line_format</a></strong></dt>
+<dd>
+<p>Getter method for the line-ending format (Mac, Unix, DOS or
+Unknown). Used in reports.</p>
+</dd>
+<dt><strong><a name="item_set_exp_data" id=
+"item_set_exp_data">set_exp_data</a></strong></dt>
+<dd>
+<p>Setter method for storing the output of the
+$self->parse_exp_file method.</p>
+</dd>
+<dt><strong><a name="item_get_exp_data" id=
+"item_get_exp_data">get_exp_data</a></strong></dt>
+<dd>
+<p>Getter method for retrieving the output of the
+$self->parse_exp_file method.</p>
+</dd>
+<dt><strong><a name="item_set_mage_qtd" id=
+"item_set_mage_qtd">set_mage_qtd</a></strong></dt>
+<dd>
+<p>Setter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object
+associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_mage_qtd" id=
+"item_get_mage_qtd">get_mage_qtd</a></strong></dt>
+<dd>
+<p>Getter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object
+associated with the file.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix.html b/docs/ArrayExpress/Datafile/Affymetrix.html
new file mode 100644
index 0000000..52f5a6b
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix.html
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix - a factory class for
+generating Affymetrix data file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Affymetrix.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix - a factory class for
+generating Affymetrix data file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix->new();
+</pre>
+<pre>
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+</pre>
+<pre>
+ $cel->parse();
+</pre>
+<pre>
+ $cel->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix file formats. CEL, CHP and EXP formats are
+supported, with limited CDF parsing. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDF.html b/docs/ArrayExpress/Datafile/Affymetrix/CDF.html
new file mode 100644
index 0000000..9df5728
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDF.html
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CDF.pm - CDF data file
+parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CDF.pm - CDF data file
+parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::CDF );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is an abstract superclass used in parsing and export
+of data from Affymetrix CDF files.</p>
+<p>Please see <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_get_num_qc_cells" id=
+"item_get_num_qc_cells"><code>get_num_qc_cells()</code></a></strong></dt>
+<dd>
+<p>The number of cells on the chip dedicated to QC
+measurements.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html b/docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html
new file mode 100644
index 0000000..5789271
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::GDAC_CDF.pm - GDAC CDF
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: GDAC_CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::GDAC_CDF.pm - GDAC CDF data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF;
+</pre>
+<pre>
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF->new({
+ input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CDF files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most accessors and methods are implemented in the superclass;
+see <a href="../CDF.html">the
+ArrayExpress::Datafile::Affymetrix::CDF manpage</a>.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html b/docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html
new file mode 100644
index 0000000..683a7f9
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF.pm - XDA
+CDF data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: XDA_CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF.pm - XDA CDF
+data file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF;
+</pre>
+<pre>
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF->new({
+ input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix XDA CDF files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most parsing methods and accessors are implemented in the
+superclasses. See <a href="./GDAC_CDF.html">the
+ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF manpage</a> for
+information.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html
new file mode 100644
index 0000000..987a2c8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CDFFactory.html
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CDFFactory - a factory
+class for generating Affymetrix CDF file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CDFFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CDFFactory - a factory class
+for generating Affymetrix CDF file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CDFFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::CDFFactory->new();
+</pre>
+<pre>
+ my $cdf = $fac->make_parser( 'Data1.CDF' );
+</pre>
+<pre>
+ $cdf->parse();
+</pre>
+<pre>
+ $cdf->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix CDF files. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CEL.html b/docs/ArrayExpress/Datafile/Affymetrix/CEL.html
new file mode 100644
index 0000000..026a053
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CEL.html
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CEL.pm - CEL data file
+parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CEL.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CEL.pm - CEL data file
+parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CEL;
+</pre>
+<pre>
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL->new({
+ input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements an abstract superclass used in parsing
+and export of data from Affymetrix CEL files.</p>
+<p>Please see <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_header" id=
+"item_parse_header"><code>parse_header()</code></a></strong></dt>
+<dd>
+<p>This method will take the <code>input</code> attribute and parse
+only the header metadata. Note that for older CEL file formats, the
+numbers of masked, outlier or modified cells are not set by this
+method, since these values are embedded in the main body of the
+data.</p>
+</dd>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints the parsed expression
+data out to it. The QuantitationTypeDimension and
+DesignElementDimension of the resulting matrix are given by the
+<code>get_qtd</code> and <a href=
+"#item_get_ded"><code>get_ded</code></a> methods, respectively.</p>
+</dd>
+<dt><strong><a name="item_get_ded" id=
+"item_get_ded"><code>get_ded($chip_type)</code></a></strong></dt>
+<dd>
+<p>This method takes an optional string argument representing the
+chip type (e.g., ``HG-U133A'') and uses it to generate a reference
+to an array of Feature identifiers arranged in the order that the
+<strong>export</strong> method outputs them (i.e., a
+DesignElementDimension). The chip type can be derived in a number
+of ways; if EXP files are available, for instance, the
+$exp-><code>get_chip_type()</code> method should return an
+appropriate string. If this argument is not supplied then the
+$cel-><code>get_chip_type()</code> method is used to derive a
+value; note however that that method relies on parsing an
+undocumented tag and as such it may fail.</p>
+</dd>
+<dt><strong><a name="item_get_num_masked" id=
+"item_get_num_masked"><code>get_num_masked()</code></a></strong></dt>
+<dd>
+<p>The number of masked cells.</p>
+</dd>
+<dt><strong><a name="item_get_num_outliers" id=
+"item_get_num_outliers"><code>get_num_outliers()</code></a></strong></dt>
+<dd>
+<p>The number of outlier cells.</p>
+</dd>
+<dt><strong><a name="item_get_num_modified" id=
+"item_get_num_modified"><code>get_num_modified()</code></a></strong></dt>
+<dd>
+<p>The number of modified cells (this should always be zero).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html
new file mode 100644
index 0000000..e21830e
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CEL::CELv3.pm - CELv3
+data file parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CELv3.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CEL::CELv3.pm - CELv3 data
+file parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv3;
+</pre>
+<pre>
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv3->new({
+ input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CEL (v3) files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. See <a href=
+"../CEL.html">the ArrayExpress::Datafile::Affymetrix::CEL
+manpage</a> for details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html
new file mode 100644
index 0000000..ee00cc8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CEL::CELv4.pm - CELv4
+data file parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CELv4.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CEL::CELv4.pm - CELv4 data
+file parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv4;
+</pre>
+<pre>
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv4->new({
+ input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix XDA CEL (v4) files.</p>
+<p>Please see <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most parsing methods and accessors are implemented in the
+superclasses. See <a href="./CELv3.html">the
+ArrayExpress::Datafile::Affymetrix::CEL::CELv3 manpage</a> for
+information. The following attributes are specific to CELv4
+files:</p>
+<dl>
+<dt><strong><a name="item_get_cell_margin" id=
+"item_get_cell_margin"><code>get_cell_margin()</code></a></strong></dt>
+<dd>
+<p>The cell margin.</p>
+</dd>
+<dt><strong><a name="item_get_num_subgrids" id=
+"item_get_num_subgrids"><code>get_num_subgrids()</code></a></strong></dt>
+<dd>
+<p>The number of subgrids used on the chip (N.B. this is currently
+UNTESTED).</p>
+</dd>
+<dt><strong><a name="item_get_subgrids" id=
+"item_get_subgrids"><code>get_subgrids()</code></a></strong></dt>
+<dd>
+<p>A reference to a hash containing subgrid information (N.B. this
+is currently UNTESTED).</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html
new file mode 100644
index 0000000..46438f8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CELFactory.html
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CELFactory - a factory
+class for generating Affymetrix CEL file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CELFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CELFactory - a factory class
+for generating Affymetrix CEL file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CELFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::CELFactory->new();
+</pre>
+<pre>
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+</pre>
+<pre>
+ $cel->parse();
+</pre>
+<pre>
+ $cel->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix CEL files. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP.html
new file mode 100644
index 0000000..7b02819
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP.html
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP - GDAC CHP data file
+parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHP.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP - GDAC CHP data file
+parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>Abstract superclass for parsing and export of data from
+Affymetrix CHP files.</p>
+<p>Please see the <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_header" id=
+"item_parse_header"><code>parse_header()</code></a></strong></dt>
+<dd>
+<p>This method will take the <code>input</code> attribute and parse
+only the header metadata.</p>
+</dd>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh, $cdf)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints the parsed expression
+data out to it. The method also requires a pre-parsed CDF object to
+be passed to it. The QuantitationTypeDimension and
+DesignElementDimension of the resulting matrix are given by the
+<code>get_qtd</code> and <a href=
+"#item_get_ded"><code>get_ded</code></a> methods, respectively.</p>
+</dd>
+<dt><strong><a name="item_get_ded" id=
+"item_get_ded"><code>get_ded($cdf,
+$chip_type)</code></a></strong></dt>
+<dd>
+<p>This method takes a pre-parsed CDF object, and an optional
+string argument representing the chip type (e.g., ``HG-U133A'').
+The method uses these arguments to generate a reference to an array
+of CompositeSequence identifiers, arranged in the order that the
+<strong>export</strong> method outputs them (i.e., a
+DesignElementDimension). The chip type can be derived in a number
+of ways; if EXP files are available, for instance, the
+$exp-><code>get_chip_type()</code> method should return an
+appropriate string. If this argument is not supplied then the
+<code>chip_type()</code> methods for the CHP and CDF objects are
+inspected.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html
new file mode 100644
index 0000000..21775b1
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::CHPv12 - CHPv12
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPv12.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::CHPv12 - CHPv12 data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv12;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv12->new({
+ input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CHP (v12) files.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html
new file mode 100644
index 0000000..42c8920
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::CHPv13 - CHPv13
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPv13.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::CHPv13 - CHPv13 data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv13;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv13->new({
+ input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CHP (v12) files.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html
new file mode 100644
index 0000000..45b6010
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::CHPv8 - CHPv8 data
+file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPv8.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::CHPv8 - CHPv8 data file
+parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv8;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv8->new({
+ input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse_header();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix GDAC CHP (v8) files. Note that only the data file header
+can be parsed using this module.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html
new file mode 100644
index 0000000..5395fd8
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP - GDAC CHP
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: GDAC_CHP.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP - GDAC CHP
+data file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>Abstract superclass for parsing and export of data from
+Affymetrix CHP files. For older GDAC CHP files this class also
+implements a file header parsing method, which can be used to
+determine the version of the file (e.g. v8, v12, v13).</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_parse_header" id=
+"item_parse_header"><code>parse_header()</code></a></strong></dt>
+<dd>
+<p>This method will take the <code>input</code> attribute and parse
+only the header metadata.</p>
+</dd>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh, $cdf)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints the parsed expression
+data out to it. The method also requires a pre-parsed CDF object to
+be passed to it. The QuantitationTypeDimension and
+DesignElementDimension of the resulting matrix are given by the
+<code>get_qtd</code> and <a href=
+"#item_get_ded"><code>get_ded</code></a> methods, respectively.</p>
+</dd>
+<dt><strong><a name="item_get_ded" id=
+"item_get_ded"><code>get_ded($cdf,
+$chip_type)</code></a></strong></dt>
+<dd>
+<p>This method takes a pre-parsed CDF object, and an optional
+string argument representing the chip type (e.g., ``HG-U133A'').
+The method uses these arguments to generate a reference to an array
+of CompositeSequence identifiers, arranged in the order that the
+<strong>export</strong> method outputs them (i.e., a
+DesignElementDimension). The chip type can be derived in a number
+of ways; if EXP files are available, for instance, the
+$exp-><code>get_chip_type()</code> method should return an
+appropriate string. If this argument is not supplied then the
+<code>chip_type()</code> methods for the CHP and CDF objects are
+inspected.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html b/docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html
new file mode 100644
index 0000000..4d54e2c
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP - XDA CHP
+data file parsing.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: XDA_CHP.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP - XDA CHP data
+file parsing.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP;
+</pre>
+<pre>
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP->new({
+ input => 'mydata.CHP',
+ });
+</pre>
+<pre>
+ $chp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix XDA CHP files.</p>
+<p>Please see the <a href="../Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a>
+documentation for methods common to all the Affymetrix file
+classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>Most methods are implemented in the superclass. Please see
+<a href="./GDAC_CHP.html">the
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP manpage</a> for
+details.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html
new file mode 100644
index 0000000..b2edf5a
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/CHPFactory.html
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::CHPFactory - a factory
+class for generating Affymetrix CHP file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CHPFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::CHPFactory - a factory class
+for generating Affymetrix CHP file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::CHPFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::CHPFactory->new();
+</pre>
+<pre>
+ my $cel = $fac->make_parser( 'Data1.CHP' );
+</pre>
+<pre>
+ $cel->parse();
+</pre>
+<pre>
+ $cel->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix CHP files. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/EXP.html b/docs/ArrayExpress/Datafile/Affymetrix/EXP.html
new file mode 100644
index 0000000..e5e17f4
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/EXP.html
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::EXP - EXP data file
+parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: CDF.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::EXP - EXP data file
+parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::EXP;
+</pre>
+<pre>
+ my $exp = ArrayExpress::Datafile::Affymetrix::EXP->new({
+ input => 'data1.EXP',
+ });
+ $exp->parse();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module implements parsing and export of data from
+Affymetrix EXP files.</p>
+<p>Please see <a href="./Parser.html">the
+ArrayExpress::Datafile::Affymetrix::Parser manpage</a> for methods
+common to all the Affymetrix parser classes.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_export" id=
+"item_export"><code>export($fh)</code></a></strong></dt>
+<dd>
+<p>This method takes a filehandle and prints out the EXP file in
+its original format. This method is not particularly useful and is
+really for testing purposes only.</p>
+</dd>
+<dt><strong><a name="item_get_chip_lot" id=
+"item_get_chip_lot"><code>get_chip_lot()</code></a></strong></dt>
+<dd>
+<p>The lot number of the chip.</p>
+</dd>
+<dt><strong><a name="item_get_operator" id=
+"item_get_operator"><code>get_operator()</code></a></strong></dt>
+<dd>
+<p>The person who performed the procedure.</p>
+</dd>
+<dt><strong><a name="item_get_protocol" id=
+"item_get_protocol"><code>get_protocol()</code></a></strong></dt>
+<dd>
+<p>The name of the hybridization protocol used (e.g.
+EukGE-WS2v4).</p>
+</dd>
+<dt><strong><a name="item_get_station" id=
+"item_get_station"><code>get_station()</code></a></strong></dt>
+<dd>
+<p>The station number.</p>
+</dd>
+<dt><strong><a name="item_get_module" id=
+"item_get_module"><code>get_module()</code></a></strong></dt>
+<dd>
+<p>The module number.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_date" id=
+"item_get_hyb_date"><code>get_hyb_date()</code></a></strong></dt>
+<dd>
+<p>The date on which the hybridization was performed. This is
+returned in the same format as in the EXP file; no sanitization is
+performed.</p>
+</dd>
+<dt><strong><a name="item_get_pixel_size" id=
+"item_get_pixel_size"><code>get_pixel_size()</code></a></strong></dt>
+<dd>
+<p>Pixel size (integer).</p>
+</dd>
+<dt><strong><a name="item_get_filter" id=
+"item_get_filter"><code>get_filter()</code></a></strong></dt>
+<dd>
+<p>Filter (570nm).</p>
+</dd>
+<dt><strong><a name="item_get_scan_temp" id=
+"item_get_scan_temp"><code>get_scan_temp()</code></a></strong></dt>
+<dd>
+<p>Scan temperature.</p>
+</dd>
+<dt><strong><a name="item_get_scan_date" id=
+"item_get_scan_date"><code>get_scan_date()</code></a></strong></dt>
+<dd>
+<p>The date on which the scanning was performed. This is returned
+in the same format as in the EXP file; no sanitization is
+performed.</p>
+</dd>
+<dt><strong><a name="item_get_scanner_id" id=
+"item_get_scanner_id"><code>get_scanner_id()</code></a></strong></dt>
+<dd>
+<p>Scanner ID.</p>
+</dd>
+<dt><strong><a name="item_get_num_scans" id=
+"item_get_num_scans"><code>get_num_scans()</code></a></strong></dt>
+<dd>
+<p>Number of scans performed.</p>
+</dd>
+<dt><strong><a name="item_get_scanner_type" id=
+"item_get_scanner_type"><code>get_scanner_type()</code></a></strong></dt>
+<dd>
+<p>Scanner type.</p>
+</dd>
+<dt><strong><a name="item_get_hyb_parameters" id=
+"item_get_hyb_parameters"><code>get_hyb_parameters()</code></a></strong></dt>
+<dd>
+<p>A reference to an array of named hybridization parameters. Each
+parameter is coded as a separate hash with a single {name =>
+value} pair. Typically this method is not very useful; you should
+probably be using the <strong>parameters</strong> or
+<strong>add_parameters</strong> methods instead.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html b/docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html
new file mode 100644
index 0000000..4e2575a
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/EXPFactory.html
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::EXPFactory - a factory
+class for generating Affymetrix EXP file parsing objects.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: EXPFactory.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::EXPFactory - a factory class
+for generating Affymetrix EXP file parsing objects.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Affymetrix::EXPFactory;
+</pre>
+<pre>
+ my $fac = ArrayExpress::Datafile::Affymetrix::EXPFactory->new();
+</pre>
+<pre>
+ my $exp = $fac->make_parser( 'Data1.EXP' );
+</pre>
+<pre>
+ $exp->parse();
+</pre>
+<pre>
+ $exp->export($output_filehandle);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is a factory class used in to create data file
+parsers for Affymetrix EXP files.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new()</code></a></strong></dt>
+<dd>
+<p>The class constructor.</p>
+</dd>
+<dt><strong><a name="item_make_parser" id=
+"item_make_parser"><code>make_parser($file)</code></a></strong></dt>
+<dd>
+<p>This method takes a filename argument and returns a parser
+object that can then be used to process the data file and return
+interesting values.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Affymetrix/Parser.html b/docs/ArrayExpress/Datafile/Affymetrix/Parser.html
new file mode 100644
index 0000000..8639c17
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Affymetrix/Parser.html
@@ -0,0 +1,183 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Affymetrix::Parser - an Affymetrix
+data file parsing module.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <ul>
+
+ <li><a href="#accessor_methods">Accessor methods</a></li>
+ </ul>
+
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: Affymetrix/Parser.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Affymetrix::Parser - an Affymetrix data
+file parsing module.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use base qw( ArrayExpress::Datafile::Affymetrix::Parser );
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module is an abstract superclass used in the parsing and
+export of data from Affymetrix file formats. CEL, CHP and EXP
+formats are supported, with limited CDF parsing. Both old (GDAC)
+and new (GCOS/XDA) file formats can be parsed.</p>
+<p>There is a set of methods common to all the Affymetrix file
+classes, listed below. There are additional methods, specific to
+each class, which are documented in the relevant pages.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<p>The following methods are common to all classes.</p>
+<dl>
+<dt><strong><a name="item_new" id="item_new">new({ input =>
+'myfile.CEL' })</a></strong></dt>
+<dd>
+<p>The class constructor. This method returns a an object of the
+appropriate class, without performing any additional
+processing.</p>
+</dd>
+<dt><strong><a name="item_parse" id=
+"item_parse"><code>parse()</code></a></strong></dt>
+<dd>
+<p>This method will take the value for the <code>input</code>
+attribute and parse the data into memory so that it can be
+interrogated using the methods below.</p>
+</dd>
+</dl>
+<h2><a name="accessor_methods" id="accessor_methods">Accessor
+methods</a></h2>
+<p>Each of these methods acts as both setter and getter for the
+attributes in question. Typically these will be used to access the
+data and metadata which was extracted using the
+<strong>parse</strong> method. Please see the respective subclass
+documentation for information on the <strong>export</strong> and
+<strong>get_ded</strong> methods. Note that many of the following
+will have no meaning for EXP file metadata.</p>
+<dl>
+<dt><strong><a name="item_get_version" id=
+"item_get_version"><code>get_version()</code></a></strong></dt>
+<dd>
+<p>The version number or string associated with the file. For CEL
+and CHP files this should be either 3 or 4.</p>
+</dd>
+<dt><strong><a name="item_get_num_columns" id=
+"item_get_num_columns"><code>get_num_columns()</code></a></strong></dt>
+<dd>
+<p>The number of columns on the array.</p>
+</dd>
+<dt><strong><a name="item_get_num_rows" id=
+"item_get_num_rows"><code>get_num_rows()</code></a></strong></dt>
+<dd>
+<p>The number of rows on the array.</p>
+</dd>
+<dt><strong><a name="item_get_num_cells" id=
+"item_get_num_cells"><code>get_num_cells()</code></a></strong></dt>
+<dd>
+<p>The number of cells on the array. For CEL files this corresponds
+to the number of columns multiplied by the number of rows.</p>
+</dd>
+<dt><strong><a name="item_get_algorithm" id=
+"item_get_algorithm"><code>get_algorithm()</code></a></strong></dt>
+<dd>
+<p>The name of the algorithm used to produce the data (e.g.
+``Percentile'', ``ExpressionStat'').</p>
+</dd>
+<dt><strong><a name="item_get_chip_type" id=
+"item_get_chip_type"><code>get_chip_type()</code></a></strong></dt>
+<dd>
+<p>The type of chip used (e.g., HG-U133A). This is supported for
+EXP files, CEL files, CHP files and GDAC format CDF files. Note
+that for CEL files this relies on parsing a header tag which is not
+actually documented by Affymetrix, and so it is possible that this
+method is not to be trusted in such cases.</p>
+</dd>
+<dt><strong><a name="item_get_parameters" id=
+"item_get_parameters"><code>get_parameters()</code></a></strong></dt>
+<dd>
+<p>A reference to a hash with parameter {name => value} pairs.
+Parameters are grouped as follows:</p>
+</dd>
+<dd>
+<pre>
+ CEL: Feature extraction parameters
+ CHP: Normalization parameters
+ EXP: Hybridization parameters (numbered).
+</pre></dd>
+<dt><strong><a name="item_get_stats" id=
+"item_get_stats"><code>get_stats()</code></a></strong></dt>
+<dd>
+<p>A reference to a hash with statistic {name => value} pairs.
+Statistics are grouped as follows:</p>
+</dd>
+<dd>
+<pre>
+ CEL: Feature extraction summary statistics
+ CHP: Normalization summary statistics
+</pre></dd>
+<dt><strong><a name="item_get_qtd" id=
+"item_get_qtd"><code>get_qtd()</code></a></strong></dt>
+<dd>
+<p>A reference to an array listing the QuantitationType identifiers
+(long form) in the column order that they are output by the
+<strong>export</strong> method. See also the
+<strong>headings</strong> method below.</p>
+</dd>
+<dt><strong><a name="item_get_headings" id=
+"item_get_headings"><code>get_headings()</code></a></strong></dt>
+<dd>
+<p>A reference to an array listing the QuantitationType names
+(short form) in the column order that they are output by the
+<strong>export</strong> method. Using this method also populates
+the <strong>qtd</strong> method data structure, but not vice
+versa.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/ArrayDesign.html b/docs/ArrayExpress/Datafile/ArrayDesign.html
new file mode 100644
index 0000000..7b26568
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/ArrayDesign.html
@@ -0,0 +1,126 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::ArrayDesign - memory-efficient
+handling of array design information.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <ul>
+
+ <li><a href="#accessor_methods">Accessor methods</a></li>
+ </ul>
+
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::ArrayDesign - memory-efficient handling
+of array design information.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile:::ArrayDesign
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>ArrayDesign objects are used during experiment checking to
+handle array design elements in a memory-efficient manner.</p>
+<h2><a name="accessor_methods" id="accessor_methods">Accessor
+methods</a></h2>
+<dl>
+<dt><strong><a name="item_set_accession" id=
+"item_set_accession">set_accession</a></strong></dt>
+<dd>
+<p>Setter method for the array accession number relating to this
+design.</p>
+</dd>
+<dt><strong><a name="item_get_accession" id=
+"item_get_accession">get_accession</a></strong></dt>
+<dd>
+<p>Getter method for the array accession number relating to this
+design.</p>
+</dd>
+<dt><strong><a name="item_set_adf_features" id=
+"item_set_adf_features">set_adf_features</a></strong></dt>
+<dd>
+<p>Setter method for a hashref with keys which are period-delim
+strings of the feature coordinates found in the array design
+associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_adf_features" id=
+"item_get_adf_features">get_adf_features</a></strong></dt>
+<dd>
+<p>Getter method for a hashref with keys which are period-delim
+strings of the feature coordinates found in the array design
+associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_adf_reporters" id=
+"item_set_adf_reporters">set_adf_reporters</a></strong></dt>
+<dd>
+<p>Setter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.</p>
+</dd>
+<dt><strong><a name="item_get_adf_reporters" id=
+"item_get_adf_reporters">get_adf_reporters</a></strong></dt>
+<dd>
+<p>Getter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.</p>
+</dd>
+<dt><strong><a name="item_set_adf_compseqs" id=
+"item_set_adf_compseqs">set_adf_compseqs</a></strong></dt>
+<dd>
+<p>Setter method for a hashref with keys which are the composite
+sequence identifiers found in the array design associated with the
+file.</p>
+</dd>
+<dt><strong><a name="item_get_adf_compseqs" id=
+"item_get_adf_compseqs">get_adf_compseqs</a></strong></dt>
+<dd>
+<p>Getter method for a hashref with keys which are the composite
+sequence identifiers found in the array design associated with the
+file.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/Parser.html b/docs/ArrayExpress/Datafile/Parser.html
new file mode 100644
index 0000000..dbbc093
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/Parser.html
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::Parser - a module handling Datafile
+parsing</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../../index.html"><img src="../../../T2M_logo.png" border=
+"0" height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::Parser - a module handling Datafile
+parsing</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::Parser;
+ my $parser = ArrayExpress::Datafile::Parser->new;
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>The ArrayExpress::Datafile module provides methods for parsing
+individual data files. This module provides a global handler for
+Datafile objects.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/Datafile/QT_list.html b/docs/ArrayExpress/Datafile/QT_list.html
new file mode 100644
index 0000000..377aa0e
--- /dev/null
+++ b/docs/ArrayExpress/Datafile/QT_list.html
@@ -0,0 +1,248 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::Datafile::QT_list.pm - a module handling
+QuantitationType information.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#quantitation_types">QUANTITATION TYPES</a></li>
+ <ul>
+
+ <li><a href="#qt_file_columns">QT file columns</a></li>
+ </ul>
+
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: QT_list.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::Datafile::QT_list.pm - a module handling
+QuantitationType information.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::Datafile::QT_list qw(get_QTs writeQTs)
+ my $QT_hash = get_QTs();
+ write_QTs($QT_hash);
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This is a module providing QuantitationType support information
+and subroutines for the Tab2MAGE and experiment checker
+package.</p>
+<hr />
+<h1><a name="quantitation_types" id=
+"quantitation_types">QUANTITATION TYPES</a></h1>
+<p>Known QuantitationTypes are listed in the QT_list.pm module
+included with this distribution. This file includes a simple
+tab-delimited text section with subsections specific to a given
+software/manufacturer type. Each subsection begins with the tag
+``<code>>>></code>''. There are several columns, but only
+the first is required:</p>
+<table class="example">
+<tr>
+<th>>>>QT software 1</th>
+</tr>
+<tr>
+<td>QT1 name</td>
+<td>QT1 datatype</td>
+<td>QT1 scale</td>
+<td>QT1 subclass</td>
+<td>QT1 is_background</td>
+<td>QT1 target (confidence indicators)</td>
+<td>QT1 channel</td>
+<td>QT1 description</td>
+</tr>
+<tr>
+<td>QT2 name</td>
+<td>QT2 datatype</td>
+<td>QT2 scale</td>
+<td>QT2 subclass</td>
+<td>QT2 is_background</td>
+<td>QT2 target (confidence indicators)</td>
+<td>QT2 channel</td>
+<td>QT2 description</td>
+</tr>
+<tr>
+<td>QT3 name</td>
+<td>QT3 datatype</td>
+<td>QT3 scale</td>
+<td>QT3 subclass</td>
+<td>QT3 is_background</td>
+<td>QT3 target (confidence indicators)</td>
+<td>QT3 channel</td>
+<td>QT3 description</td>
+</tr>
+<tr>
+<td>QT4 name...</td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+</tr>
+<tr>
+<th>>>>QT software 2</th>
+</tr>
+<tr>
+<td>QT5 name</td>
+<td>QT5 datatype</td>
+<td>QT5 scale</td>
+<td>QT5 subclass</td>
+<td>QT5 is_background</td>
+<td>QT5 target (confidence indicators)</td>
+<td>QT5 channel</td>
+<td>QT5 description</td>
+</tr>
+<tr>
+<td>QT6 name...</td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+<td> </td>
+</tr>
+</table>
+<p>where QT1, QT2 and so on are the quantitation types. Note that
+for the resulting MAGE identifiers to have the correct namespace,
+the ``>>>QT software'' strings should contain the
+namespace in square brackets, for example:</p>
+<pre>
+ >>>Feature Extraction Software[Agilent Technologies]
+</pre>
+<p>Note also that to add to a pre-existing set of quantitation
+types from a given software, this initial string
+<strong>must</strong> match that included in the Tab2MAGE package.
+Currently included software types are as follows:</p>
+<pre>
+ AIDA[Raytest]
+ Affymetrix[Affymetrix]
+ AppliedBiosystems
+ ArrayGauge[FUJIFILM]
+ ArrayVision[Imaging Research]
+ BZScan[TAGC ERM206]
+ BlueFuse[BlueGnome]
+ ChipSkipper[EMBL]
+ CodeLink[Motorola Life Sciences]
+ CodeLink Expression Analysis[GE Healthcare]
+ Feature Extraction Software[Agilent Technologies]
+ GEMTools[Incyte Genomics]
+ GLEAMS[NuTec Sciences]
+ GenePix[Axon Instruments]
+ GeneTAC
+ ImaGene[BioDiscovery]
+ NimbleScan[NimbleGen Systems]
+ QuantArray[PerkinElmer]
+ ScanAlyze[Stanford University]
+ ScanArray Express[PerkinElmer]
+ SpotFinder[TIGR]
+ UCSF Spot
+ Uni of Toronto in-house analysis software
+ Tab2MAGE_FGEM[ebi.ac.uk]
+</pre>
+<p>Each file may contain QT information from any number of software
+vendors. The contents of each column are described below:</p>
+<h2><a name="qt_file_columns" id="qt_file_columns">QT file
+columns</a></h2>
+<dl>
+<dt><strong><a name="item_name" id=
+"item_name">name</a></strong></dt>
+<dd>
+<p>The name of the QuantitationType. This corresponds to the exact
+string found in the data files.</p>
+</dd>
+<dt><strong><a name="item_datatype" id=
+"item_datatype">datatype</a></strong></dt>
+<dd>
+<p>The DataType OntologyEntry to be used (MGED Ontology). Default:
+float</p>
+</dd>
+<dt><strong><a name="item_scale" id=
+"item_scale">scale</a></strong></dt>
+<dd>
+<p>The Scale OntologyEntry to be used (MGED Ontology). Default:
+linear_scale</p>
+</dd>
+<dt><strong><a name="item_subclass" id=
+"item_subclass">subclass</a></strong></dt>
+<dd>
+<p>The QuantitationType subclass to use (e.g., MeasuredSignal,
+Error etc.). Default: SpecializedQuantitationType</p>
+</dd>
+<dt><strong><a name="item_is_background" id=
+"item_is_background">is_background</a></strong></dt>
+<dd>
+<p>Boolean flag (0 or 1) indicating whether the QT is background.
+Default: 0</p>
+</dd>
+<dt><strong><a name="item_confidence_indicator_target" id=
+"item_confidence_indicator_target">confidence indicator
+target</a></strong></dt>
+<dd>
+<p>Only used for ConfidenceIndicator QTs (Error, PValue, and
+ExpectedValue). Indicates the target QT name to which the
+ConfidenceIndicator applies. Defaults to undefined.</p>
+</dd>
+<dt><strong><a name="item_channel" id=
+"item_channel">channel</a></strong></dt>
+<dd>
+<p>The channel to be associated with the QT. Note that QTs can only
+have one channel; this is a current limitation of the MAGE object
+model. QTs linked to more than one channel in the real world (e.g.
+ratios) should omit the channel value here.</p>
+</dd>
+<dt><strong><a name="item_description" id=
+"item_description">description</a></strong></dt>
+<dd>
+<p>A plain-text description of the QT.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.
+Particular credit goes to Ele Holloway, who was responsible for
+curating the lists of QuantitationTypes included with this
+script.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/MAGETAB.html b/docs/ArrayExpress/MAGETAB.html
new file mode 100644
index 0000000..5b6cb6f
--- /dev/null
+++ b/docs/ArrayExpress/MAGETAB.html
@@ -0,0 +1,286 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::MAGETAB - A parser class for MAGE-TAB
+documents.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#methods">METHODS</a></li>
+ <li><a href="#protocol_accessions">PROTOCOL_ACCESSIONS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../T2M_logo.png" border="0"
+height="50" alt="Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Module detail: MAGETAB.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::MAGETAB - A parser class for MAGE-TAB
+documents.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::MAGETAB;
+ my $magetab = ArrayExpress::MAGETAB->new({
+ idf => $idf_file,
+ target_directory => $dir,
+ expt_accession => $accn,
+ });
+</pre>
+<pre>
+ $magetab->write_mageml();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module acts as a front end to the MAGE-TAB parsing API.
+Parser objects are instantiated with a number of attributes to
+control how the MAGE-TAB document is parsed. Support is only
+provided for IDF and SDRF documents at present; it is anticipated
+that the parser will be extended to support ADF at a later
+date.</p>
+<p>Currently the parser is built using a MAGEv1.1 object model to
+store the MAGE-TAB metadata. It is envisioned that this dependence
+on the Bio::MAGE modules may be removed once a full MAGE-TAB object
+model is agreed upon by the community.</p>
+<p>To simplify the process of data submission, ArrayExpress has
+introduced a new flavour of MAGE-TAB in which the IDF and SDRF
+sections are combined into a single worksheet. This parser supports
+both MAGE-TAB v1.1 documents (with separate IDF and SDRF) and these
+combined documents.</p>
+<hr />
+<h1><a name="methods" id="methods">METHODS</a></h1>
+<dl>
+<dt><strong><a name="item_new" id=
+"item_new"><code>new</code></a></strong></dt>
+<dd>
+<p>Object constructor. This recognises the following
+attributes:</p>
+</dd>
+<dd>
+<dl>
+<dt><strong><a name="item_idf" id=
+"item_idf"><code>idf</code></a></strong></dt>
+<dd>
+<p>The path of the IDF file with which to start parsing.</p>
+</dd>
+<dt><strong><a name="item_magetab_doc" id=
+"item_magetab_doc"><code>magetab_doc</code></a></strong></dt>
+<dd>
+<p>The path of a combined IDF+SDRF file to parse.</p>
+</dd>
+<dt><strong><a name="item_output_file" id=
+"item_output_file"><code>output_file</code></a></strong></dt>
+<dd>
+<p>The name of the output MAGE-ML file.</p>
+</dd>
+<dt><strong><a name="item_namespace" id=
+"item_namespace"><code>namespace</code></a></strong></dt>
+<dd>
+<p>The namespace to use in MAGE identifier creation.</p>
+</dd>
+<dt><strong><a name="item_authority" id=
+"item_authority"><code>authority</code></a></strong></dt>
+<dd>
+<p>The authority to use in MAGE identifier creation.</p>
+</dd>
+<dt><strong><a name="item_expt_accession" id=
+"item_expt_accession"><code>expt_accession</code></a></strong></dt>
+<dd>
+<p>The accession number assigned to the experiment.</p>
+</dd>
+<dt><strong><a name="item_target_directory" id=
+"item_target_directory"><code>target_directory</code></a></strong></dt>
+<dd>
+<p>The directory into which to write the output files.</p>
+</dd>
+<dt><strong><a name="item_source_directory" id=
+"item_source_directory"><code>source_directory</code></a></strong></dt>
+<dd>
+<p>The directory which contains all data and SDRF files.</p>
+</dd>
+<dt><strong><a name="item_is_standalone" id=
+"item_is_standalone"><code>is_standalone</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether the script is able to connect to
+ArrayExpress to retrieve array design information. It is sometimes
+desirable to skip these downloads, which can be quite large.</p>
+</dd>
+<dt><strong><a name="item_qt_filename" id=
+"item_qt_filename"><code>qt_filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+as part of the Tab2MAGE package. See <a href=
+"Datafile/QT_list.html">the ArrayExpress::Datafile::QT_list
+manpage</a> for more information.</p>
+</dd>
+<dt><strong><a name="item_include_default_qts" id=
+"item_include_default_qts"><code>include_default_qts</code></a></strong></dt>
+<dd>
+<p>This option can be used in conjunction with <a href=
+"#item_qt_filename"><code>qt_filename</code></a> to indicate that
+the QuantitationType listing from the Tab2MAGE package itself
+should be included in the lists of known QuantitationTypes used in
+data file parsing. The default behaviour is to deactivate these
+known QTs if a custom QT file is to be used.</p>
+</dd>
+<dt><strong><a name="item_keep_all_qts" id=
+"item_keep_all_qts"><code>keep_all_qts</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether unrecognised QuantitationTypes in data
+files should be kept or not. The default behaviour is to strip
+unrecognised columns out of the data files.</p>
+</dd>
+<dt><strong><a name="item_reporter_prefix" id=
+"item_reporter_prefix"><code>reporter_prefix</code></a></strong></dt>
+<dd>
+<p>The prefix to be used during Reporter identifier construction.
+This prefix is prepended to the identifiers listed in the data
+files.</p>
+</dd>
+<dt><strong><a name="item_compseq_prefix" id=
+"item_compseq_prefix"><code>compseq_prefix</code></a></strong></dt>
+<dd>
+<p>The prefix to be used during CompositeElement
+(CompositeSequence) identifier construction. This prefix is
+prepended to the identifiers listed in the data files.</p>
+</dd>
+<dt><strong><a name="item_protocol_accession_service" id=
+"item_protocol_accession_service"><code>protocol_accession_service</code></a></strong></dt>
+<dd>
+<p>A code reference used to reassign protocol accessions. See
+<em>PROTOCOL_ACCESSIONS</em>, below.</p>
+</dd>
+<dt><strong><a name="item_protocol_accession_prefix" id=
+"item_protocol_accession_prefix"><code>protocol_accession_prefix</code></a></strong></dt>
+<dd>
+<p>The prefix to be used for protocol accession creation, when the
+autosubmissions system is in use. See <em>PROTOCOL_ACCESSIONS</em>,
+below.</p>
+</dd>
+<dt><strong><a name="item_keep_protocol_accns" id=
+"item_keep_protocol_accns"><code>keep_protocol_accns</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether the protocols in the IDF should be
+assigned new accession numbers. This option overrides <a href=
+"#item_protocol_accession_service"><code>protocol_accession_service</code></a>.</p>
+</dd>
+<dt><strong><a name="item_use_plain_text" id=
+"item_use_plain_text"><code>use_plain_text</code></a></strong></dt>
+<dd>
+<p>Some file formats are only supported in their native forms by
+ArrayExpress. Nonetheless, this package can parse some of these
+data formats into tab-delimited representations fully encoded in
+the MAGE-ML document (examples include Nimblegen data, Affymetrix
+CEL files).</p>
+</dd>
+<dt><strong><a name="item_skip_datafiles" id=
+"item_skip_datafiles"><code>skip_datafiles</code></a></strong></dt>
+<dd>
+<p>This option tells the parser to skip attempting to read the data
+files referenced by a given MAGE-TAB document, and instead attempts
+to generate MAGE in their absence. This option is particularly
+useful for unsupported data file formats.</p>
+</dd>
+<dt><strong><a name="item_ignore_size_limits" id=
+"item_ignore_size_limits"><code>ignore_size_limits</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE configuration file allows the user to set maximum
+data file sizes for parsing and web download, to provide some
+protection from overloading the system in a production pipeline
+setting. To temporarily ignore these size limits, use this
+option.</p>
+</dd>
+<dt><strong><a name="item_in_relaxed_mode" id=
+"item_in_relaxed_mode"><code>in_relaxed_mode</code></a></strong></dt>
+<dd>
+<p>A flag indicating whether to allow minor errors during parsing.
+At the moment the only errors which are ignored by this option are
+<code>Term Source REF</code>, <code>Protocol REF</code>,
+<code>Parameter Value []</code> and <code>Factor Value []</code>
+columns which reference Names which have not been defined in the
+IDF.</p>
+</dd>
+<dt><strong><a name="item_clobber" id=
+"item_clobber"><code>clobber</code></a></strong></dt>
+<dd>
+<p>Flag indicating whether or not to overwrite existing files
+without prompting the user.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="item_parse" id=
+"item_parse"><code>parse</code></a></strong></dt>
+<dd>
+<p>Starts the MAGE-TAB parse and loads the document into
+memory.</p>
+</dd>
+<dt><strong><a name="item_write_mageml" id=
+"item_write_mageml"><code>write_mageml</code></a></strong></dt>
+<dd>
+<p>Writes out MAGE-ML corresponding to the input MAGE-TAB document.
+If the MAGE-TAB has not yet been parsed, <a href=
+"#item_parse"><code>parse()</code></a> is called automatically.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="protocol_accessions" id=
+"protocol_accessions">PROTOCOL_ACCESSIONS</a></h1>
+<p>The parser provides a set of callbacks which can be used to
+assign MAGE-TAB Protocol Names to unique accessions at the point of
+parsing. If the autosubmissions system has been set up and
+configured, then the parser will default to using that mechanism to
+assign protocol accessions. If you wish to use your own service,
+you may use the <a href=
+"#item_protocol_accession_service"><code>protocol_accession_service</code></a>
+and <a href=
+"#item_protocol_accession_prefix"><code>protocol_accession_prefix</code></a>
+attributes to control this. The <a href=
+"#item_protocol_accession_service"><code>protocol_accession_service</code></a>
+should point to a code reference which will accept two arguments:
+(a) the Protocol Name as given in the IDF, and (b) the experiment
+accession. The code reference should return a unique accession
+which will then be assigned to the protocol in the output
+MAGE-ML.</p>
+<p>If the autosubmissions system is to be used, the <a href=
+"#item_protocol_accession_prefix"><code>protocol_accession_prefix</code></a>
+attribute must be set, e.g. to ``P-MTAB-''.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2008.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/ArrayExpress/MAGETAB/Checker.html b/docs/ArrayExpress/MAGETAB/Checker.html
new file mode 100644
index 0000000..ac251cd
--- /dev/null
+++ b/docs/ArrayExpress/MAGETAB/Checker.html
@@ -0,0 +1,215 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>ArrayExpress::MAGETAB::Checker - MAGE-TAB experiment
+checking.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#tests">TESTS</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../../../index.html"><img src="../../MAGETAB_logo.png" border="0"
+height="50" alt="MAGE-TAB logo" /></a></td>
+<td class="pagetitle">Module detail: Checker.pm</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>ArrayExpress::MAGETAB::Checker - MAGE-TAB experiment
+checking.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ use ArrayExpress::MAGETAB::Checker;
+
+ my $checker = ArrayExpress::MAGETAB::Checker->new({
+ idf => $magetab_idf,
+ });
+
+ $checker->check();
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This module provides the basic operations needed for checking a
+MAGE-TAB document and associated data files.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<p>The following options must be used in addition to those provided
+by the parent class (see <a href=
+"../Curator/ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a>):</p>
+<dl>
+<dt><strong><a name="item_idf" id=
+"item_idf"><code>idf</code></a></strong></dt>
+<dd>
+<p>The MAGE-TAB IDF filename to be checked.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>The following tests are performed by this module, with output
+printed to the error and/or report logs:</p>
+<dl>
+<dt><strong><a name="item_idf_contents" id=
+"item_idf_contents"><strong>IDF Contents</strong></a></strong></dt>
+<dd>
+<p>Checks that at least one of each of the following items are
+present in the IDF, warning the user if any are missing:</p>
+</dd>
+<dd>
+<pre>
+ Experimental Factor
+ Contact
+ Publication
+ Protocol
+ SDRF
+</pre></dd>
+<dt><strong><a name="item_experiment_info" id=
+"item_experiment_info"><strong>Experiment
+info</strong></a></strong></dt>
+<dd>
+<p>Warns the user if any of the following pieces of experiment
+annotation are missing:</p>
+</dd>
+<dd>
+<pre>
+ Experiment Design
+ Release Date
+ Title
+ Description
+</pre></dd>
+<dt><strong><a name="item_term_sources" id=
+"item_term_sources"><strong>Term Sources</strong></a></strong></dt>
+<dd>
+<p>Checks all instances of Term Source REFs, and warns the user if
+an undeclared Term Source Name has been used. Also notifies the
+user if a Term Source declared in the IDF has not been used in the
+SDRF.</p>
+</dd>
+<dt><strong><a name="item_experimental_factors" id=
+"item_experimental_factors"><strong>Experimental
+Factors</strong></a></strong></dt>
+<dd>
+<p>Checks all SDRF Factor Value columns, and warns the user if an
+undeclared Experimental Factor Name has been used. Also notifies
+the user if a Experimental Factor declared in the IDF has not been
+used in the SDRF.</p>
+</dd>
+<dt><strong><a name="item_undeclared_experimental_factors" id=
+"item_undeclared_experimental_factors"><strong>Undeclared
+Experimental Factors</strong></a></strong></dt>
+<dd>
+<p>Checks all material characteristics against the factors
+described by the document, alerting the user if any such
+characteristics vary during the experiment without having been
+declared as an experimental factor.</p>
+</dd>
+<dt><strong><a name="item_source_characteristics" id=
+"item_source_characteristics"><strong>Source
+Characteristics</strong></a></strong></dt>
+<dd>
+<p>Checks that each Source material has at least some annotation.
+This is not, however, a full test of MIAME compliance.</p>
+</dd>
+<dt><strong><a name="item_pubmed_id" id=
+"item_pubmed_id"><strong>PubMed ID</strong></a></strong></dt>
+<dd>
+<p>Warns the user if a non-numeric PubMed ID has been entered.</p>
+</dd>
+<dt><strong><a name="item_submission_and_release_dates" id=
+"item_submission_and_release_dates"><strong>Submission and release
+dates</strong></a></strong></dt>
+<dd>
+<p>Warns the user if experiment or release dates are in an
+incorrect format.</p>
+</dd>
+<dt><strong><a name="item_submitter" id=
+"item_submitter"><strong>Submitter</strong></a></strong></dt>
+<dd>
+<p>Checks that a submitter contact has been provided, and that
+their last name and email address have both been included in the
+IDF.</p>
+</dd>
+<dt><strong><a name="item_protocols" id=
+"item_protocols"><strong>Protocols</strong></a></strong></dt>
+<dd>
+<p>Checks that all protocols referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_protocol_description_length" id=
+"item_protocol_description_length"><strong>Protocol description
+length</strong></a></strong></dt>
+<dd>
+<p>Warns if any of the protocol texts seem too brief.</p>
+</dd>
+<dt><strong><a name="item_protocol_usage" id=
+"item_protocol_usage"><strong>Protocol
+usage</strong></a></strong></dt>
+<dd>
+<p>Checks that a protocol has been attached to each step of the
+experiment (biomaterial treatments, hybridization, scanning and
+normalization).</p>
+</dd>
+<dt><strong><a name="item_parameters" id=
+"item_parameters"><strong>Parameters</strong></a></strong></dt>
+<dd>
+<p>Checks that all parameters referenced in the Hybridization
+spreadsheet section are declared in the Protocol section (and vice
+versa).</p>
+</dd>
+<dt><strong><a name="item_files" id=
+"item_files"><strong>Files</strong></a></strong></dt>
+<dd>
+<p>Confirms that each data file is associated with an array design
+(i.e., Array Design REF or Array Design File), and at least one
+experimental factor value.</p>
+</dd>
+<dt><strong><a name="item_experiment_design_graph" id=
+"item_experiment_design_graph"><strong>Experiment Design
+Graph</strong></a></strong></dt>
+<dd>
+<p>Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the
+numbers of each. If the Graphviz software is installed
+(http://www.graphviz.org) then the script will use the 'dot'
+program to produce a PNG format graph showing how the various
+components relate to each other.</p>
+</dd>
+</dl>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2007.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/MAGETAB_logo.png b/docs/MAGETAB_logo.png
new file mode 100644
index 0000000..dbc8baa
Binary files /dev/null and b/docs/MAGETAB_logo.png differ
diff --git a/docs/MAGETAB_logo_small.png b/docs/MAGETAB_logo_small.png
new file mode 100644
index 0000000..9f19870
Binary files /dev/null and b/docs/MAGETAB_logo_small.png differ
diff --git a/docs/T2M_logo.png b/docs/T2M_logo.png
new file mode 100644
index 0000000..72d844b
Binary files /dev/null and b/docs/T2M_logo.png differ
diff --git a/docs/T2M_logo_small.png b/docs/T2M_logo_small.png
new file mode 100644
index 0000000..35f704f
Binary files /dev/null and b/docs/T2M_logo_small.png differ
diff --git a/docs/aelogo.png b/docs/aelogo.png
new file mode 100644
index 0000000..29f140c
Binary files /dev/null and b/docs/aelogo.png differ
diff --git a/docs/aesubs.html b/docs/aesubs.html
new file mode 100644
index 0000000..dd64d2d
--- /dev/null
+++ b/docs/aesubs.html
@@ -0,0 +1,182 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>ArrayExpress submissions</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+ <td class="pagetitle">ArrayExpress submissions</td>
+ </tr>
+ </table>
+ </div>
+
+ <div CLASS="section">
+ <table class="layout">
+ <tr>
+ <td class="layout">
+
+ <ul>
+ <li><a href="#routes">Submission routes</a></li>
+ <li><a href="#spreadsheet">Spreadsheet details</a></li>
+ <li><a href="#submission">Data upload instructions</a></li>
+ </ul>
+
+ <p class="subhead">See also:</p>
+
+ <ul>
+ <li><a href="spreadsheet.html">Constructing a Tab2MAGE spreadsheet</a></li>
+ <li><a href="datafiles.html">Supported data file formats</a></li>
+ <li><a href="http://www.ebi.ac.uk/miamexpress/help/tab2mage_help.html">ArrayExpress spreadsheet submission help</a>
+ </ul>
+ </td>
+ <td class="layout" valign="top">
+ <div CLASS="imghead">
+ <a href="http://www.ebi.ac.uk/arrayexpress/">
+ <img src="aelogo.png" border="0" alt="ArrayExpress logo">
+ </a>
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <p class="text">For further help with data submissions to ArrayExpress, please contact the
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "'>ArrayExpress curators<" + "/a>")
+ //-->
+ </script>
+ </p>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="routes">Submission routes</a></div>
+
+ <p class="text">Tab2MAGE can be used to submit microarray data
+ to ArrayExpress in one of two ways. For advanced users with a
+ need to submit large volumes of data, Tab2MAGE can be used to
+ set up a MAGE-ML pipeline to ArrayExpress. For more casual use,
+ it is recommended that submitters upload a completed Tab2MAGE
+ spreadsheet alongside their data files, and the ArrayExpress
+ curators will process it for you.</p>
+
+ <p class="text">Submitters wishing to set up a MAGE-ML pipeline
+ to ArrayExpress should contact the
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "'>curators<" + "/a>")
+ //-->
+ </script>
+ to discuss their requirements, and read this web page: <a
+ href="http://www.ebi.ac.uk/~ele/ext/submitter.html">Instructions
+ for submitters of MAGE-ML</a>.</p>
+
+ <p class="text"><em>For data submissions using the spreadsheet,
+ please read the rest of this page.</em></p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="spreadsheet">Spreadsheet details</a></div>
+
+ <p class="text">There are a few things to bear in mind
+ when using the spreadsheet submission route to ArrayExpress:</p>
+
+ <ul>
+
+ <li class="text">Please leave the experiment <a
+ href="detail.html#accession">accession</a> field
+ blank. <em>The ArrayExpress curators will assign your
+ experiment an accession once they are satisfied with your
+ submission.</em> The experiment <a
+ href="detail.html#domain">domain</a> field should contain the
+ text "ebi.ac.uk" or be left blank.</li>
+
+ <li class="text">You may re-use previously submitted protocols
+ by entering their accession numbers directly into the
+ spreadsheet. You may retrieve these accessions using the
+ ArrayExpress web page. If your protocols have not yet been
+ loaded into ArrayExpress, please contact the
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "'>curators<" + "/a>")
+ //-->
+ </script>
+ to obtain the accession numbers.
+ </li>
+
+ <li class="text">For new protocols, please
+ generate your own protocol accession numbers in the form
+ "P-TABM-{yourname}{number}", for example P-TABM-rayner1,
+ P-TABM-rayner2 and so on. These accessions will be reassigned
+ during curation to fit the standard format.</li>
+
+ </ul>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="submission">Upload instructions</a></div>
+
+ <p class="text">Once you have finished preparing your
+ spreadsheet, please upload it with your data files to the
+ ArrayExpress <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/tab2mage.cgi">Tab2MAGE
+ submissions website</a>.</p>
+
+ <p class="text">To submit data using this website:
+ <ol>
+
+ <li class="text">Create an account for yourself and log in.</li>
+
+ <li class="text">Click 'Create experiment' to start your submission.</li>
+
+ <li class="text">Enter your experiment name. If you want to generate a
+ template spreadsheet then select one or more terms from each
+ of the lists provided to describe your experiment
+ (optional). Click 'Save experiment'.</li>
+
+ <li class="text">Click 'Generate template' if you want to use the template
+ (optional).</li>
+
+ <li class="text">Click 'Upload files' then upload your completed
+ spreadsheet and one or more zipped archives containing all
+ your data files.</li>
+
+ <li class="text">Click 'Submit experiment' to send this submission to us
+ for curation. We will contact you with any questions and will
+ email you an accession number when your spreadsheet has been
+ processed and loaded into ArrayExpress.</li>
+
+ </ol>
+ </p>
+
+ <p class="text">If you have any questions please email the curation team at
+ <a href="mailto:miamexpress at ebi.ac.uk?subject=Tab2MAGE submission">miamexpress at ebi.ac.uk</a>.
+ If you are emailing about a specific submission tell us your
+ username and experiment name in the email.</p>
+
+ </div>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Thu Nov 24 14:18:20 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Wed Sep 19 15:55:53 BST 2007
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/datafiles.html b/docs/datafiles.html
new file mode 100644
index 0000000..3e87eac
--- /dev/null
+++ b/docs/datafiles.html
@@ -0,0 +1,465 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Supported data files</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+ <td class="pagetitle">Supported data files</td>
+ </tr>
+ </table>
+ </div>
+
+ <p class="text">Data file support in Tab2MAGE can be divided into
+ two categories: Affymetrix data files, and everything
+ else. Non-Affymetrix data must be supplied as plain ASCII
+ tab-delimited text, and the column headings must be left
+ intact. Ideally, you should be able to use the raw unprocessed
+ data files from any of the supported software types without having
+ to edit the data files in any way. Tab2MAGE uses the column
+ headings within the file to identify which kind of file it is
+ dealing with, and what the quantitation types are. The script will
+ reformat recognized file types into MetaColumn-MetaRow format, and
+ then strip the feature coordinates (or reporter identifiers) and
+ the column headings, in the process generating the necessary MAGE
+ objects to describe the data.</p>
+
+ <p class="text">The following formats are supported:</p>
+
+ <ul>
+ <li><a href="#raw" class="subhead">Unprocessed (raw) data files</a></li>
+ <ul>
+ <li><a href="#generic">Generic</a></li>
+ <li><a href="#affymetrix">Affymetrix</a></li>
+ <li><a href="#genepix">GenePix</a></li>
+ <li><a href="#agilent">Agilent</a></li>
+ <li><a href="#scanalyze">ScanAlyze</a></li>
+ <li><a href="#scanarray">ScanArray</a></li>
+ <li><a href="#scanarray">QuantArray</a></li>
+ <li><a href="#arrayvision">Arrayvision</a></li>
+ <li><a href="#spotfinder">Spotfinder</a></li>
+ <li><a href="#bluefuse">BlueFuse</a></li>
+ <li><a href="#ucsfspot">UCSF Spot</a></li>
+ <li><a href="#nimblescan">NimbleScan</a></li>
+ <li><a href="#appliedbiosystems">Applied Biosystems</a></li>
+ <li><a href="#codelink">CodeLink</a></li>
+ <li><a href="#illumina">Illumina</a></li>
+ <li><a href="#imagene">Imagene</a></li>
+ <li><a href="#csirospot">CSIRO Spot</a></li>
+ </ul>
+ <li><a href="#normalized" class="subhead">Normalized data files</a></li>
+ <ul>
+ <li><a href="#reporter">Generic</a></li>
+ <li><a href="#affynorm">Affymetrix</a></li>
+ <li><a href="#illumina">Illumina</a></li>
+ </ul>
+ <li><a href="#final_data_matrix" class="subhead">Final data matrix files</a></li>
+ </ul>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="raw">Unprocessed (raw) data files</a></div>
+
+ <p class="text">The following list gives a brief overview of how
+ Tab2MAGE recognizes different file formats. In each case, the
+ data file row containing the column headings is identified by
+ matching it to these sets of known column headings.</p>
+
+ <p class="text"><a name="generic" class="subhead">Generic</a>
+ <br><a
+ href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html">
+ MetaColumn/MetaRow</a> format files are recognized using the
+ following column headings:</p>
+
+ <table class="leftexample">
+ <tr><td>MetaColumn</td><td>MetaRow</td><td>Column</td><td>Row</td></tr>
+ </table>
+
+ <p class="text"><a name="affymetrix"
+ class="subhead">Affymetrix</a> <br>Tab2MAGE recognizes and
+ parses CEL and EXP files using both the old GDAC formats and the
+ newer GCOS/XDA formats. These file formats are detected using
+ the Affymetrix data file parser incorporated into the Tab2MAGE
+ package. See below for notes on <a href="#affynorm">Affymetrix
+ normalized data file formats</a>.</p>
+
+ <p class="text"><a name="genepix" class="subhead">GenePix</a>
+ <br>GenePix format files are recognized using the following column headings:</p>
+
+ <table class="leftexample">
+ <tr><td>Block</td><td>Column</td><td>Row</td><td>X</td><td>Y</td></tr>
+ </table>
+
+ <p class="text"><a name="agilent" class="subhead">Agilent</a>
+ <br>A file containing these headings is recognized as Agilent format file:</p>
+
+ <table class="leftexample">
+ <tr><td>Row</td><td>Col</td><td>PositionX</td><td>PositionY</td></tr>
+ </table>
+
+ <p class="text"><a name="scanalyze"
+ class="subhead">ScanAlyze</a> <br>The following column headings
+ are recognized as being from a ScanAlyze format file:</p>
+
+ <table class="leftexample">
+ <tr><td>GRID</td><td>COL</td><td>ROW</td><td>LEFT</td><td>TOP</td><td>RIGHT</td><td>BOT</td></tr>
+ </table>
+
+ <p class="text"><a name="scanarray"
+ class="subhead">ScanArray/QuantArray</a> <br>ScanArray Express
+ files are recognized from the following headings:</p>
+
+ <table class="leftexample">
+ <tr><td>Array Column</td><td>Array Row</td><td>Spot Column</td><td>Spot Row</td><td>X</td><td>Y</td></tr>
+ </table>
+
+ <p class="text">while the older QuantArray format has these headings:</p>
+
+ <table class="leftexample">
+ <tr><td>Array Column</td><td>Array Row</td><td>Column</td><td>Row</td></tr>
+ </table>
+
+ <p class="text"><a name="arrayvision"
+ class="subhead">ArrayVision</a> <br>The following column
+ headings are recognized as indicating an ArrayVision format
+ file:</p>
+
+ <table class="leftexample">
+ <tr><td>Primary</td><td>Secondary</td></tr>
+ </table>
+
+ <p class="text">Newer "lg2" ArrayVision files are identified by
+ the following column headings:</p>
+
+ <table class="leftexample">
+ <tr><td>Spot labels</td></tr>
+ </table>
+
+ <p class="text"><a name="spotfinder"
+ class="subhead">Spotfinder</a> <br>Spotfinder files are
+ recognized by the following column headings:</p>
+
+ <table class="leftexample">
+ <tr><td>MC</td><td>MR</td><td>SC</td><td>SR</td></tr>
+ </table>
+
+ <p class="text"><a name="bluefuse" class="subhead">BlueFuse</a>
+ <br>A file containing the following headings is recognized as a
+ BlueFuse file:</p>
+
+ <table class="leftexample">
+ <tr><td>COL</td><td>ROW</td><td>SUBGRIDCOL</td><td>SUBGRIDROW</td></tr>
+ </table>
+
+ <p class="text"><a name="ucsfspot"
+ class="subhead">UCSF Spot</a> <br>UCSF Spot files are
+ recognized by the following column headings:</p>
+
+ <table class="leftexample">
+ <tr><td>Arr-colx</td><td>Arr-coly</td><td>Spot-colx</td><td>Spot-coly</td></tr>
+ </table>
+
+ <p class="text"><a name="nimblescan"
+ class="subhead">NimbleScan</a> <br>NimbleScan files (Feature,
+ Probe and Pair) all contain the following headings:</p>
+
+ <table class="leftexample">
+ <tr><td>PROBE_ID</td><td>X</td><td>Y</td></tr>
+ </table>
+
+ <p class="text"><a name="appliedbiosystems"
+ class="subhead">Applied Biosystems</a> <br>Files generated by
+ Applied Biosystems software have the following headings:</p>
+
+ <table class="leftexample">
+ <tr><td>Probe_ID</td><td>Gene_ID</td></tr>
+ </table>
+
+ <p class="text"><a name="codelink" class="subhead">CodeLink</a>
+ <br>CodeLink Expression Analysis files are identified using the
+ following:</p>
+
+ <table class="leftexample">
+ <tr><td>Logical_row</td><td>Logical_col</td><td>Center_X</td><td>Center_Y</td></tr>
+ </table>
+
+ <p class="text"><a name="imagene" class="subhead">ImaGene</a>
+ <br>ImaGene files are recognized using the following columns:</p>
+
+ <table class="leftexample">
+ <tr><td>Meta Column</td><td>Meta Row</td><td>Column</td><td>Row</td><td>Field</td><td>Gene ID</td></tr>
+ </table>
+
+ <p class="text">The ImaGene 3.0 format is also supported:</p>
+
+ <table class="leftexample">
+ <tr><td>Meta_col</td><td>Meta_row</td><td>Sub_col</td><td>Sub_row</td><td>Name</td><td>Selected</td></tr>
+ </table>
+
+ <p class="text"><a name="csirospot" class="subhead">CSIRO Spot</a>
+ <br>CSIRO Spot files contain the following columns:</p>
+
+ <table class="leftexample">
+ <tr><td>grid_c</td><td>grid_r</td><td>spot_c</td><td>spot_r</td><td>indexs</td></tr>
+ </table>
+
+ <p class="text">Obviously, this method of determining which file type is being processed is not infallible. You are therefore encouraged to test your data files with Tab2MAGE and report any problems to
+ <script type="text/javascript">
+ <!--
+ var uname = "rayner";
+ var dname = "ebi";
+ var hname = "ac.uk"
+ var linktext = "the author";
+ document.write("<a href='" + "mail" + "to:" + uname + "@" + dname
+ + "." + hname + "'>" + linktext + "<" + "/a>")
+ //-->
+ </script>.
+ </p>
+
+ <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="normalized">Normalized data files</a></div>
+
+ <p class="text">Normalized data files may be submitted in any of
+ the above formats. In addition, files may be parsed using a
+ number of special column headings which can be used to designate
+ a column containing reporter or composite sequence
+ identifiers:</p>
+
+ <p class="text"><a name="reporter" class="subhead">Generic
+ normalized data </a> <br>If you have normalized data mapped to
+ the identifiers used in your array design, you can simply use a
+ single column containing those identifiers to include your data
+ in the final MAGE-ML. Tab2MAGE supports the use of either
+ Reporter Identifiers or CompositeSequence Identifiers for this
+ purpose. Please see these <a
+ href="http://www.ebi.ac.uk/miamexpress/help/adf/">ADF help
+ notes</a> for a discussion on these identifier types. Thus,
+ either of the following sets of column headers may be used:</p>
+
+ <table class="leftexample">
+ <tr><td>Reporter Identifier</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+ </table>
+ <p>
+ <table class="leftexample">
+ <tr><td>CompositeSequence Identifier</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+ </table>
+
+ <p class="text">where <<i>QT1</i>>, <<i>QT2</i>> etc. are the names of your
+ quantitation types (see the <a href="usage.html">usage notes</a>
+ for more information on including novel quantitation types).</p>
+
+ <p class="text"><a name="affynorm" class="subhead">Affymetrix
+ normalized data</a> <br>Tab2MAGE recognizes and parses CHP files
+ using both the old GDAC formats and the newer GCOS/XDA
+ formats. In addition, Affymetrix data normalized by
+ non-Affymetrix methods (e.g. RMA normalization) can be
+ parsed. Either CompositeSequence identifiers (<a
+ href="#reporter">above</a>) or either of the following sets of
+ column headers may be used:</p>
+
+ <table class="leftexample">
+ <tr><td>ProbeSet ID</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+ </table>
+ <p>
+ <table class="leftexample">
+ <tr><td>ProbeSet Name</td><td><<i>QT1</i>></td><td><<i>QT2</i>></td><td><<i>QT3</i>></td></tr>
+ </table>
+
+ <p class="text">Again, <<i>QT1</i>>, <<i>QT2</i>> etc. are the names of your
+ quantitation types (see the <a href="usage.html">usage notes</a>
+ for more information on including novel quantitation types).</p>
+
+ <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="final_data_matrix">Final data matrix files</a></div>
+
+ <p class="text"><em>(Tab2MAGE only)</em> Often it is desirable to store the normalized
+ data from all your hybridizations in a single file. This file
+ must then contain information on the quantitation types measured
+ (log ratio, RMA values etc.) and the hybridizations to which
+ these values apply. This information must be encoded in the data
+ file column headings, for example:</p>
+
+ <table class="example">
+ <tr><td>Reporter Identifier</td><td>RMA(Hyb1)(Hyb2)</td><td>RMA(Hyb3)(Hyb4)</td></tr>
+ <tr><td>A102340</td><td>0.147</td><td>0.473</td></tr>
+ <tr><td>A102341</td><td>0.53</td><td>0.484</td></tr>
+ <tr><td>A102342</td><td>0.169</td><td>0.188</td></tr>
+ <tr><td>A102343</td><td>0.742</td><td>0.684</td></tr>
+ <tr><td>A102344</td><td>0.479</td><td>0.514</td></tr>
+ </table>
+
+ <p class="text">Any of the identifier headings described in the
+ <a href="#normalized">normalized data section</a> can be used in
+ the final data matrix file. <em>Please note</em> that the identifier
+ column <em>must</em> be the first column in the file.</p>
+
+ <p class="text">In
+ addition, for Tab2MAGE to correctly construct the mapping the
+ hybridization names must also be included in the annotation
+ spreadsheet, using the "<a href="detail.html#hyb">Hybridization</a>"
+ column (in the Hybridization section):</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+ <td><a class="plain" href="detail.html#filefgem">File[transformed]</a></td>
+ <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+ <td><a class="plain" href="detail.html#hyb">Hybridization</a></td></tr>
+ <tr><td>Data1.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb1</td></tr>
+ <tr><td>Data2.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb2</td></tr>
+ <tr><td>Data3.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb3</td></tr>
+ <tr><td>Data4.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb4</td></tr>
+ </table>
+
+ <p class="text"><em>Please note:</em> There are currently some
+ limitations imposed by Tab2MAGE when parsing final data matrix
+ files. Firstly, each file must correspond to a single array
+ design. In experiments where multiple array designs have been
+ used, separate final data matrix files should be included for
+ each. For example:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+ <td><a class="plain" href="detail.html#filefgem">File[transformed]</a></td>
+ <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+ <td><a class="plain" href="detail.html#hyb">Hybridization</a></td></tr>
+ <tr><td>Data1.txt</td><td>FinalDataMatrix1.txt</td><td>A-EXML-1</td><td>Hyb1</td></tr>
+ <tr><td>Data2.txt</td><td>FinalDataMatrix1.txt</td><td>A-EXML-1</td><td>Hyb2</td></tr>
+ <tr><td>Data3.txt</td><td>FinalDataMatrix2.txt</td><td>A-EXML-2</td><td>Hyb3</td></tr>
+ <tr><td>Data4.txt</td><td>FinalDataMatrix2.txt</td><td>A-EXML-2</td><td>Hyb4</td></tr>
+ </table>
+
+ <p class="text">Secondly, the columns in the final data matrix
+ are currently mapped to hybridizations, rather than scanning
+ events. As a result, this system does not readily support
+ experiment designs where hybridizations are scanned multiple
+ times, e.g., for quality control purposes. To accurately reflect
+ such treatments, please include your normalized data as one file
+ per scan, and add a "<a
+ href="ArrayExpress/Curator/MAGE/Definitions.html#item_scan">Scan</a>"
+ column to explicitly specify which scan events belong to which
+ hybridization, as in the following example:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#hyb">Hybridization</a></td>
+ <td><a class="plain" href="detail.html#scan">Scan</a></td>
+ <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+ <td><a class="plain" href="detail.html#filenorm">File[normalized]</a></td></tr>
+ <tr><td>Hyb1</td><td>Scan1a</td><td>Data1a.txt</td><td>NormData1a.txt</td></tr>
+ <tr><td>Hyb1</td><td>Scan1b</td><td>Data1b.txt</td><td>NormData1b.txt</td></tr>
+ <tr><td>Hyb2</td><td>Scan2a</td><td>Data2a.txt</td><td>NormData2a.txt</td></tr>
+ <tr><td>Hyb2</td><td>Scan2b</td><td>Data2b.txt</td><td>NormData2b.txt</td></tr>
+ </table>
+
+ <p class="text">Please see the MIAMExpress data file help notes
+ (<a
+ href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html#affy_gem">Affymetrix</a>,
+ <a
+ href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html#fgedm">two-channel</a>)
+ for further discussion of final data matrix files. There is also
+ an <a href="../examples/final_data_matrix/">example</a> included
+ with the Tab2MAGE package of how to incorporate a final data
+ matrix into your MAGE-ML output.</p>
+
+ <p class="text"><em>For MAGE-TAB users:</em> This
+ Tab2MAGE/MIAMExpress final data matrix format is not supported
+ by MAGE-TAB, which defines its own data matrix format: <a
+ href="magetab_docs.html#datamatrix">MAGE-TAB Data Matrix
+ notes</a></p>
+
+ <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="illumina">Illumina data files</a></div>
+
+ <p class="text">The Illumina BeadStudio software (see <a
+ href="http://www.illumina.com">www.illumina.com</a>) generates
+ data files in several closely related formats. Tab2MAGE only
+ supports such files reporting Probe-level (PROBE_ID) data,
+ exported from BeadStudio v3 and above, in tab-delimited
+ format. These files are characterised by having PROBE_ID as the
+ first column, with subsequent column headers following the
+ pattern "Hybridization name.QuantitationType name".</p>
+
+ <p class="text">Illumina have recently released the
+ 'ArrayExpress Data Submission Report Plug-in' for BeadStudio
+ which will generate a raw data file, plus a basic Tab2MAGE
+ spreadsheet for you to fill in with your experiment and sample
+ information. This plug-in is available from the downloads
+ section of the <a href="https://icom.illumina.com/">Illumina
+ iCom website</a>.</p>
+
+ <p class="text">BeadStudio files may contain data columns
+ arranged per sample, or per group of samples (so-called
+ "Grouped data"). In contrast to the other scanning softwares
+ noted above, Illumina files may contain data from single or
+ multiple hybridizations. </p>
+
+ <p class="text">Sample-level data is supported in "File[raw]",
+ "File[normalized]" or "File[transformed]" columns of the Tab2MAGE
+ spreadsheet Hybridization section. The file name should be included
+ in all of the rows corresponding to the hybridizations it
+ covers. The array designator for each hybridization (found in
+ the data file column headings) must be entered into the
+ "Hybridization" column of the spreadsheet in each appropriate row.</p>
+
+ <p class="text">Grouped data is supported only as
+ "File[normalized]" or "File[transformed]". The column headings in
+ these data files contain user-defined tags for each group, and
+ these tags must be included in the "Normalization" column of the
+ spreadsheet. This allows the Tab2MAGE software to map the grouped
+ data back to the original hybridizations.</p>
+
+ <p class="text">There is one further data file type exported by
+ BeadStudio, which is not yet supported by Tab2MAGE: the
+ "differential expression" file. This file contains either
+ grouped or ungrouped data reported at the gene level, but with
+ extra columns reporting on "DiffScore" and "Concordance" between
+ samples or groups. Such data need to be split into multiple data
+ cubes when coding in MAGE, and it is this capability which
+ Tab2MAGE does not currently support.</p>
+
+ <p class="text">An example of a Tab2MAGE spreadsheet describing
+ sample data at the probe level and grouped data at the gene
+ level can be found in the <a
+ href="spreadsheet.html#examples">examples section</a>.</p>
+
+ <p class="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Jun 10 15:14:04 BST 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/detail.html b/docs/detail.html
new file mode 100644
index 0000000..c87684e
--- /dev/null
+++ b/docs/detail.html
@@ -0,0 +1,392 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Spreadsheet construction - notes</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+ <td class="pagetitle">Creating your spreadsheet - notes</td>
+ </tr>
+ </table>
+ </div>
+
+ <ul>
+ <li><a href="#experiment">Experiment section</a></li>
+ <li><a href="#protocol">Protocol section</a></li>
+ <li><a href="#hybridization">Hybridization section</a></li>
+ </ul>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="experiment">Experiment section</a></div>
+
+ <p CLASS="text"><a name="exptsection" class="subhead">Experiment
+ section</a> <br> This is a required tag indicating the start
+ of the Experiment section.</p>
+
+ <p CLASS="text"><a name="domain" class="subhead">domain</a> <br>
+ The domain tag provides information on the originator of the
+ output MAGE-ML document. This field can contain any suitable
+ string, such as the originating internet domain name (e.g.,
+ "ebi.ac.uk").</p>
+
+ <p CLASS="text"><a name="accession" class="subhead">accession</a> <br> The
+ experiment accession number is a unique identifier assigned to
+ each experiment. Accession numbers for experiments submitted to
+ ArrayExpress have the format E-XXXX-n.</p>
+
+ <p CLASS="text"><a name="quality_control"
+ class="subhead">quality_control</a> <br> This is a
+ comma-separated list of terms taken from the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>. The terms should be instances of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#QualityControlDescriptionType">QualityControlDescriptionType</a>. Typical
+ terms which have been used here include "biological_replicate",
+ "technical_replicate" and "dye_swap_quality_control".</p>
+
+ <p CLASS="text"><a name="experiment_design_type"
+ class="subhead">experiment_design_type</a> <br> This is a
+ comma-separated list of terms taken from the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>. The terms should be instances of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentDesignType">ExperimentDesignType</a>. Examples
+ of terms which have been used here include
+ "disease_state_design", "stimulus_or_stress_design" and
+ "compound_treatment_design".</p>
+
+ <p CLASS="text"><a name="name" class="subhead">name</a> <br> The name of the experiment.</p>
+
+ <p CLASS="text"><a name="description" class="subhead">description</a>
+ <br> A short paragraph (contained within this single spreadsheet
+ cell) describing the purpose of the experiment.</p>
+
+ <p CLASS="text"><a name="release_date" class="subhead">release_date</a>
+ <br> Date for public release, in the format YYYY-MM-DD.</p>
+
+ <p CLASS="text"><a
+ name="submission_date" class="subhead">submission_date</a> <br> Date of
+ submission, in the format YYYY-MM-DD.</p>
+
+ <p CLASS="text"><a name="submitter" class="subhead">submitter</a> <br>
+ The name of the person responsible for submitting the experiment
+ to the database.</p>
+
+ <p CLASS="text"><a name="organization" class="subhead">organization</a> <br>
+ The organization to which the submitter is affiliated.</p>
+
+ <p CLASS="text"><a name="publication"
+ class="subhead">publication_title, authors, journal, volume,
+ issue, pages, year, pubmed_id</a> <br> Publication details for any
+ manuscript associated with the experiment. The <b>journal</b>
+ field should contain a standard Pubmed journal abbreviated
+ name. The <b>authors</b> list is a semicolon-delimited list of
+ names.</p>
+
+ <p CLASS="sectionfoot"> [ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="protocol">Protocol section</a></div>
+
+ <p CLASS="text"><a name="protosection" class="subhead">Protocol
+ section</a> <br> This is a required tag indicating the start of
+ the Protocol section.</p>
+
+ <p CLASS="text"><a name="protoaccession"
+ class="subhead">accession</a> <br> The protocol accession number
+ is a unique identifier assigned to each protocol. Accession
+ numbers for protocols submitted to ArrayExpress have the format
+ P-XXXX-n.</p>
+
+ <p CLASS="text"><a name="prototext" class="subhead">text</a>
+ <br> The full text of the protocol, <em>inserted into a single
+ spreadsheet cell</em>. Note that as of version 1.8.1, Tab2MAGE
+ now supports line breaks in protocol text when exported from MS
+ Excel or OpenOffice.org. For this to work correctly, the
+ spreadsheet should be exported using the default settings for
+ tab-delimited text, i.e., enclosing all text fields in double
+ quotes ("). These breaks will then be automatically converted
+ into <br> tags for correct HTML formatting. Similarly, the
+ ampersand (&) character is now automatically quoted
+ correctly as an HTML entity. Protocol text may also be formatted
+ using standard HTML tags such as <br>. Inequality signs
+ < and > should be represented by the HTML entities
+ < and > respectively. Please be aware when using
+ MS Excel that this application may truncate long text in a
+ spreadsheet cell, without warning the user.</p>
+
+ <p CLASS="text"><a name="protoname" class="subhead">name</a> <br> The
+ name of the protocol.</p>
+
+ <p CLASS="text"><a name="prototype" class="subhead">type</a>
+ <br>This (very) optional column should contain terms taken from the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>. The term should be an instance of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalProtocolType">ExperimentalProtocolType</a>
+ or <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#DataTransformationProtocolType">DataTransformationProtocolType</a>. Examples
+ of terms which have been used here include "grow",
+ "compound_based_treatment" and "hybridization". <em>Note:</em>
+ use of this column is not recommended in the majority of
+ cases. If this column is not present, suitable defaults will be
+ used based on how the protocols are referenced in the
+ Hybridization section.</p>
+
+ <p CLASS="text"><a name="protoparams"
+ class="subhead">parameters</a> <br> A list of parameters,
+ separated by semicolons, in the format "name(units)". Each
+ parameter name should be unique within the spreadsheet. Provided
+ that the "units" string matches one of those which are
+ hard-coded into the MAGE object model (e.g., "ug" for microgram
+ MassUnit, "degree_C" for degrees Celsius TemperatureUnit etc.), the
+ correct unit subclass will be created. Otherwise the script will
+ create a generic QuantityUnit object. If no unit is given, no
+ Unit object is created in the output MAGE-ML. <em>Note</em>: There
+ is a clash between using "m" for minutes and meters. Tab2MAGE only
+ supports using "m" for minutes in this context.</p>
+
+ <p CLASS="sectionfoot"> [ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="hybridization">Hybridization section</a></div>
+
+ <p CLASS="text"><a name="hybsection"
+ class="subhead">Hybridization section</a> <br> This is a
+ required tag indicating the start of the Hybridization
+ section.</p>
+
+ <p CLASS="text"><a name="biosource"
+ class="subhead">BioSource</a> <br> The name of the
+ biosource. This is typically the biological material as it was
+ originally brought into the lab.</p>
+
+ <p CLASS="text"><a name="sample" class="subhead">Sample</a> <br>
+ The name of the sample derived from the biosource.</p>
+
+ <p CLASS="text"><a name="extract" class="subhead">Extract</a>
+ <br> The name of the extract made from the sample.</p>
+
+ <p CLASS="text"><a name="label"
+ class="subhead">LabeledExtract</a> <br> The name of the labeled
+ extract.</p>
+
+ <p CLASS="text"><a name="dye" class="subhead">Dye</a> <br> The
+ name of the dye used in labeling the extract. </p>
+
+ <p CLASS="text"><a name="hyb" class="subhead">Hybridization</a>
+ <br> The name of the hybridization event. <em>Note</em>: This is
+ the same name as must be used in the <a
+ href="datafiles.html#final_data_matrix">final data matrix
+ file</a>, where used.</p>
+
+ <p CLASS="text"><a name="scan" class="subhead">Scan</a>
+ <br> The name of the array scanning event.</p>
+
+ <p CLASS="text"><a name="norm" class="subhead">Normalization</a>
+ <br> The name of the normalization event.</p>
+
+ <p CLASS="text"><a name="materialtype"
+ class="subhead">BioSourceMaterial, SampleMaterial,
+ ExtractMaterial, LabeledExtractMaterial</a> <br> The types of
+ material used in the experiment. The terms should be taken from
+ the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>, and should be instances of the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType">MaterialType</a>
+ class.</p>
+
+ <p CLASS="text"><a name="bmc"
+ class="subhead">BioMaterialCharacteristics[<i><category></i>]</a>
+ <br> These columns contain annotation for each of your <a
+ href="#biosource">biosources</a>. The terms in these columns
+ should be from one or more standard ontologies or other
+ controlled vocabularies. Examples of suitable ontologies and/or
+ vocabularies may be found on the following web sites:
+
+ <ul>
+ <li><a href="http://obo.sourceforge.net/main.html">Open Biomedical Ontologies</a></li>
+ <li><a href="http://ncimeta.nci.nih.gov/indexMetaphrase.html">NCI Metathesaurus</a></li>
+ <li><a href="http://www.informatics.jax.org/searches/anatdict_form.shtml">Mouse Anatomical Dictionary</a></li>
+ <li><a href="http://www.arabidopsis.org/info/growth.jsp">Arabidopsis Growth Stages</a></li>
+ </ul>
+
+ <p class="text">The <i><category></i> field should contain
+ any of the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristics">BioMaterialCharacteristics</a>
+ category terms from the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>, such as "DiseaseState", "Genotype", "Age" or
+ "Histology". The spreadsheet may contain as many
+ BioMaterialCharacteristics columns as required to fully describe
+ your biosources.</p>
+
+ <p CLASS="text"><a name="pgrow"
+ class="subhead">Protocol[grow]</a> <br>The accession number of
+ the protocol used to propagate the biosource.</p>
+
+ <p CLASS="text"><a name="ptreat"
+ class="subhead">Protocol[treatment]</a> <br>This is a
+ general-purpose protocol type describing any treatments of the
+ sample prior to extraction (e.g., administration of drugs,
+ tissue dissection etc.). </p>
+
+ <p CLASS="text"><a name="pextract"
+ class="subhead">Protocol[extraction]</a> <br>The method by which
+ nucleic acid is extracted from the sample.</p>
+
+ <p CLASS="text"><a name="plabel"
+ class="subhead">Protocol[labeling]</a> <br>The protocol used to
+ derive the labeled extract from the label.</p>
+
+ <p CLASS="text"><a name="phyb"
+ class="subhead">Protocol[hybridization]</a> <br>The protocol
+ used to hybridize the labeled extract to the array.</p>
+
+ <p CLASS="text"><a name="pscan"
+ class="subhead">Protocol[scanning]</a> <br>The protocol by which
+ the hybridized array is imaged.</p>
+
+ <p CLASS="text"><a name="pfext"
+ class="subhead">Protocol[image_analysis]</a> <br>The protocol
+ used in the feature extraction step in which the scanned image
+ is converted into raw numerical data.</p>
+
+ <p CLASS="text"><a name="pnorm"
+ class="subhead">Protocol[normalization]</a> <br>The procedure
+ used to derive normalized data from the raw data.</p>
+
+ <p CLASS="text"><a name="param"
+ class="subhead">Parameter[<i><parameter name></i>]</a>
+ <br>This class of column allows you to specify the values for
+ each of your protocols on a hyb-by-hyb basis. The
+ <i><parameter name></i> field must refer to a predeclared
+ <a href="#protoparams">parameter</a> defined in the <a
+ href="#protocol">Protocol section</a>. You should use as many
+ Parameter[] columns as you have predefined parameters.</p>
+
+ <p CLASS="text"><a name="fv"
+ class="subhead">FactorValue[<i><category></i>]</a>
+ <br>
+ These columns are used to indicate which of your experimental
+ factors apply to each hybridization. The terms in these
+ columns should be from one or more standard ontologies or
+ other controlled vocabularies. Examples of suitable ontologies
+ and/or vocabularies may be found in the notes on the <a
+ href="#bmc">BioMaterialCharacteristics[]</a> columns. The
+ <i><category></i> field should contain a subclass of the
+ <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalFactorCategory">ExperimentalFactorCategory</a>
+ class from the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>. The relevant categories may be taken from any
+ depth of the ExperimentalFactorCategory heirarchy, and you may
+ find that you have to descend several levels to obtain a
+ biologically useful category. Examples of suitable categories
+ include "DiseaseState", "OrganismPart", "Compound" or
+ "EnvironmentalHistory". The spreadsheet may contain as many
+ FactorValue columns as required to fully describe your
+ experiment.</p>
+
+ <p class="text"><em>FactorValues may be linked to either
+ "Values" or "Measurements"</em>. As a rule of thumb, Tab2MAGE
+ currently assumes that a purely numerical value in a FactorValue
+ column should be encoded as a Measurement. In such situations
+ the <i><category></i> field should indicate the class and
+ name of the measurement unit, in the form:</p>
+
+ <pre>FactorValue[<unit class>(<unit name>)]</pre>
+
+ <p class="text">For example:</p>
+
+ <pre>FactorValue[Temperature(degree_C)]</pre>
+
+ <p class="text">In each case the unit class and name should be
+ derived from the values defined by the MAGE object model. If a
+ match is not found, Tab2MAGE will generate a generic
+ QuantityUnit as a placeholder. <em>Note</em>: There
+ is a clash between using "m" for minutes and meters. Tab2MAGE only
+ supports using "m" for minutes in this context.</p>
+
+ <p CLASS="text"><a name="fileraw" class="subhead">File[raw]</a>
+ <br>The name of the file containing the raw data. For Affymetrix
+ submissions this should be the name of the CEL file. A single
+ hybridization may be linked to multiple raw data files (e.g.,
+ derived from different scanning events) using multiple rows
+ within the Tab2MAGE spreadsheet.</p>
+
+ <p CLASS="text"><a name="filenorm"
+ class="subhead">File[normalized]</a> <br>The name of the file
+ containing the normalized data. For Affymetrix submissions this
+ should be the name of the CHP file. A single hybridization may
+ be linked to multiple normalized data files (e.g., derived using
+ different normalization protocols) using multiple rows within
+ the Tab2MAGE spreadsheet.</p>
+
+ <p CLASS="text"><a name="filefgem"
+ class="subhead">File[transformed]</a> <br>The name of the combined
+ transformed final data matrix file. The format of this file is described in
+ the <a href="datafiles.html">data file notes</a>. The column
+ headings in this file must contain the name(s) of the
+ hybridization(s) with which each column is associated.</p>
+
+ <p CLASS="text"><a name="fileexp" class="subhead">File[exp]</a>
+ <br>(Affymetrix submissions only). The name of the EXP file for
+ a given hybridization.</p>
+
+ <p CLASS="text"><a name="filecdf" class="subhead">File[cdf]</a>
+ <br>(Affymetrix submissions only). The name of the library CDF
+ file provided by Affymetrix. The actual CDF file(s) need only be
+ supplied to ArrayExpress if you are using custom arrays.</p>
+
+ <p CLASS="text"><a name="arrayacc"
+ class="subhead">Array[accession]</a> <br>The ArrayExpress
+ accession number for the array design used in the
+ hybridization. <em>Please note</em> that if your array design has not
+ yet been submitted to ArrayExpress, then you should be prepared
+ to submit it yourself using the <a
+ href="http://www.ebi.ac.uk/miamexpress/">MIAMExpress submission
+ tool</a>. In cases where you have used commercial arrays, please
+ contact the
+ <script type="text/javascript">
+ <!--
+ var aname = "arraysubs@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "'>ArrayExpress curators<" + "/a>")
+ //-->
+ </script> to ask about adding the array
+ design to the database.</p>
+
+ <p CLASS="text"><a name="arrayser"
+ class="subhead">Array[serial]</a> <br>The serial number of the
+ array. For Affymetrix submissions this should be the Chip Lot
+ number.</p>
+
+ <p CLASS="sectionfoot"> [ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <p class="text">Please see the <a href="ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">
+ MAGE mappings</a> for other supported column headings, and further
+ information on how the spreadsheet information is used to create the MAGE-ML document.</p>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Wed Oct 4 14:06:05 BST 2006
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/expt_check.html b/docs/expt_check.html
new file mode 100644
index 0000000..8e2281a
--- /dev/null
+++ b/docs/expt_check.html
@@ -0,0 +1,347 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>expt_check.pl - a script to check experiment data files
+submitted to MIAMExpress</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#quantitation_types">QUANTITATION TYPES</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#tests">TESTS</a></li>
+ <li><a href="#local_miamexpress_installations">LOCAL MIAMEXPRESS INSTALLATIONS</a></li>
+ <li><a href="#see_also">SEE ALSO</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+ <li><a href="#bugs">BUGS</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="T2M_logo.png" border="0" height="50" alt=
+"Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Script detail: expt_check.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>expt_check.pl - a script to check experiment data files
+submitted to MIAMExpress</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<dl>
+<dt><strong><a name="item_tab2mage_mode_3a" id=
+"item_tab2mage_mode_3a"><strong>Tab2MAGE
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+ expt_check.pl -e <Tab2MAGE spreadsheet>
+</pre></dd>
+<dt><strong><a name="item_mage_2dtab_mode_3a" id=
+"item_mage_2dtab_mode_3a"><strong>MAGE-TAB
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+ expt_check.pl -i <IDF file>
+</pre></dd>
+<dt><strong><a name="item_miamexpress_mode_3a" id=
+"item_miamexpress_mode_3a"><strong>MIAMExpress
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+ expt_check.pl -l <login name> -t <experiment title>
+</pre></dd>
+<dt><strong><a name="item_standalone_mode_3a" id=
+"item_standalone_mode_3a"><strong>Standalone
+mode:</strong></a></strong></dt>
+<dd>
+<pre>
+ expt_check.pl -s <list of data files>
+</pre></dd>
+<dd>
+<pre>
+ (use -A or -a to check files against an array design in standalone mode).
+</pre></dd>
+</dl>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script can be used to check experimental data for
+submission to ArrayExpress in a few different ways. For Tab2MAGE
+submissions it parses the Tab2MAGE spreadsheet format and reports
+on problems with data files and MIAME metadata. Used in conjunction
+with a local MIAMExpress installation the script takes the
+experiment title and the submitter login name, and checks the
+submitted data files for errors. Several log files are written to
+the relevant MIAMExpress submission directory unless the
+<strong>-p</strong> option is used to redirect them to the current
+directory. Normally the script will be able to figure out which
+array design to use from the Tab2MAGE spreadsheet or by querying
+the MIAMExpress database. An optional ADF filename argument may
+also be provided using the <strong>-a</strong> option, and
+ArrayExpress accession numbers may be specified using the
+<strong>-A</strong> option. Specifying your own ADFs/accession
+numbers will make the script ignore any array designs pointed to by
+the spreadsheet or database entry for the experiment.</p>
+<hr />
+<h1><a name="quantitation_types" id=
+"quantitation_types">QUANTITATION TYPES</a></h1>
+<p>Known QuantitationTypes are listed in a separate file or files,
+created with a simple tab-delimited format. The layout of these
+files is described in <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a>.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2de_spreadsheet_filename" id=
+"item__2de_spreadsheet_filename"><strong>-e</strong>
+<code>spreadsheet filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to be checked.</p>
+</dd>
+<dt><strong><a name="item__2di_idf_filename" id=
+"item__2di_idf_filename"><strong>-i</strong> <code>IDF
+filename</code></a></strong></dt>
+<dd>
+<p>The MAGE-TAB IDF file to be checked.</p>
+</dd>
+<dt><strong><a name="item__2dl_login_name" id=
+"item__2dl_login_name"><strong>-l</strong> <code>login
+name</code></a></strong></dt>
+<dd>
+<p>The MIAMExpress login name of the experiment submitter</p>
+</dd>
+<dt><strong><a name="item__2dt_experiment_title" id=
+"item__2dt_experiment_title"><strong>-t</strong> <code>experiment
+title</code></a></strong></dt>
+<dd>
+<p>The MIAMExpress title of the experiment, surrounded by quotes if
+the title contains spaces.</p>
+</dd>
+<dt><strong><a name="item__2da_adf_filename" id=
+"item__2da_adf_filename"><strong>-a</strong> <code>ADF
+filename</code></a></strong></dt>
+<dd>
+<p>The <strong>-a</strong> switch designates the ADF filename to be
+used for all the hybridizations in the experiment. This option
+overrides any database links between hybridizations and array
+designs, and is provided initially as a convenience.</p>
+</dd>
+<dt><strong><a name="item_number" id=
+"item_number"><strong>-A</strong> <code>Array accession number
+(ArrayExpress)</code></a></strong></dt>
+<dd>
+<p>Use the <strong>-A</strong> switch to indicate the accession
+number of an ArrayExpress array design to be used for checking the
+data files. Ordinarily this should not be needed, as the script
+should be able to link the MIAMExpress submission with ArrayExpress
+array designs automatically.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Forces overwriting of existing files (``clobber''). If this
+switch is omitted the user will be asked whether to overwrite
+already existing files.</p>
+</dd>
+<dt><strong><a name="item__2dp" id=
+"item__2dp"><strong>-p</strong></a></strong></dt>
+<dd>
+<p>Write to files in present working directory (``pwd''). The
+default is to write to files in the submission directory.</p>
+</dd>
+<dt><strong><a name="item__2ds" id=
+"item__2ds"><strong>-s</strong></a></strong></dt>
+<dd>
+<p>Standalone option. The script will check the files listed on the
+command line rather than connecting to MIAMExpress and
+ArrayExpress. When used with the <strong>-e</strong> or
+<strong>-i</strong> options, the Tab2MAGE or MAGE-TAB document is
+checked but no connection is made to ArrayExpress to retrieve array
+information. To check features and reporter identifiers with this
+option, an ADF must be specified with the <strong>-a</strong>
+option or an ArrayExpress accession number can be used with the
+<strong>-A</strong> option. In the latter case a connection is made
+to ArrayExpress without connecting to MIAMExpress.</p>
+</dd>
+<dt><strong><a name="item__2dd_directory" id=
+"item__2dd_directory"><strong>-d</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>Source directory. This indicates the directory to search for
+data files. This option is only used for Tab2MAGE submissions
+checks, as MIAMExpress defines its own directory structure which is
+automatically searched by this script. If this option is omitted,
+only the current working directory is searched for Tab2MAGE
+submission data files.</p>
+</dd>
+<dt><strong><a name="item__2dx" id=
+"item__2dx"><strong>-x</strong></a></strong></dt>
+<dd>
+<p>Skip data file checking. This option can be used to quickly
+check experiment annotation without having to wait for the script
+to validate all the data files.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+in the ArrayExpress::Curator::Config module. See <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-Q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option will add the new
+QuantitationType definitions to those included with the Tab2MAGE
+package. See <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dr_namespace" id=
+"item__2dr_namespace"><strong>-R</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Prefix of the Reporter identifier to use when checking data
+files against array designs. This prefix is added to each
+``Reporter Identifier'' in the data files prior to comparison with
+the actual identifiers in the ADF. The default is to assume
+MIAMExpress-like identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dc_namespace" id=
+"item__2dc_namespace"><strong>-C</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Prefix of the CompositeSequence identifier to use when checking
+data files against array designs. This prefix is added to each
+``CompositeSequence Identifier'' in the data files prior to
+comparison with the actual identifiers in the ADF. The default is
+to assume MIAMExpress-like identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dl" id=
+"item__2dl"><strong>-L</strong></a></strong></dt>
+<dd>
+<p>Ignore the data file size limit as configured in Config.yml
+(i.e., MAX_DATAFILE_SIZE).</p>
+</dd>
+<dt><strong><a name="item__2dm" id=
+"item__2dm"><strong>-m</strong></a></strong></dt>
+<dd>
+<p>The checker will support MAGE-TAB documents in which a single
+IDF and SDRF have been combined (in that order), with the start of
+each section marked by [IDF] and [SDRF] respectively. Note that
+such documents are not compliant with the MAGE-TAB format
+specification; this format is used by ArrayExpress to simplify data
+submissions.</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Prints the version number of the script.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Prints a short help text.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="tests" id="tests">TESTS</a></h1>
+<p>There are numerous tests performed by this script. Listed below
+are the tests which are performed on each data file. There is also
+a series of checks which are made on any MIAME metadata supplied to
+the script. This metadata may be in the form of a Tab2MAGE
+spreadsheet, or an experiment submission in a local MIAMExpress
+database. Please see <a href=
+"ArrayExpress/Curator/ExperimentChecker.html#tests">TESTS in the
+ArrayExpress::Curator::ExperimentChecker manpage</a> for a list of
+general tests performed on all submissions. See also <a href=
+"ArrayExpress/Curator/Validate.html#tests">TESTS in the
+ArrayExpress::Curator::Validate manpage</a> for the Tab2MAGE
+spreadsheet tests, <a href=
+"ArrayExpress/MAGETAB/Checker.html#tests">TESTS in the
+ArrayExpress::MAGETAB::Checker manpage</a> for MAGE-TAB document
+checking, and <a href=
+"ArrayExpress/Curator/MIAMExpress.html#tests">TESTS in the
+ArrayExpress::Curator::MIAMExpress manpage</a> for more information
+on the MIAMExpress tests.</p>
+<hr />
+<h1><a name="local_miamexpress_installations" id=
+"local_miamexpress_installations">LOCAL MIAMEXPRESS
+INSTALLATIONS</a></h1>
+<p>Users wishing to use this script with local installations of
+MIAMExpress should check the ArrayExpress::Curator::Config module
+and change whatever parameters are necessary.</p>
+<hr />
+<h1><a name="see_also" id="see_also">SEE ALSO</a></h1>
+<dl>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker" id=
+"item_arrayexpress_3a_3acurator_3a_3aexperimentchecker"></a><a href="ArrayExpress/Curator/ExperimentChecker.html">the
+ArrayExpress::Curator::ExperimentChecker manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3acurator_3a_3avalidate"
+id="item_arrayexpress_3a_3acurator_3a_3avalidate"></a><a href=
+"ArrayExpress/Curator/Validate.html">the
+ArrayExpress::Curator::Validate manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3amagetab_3a_3achecker"
+id="item_arrayexpress_3a_3amagetab_3a_3achecker"></a><a href=
+"ArrayExpress/MAGETAB/Checker.html">the
+ArrayExpress::MAGETAB::Checker manpage</a></strong></dt>
+<dt><strong><a name=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress" id=
+"item_arrayexpress_3a_3acurator_3a_3amiamexpress"></a><a href=
+"ArrayExpress/Curator/MIAMExpress.html">the
+ArrayExpress::Curator::MIAMExpress manpage</a></strong></dt>
+<dt><strong><a name="item_arrayexpress_3a_3adatafile_3a_3aqt_list"
+id="item_arrayexpress_3a_3adatafile_3a_3aqt_list"></a><a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a></strong></dt>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<h1><a name="bugs" id="bugs">BUGS</a></h1>
+<p>Affymetrix CHP files are not currently supported. Moreover, the
+script will not check the features referred to in CEL files against
+an array design until I can figure out how to retrieve a list of
+valid features in a timely fashion. CompositeSequence IDs in final
+data matrix files from Affymetrix submissions are fully supported
+and checked against the array design. Basic data quality
+calculations (percent null, Benfords law) are made on Affymetrix
+CEL and FGEM data.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/idf.html b/docs/idf.html
new file mode 100644
index 0000000..e68be7d
--- /dev/null
+++ b/docs/idf.html
@@ -0,0 +1,378 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>IDF notes</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+ <td class="pagetitle">IDF help notes</td>
+ </tr>
+ </table>
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="overall">Introduction</a></div>
+
+ <p class="text">This page provides detailed help on the
+ Investigation Design Format (IDF), and lists all the available
+ tags for use in MAGE-TAB IDF documents. For an overview of the
+ MAGE-TAB submission process, and examples of both IDF and SDRF,
+ please see these <a href="magetab_subs.html">submission help
+ notes</a> and <a href="magetab_docs.html">MAGE-TAB
+ overview</a>. For more detail on the MAGE-TAB format, please see
+ the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+ specification</a>.</p>
+
+ <p class="text">Most of the following fields can be used with
+ more than one value, so that (for example) multiple Protocols,
+ Persons, Experimental Factors and Term Sources can be defined in
+ a single IDF file. In these cases, the different "objects" are
+ separated by tabs. Those fields which can contain only one value
+ are indicated below. In some cases (Person Roles, Protocol
+ Parameters) it is possible to have multiple values within a
+ given "object". For example, one person may have many roles. In
+ such cases the multiple roles should be separated by semicolons
+ (";").</p>
+
+ <p class="text">MAGE-TAB documents may optionally include
+ information on the sources of any controlled terms used,
+ providing users with the ability to link to ontologies,
+ databases or other sources of controlled vocabularies to
+ describe their experiment. These so-called "Term Sources" are
+ defined in the IDF and may be used throughout the document. If
+ they are not used, then all controlled vocabulary terms are
+ assumed to be user-defined.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="idftags">All valid IDF tags</a></div>
+
+ <p class="text"><a name="invtitle" class="subhead">Investigation
+ Title</a> <br>The overall title of the investigation. <font
+ color="red">This tag can only have one value.</font></p>
+
+ <p class="text"><a name="exptdesign"
+ class="subhead">Experimental Design</a> <br>The experiment
+ design types which are applicable to this study. Typically these
+ terms should come from the <a
+ href="http://mged.sourceforge.net/ontologies/index.php">MGED
+ Ontology</a>. The <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentDesignType">
+ ExperimentDesignType</a> subclasses are particularly useful
+ here. See for example the list of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BiologicalProperty">BiologicalProperty
+ </a> terms available. <font color="blue">Controlled vocabulary
+ term.</font></p>
+
+ <p class="text"><a name="exptdesigntermsource"
+ class="subhead">Experimental Design Term Source REF</a> <br>The
+ source of the Experimental Design terms; this must reference one
+ of the Term Source Names defined elsewhere in the IDF file (see
+ <a href="#termsourcename">below</a>).</p>
+
+ <p class="text"><a name="exptdesigntermaccno"
+ class="subhead">Experimental Design Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="factorname"
+ class="subhead">Experimental Factor Name</a> <br>A user-defined
+ name for each experimental factor studied by the
+ experiment. These experimental factors represent the variables
+ within the investigation (e.g. growth condition, genotype,
+ organism part, disease state). The actual values of these
+ variables will be listed in the SDRF file, in "Factor Value [<factor name>]" colummns. <font
+ color="green">Used as an identifier within the MAGE-TAB
+ document.</font></p>
+
+ <p class="text"><a name="factortype"
+ class="subhead">Experimental Factor Type</a> <br> A term
+ describing the type of each experimental factor. These terms
+ will usually come from the <a
+ href="http://mged.sourceforge.net/ontologies/index.php">MGED
+ Ontology</a>. The <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalFactorCategory">
+ ExperimentalFactorCategory</a> subclasses are particularly
+ useful here. See for example the list of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristicCategory">BioMaterialCharacteristicCategory
+ terms</a> available. <font color="blue">Controlled vocabulary
+ term.</font></p>
+
+ <p class="text"><a name="factortermsource"
+ class="subhead">Experimental Factor Term Source REF</a> <br>The
+ source of the Experimental Factor Type terms; this must
+ reference one of the Term Source Names defined elsewhere in the
+ IDF file (see <a href="#termsourcename">below</a>).</p>
+
+ <p class="text"><a name="factortermaccno"
+ class="subhead">Experimental Factor Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="lastname" class="subhead">Person Last
+ Name</a> <br>The last name of each person associated with the
+ experiment.</p>
+
+ <p class="text"><a name="firstname" class="subhead">Person First
+ Name</a> <br>The first name of each person associated with the
+ experiment.</p>
+
+ <p class="text"><a name="midinitials" class="subhead">Person Mid
+ Initials</a> <br>The middle initials of each person associated
+ with the experiment.</p>
+
+ <p class="text"><a name="email" class="subhead">Person Email</a>
+ <br>The email address of each person associated with the
+ experiment.</p>
+
+ <p class="text"><a name="phone" class="subhead">Person Phone</a>
+ <br>The telephone number of each person associated with the
+ experiment.</p>
+
+ <p class="text"><a name="fax" class="subhead">Person Fax</a>
+ <br>The Fax number of each person associated with the
+ experiment.</p>
+
+ <p class="text"><a name="address" class="subhead">Person
+ Address</a> <br>The street address of each person associated
+ with the experiment.</p>
+
+ <p class="text"><a name="affiliation" class="subhead">Person
+ Affiliation</a> <br>The organization affiliation for each person
+ associated with the experiment.</p>
+
+ <p class="text"><a name="roles" class="subhead">Person Roles</a>
+ <br>The role(s) performed by each person. Typically these terms
+ should come from the <a
+ href="http://mged.sourceforge.net/ontologies/index.php">MGED
+ Ontology</a>. See for example the list of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#Roles">
+ Roles</a> terms. If more than one role is needed per person, the
+ roles should be given as a semicolon (";") delimited list, for
+ example: "submitter;data_coder;investigator". <font
+ color="blue">Controlled vocabulary term.</font></p>
+
+ <p class="text"><a name="rolestermsource" class="subhead">Person
+ Roles Term Source REF</a> <br>The source of the Person Roles
+ terms; this must reference one of the Term Source Names defined
+ elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+
+ <p class="text"><a name="rolestermaccno"
+ class="subhead">Person Roles Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="qctype" class="subhead">Quality Control
+ Type</a> <br>The quality control procedures used. Typically
+ these terms should come from the <a
+ href="http://mged.sourceforge.net/ontologies/index.php">MGED
+ Ontology</a>. See for example the list of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#QualityControlDescriptionType">
+ QualityControlDescriptionType</a> terms. <font
+ color="blue">Controlled vocabulary term.</font></p>
+
+ <p class="text"><a name="qctypetermsource"
+ class="subhead">Quality Control Term Source REF</a> <br>The
+ source of the Quality Control Type terms; this must reference
+ one of the Term Source Names defined elsewhere in the IDF
+ file (see <a href="#termsourcename">below</a>).</p>
+
+ <p class="text"><a name="qctypetermaccno"
+ class="subhead">Quality Control Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="reptype" class="subhead">Replicate
+ Type</a><br> The replicate strategies used. Typically these terms
+ should come from the <a
+ href="http://mged.sourceforge.net/ontologies/index.php">MGED
+ Ontology</a>. See for example the list of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ReplicateDescriptionType">
+ ReplicateDescriptionType</a> terms. <font
+ color="blue">Controlled vocabulary term.</font></p>
+
+ <p class="text"><a name="reptypetermsource"
+ class="subhead">Replicate Type Term Source REF</a> <br>The
+ source of the Replicate Type terms; this must reference one of
+ the Term Source Names defined elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+
+ <p class="text"><a name="reptypetermaccno"
+ class="subhead">Replicate Type Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="normtype" class="subhead"> <br>Normalization
+ Type</a> <br>The normalization strategies used. Typically these terms
+ should come from the <a
+ href="http://mged.sourceforge.net/ontologies/index.php">MGED
+ Ontology</a>. See for example the list of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#NormalizationDescriptionType">
+ NormalizationDescriptionType</a> terms. <font
+ color="blue">Controlled vocabulary term.</font></p>
+
+ <p class="text"><a name="normtypetermsource"
+ class="subhead">Normalization Type Term Source REF</a> <br>The
+ source of the Normalization Type terms; this must reference one
+ of the Term Source Names defined elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+
+ <p class="text"><a name="normtypetermaccno"
+ class="subhead">Normalization Type Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="exptdate" class="subhead">Date of
+ Experiment</a> <br>The date on which the experiment was
+ performed. <font color="red">This tag can only have one
+ value.</font></p>
+
+ <p class="text"><a name="releasedate" class="subhead">Public
+ Release Date</a> <br>The date on which the experimental data
+ will be/was released. <font color="red">This tag can only have
+ one value.</font></p>
+
+ <p class="text"><a name="pubmedid" class="subhead">PubMed ID</a>
+ <br>The PubMed IDs of the publication(s) associated with this
+ investigation (where available).</p>
+
+ <p class="text"><a name="pubdoi" class="subhead">Publication
+ DOI</a> <br>A Digital Object Identifier (DOI) for each
+ publication (where available).</p>
+
+ <p class="text"><a name="pubauthors" class="subhead">Publication
+ Author List</a> <br> The list of authors associated with each
+ publication.</p>
+
+ <p class="text"><a name="pubtitle" class="subhead">Publication
+ Title</a> <br>The title of each publication.</p>
+
+ <p class="text"><a name="pubstatus" class="subhead">Publication
+ Status</a> <br>A term describing the status of each publication
+ (e.g. "submitted", "in preparation", "published"). <font
+ color="blue">Controlled vocabulary term.</font></p>
+
+ <p class="text"><a name="pubstatustermsource"
+ class="subhead">Publication Status Term Source REF</a> <br>The
+ source of the Publication Status terms; this must reference one
+ of the Term Source Names defined elsewhere in the IDF file (see <a href="#termsourcename">below</a>).</p>
+
+ <p class="text"><a name="pubstatustermaccno"
+ class="subhead">Publication Status Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="exptdesc" class="subhead">Experiment
+ Description</a> <br> A short paragraph describing the experiment
+ as free-text. <font color="red">This tag can only have one
+ value.</font></p>
+
+ <p class="text"><a name="protoname" class="subhead">Protocol
+ Name</a> <br>The names of the protocols used within the MAGE-TAB
+ document. These will be referenced in the SDRF in the
+ "Protocol REF" columns. <font color="green">Used as an
+ identifier within the MAGE-TAB document.</font></p>
+
+ <p class="text"><a name="prototype" class="subhead">Protocol
+ Type</a> <br>The type of the protocol, taken from a controlled
+ vocabulary. Typically this term should come from the <a
+ href="http://mged.sourceforge.net/ontologies/index.php">MGED
+ Ontology</a>. See for example the list of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalProtocolType">
+ ExperimentalProtocolType</a> terms. <font
+ color="blue">Controlled vocabulary term.</font></p>
+
+ <p class="text"><a name="protodesc" class="subhead">Protocol
+ Description</a> <br>A free-text description of the
+ protocol. This text is included in a single tab-delimited
+ field. If you wish to include tab or newline characters as part
+ of this text, you must enclose the whole text within double
+ quotes (").</p>
+
+ <p class="text"><a name="protoparam" class="subhead">Protocol
+ Parameters</a> <br>A semicolon-delimited list of parameter
+ names; these names are used in the SDRF file (as
+ "Parameter Value [<parameter name>]"
+ headings) to list the values used for each protocol
+ parameter. If more than one parameter was used for a given
+ protocol, they should be separated with semicolons (";"). <font
+ color="green">Used as an identifier within the MAGE-TAB
+ document.</font></p>
+
+ <p class="text"><a name="protohw" class="subhead">Protocol
+ Hardware</a> <br>The hardware used by the protocol.</p>
+
+ <p class="text"><a name="protosw" class="subhead">Protocol
+ Software</a> <br>The software used by the protocol.</p>
+
+ <p class="text"><a name="protocontact" class="subhead">Protocol
+ Contact</a> <br>The name and contact details to be used for
+ enquiries concerning the protocol.</p>
+
+ <p class="text"><a name="prototypetermsource"
+ class="subhead">Protocol Term Source REF</a> <br>The source of
+ the Protocol Type terms; this must reference one of the Term
+ Source Names defined elsewhere in the IDF file (see <a
+ href="#termsourcename">below</a>). Examples: MGED ontology,
+ OBI.</p>
+
+ <p class="text"><a name="prototypetermaccno"
+ class="subhead">Protocol Term Accession Number</a>
+ <br>The accession number for this term, taken from the indicated
+ Term Source.</p>
+
+ <p class="text"><a name="sdrf" class="subhead">SDRF File</a>
+ <br>The name(s) of the SDRF file(s) accompanying this IDF file.</p>
+
+ <p class="text"><a name="termsourcename" class="subhead">Term
+ Source Name</a> <br>The names of the Term Sources (ontologies or
+ databases) used within the MAGE-TAB document. This name will be
+ used in all corresponding "Term Source REF"
+ fields. Examples: MGED Ontology, NCI MetaThesaurus,
+ ArrayExpress. <font color="green">Used as an identifier within
+ the MAGE-TAB document.</font></p>
+
+ <p class="text"><a name="termsourcefile" class="subhead">Term
+ Source File</a> <br>A filename or valid URI at which the Term
+ Source may be accessed.</p>
+
+ <p class="text"><a name="termsourceversion" class="subhead">Term
+ Source Version</a> <br>The version of the Term Source used
+ throughout the MAGE-TAB document.</p>
+
+ <p class="text"><a name="comment"
+ class="subhead">Comment[<user-defined tag>]</a> <br>A
+ user-defined value which is associated with the
+ investigation. For example, ArrayExpress uses
+ "Comment [ArrayExpressSubmissionDate]" to record the date of
+ submission to the database; alternatively, tags such as
+ "Comment [Goal]" might be used to indicate the purpose behind an
+ investigation.</p>
+
+ <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <p class="text">Please see the <a href="http://www.mged.org/mage-tab/">
+ MAGE-TAB specification</a> for further information and examples.</p>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Mon Feb 25 18:11:01 GMT 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..d172687
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Tab2MAGE</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css">
+ </head>
+
+ <body>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="270"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a> <a href="../index.html#magetab"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+ <td class="pagetitle">Documentation</td>
+ </tr>
+ </table>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="toc">Table of contents</a></div>
+
+ <p class="text">Please note that this documentation describes
+ the <em>development</em> version of Tab2MAGE, as used and tested
+ by the ArrayExpress curation staff. Users of the older <em>stable</em>
+ release should refer to the documentation included in their
+ downloaded package.</p>
+
+ <p class="text"><a name="tab2mage" class="subhead">Tab2MAGE
+ information</a><br> The following pages contain important
+ information on how to use Tab2MAGE to generate MAGE-ML, and data
+ submission to ArrayExpress using this format:</p>
+
+ <ul>
+ <li><a href="aesubs.html">Tab2MAGE ArrayExpress data submission</a></li>
+ <li><a href="spreadsheet.html">Constructing a Tab2MAGE spreadsheet</a></li>
+ <li><a href="datafiles.html">Supported data file formats</a></li>
+ <li><a href="usage.html#columns">Dealing with unknown data file columns</a></li>
+ <li><a href="detail.html">Tab2MAGE spreadsheet detail</a></li>
+ </ul>
+
+ <p class="text"><a name="magetab" class="subhead">MAGE-TAB
+ information</a><br> These pages contain a brief primer on the
+ MAGE-TAB format, and how to submit MAGE-TAB documents to
+ ArrayExpress:</p>
+
+ <ul>
+ <li><a href="magetab_subs.html">MAGE-TAB submission to ArrayExpress</a></li>
+ <li><a href="idf.html">Creating IDF documents - detail</a></li>
+ <li><a href="sdrf.html">Creating SDRF documents - detail</a></li>
+ </ul>
+
+ <p class="text">For more information on the MAGE-TAB format, please
+ refer to the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+ specification</a>.</p>
+
+ <p class="text">A brief summary of the differences between
+ MAGE-TAB and Tab2MAGE is given <a
+ href="magetab_vs_tab2mage.html">here</a>.</p>
+
+ <p class="text"><a name="technical" class="subhead">Technical
+ details</a><br> Also included here are some notes which have
+ been automatically generated from the Perl code documentation
+ for your convenience. They will usually give reasonably up to
+ date (although not always terribly user-friendly) information on
+ how the scripts work. For the absolute latest up-to-the-minute
+ information, please refer to the "perldoc" documentation embedded
+ in each script or module.</p>
+
+ <ul>
+ <li><a href="tab2mage.html">Tab2MAGE script</a></li>
+ <li><a href="magetab.html">MAGE-TAB script</a></li>
+ <li><a href="expt_check.html">Experiment checker script</a></li>
+ <li><a href="ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">All supported spreadsheet column headings</a></li>
+ <li><a href="ArrayExpress/Datafile/QT_list.html#quantitation_types">QuantitationType definitions file format</a></li>
+ <li><a href="ArrayExpress/Curator/Config.html">Configuration options (not needed for basic operation)</a></li>
+ </ul>
+
+ <p class="text"><a name="developer" class="subhead">Developer
+ documentation</a><br> Below is a set of API documentation for
+ the various modules which are incuded in the Tab2MAGE
+ package. These are likely to be of interest only to
+ developers:</p>
+
+ <ul>
+ <li><a href="modules.html">Perl modules</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Mon Jan 10 16:08:12 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Apr 29 13:05:11 BST 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/magetab.html b/docs/magetab.html
new file mode 100644
index 0000000..fb16023
--- /dev/null
+++ b/docs/magetab.html
@@ -0,0 +1,235 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>magetab.pl - a script to generate valid MAGE-ML from an
+input MAGE-TAB document.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#quantitationtype_file">QUANTITATIONTYPE FILE</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="MAGETAB_logo.png" border="0" height="50"
+alt="MAGE-TAB logo" /></a></td>
+<td class="pagetitle">Script detail: magetab.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>magetab.pl - a script to generate valid MAGE-ML from an input
+MAGE-TAB document.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ magetab.pl -i <IDF filename> -n <experiment accession> -t <target directory>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script will process a MAGE-TAB document and a set of data
+files to generate valid MAGE-ML. The supported data file formats
+are listed in the accompanying documentation. The MAGE-TAB format
+specification may be downloaded from this link:</p>
+<pre>
+ <a href=
+"http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz">http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz</a>
+</pre>
+<p>There is one auxilliary file which can be supplied alongside the
+data files. This file simply defines the QuantitationTypes which
+are to be extracted from the data files. This QuantitationType file
+is optional, however, as the script is supplied with a set of
+defaults determined by a survey of incoming QuantitationTypes by
+ArrayExpress curators. The script generates a log file in the
+target directory which details which columns have been ignored. To
+keep all unrecognized QuantitationTypes, invoke the script with the
+<strong>-k</strong> option.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2di_idf" id=
+"item__2di_idf"><strong>-i</strong>
+<code>IDF</code></a></strong></dt>
+<dd>
+<p>The MAGE-TAB IDF file to be parsed.</p>
+</dd>
+<dt><strong><a name="item__2dn_accession" id=
+"item__2dn_accession"><strong>-n</strong>
+<code>accession</code></a></strong></dt>
+<dd>
+<p>The experiment accession to be used as the top-level Experiment
+identifier in the generated MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item__2dt_directory" id=
+"item__2dt_directory"><strong>-t</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>The target directory to be created. This directory will contain
+the MAGE-ML file and external data files ready for validation.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+as part of the Tab2MAGE package. See <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-Q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option will add the new
+QuantitationType definitions to those included with the Tab2MAGE
+package. See <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-k</strong></a></strong></dt>
+<dd>
+<p>Keep all columns in the data files, regardless of whether they
+are recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-K</strong></a></strong></dt>
+<dd>
+<p>If the autosubmissions system is configured, magetab.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.</p>
+</dd>
+<dt><strong><a name="item__2ds" id=
+"item__2ds"><strong>-s</strong></a></strong></dt>
+<dd>
+<p>Standalone option. This prevents the script from attempting to
+connect to ArrayExpress to retrieve array information.</p>
+</dd>
+<dt><strong><a name="item__2dx" id=
+"item__2dx"><strong>-x</strong></a></strong></dt>
+<dd>
+<p>Skip data file processing during MAGE-ML generation. The script
+will attempt to generate MAGE-ML for the metadata contained in the
+IDF and SDRF only.</p>
+</dd>
+<dt><strong><a name="item__2dr_namespace" id=
+"item__2dr_namespace"><strong>-R</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Reporter identifier prefix. By default the script uses the
+MIAMExpress convention for generating reporter identifiers. This
+option allows you to override this behaviour by supplying an
+alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dc_namespace" id=
+"item__2dc_namespace"><strong>-C</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>CompositeSequence identifier prefix. By default the script uses
+the MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dd_directory" id=
+"item__2dd_directory"><strong>-d</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>Source directory containing all the data files referenced in the
+SDRF. If this is omitted, the current working directory will be
+searched for data files.</p>
+</dd>
+<dt><strong><a name="item__2dp" id=
+"item__2dp"><strong>-P</strong></a></strong></dt>
+<dd>
+<p>By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used
+for ArrayExpress submissions, this option allows for such files to
+be directly downloadable from the ArrayExpress web interface.
+However, in unusual circumstances when you might wish to use
+plain-text encoding for these data files, use this option.</p>
+</dd>
+<dt><strong><a name="item__2dl" id=
+"item__2dl"><strong>-L</strong></a></strong></dt>
+<dd>
+<p>Ignore the data file size limit as configured in Config.yml
+(i.e., MAX_DATAFILE_SIZE).</p>
+</dd>
+<dt><strong><a name="item__2dm" id=
+"item__2dm"><strong>-m</strong></a></strong></dt>
+<dd>
+<p>The parser will support MAGE-TAB documents in which a single IDF
+and SDRF have been combined (in that order), with the start of each
+section marked by [IDF] and [SDRF] respectively. Note that such
+documents are not compliant with the MAGE-TAB format specification;
+this format is used by ArrayExpress to simplify data
+submissions.</p>
+</dd>
+<dt><strong><a name="item__2df_font_name" id=
+"item__2df_font_name"><strong>-f</strong> <code>font
+name</code></a></strong></dt>
+<dd>
+<p>Name of the font to be used for Graphviz-generated PNGs.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Overwrite preexisting files (``clobber'' option).</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Prints the version number of the script.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Print a short help text summarizing these options.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="quantitationtype_file" id=
+"quantitationtype_file">QUANTITATIONTYPE FILE</a></h1>
+<p>Please see <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for a description of
+the format of this file.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2007.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/magetab_docs.html b/docs/magetab_docs.html
new file mode 100644
index 0000000..fde03e9
--- /dev/null
+++ b/docs/magetab_docs.html
@@ -0,0 +1,429 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>MAGE-TAB Overview</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ </head>
+
+ <body>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+ <td class="pagetitle">Overview of the MAGE-TAB format</td>
+ </tr>
+ </table>
+
+ <div CLASS="section">
+
+ <table class="layout">
+ <tr>
+ <td class="layout">
+
+ <p class="subhead">On this page:</p>
+
+ <ul>
+ <li><a href="#introduction">Introduction</a></li>
+ <li><a href="#overall">Overall structure of MAGE-TAB</a></li>
+ <li><a href="#idf">IDF overview</a></li>
+ <li><a href="#sdrf">SDRF overview</a></li>
+ <li><a href="#datafiles">Data files</a></li>
+ <li><a href="#datamatrix">Data Matrices</a></li>
+ </ul>
+
+ <p class="subhead">See also:</p>
+
+ <ul>
+ <li><a href="idf.html">IDF detail</a></li>
+ <li><a href="sdrf.html">SDRF detail</a></li>
+ <li><a href="datafiles.html">Supported data file formats</a></li>
+ <li><a href="magetab_subs.html">Submitting MAGE-TAB to ArrayExpress</a></li>
+ <li><a href="magetab_subs.html#realexamples">Example MAGE-TAB documents</a></li>
+ </ul>
+ </td>
+ <td class="layout" valign="top">
+ <div CLASS="imghead">
+ <a href="http://www.ebi.ac.uk/arrayexpress/">
+ <img src="aelogo.png" border="0" alt="ArrayExpress logo">
+ </a>
+ </div>
+ </td>
+
+ </tr>
+ </table>
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="introduction">Introduction</a></div>
+
+ <p class="text">These notes provide an introduction to the
+ MAGE-TAB format and a brief primer on how to submit microarray
+ data to ArrayExpress in this format. MAGE-TAB is the successor
+ to the Tab2MAGE format, developed by members of the microarray
+ research community to extend the capabilities of the spreadsheet
+ format. A <a href="http://www.mged.org/mage-tab/">full MAGE-TAB
+ specification document</a> is available from the <a
+ href="http://www.mged.org">MGED web site</a>.</p>
+
+ <p class="text">ArrayExpress supports submission of MAGE-TAB
+ documents via a <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">web-based
+ submission system</a>. This system allows the user to create an
+ experiment submission and download an automatically-generated
+ template MAGE-TAB document. This document can then be completed
+ by the user and uploaded to the web page alongside their data
+ files. For more information, please see the <a
+ href="magetab_subs.html">ArrayExpress submission help
+ notes</a></p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="overall">Overall structure of MAGE-TAB</a></div>
+
+ <p class="text">The MAGE-TAB format uses a number of different
+ files to capture information about a microarray experiment:</p>
+
+ <ul>
+
+ <li><a href="#idf">IDF</a>: Investigation Description Format</li>
+
+ <li><a href="#sdrf">SDRF</a>: Sample and Data Relationship Format</li>
+
+ <li><a href="#adf">ADF</a>: Array Design Format</li>
+
+ <li><a href="#datafiles">Raw and processed data files</a>,
+ including "<a href="#datamatrix">data matrix</a>" files</li>
+
+ </ul>
+
+ <p class="text">The IDF file is used to give an overview of the
+ experiment, including the experimental variables (factors) used,
+ protocols, quality control strategy, publication information and
+ contact details. Also included in the IDF file is an (optional)
+ list of sources from which controlled vocabulary terms may have
+ been used elsewhere in the MAGE-TAB document. These term sources
+ may be fully-fledged ontologies (e.g. the MGED ontology),
+ databases providing queryable accession numbers
+ (e.g. ArrayExpress), or simply a file defining terms for local
+ users.</p>
+
+ <p class="text">The SDRF file describes the relationship between
+ every step in the chain of biological materials used in the
+ experiment through to the hybridization, and the acquisition and
+ normalization of data. It is similar in concept to the <a
+ href="spreadsheet.html#hybridization">Hybridization section</a>
+ found in Tab2MAGE spreadsheets. Experimental factors, protocols,
+ protocol parameters and term sources defined in the IDF are
+ referenced by the SDRF.</p>
+
+ <p class="text"><a name="adf"/>The ADF file provides the
+ array-level annotation for the experiment, relating the
+ row-level identifiers in the data files to biological sequence
+ annotation. Array designs are usually deposited in ArrayExpress
+ as separate submissions to the experimental data, and in the
+ case of commercial arrays may not need to be submitted to
+ ArrayExpress at all.</p>
+
+ <p class="text">Currently, ArrayExpress only supports submission
+ of array designs using ADF files via the <a
+ href="http://www.ebi.ac.uk/miamexpress/">MIAMExpress web
+ submission</a> system. For help with this process, please see
+ the <a
+ href="http://wwwe.ebi.ac.uk/miamexpress/help/adf/index.html">MIAMExpress
+ ADF help documentation</a>. This array design component of
+ MAGE-TAB will not be discussed further here.</p>
+
+ <p class="text">An experimental data submission will usually
+ consist of an IDF file, an SDRF file, and a series of data
+ files. Typically there will be one raw data file per
+ hybridization. Each hybridization may also have a normalized
+ data file, or the final transformed data may be combined into a
+ data matrix file. Note that the ArrayExpress MAGE-TAB generating
+ system combines both the IDF and the SDRF into a single file
+ format, for the sake of convenience.</p>
+
+ <p class="text">For more detail on the MAGE-TAB document format,
+ please see the <a
+ href="http://www.mged.org/mage-tab/">MAGE-TAB
+ specification document,</a> available from the <a
+ href="http://www.mged.org/">MGED web site</a>.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="idf">IDF overview</a></div>
+
+ <p class="text">The IDF component of a MAGE-TAB document
+ consists of a set of unique tags attached to their corresponding
+ values in a simple tab-delimited text format. For example,
+ "Experiment Description" should be followed by a free-text
+ description of the experiment. Most of the fields in the IDF
+ document can handle multiple values. A full description of every
+ valid IDF tag is given in the <a href="idf.html">detailed IDF
+ help notes</a>. An example of an IDF document is given
+ below:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="idf.html#invtitle">Investigation Title</a></td><td>Invasive vs. non-invasive strains of yeast</td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#exptdesign">Experimental Design</a></td><td>individual_genetic_characteristics_design</td><td>growth_condition_design</td></tr>
+ <tr><td><a class="plain" href="idf.html#factorname">Experimental Factor Name</a></td><td>EF_Genotype</td><td>EF_GrowthCond</td></tr>
+ <tr><td><a class="plain" href="idf.html#factortype">Experimental Factor Type</a></td><td>genotype</td><td>growth_condition</td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#lastname">Person Last Name</a></td><td>Falstaff</td><td>Shakespeare</td></tr>
+ <tr><td><a class="plain" href="idf.html#firstname">Person First Name</a></td><td>John</td><td>Bill</td></tr>
+ <tr><td><a class="plain" href="idf.html#email">Person Email</a></td><td>jfalstaff at wagglespike.com</td><td>bills at wagglespike.com</td></tr>
+ <tr><td><a class="plain" href="idf.html#address">Person Address</a></td><td>Ontario, Canada</td><td>Ontario, Canada</td></tr>
+ <tr><td><a class="plain" href="idf.html#affiliation">Person Affiliation</a></td><td>Windsor Laboratories</td><td>Windsor Laboratories</td></tr>
+ <tr><td><a class="plain" href="idf.html#roles">Person Roles</a></td><td>submitter;investigator</td><td>investigator</td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#qctype">Quality Control Type</a></td><td>dye_swap_quality_control</td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#releasedate">Public Release Date</a></td><td>2004-08-30</td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#pubmedid">PubMed ID</a></td><td>12345678</td><td>87654321</td></tr>
+ <tr><td><a class="plain" href="idf.html#pubauthors">Publication Author List</a></td><td>Falstaff, J. and Shakespeare, B.</td><td>Goodfellow, R. et al.</td></tr>
+ <tr><td><a class="plain" href="idf.html#pubtitle">Publication Title</a></td><td>Improved yeast flocculation</td><td>Yeast and beer: a retrospective</td></tr>
+ <tr><td><a class="plain" href="idf.html#pubstatus">Publication Status</a></td><td>in preparation</td><td>submitted</td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#exptdesc">Experiment Description</a></td><td>An experiment was performed to...</td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#protoname">Protocol Name</a></td><td>Yeast Growth</td><td>RNA extraction</td></tr>
+ <tr><td><a class="plain" href="idf.html#prototype">Protocol Type</a></td><td>grow</td><td>nucleic_acid_extraction</td></tr>
+ <tr><td><a class="plain" href="idf.html#protodesc">Protocol Description</a></td><td>S. cerevisiae cultures were grown on...</td><td>Total cellular RNA was extracted...</td></tr>
+ <tr><td><a class="plain" href="idf.html#protoparam">Protocol Parameters</a></td><td>carbon source;temperature</td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="idf.html#sdrf">SDRF File</a></td><td>my_sdrf_file.txt</td><td> </td></tr>
+ </table>
+
+ <p class="text">Note that this is only a minimal subset of the
+ available IDF tags. Blank lines may be included for
+ legibility. Lines beginning with the "#" symbol are treated as
+ comments and ignored. A full listing of all supported IDF tags
+ can be found in these <a href="idf.html">IDF help notes</a>.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="sdrf">SDRF overview</a></div>
+
+ <p class="text">The SDRF file consists of a table in which each
+ hybridization channel is represented by a row, and columns
+ represent the steps of the experiment. In contrast to the
+ Tab2MAGE format, the ordering of these columns is important, and
+ should read left-to-right in chronological order. The overall
+ organization of this table is shown below. To get more detail on
+ the properties of each section, click on the relevant box below
+ or read these <a href="sdrf.html">detailed SDRF notes</a>.</p>
+
+ <map name="sdrf_layout_Map">
+ <area shape="rect" alt="Derived Array Data File" coords="557,402,716,493" href="sdrf.html#derivedarraydatafile_section"/>
+ <area shape="rect" alt="Scan" coords="86,402,245,493" href="sdrf.html#scan_section"/>
+ <area shape="rect" alt="Array Data File" coords="243,402,402,493" href="sdrf.html#arraydatafile_section"/>
+ <area shape="rect" alt="Normalization" coords="400,402,559,493" href="sdrf.html#normalization_section"/>
+ <area shape="rect" alt="Labeled Extract" coords="324,0,434,94" href="sdrf.html#labeledextract_section"/>
+ <area shape="rect" alt="Extract" coords="216,0,326,94" href="sdrf.html#extract_section"/>
+ <area shape="rect" alt="Sample" coords="108,0,218,94" href="sdrf.html#sample_section"/>
+ <area shape="rect" alt="Source" coords="0,0,110,94" href="sdrf.html#source_section"/>
+ <area shape="rect" alt="Hybridization" coords="268,168,428,281" href="sdrf.html#hybridization_section"/>
+ </map>
+ <div align="center"><img src="sdrf_layout.png" width="716" height="493" border="0" alt="SDRF layout" usemap="#sdrf_layout_Map"></div>
+
+ <p class="text">Each block in the diagram above starts with a
+ "Name" or "File" column (e.g. "Extract Name", "Array Data
+ File"), followed by a set of attribute columns. Each block is
+ separated from its predecessor by "<a
+ href="sdrf.html#protocol_section">Protocol REF</a>" columns
+ containing references to the "Protocol Name" values defined in
+ the IDF.</p>
+
+ <p class="text">A further set of columns is used to specify the
+ values for the variables ("experimental factors") within the
+ experiment. These <a href="sdrf.html#factorvalue_section">Factor
+ Value[]</a> columns reference the Experimental Factor Names
+ defined in the IDF, and should be placed after the hybridization
+ section (i.e., to the right of it, in or after the scanning,
+ normalization and data section in the image above). The contents
+ of these columns will usually duplicate those in a material <a
+ href="sdrf.html#characteristics">Characteristics</a> or a
+ protocol <a href="sdrf.html#parameter_value">Parameter Value</a>
+ column. See below for an example.</p>
+
+ <p class="text">Below is a very simple example SDRF showing the
+ links between materials and hybridization for a simple
+ Affymetrix-based experiment:</p>
+
+ <table class="example">
+ <tr>
+ <td><a class="plain" href="sdrf.html#source_name">Source Name</a></td>
+ <td><a class="plain" href="sdrf.html#characteristics">Characteristics [OrganismPart]</a></td>
+ <td><a class="plain" href="sdrf.html#labeled_extract_name">Labeled Extract Name</a></td>
+ <td><a class="plain" href="sdrf.html#label">Label</a></td>
+ <td><a class="plain" href="sdrf.html#protocol_ref">Protocol REF</a></td>
+ <td><a class="plain" href="sdrf.html#hybridization_name">Hybridization Name</a></td>
+ <td><a class="plain" href="sdrf.html#array_design_ref">Array Design REF</a></td>
+ </tr>
+ <tr><td>liver sample 1</td><td>liver</td><td>LE 1</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 1</td><td>A-AFFY-33</td></tr>
+ <tr><td>liver sample 2</td><td>liver</td><td>LE 2</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 2</td><td>A-AFFY-33</td></tr>
+ <tr><td>kidney sample 1</td><td>kidney</td><td>LE 3</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 3</td><td>A-AFFY-33</td></tr>
+ <tr><td>kidney sample 2</td><td>kidney</td><td>LE 4</td><td>biotin</td><td>Hyb protocol name</td><td>Hyb 4</td><td>A-AFFY-33</td></tr>
+ </table>
+
+ <p class="text">Note that normally, many more
+ "Characteristics[]" columns would be used to fully describe the
+ Source. The SDRF might then continue in the following fashion,
+ linking the Hybridization to data files and experimental factor
+ values:</p>
+
+ <table class="example">
+ <tr>
+ <td><a class="plain" href="sdrf.html#hybridization_name">Hybridization Name</a></td>
+ <td><a class="plain" href="sdrf.html#array_design_ref">Array Design REF</a></td>
+ <td><a class="plain" href="sdrf.html#scan_name">Scan Name</a></td>
+ <td><a class="plain" href="sdrf.html#array_data_file">Array Data File</a></td>
+ <td><a class="plain" href="sdrf.html#protocol_ref">Protocol REF</a></td>
+ <td><a class="plain" href="sdrf.html#normalization_name">Normalization Name</a></td>
+ <td><a class="plain" href="sdrf.html#derived_array_data_file">Derived Array Data File</a></td>
+ <td><a class="plain" href="sdrf.html#derived_array_data_file">Factor Value [FactorOP]</a></td>
+ </tr>
+ <tr><td>Hyb 1</td><td>A-AFFY-33</td><td>Scan 1</td><td>Data1.CEL</td><td>Norm protocol name</td><td>Norm 1</td><td>Data1.CHP</td><td>liver</td></tr>
+ <tr><td>Hyb 2</td><td>A-AFFY-33</td><td>Scan 2</td><td>Data2.CEL</td><td>Norm protocol name</td><td>Norm 2</td><td>Data2.CHP</td><td>liver</td></tr>
+ <tr><td>Hyb 3</td><td>A-AFFY-33</td><td>Scan 3</td><td>Data3.CEL</td><td>Norm protocol name</td><td>Norm 3</td><td>Data3.CHP</td><td>kidney</td></tr>
+ <tr><td>Hyb 4</td><td>A-AFFY-33</td><td>Scan 4</td><td>Data4.CEL</td><td>Norm protocol name</td><td>Norm 4</td><td>Data4.CHP</td><td>kidney</td></tr>
+ </table>
+
+ <p class="text">In this case, the "Factor Value [FactorOP]"
+ column refers to an Experimental Factor named "FactorOP", with
+ Type "organism_part", from the IDF which would accompany this
+ SDRF. Blank lines may be included for legibility. Lines
+ beginning with the "#" symbol are treated as comments and
+ ignored. Note that the examples on this page only illustrate a
+ very minimal set of the available SDRF columns available. A full
+ listing of all supported column names can be found in these <a
+ href="sdrf.html">SDRF help notes</a>.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="datafiles">Data Files</a></div>
+
+ <p class="text">A variety of data file formats, produced by
+ several different scanner makes and models, are supported by the
+ ArrayExpress MAGE-TAB parser. A full list of supported formats
+ can be found in the <a href="datafiles.html">Tab2MAGE data file
+ documentation</a>. Note that the MAGE-TAB specification introduces
+ a new file format, that of the <em>data matrix</em>, for files which
+ contain data from multiple hybridizations. This new format is
+ discussed below.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="datamatrix">Data Matrices</a></div>
+
+ <p class="text">If you wish to represent data from more than one
+ hybridization, scan or normalization in a single data file, you
+ will need to reformat it as a MAGE-TAB Data Matrix. This is a
+ simplified format which allows data columns to be mapped to rows
+ in the SDRF file. The first header line of a Data Matrix file
+ describes this mapping, and the second lists the quantitation
+ types for each column (e.g. "log2 ratio"). The first column is
+ used to map the data rows to identifiers from the array design
+ used. Examples are shown here: </p>
+
+ <div align="center"><h4>Example two-color data matrix</h4></div>
+
+ <table class="example">
+ <tr><td>Hybridization REF</td><td>Hyb1</td><td>Hyb2</td><td>Hyb3</td><td>Hyb4</td><td>Hyb5</td><td>Hyb6</td></tr>
+ <tr><td>Reporter REF</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td><td>log2 ratio</td></tr>
+ <tr><td>Probe 1</td><td>0.27</td><td>0.43</td><td>0.32</td><td>0.12</td><td>0.54</td><td>0.28</td></tr>
+ <tr><td>Probe 2</td><td>1.67</td><td>1.46</td><td>1.91</td><td>1.49</td><td>1.50</td><td>1.89</td></tr>
+ <tr><td>Probe 3</td><td>0.78</td><td>0.69</td><td>0.91</td><td>0.99</td><td>0.75</td><td>0.80</td></tr>
+ </table>
+
+ <p class="text">In this example, six hybridizations from the
+ SDRF (Hyb1 - Hyb6) are being mapped to log2 ratio values. Each row
+ of data is mapped to a Reporter Identifier defined by the array
+ design (itself indicated in the SDRF file).</p>
+
+ <div align="center"><h4>Example Affymetrix data matrix</h4></div>
+
+ <table class="example">
+ <tr><td>Hybridization REF</td><td>Hyb1</td><td>Hyb1</td><td>Hyb2</td><td>Hyb2</td><td>Hyb3</td><td>Hyb3</td></tr>
+ <tr><td>CompositeElement REF</td><td>CELIntensity</td><td>CELStdev</td><td>CELIntensity</td><td>CELStdev</td><td>CELIntensity</td><td>CELStdev</td></tr>
+ <tr><td>Gene 1</td><td>22287.9</td><td>56.8</td><td>3222.9</td><td>111.1</td><td>9984.3</td><td>34.8</td></tr>
+ <tr><td>Gene 2</td><td>267.4</td><td>7.6</td><td>118.1</td><td>7.6</td><td>236.8</td><td>9.0</td></tr>
+ <tr><td>Gene 3</td><td>876.5</td><td>16.7</td><td>936.8</td><td>14.9</td><td>735.6</td><td>8.0</td></tr>
+ </table>
+
+ <p class="text">In this example, three hybridizations from the
+ SDRF file (Hyb1, Hyb2 and Hyb3) are being mapped to data
+ with two different quantitation types (CELIntensity,
+ CELStdev). Each row of data is mapped to a CompositeElement
+ Identifier (equivalent to MAGE CompositeSequence Identifiers)
+ defined by the array design.</p>
+
+ <p class="text">There are some limitations imposed by
+ ArrayExpress when submitting data in this format. Firstly, each
+ data matrix should correspond to hybridizations performed on a
+ single array design. Experiments using multiple array designs
+ should use one data matrix per design. Secondly, we rely on
+ there being an ordered and regular organisation of the columns:
+ first by hybridization, and then by quantitation type:</p>
+
+ <div align="center"><em>Correct:</em></div>
+
+ <table class="example">
+ <tr><td>Hybridization REF</td><td><font color="blue">Hyb1</font></td><td><font color="blue">Hyb1</font></td><td><font color="red">Hyb2</font></td><td><font color="red">Hyb2</font></td></tr>
+ <tr><td>Reporter REF</td><td><font color="blue">QT X</font></td><td><font color="red">QT Y</font></td><td><font color="blue">QT X</font></td><td><font color="red">QT Y</font></td></tr>
+ </table>
+
+ <br/>
+
+ <div align="center"><em>Wrong:</em></div>
+
+ <table class="example">
+ <tr><td>Hybridization REF</td><td><font color="blue">Hyb1</font></td><td><font color="red">Hyb2</font></td><td><font color="blue">Hyb1</font></td><td><font color="red">Hyb2</font></td></tr>
+ <tr><td>Reporter REF</td><td><font color="blue">QT X</font></td><td><font color="blue">QT X</font></td><td><font color="red">QT Y</font></td><td><font color="red">QT Y</font></td></tr>
+ </table>
+
+ <p class="text">If your processed data does not readily fall
+ into such a structure you may need to break the data up into
+ multiple data matrices, and use the SDRF file to represent the
+ relationships between them.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <p class="text">Please see the <a href="http://www.mged.org/mage-tab/">
+ MAGE-TAB specification</a> for further information and examples.</p>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Tue Jul 10 11:10:10 BST 2007 -->
+<!-- hhmts start -->
+Last modified: Mon Feb 25 18:04:42 GMT 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/magetab_subs.html b/docs/magetab_subs.html
new file mode 100644
index 0000000..fec7ca9
--- /dev/null
+++ b/docs/magetab_subs.html
@@ -0,0 +1,589 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>MAGE-TAB ArrayExpress submissions</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+ <td class="pagetitle">Submitting MAGE-TAB to ArrayExpress</td>
+ </tr>
+ </table>
+ </div>
+
+ <div CLASS="section">
+
+ <table class="layout">
+ <tr>
+ <td class="layout">
+
+ <p class="subhead">On this page:</p>
+
+ <ul>
+ <li><a href="#intro">Introduction</a></li>
+ <li><a href="#preparation">Preparing your submission</a></li>
+ <li><a href="#supporteddata">Supported data file types</a></li>
+ <li><a href="#submission">The MAGE-TAB submissions system</a></li>
+ <li><a href="#postsubs">After completing your experiment submission</a></li>
+ <li><a href="#magetabtips">Tips on creating a MAGE-TAB spreadsheet</a></li>
+ <li><a href="#realexamples">Example MAGE-TAB documents</a></li>
+ </ul>
+
+ <p class="subhead">See also:</p>
+
+ <ul>
+ <li><a href="magetab_docs.html">Constructing a MAGE-TAB document</a>, including:</li>
+ <ul>
+ <li><a href="idf.html">IDF detail</a></li>
+ <li><a href="sdrf.html">SDRF detail</a></li>
+ </ul>
+ <li><a href="datafiles.html">Supported data file formats</a></li>
+ </ul>
+ </td>
+ <td class="layout" valign="top">
+ <div CLASS="imghead">
+ <a href="http://www.ebi.ac.uk/arrayexpress/">
+ <img src="aelogo.png" border="0" alt="ArrayExpress logo">
+ </a>
+ </div>
+ </td>
+
+ </tr>
+ </table>
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="intro">Introduction</a></div>
+
+ <p class="text">MAGE-TAB is a new data format, designed by
+ members of the microarray community as a simplified standard
+ representation format for microarray data. MAGE-TAB documents
+ can be used to provide experimental annotations for data
+ submissions to the ArrayExpress repository database. MAGE-TAB is
+ a tabular format, easily edited in any spreadsheet-based
+ application (e.g., Microsoft Excel). The format is designed to
+ be able to describe the most common experimental designs,
+ including annotation required by the <acronym title="Minimum
+ Information About A Microarray Experiment">MIAME</acronym>
+ standard. MAGE-TAB is suitable for submitting both
+ single-channel (e.g. Affymetrix) and two-channel data. It is
+ somewhat related to the older Tab2MAGE format, but allows for
+ much greater flexibility in describing microarray
+ investigations.</p>
+
+ <p class="text">A typical MAGE-TAB document consists of two
+ parts, the <acronym title="Investigation Design
+ Format">IDF</acronym> and the <acronym title="Sample and Data
+ Relationship Format">SDRF</acronym>. The IDF contains overall
+ information about the investigation, including the title,
+ description, publication and contact details. It also contains
+ information on the protocols used. The SDRF component describes
+ the links between each sample step and the data acquisition and
+ analysis steps, linking each ultimately to the data files.</p>
+
+ <p class="text">MAGE-TAB can be used to submit microarray data
+ to ArrayExpress in one of two ways. For most users, we recommend
+ using our <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">online
+ MAGE-TAB submission system</a> to create a template document,
+ which should then be filled in by the user and uploaded to our
+ web site alongside their data files.</p>
+
+ <p class="text">For advanced users who wish to generate MAGE-ML
+ from their MAGE-TAB documents, we provide a set of downloadable
+ scripts to help with document conversion:</p>
+
+ <ul>
+ <li><a href="../index.html#magetab">MAGE-TAB to MAGE-ML</a></li>
+ <li><a href="../index.html#tabconverter">Tab2MAGE to MAGE-TAB</a></li>
+ <li><a href="../index.html#tab2mage">Tab2MAGE to MAGE-ML</a></li>
+ </ul>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="preparation">Preparing your submission</a></div>
+
+ <p class="text">Our MAGE-TAB submissions page is here: <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">ArrayExpress
+ MAGE-TAB submissions</a>.</p>
+
+ <p class="text">There is general information about ArrayExpress
+ submissions, accession number assignment and data privacy
+ here: <a
+ href="http://www.ebi.ac.uk/microarray/submissions.html">ArrayExpress
+ submissions help page</a> </p>
+
+ <p>To submit data using MAGE-TAB you will need to provide the
+ following information:</p>
+
+ <ul>
+
+ <li class="text">Protocols describing your sample growth/treatment and
+ processing steps.</li>
+
+ <li class="text">The ArrayExpress accession number of the
+ array design (platform) used - <a
+ href="http://www.ebi.ac.uk/miamexpress/help/array_designs.html">more
+ information on array designs here</a>. </li>
+
+ <li class="text">A description of your experiment.</li>
+
+ <li class="text">Details of all the samples used in your experiment.</li>
+
+ <li class="text">Raw data files (e.g. .CEL, .gpr, .txt) for each
+ hybridization you performed.</li>
+
+ <li class="text">Per-hyb normalized data and/or a <a
+ href="magetab_docs.html#datamatrix">Data Matrix</a> file
+ (submission of processed data is required for full <acronym
+ title="Minimum Information About A Microarray
+ Experiment">MIAME</acronym> compliance, but we will still
+ accept your submission if this is not available).</li>
+
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="supporteddata">Supported data file types</a></div>
+
+ <p class="text">This table shows which files you can
+ submit using MAGE-TAB. Do not edit your raw data files. There
+ are definitions and more information on how to format normalized
+ and combined data here:</p>
+
+ <ul>
+ <li><a href="datafiles.html">Supported raw data file formats</a></li>
+ <li><a href="magetab_docs.html#datamatrix">MAGE-TAB Data Matrix format</a></li>
+ </ul>
+
+ <table class="information" summary="This table shows which files you can submit using MAGE-TAB">
+ <tr>
+ <th>Technology</th>
+ <th>Data type </th>
+ <th>File format</th>
+ <th>Quantity</th>
+ </tr>
+ <tr>
+ <td>Affymetrix</td>
+ <td>Raw</td>
+ <td>CEL plus EXP </td>
+ <td>1 CEL per hybridization - REQUIRED*<br />1 EXP per hybridization - optional</td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td>Normalized</td>
+ <td>CHP and/or .txt </td>
+ <td>1 or more CHP and/or .txt per hybridization</td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td>Combined "Data Matrix" file </td>
+ <td>.txt</td>
+ <td>1 or more .txt per experiment</td>
+ </tr>
+ <tr>
+ <td>Other</td>
+ <td>Raw</td>
+ <td>.gpr or .txt </td>
+ <td>1 or more .gpr or .txt per hybridization - REQUIRED*</td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td>Normalized</td>
+ <td>.txt</td>
+ <td>1 or more .txt per hybridization</td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td>Combined "Data Matrix" file</td>
+ <td>.txt</td>
+ <td>1 or more .txt per experiment</td>
+ </tr>
+ </table>
+
+ <p>* Raw files are required for <acronym title="Minimum
+ Information About A Microarray Experiment">MIAME</acronym>
+ compliance, but if you really cannot provide them and you have
+ processed data instead, then we can still accept the MAGE-TAB
+ submission.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="submission">The MAGE-TAB submissions system</a></div>
+
+ <p class="text">Once you have finished preparing your
+ MAGE-TAB document, please upload it with your data files to the
+ ArrayExpress <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">MAGE-TAB
+ submissions website</a>.</p>
+
+ <p class="text">To submit data using this website:</p>
+
+ <ol>
+
+ <li class="text">Login or create a new user account if this
+ is your first MAGE-TAB submission (note - previous Tab2MAGE
+ submitters may re-use their old account).</li>
+
+ <li class="text">Click 'Create experiment' to start your submission.</li>
+
+ <li class="text">Enter your experiment name. If you want to
+ generate a template spreadsheet then select one or more
+ terms from each of the lists provided to describe your
+ experiment (optional). Click 'Save experiment'.</li>
+
+ <li class="text">Click 'Generate template' if you want to
+ use the template (optional).</li>
+
+ <li class="text">Click 'Upload files' then upload your
+ completed spreadsheet and one or more zipped archives
+ containing all your data files. If you are using separate
+ IDF and SDRF documents, include your SDRF file in your
+ zipped data file archive.</li>
+
+ <li class="text">Click 'Submit experiment' to send this submission to us
+ for curation.</li>
+
+ </ol>
+
+ <p class="text">If you have any questions please email the curation team at
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>" + aname + "<" + "/a>")
+ //-->
+ </script>.
+ If you are emailing about a specific submission tell us your
+ username and experiment name in the email.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="postsubs">After completing your experiment submission</a></div>
+
+ <ul>
+
+ <li class="text"> You will receive a immediate confirmation
+ email from the MAGE-TAB submission system saying that your
+ experiment has been submitted.</li>
+
+ <li class="text">Your submission will be put into curation
+ status and locked so that you cannot edit it. If you need to
+ edit the submission after this time please email us at
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>" + aname + "<" + "/a>")
+ //-->
+ </script>.
+ </li>
+
+ <li class="text"> The curation team will review your
+ submission and will email you with any questions. </li>
+
+ <li class="text">We will send you an accession number when all
+ the required information has been provided.</li>
+
+ <li class="text"> We will load your experiment into
+ ArrayExpress and provide you with a reviewer login for viewing
+ the data before it is made public. </li>
+
+ <li class="text">To check the status of your experiment
+ submission email us at
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>" + aname + "<" + "/a>")
+ //-->
+ </script>.
+ and remember to include your username and experiment name so
+ that we can identify your submission. </li>
+
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="magetabtips">Tips on creating a MAGE-TAB spreadsheet</a></div>
+
+ <p class="text">In most cases the MAGE-TAB document templates
+ generated by our <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">MAGE-TAB
+ submissions website</a> should help to get you started. To
+ create a template you will need to create a new user account
+ and start a new experiment submission. The system then asks you
+ for a few basic details concerning your experiment and generates
+ a template document which you can download and edit on your
+ computer. If you have submitted MAGE-TAB to us before you can
+ skip these steps and re-use your old template.</p>
+
+ <p class="text">In some cases our standard templates may be too
+ simple to capture all the detail of your experiment. To describe
+ experiments which do not fit into our templates, the full
+ expressivity of MAGE-TAB can be used instead. Please see <a
+ href="magetab_docs.html">these MAGE-TAB help notes</a> for
+ further information on what is possible with MAGE-TAB, or read
+ the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+ specification document</a>. It is hoped that our template
+ generation system will suffice for most users.</p>
+
+ <p class="text">Here are some general tips on submitting
+ MAGE-TAB documents to ArrayExpress:</p>
+
+ <ul>
+
+ <li class="text">The templates generated by our system combine
+ the IDF and SDRF components into a single document, to further
+ simplify the process of submitting your data. If you prefer to
+ create separate IDF and SDRF documents, these can also be
+ uploaded to the submission web site.</li>
+
+ <li class="text">In the 'Experiment Description' part of the
+ IDF section please clearly explain what you did in your
+ experiment - this will help the curation team to check and
+ process your spreadsheet. </li>
+
+ <li class="text">Enter an estimated release date in the
+ 'Public Release Date' part of the IDF section. We will set your
+ data to be made public on this date but you can ask us to
+ change this later.</li>
+
+ <li class="text">When describing your protocols in the IDF
+ section, give each of your protocols a unique identifier in
+ the 'Protocol Name' row, e.g. 'My protocol 1'. Use these names
+ in the SDRF section to refer to the relevant protocols.</li>
+
+ <li class="text">You may re-use previously submitted protocols
+ by entering their accession numbers directly into the
+ 'Protocol Name' IDF row. You may retrieve these accessions
+ using the ArrayExpress web page. If your protocols have not
+ yet been loaded into ArrayExpress, please contact the
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>curators<" + "/a>")
+ //-->
+ </script>
+ to obtain the accession numbers.
+ </li>
+
+ <li class="text">The SDRF section is a table containing all
+ the information about the samples you used and the
+ hybridizations you performed. It must include the names of the
+ data files that were produced for each hybridization.</li>
+
+ <li class="text">For most two-channel experiments the SDRF
+ section will contain two lines for each hybridization to
+ represent the two channels. Single-channel experiments usually
+ have one line per hybridization.</li>
+
+ <li class="text">The number of 'Characteristics' columns in
+ the SDRF section will depend on how much information you have
+ about each of your samples. If you are using a template
+ spreadsheet some suggested columns will be included but you
+ can add or remove 'Characteristics' and 'Factor Value' columns
+ as required.</li>
+
+ </ul>
+
+ <p class="text">For further help with MAGE-TAB document
+ preparation, including the definitions of all the available
+ fields, please see the following pages:</p>
+
+ <ul>
+ <li><a href="magetab_docs.html">MAGE-TAB overview</a></li>
+ <li><a href="idf.html">IDF detail</a></li>
+ <li><a href="sdrf.html">SDRF detail</a></li>
+ </ul>
+
+ <p class="text">For further help with MAGE-TAB submissions to ArrayExpress, please contact the
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "?subject=MAGE-TAB submission'>ArrayExpress curators<" + "/a>")
+ //-->
+ </script>
+ </p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="realexamples">Real-world Examples</a></div>
+
+ <p class="text">Below is a selection of some of the real-world
+ curated MAGE-TAB documents, created from Tab2MAGE spreadsheets
+ submitted to ArrayExpress over the past few months:</p>
+
+ <table class="information">
+
+ <tr>
+ <th>IDF</th>
+ <th>SDRF</th>
+ <th>Experiment name</th>
+ <th>Platform</th>
+ <th>Link to experiment</th>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-16.idf">E-TABM-16.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-16_sdrf.txt">E-TABM-16_sdrf.txt</a></td>
+ <td>FDA-CDER MTS RNA reagent cross platform test</td>
+ <td>Multiple platforms</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-16">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-18.idf">E-TABM-18.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-18_sdrf.txt">E-TABM-18_sdrf.txt</a></td>
+ <td>Transcription profiling of 35 different Arabidopsis thaliana ecotypes</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-18">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-22.idf">E-TABM-22.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-22_sdrf.txt">E-TABM-22_sdrf.txt</a></td>
+ <td>Transcription profiling of human lung cancers and lung cancer cell lines miRNA expression</td>
+ <td>One-channel custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-22">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-33.idf">E-TABM-33.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-33_sdrf.txt">E-TABM-33_sdrf.txt</a></td>
+ <td>Transcription profiling of zebrafish development</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-33">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-35.idf">E-TABM-35.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-35_sdrf.txt">E-TABM-35_sdrf.txt</a></td>
+ <td>Comparative genomic hybridization of 25 Coxiella burnetii isolates relative to the Nine Mile (RSA493) reference isolate</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-35">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-54.idf">E-TABM-54.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-54_sdrf.txt">E-TABM-54_sdrf.txt</a></td>
+ <td>Comparative genome hybridization of 137 Bordetella pertussis strains</td>
+ <td>Two-color custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-54">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-66.idf">E-TABM-66.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-66_sdrf.txt">E-TABM-66_sdrf.txt</a></td>
+ <td>Transcription profiling of normal and malignant human breast epithelial cells</td>
+ <td>Multiple platforms</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-66">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-70.idf">E-TABM-70.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-70_sdrf.txt">E-TABM-70_sdrf.txt</a></td>
+ <td>Transcription profiling of human cell lines treated with cytochalasin D and nocodazole with the aim of characterising tetraploid clones</td>
+ <td>Agilent</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-70">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-102.idf">E-TABM-102.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-102_sdrf.txt">E-TABM-102_sdrf.txt</a></td>
+ <td>Transcription profiling of wild type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide over time</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-102">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-134.idf">E-TABM-134.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-134_sdrf.txt">E-TABM-134_sdrf.txt</a></td>
+ <td>WGA-LCM and Genomewide Survey of Lung Cancer</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-134">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-136.idf">E-TABM-136.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-136_sdrf.txt">E-TABM-136_sdrf.txt</a></td>
+ <td>Transcription profiling of human and chimpanzee heart, brain, testis and lymphblastoid cell lines to study functionality of intergenic transcription</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-136">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-140.idf">E-TABM-140.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-140_sdrf.txt">E-TABM-140_sdrf.txt</a></td>
+ <td>Chromatin immunoprecipitation (ChIP-chip) of human erythroleukemia cell line K-562 with anti-histone antibodies using an ENCODE array</td>
+ <td>Two-color custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-140">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-TABM-163.idf">E-TABM-163.idf</a></td>
+ <td><a href="../examples/magetab/real/E-TABM-163_sdrf.txt">E-TABM-163_sdrf.txt</a></td>
+ <td>Transcription profiling of murine presomitic mesoderms of 17 samples at various time points to identify cyclic genes of the mouse segmentation clock</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-163">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/E-MEXP-880.idf">E-MEXP-880.idf</a></td>
+ <td><a href="../examples/magetab/real/E-MEXP-880_sdrf.txt">E-MEXP-880_sdrf.txt</a></td>
+ <td>Methylation profiling of normal and cancerous breast cells from human patients and cell lines in a 125 kB region of the HOXA cluster</td>
+ <td>Two-color custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-MEXP-880">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/magetab/real/solexa_example.idf">solexa_example.idf</a></td>
+ <td><a href="../examples/magetab/real/solexa_example.sdrf.txt">solexa_example.sdrf.txt</a></td>
+ <td>Example MAGE-TAB documents for a hypothetical high-throughput sequencing submission (e.g. Solexa, 454)</td>
+ <td>Solexa</td>
+ <td>Not available</td>
+ </tr>
+ </table>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <hr>
+ <a href="http://sourceforge.net">
+
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Thu Nov 24 14:18:20 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Feb 26 14:12:35 GMT 2008
+<!-- hhmts end -->
+ </body>
+</html>
\ No newline at end of file
diff --git a/docs/magetab_vs_tab2mage.html b/docs/magetab_vs_tab2mage.html
new file mode 100644
index 0000000..608243b
--- /dev/null
+++ b/docs/magetab_vs_tab2mage.html
@@ -0,0 +1,202 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>MAGE-TAB and Tab2MAGE: a comparison</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="270"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a> <a href="../index.html#magetab"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+ <td class="pagetitle">MAGE-TAB and Tab2MAGE: a comparison</td>
+ </tr>
+ </table>
+ </div>
+
+ <div CLASS="section">
+
+ <table class="layout">
+ <tr>
+ <td class="layout">
+
+ <p class="subhead">On this page:</p>
+
+ <ul>
+ <li><a href="#intro">Introduction</a></li>
+ <li><a href="#proscons">Pros and Cons of MAGE-TAB and Tab2MAGE</a></li>
+ </ul>
+
+ <p class="subhead">See also:</p>
+
+ <ul>
+ <li><a href="aesubs.html">Submitting Tab2MAGE to ArrayExpress</a>, including:</li>
+ <ul>
+ <li><a href="spreadsheet.html">Constructing a Tab2MAGE spreadsheet</a></li>
+ <li><a href="detail.html">Tab2MAGE spreadsheet detail</a></li>
+ </ul>
+ <li><a href="magetab_subs.html">Submitting MAGE-TAB to ArrayExpress</a>, including:</li>
+ <ul>
+ <li><a href="magetab_docs.html">Constructing a MAGE-TAB document</a></li>
+ <li><a href="idf.html">IDF detail</a></li>
+ <li><a href="sdrf.html">SDRF detail</a></li>
+ </ul>
+ </ul>
+ </td>
+ <td class="layout" valign="top">
+ <div CLASS="imghead">
+ <a href="http://www.ebi.ac.uk/arrayexpress/">
+ <img src="aelogo.png" border="0" alt="ArrayExpress logo">
+ </a>
+ </div>
+ </td>
+
+ </tr>
+ </table>
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="intro">Introduction</a></div>
+
+ <p class="text">MAGE-TAB is a new data format, designed by
+ members of the microarray community as a simplified standard
+ representation format for microarray data. In contrast, Tab2MAGE
+ is an older, simplified tabular format developed by ArrayExpress
+ some years ago. In most cases these two formats can be used
+ interchangeably, but there are a number of situations in which
+ one or the other may have an advantage. As a rule, anything that
+ can be expressed in Tab2MAGE can also be described in MAGE-TAB,
+ but <em>not</em> vice versa. This is because MAGE-TAB extends
+ the ideas behind Tab2MAGE to give it a far greater flexibility
+ and scope.</p>
+
+ <p class="text">We have created a <a
+ href="http://tab2mage.sourceforge.net/index.html#tabconverter">Tab2MAGE
+ to MAGE-TAB converter tool</a> which is now <a
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=226041">available
+ for download here</a>.</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="proscons">Pros and Cons of MAGE-TAB vs. Tab2MAGE</a></div>
+
+ <p class="text">Below is a table summarising the strengths and
+ weaknesses of these two tabular formats:</p>
+
+ <table class="information">
+ <tr>
+ <th></th><th>Tab2MAGE</th><th>MAGE-TAB</th>
+ </tr>
+ <tr>
+ <th>Simpler to use for array-based gene expression data <a href="#four">(4)</a></th>
+ <td><font color="green">Yes</font></td>
+ <td><font color="red">No</font></td>
+ </tr>
+ <tr>
+ <th>Can be annotated with ontology term accession and source <a href="#one">(1)</a></th>
+ <td><font color="red">No</font></td>
+ <td><font color="green">Yes</font></td>
+ </tr>
+ <tr>
+ <th>Supports metabolomic and proteomic data <a href="#two">(2)</a></th>
+ <td><font color="red">No</font></td>
+ <td><font color="green">Yes</font></td>
+ </tr>
+ <tr>
+ <th>Supports high-throughput sequencing data <a href="#two">(2)</a></th>
+ <td><font color="red">No</font></td>
+ <td><font color="green">Yes</font></td>
+ </tr>
+ <tr>
+ <th>Experiment must follow a standard, predefined scheme <a href="#three">(3)</a></th>
+ <td><font color="green">Yes</font></td>
+ <td><font color="red">No</font></td>
+ </tr>
+ <tr>
+ <th>Column ordering is flexible <a href="#four">(4)</a></th>
+ <td><font color="green">Yes</font></td>
+ <td><font color="red">No</font></td>
+ </tr>
+ <tr>
+ <th>Columns with the same name can appear more than once <a href="#four">(4)</a></th>
+ <td><font color="red">No</font></td>
+ <td><font color="green">Yes</font></td>
+ </tr>
+ <tr>
+ <th>Tries to "Do What You Mean" <a href="#four">(4)</a></th>
+ <td><font color="green">Yes</font></td>
+ <td><font color="red">No</font></td>
+ </tr>
+ <tr>
+ <th>Forces consistency of internal references <a href="#four">(4)</a></th>
+ <td><font color="red">No</font></td>
+ <td><font color="green">Yes</font></td>
+ </tr>
+ <tr>
+ <th>Supported by other members of the microarray community</th>
+ <td><font color="red">No</font></td>
+ <td><font color="green">Yes</font></td>
+ </tr>
+ </table>
+
+ <p class="text"><a name="one" class="subhead">1.</a> While
+ Tab2MAGE allows (and encourages) the use of ontology terms to
+ annotate your data, it provides no means to include the source
+ of these terms, or formal identifiers/accessions. MAGE-TAB
+ corrects this by adding "Term Source REF" and "Term Accession
+ Number" fields to help capture this information.</p>
+
+ <p class="text"><a name="two" class="subhead">2.</a> Tab2MAGE
+ was developed solely to describe microarray experiments, and as
+ such the concept of "hybridization" is central. MAGE-TAB has
+ been extended to encompass other kinds of biological assay,
+ enabling the user to describe data from multiple technologies
+ (e.g., proteomics, metabolomics, high-throughput sequencing) in
+ a single document.</p>
+
+ <p class="text"><a name="three" class="subhead">3.</a> Tab2MAGE
+ assumes that your experiment is a standard microarray
+ hybridization study, and that your sample processing followed a
+ defined series of steps: Source -> Sample -> Extract -> Labeled
+ Extract -> Hybridization (for ChIP-chip experiments an
+ Immunoprecipitate step may also be used). In contrast, MAGE-TAB
+ allows you to create sample processing schemes of arbitrary
+ complexity.</p>
+
+ <p class="text"><a name="four" class="subhead">4.</a> While
+ increasing the flexibility of the tabular format, MAGE-TAB has
+ sacrificed some ease-of-use. For example, while the columns in a
+ Tab2MAGE spreadsheet can be ordered in whichever way suits you,
+ MAGE-TAB constrains this order to follow the steps performed in
+ the experiment. That is, columns in a MAGE-TAB SDRF must be
+ ordered left-to-right in chronological order of
+ execution. MAGE-TAB also forces you to be explicit about all the
+ steps of your experiment, while Tab2MAGE will attempt to infer
+ what was done from a minimal subset of spreadsheet
+ columns. Finally, there are several places in a MAGE-TAB SDRF
+ document which may refer back to elements defined in the IDF,
+ and these must be internally consistent. No such requirement is
+ made for Tab2MAGE documents.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <hr>
+ <a href="http://sourceforge.net">
+
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Thu Nov 24 14:18:20 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Thu Feb 7 15:38:30 GMT 2008
+<!-- hhmts end -->
+ </body>
+</html>
\ No newline at end of file
diff --git a/docs/modules.html b/docs/modules.html
new file mode 100644
index 0000000..45f3697
--- /dev/null
+++ b/docs/modules.html
@@ -0,0 +1,282 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Tab2MAGE and MAGE-TAB Perl modules</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css">
+ </head>
+
+ <body>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="270"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a> <a href="../index.html#magetab"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+ <td class="pagetitle">Perl module documentation</td>
+ </tr>
+ </table>
+
+ <div CLASS="section">
+
+ <table class="layout">
+ <tr>
+ <td class="layout">
+
+ <ul>
+ <li><a href="#config">Configuration</a></li>
+ <li><a href="#common">Common modules</a></li>
+ <li><a href="#magetab">MAGE-TAB parsing</a></li>
+ <li><a href="#expt_check">Experiment Checker modules</a></li>
+ <li><a href="#datafile">Data file parsing modules</a></li>
+ <li><a href="#affy">Affymetrix file parsing modules</a></li>
+ <li><a href="#visualize">Experiment design graph visualization</a></li>
+ <li><a href="#arraymage">Array design MAGE-ML generation</a></li>
+ <li><a href="#tracking">Submissions tracking database</a></li>
+ <li><a href="#tab2mage">Tab2MAGE column headings</a></li>
+ </ul>
+
+ </td>
+ <td class="layout" valign="top">
+ <div CLASS="imghead">
+ <a href="http://www.ebi.ac.uk/arrayexpress/">
+ <img src="aelogo.png" border="0" alt="ArrayExpress logo">
+ </a>
+ </div>
+ </td>
+
+ </tr>
+ </table>
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="config">Configuration</a></div>
+
+ <p class="text">Configuration for all the modules is handled by
+ a single class, which generates a Config::YAML object that is
+ then imported into each module that needs it. Documentation on
+ configuration options is available here:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Curator/Config.html">ArrayExpress::Curator::Config</a></li>
+ </ul>
+
+ <p class="text">The actual configuration file to be used can be
+ set by editing the installed version of this module ("site
+ config"); alternatively the Config.yml file included with the
+ Tab2MAGE package may be edited directly ("module config"). For
+ convenience, the configuration system will also look for a file
+ named ".tab2mage.conf" in the user's home directory and use that
+ if found ("user config"). All found configuration files are
+ parsed, with priority for each specified option running in this
+ order: user config; site config; module config. </p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="common">Common modules</a></div>
+
+ <p class="text">The following modules provide a series of
+ miscellaneous functions used by many of the package
+ subsystems:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Curator/Logger.html">ArrayExpress::Curator::Logger</a></li>
+ <li><a href="ArrayExpress/Curator/Common.html">ArrayExpress::Curator::Common</a></li>
+ <li><a href="ArrayExpress/Curator/Database.html">ArrayExpress::Curator::Database</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="magetab">MAGE-TAB parsing</a></div>
+
+ <p class="text">The core MAGE-TAB parsing subsystem consists of
+ the following modules:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/MAGETAB.html">ArrayExpress::MAGETAB</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="expt_check">Experiment Checker modules</a></div>
+
+ <p class="text">The Experiment Checker subsystem consists of a
+ core ExperimentChecker class which is subclassed to provide
+ support for MAGE-TAB, Tab2MAGE, MIAMExpress or stand-alone
+ checks:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Curator/ExperimentChecker.html">ArrayExpress::Curator::ExperimentChecker</a></li>
+ <li><a href="ArrayExpress/MAGETAB/Checker.html">ArrayExpress::MAGETAB::Checker</a></li>
+ <li><a href="ArrayExpress/Curator/Validate.html">ArrayExpress::Curator::Validate</a></li>
+ <li><a href="ArrayExpress/Curator/MIAMExpress.html">ArrayExpress::Curator::MIAMExpress</a></li>
+ <li><a href="ArrayExpress/Curator/Standalone.html">ArrayExpress::Curator::Standalone</a></li>
+ </ul>
+
+ <p class="text">The following helper modules provide functions
+ used by these checker modules:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Curator/Report.html">ArrayExpress::Curator::Report</a></li>
+ <li><a href="ArrayExpress/Curator/Entrez_list.html">ArrayExpress::Curator::Entrez_list</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="datafile">Data file parsing modules</a></div>
+
+ <p class="text">The core data file parsing subsystem consists of
+ a main Datafile class, a Parser class to handle MAGE-ML
+ generation, and helper classes to handle quantitation type and
+ array design information:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Datafile.html">ArrayExpress::Datafile</a></li>
+ <li><a href="ArrayExpress/Datafile/Parser.html">ArrayExpress::Datafile::Parser</a></li>
+ <li><a href="ArrayExpress/Datafile/QT_list.html">ArrayExpress::Datafile::QT_list</a></li>
+ <li><a href="ArrayExpress/Datafile/ArrayDesign.html">ArrayExpress::Datafile::ArrayDesign</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="affy">Affymetrix file parsing modules</a></div>
+
+ <p class="text">Affymetrix data files are handled as special
+ cases throughout the <a href="#datafile">Datafile
+ subsystem</a>. The following modules provide comprehensive
+ support for reading most commonly-used Affymetrix gene
+ expression, SNP, and resequencing data files:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Datafile/Affymetrix.html">ArrayExpress::Datafile::Affymetrix</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CDFFactory.html">ArrayExpress::Datafile::Affymetrix::CDFFactory</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CELFactory.html">ArrayExpress::Datafile::Affymetrix::CELFactory</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CHPFactory.html">ArrayExpress::Datafile::Affymetrix::CHPFactory</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/EXPFactory.html">ArrayExpress::Datafile::Affymetrix::EXPFactory</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/Parser.html">ArrayExpress::Datafile::Affymetrix::Parser</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CDF.html">ArrayExpress::Datafile::Affymetrix::CDF</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.html">ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.html">ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CEL.html">ArrayExpress::Datafile::Affymetrix::CEL</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CEL/CELv3.html">ArrayExpress::Datafile::Affymetrix::CEL::CELv3</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CEL/CELv4.html">ArrayExpress::Datafile::Affymetrix::CEL::CELv4</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CHP.html">ArrayExpress::Datafile::Affymetrix::CHP</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.html">ArrayExpress::Datafile::Affymetrix::CHP::CHPv12</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.html">ArrayExpress::Datafile::Affymetrix::CHP::CHPv13</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.html">ArrayExpress::Datafile::Affymetrix::CHP::CHPv8</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.html">ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.html">ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP</a></li>
+ <li><a href="ArrayExpress/Datafile/Affymetrix/EXP.html">ArrayExpress::Datafile::Affymetrix::EXP</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="visualize">Experiment design graph visualization</a></div>
+
+ <p class="text">Limited support is provided for visualizing the
+ experiment design graphs represented by MAGE-TAB and Tab2MAGE
+ documents. For more detailed visualization of MAGE-ML documents, the <a
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=163063">MAGE-ML
+ Visualize</a> script is recommended.</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Curator/Visualize.html">ArrayExpress::Curator::Visualize</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="arraymage">Array design MAGE-ML generation</a></div>
+
+ <p class="text">Due to their increasingly large size, generating
+ full MAGE-ML documents for the array designs in use today often
+ taxes the resources of the average desktop computer. The
+ ArrayMAGE modules were created to remove the memory and CPU
+ overhead of this process, allowing them to support arbitrarily
+ large array designs:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/ArrayMAGE.html">ArrayExpress::ArrayMAGE</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="tracking">Submissions tracking database</a></div>
+
+ <p class="text">A data submissions tracking database subsystem
+ is included in the Tab2MAGE package. This database drives the
+ main ArrayExpress MAGE-TAB and Tab2MAGE submissions web pages,
+ the automated checking of submitted experiments and export to
+ MAGE-ML, and automated import of data from NCBI GEO (see the <a
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=243607">GEOImport
+ package</a>). It also provides back-end support for data quality
+ metrics calculation against the full ArrayExpress database.</p>
+
+ <ul>
+ <li><a href="ArrayExpress/AutoSubmission/DB.html">ArrayExpress::AutoSubmission::DB</a></li>
+ <li><a href="ArrayExpress/AutoSubmission/Creator.html">ArrayExpress::AutoSubmission::Creator</a></li>
+ <li><a href="ArrayExpress/AutoSubmission/WebForm.html">ArrayExpress::AutoSubmission::WebForm</a></li>
+ <li><a href="ArrayExpress/AutoSubmission/Spreadsheet.html">ArrayExpress::AutoSubmission::Spreadsheet</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="tab2mage">Tab2MAGE column headings</a></div>
+
+ <p class="text">Tab2MAGE spreadsheet parsing is documented,
+ albeit sparsely, here:</p>
+
+ <ul>
+ <li><a href="ArrayExpress/Curator/MAGE/Definitions.html">ArrayExpress::Curator::MAGE::Definitions</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Tue Apr 29 11:57:20 BST 2008 -->
+<!-- hhmts start -->
+Last modified: Wed Apr 30 15:53:04 BST 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/parse_affy.html b/docs/parse_affy.html
new file mode 100644
index 0000000..28940e4
--- /dev/null
+++ b/docs/parse_affy.html
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>parse_affy.pl - an example Affymetrix data file parsing
+script.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>parse_affy.pl - an example Affymetrix data file parsing
+script.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ parse_affy.pl -i <Affymetrix data file (CEL or CHP)>
+ -o <tab-delimited data matrix output file>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script is a 'toy' script to illustrate the use of the
+ArrayExpress::Datafile::Affymetrix parsing module supplied with the
+Tab2MAGE distribution. It may nonetheless be useful in
+circumstances where other Affymetrix parsers fail. The script reads
+data files (CEL and CHP) in both old (version 3, GDAC) and new
+(version 4, XDA/GCOS) formats. The script then outputs any
+combination of the following: (a) a tab-delimited data matrix, (b)
+a set of MAGE identifiers making up the DesignElementDimension of
+the matrix, and/or (c) a set of identifiers comprising the
+QuantitationTypeDimension of the data matrix. Please see <a href=
+"ArrayExpress/Datafile/Affymetrix.html">the
+ArrayExpress::Datafile::Affymetrix manpage</a> for more information
+on the Affymetrix parsing API.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2di_input_filename" id=
+"item__2di_input_filename"><strong>-i</strong> <code>input
+filename</code></a></strong></dt>
+<dd>
+<p>Affymetrix CEL or CHP data file to parse.</p>
+</dd>
+<dt><strong><a name="item__2do_output_filename" id=
+"item__2do_output_filename"><strong>-o</strong> <code>output
+filename</code></a></strong></dt>
+<dd>
+<p>File to be used for output (tab-delimited data).</p>
+</dd>
+<dt><strong><a name="item__2dd_ded_filename" id=
+"item__2dd_ded_filename"><strong>-d</strong> <code>DED
+filename</code></a></strong></dt>
+<dd>
+<p>File to which the DesignElementDimension is to be written
+(optional).</p>
+</dd>
+<dt><strong><a name="item__2dq_qtd_filename" id=
+"item__2dq_qtd_filename"><strong>-q</strong> <code>QTD
+filename</code></a></strong></dt>
+<dd>
+<p>File to which the QuantitationTypeDimension is to be written
+(optional).</p>
+</dd>
+<dt><strong><a name="item__2dc_input_cdf_filename" id=
+"item__2dc_input_cdf_filename"><strong>-c</strong> <code>input CDF
+filename</code></a></strong></dt>
+<dd>
+<p>Affymetrix library CDF file to use for CHP file parsing.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/sdrf.html b/docs/sdrf.html
new file mode 100644
index 0000000..ce32768
--- /dev/null
+++ b/docs/sdrf.html
@@ -0,0 +1,941 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>SDRF notes</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></a></td>
+ <td class="pagetitle">SDRF help notes</td>
+ </tr>
+ </table>
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="overall">Introduction</a></div>
+
+ <p class="text">This page provides detailed help on the
+ Sample and Data Relationship Format (SDRF), and lists all the available
+ tags for use in MAGE-TAB SDRF documents. For an overview of the
+ MAGE-TAB submission process, and examples of both IDF and SDRF,
+ please see these <a href="magetab_subs.html">submission help
+ notes</a> and <a href="magetab_docs.html">MAGE-TAB
+ overview</a>. For more detail on the MAGE-TAB format, please see
+ the <a href="http://www.mged.org/mage-tab/">MAGE-TAB
+ specification</a>.</p>
+
+ <p class="text">MAGE-TAB documents may optionally include
+ information on the sources of any controlled terms used,
+ providing users with the ability to link to ontologies,
+ databases or other sources of controlled vocabularies to
+ describe their experiment. These so-called "Term Sources" are
+ defined in the IDF and may be used throughout the document. If
+ they are not used, then all controlled vocabulary terms are
+ assumed to be user-defined.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="sdrf_layout">Overall SDRF layout</a></div>
+
+ <p class="text">The SDRF file consists of a table in which each
+ hybridization channel is represented by a row, and columns
+ represent the steps of the experiment. In contrast to the
+ Tab2MAGE format, the ordering of these columns is important, and
+ should read left-to-right in chronological order. The overall
+ organization of this table is shown below. To get more detail on
+ the properties of each section, click on the relevant box:</p>
+
+ <map name="sdrf_layout_Map">
+ <area shape="rect" alt="Derived Array Data File" coords="557,402,716,493" href="#derivedarraydatafile_section"/>
+ <area shape="rect" alt="Scan" coords="86,402,245,493" href="#scan_section"/>
+ <area shape="rect" alt="Array Data File" coords="243,402,402,493" href="#arraydatafile_section"/>
+ <area shape="rect" alt="Normalization" coords="400,402,559,493" href="#normalization_section"/>
+ <area shape="rect" alt="Labeled Extract" coords="324,0,434,94" href="#labeledextract_section"/>
+ <area shape="rect" alt="Extract" coords="216,0,326,94" href="#extract_section"/>
+ <area shape="rect" alt="Sample" coords="108,0,218,94" href="#sample_section"/>
+ <area shape="rect" alt="Source" coords="0,0,110,94" href="#source_section"/>
+ <area shape="rect" alt="Hybridization" coords="268,168,428,281" href="#hybridization_section"/>
+ </map>
+ <div align="center"><img src="sdrf_layout.png" width="716" height="493" border="0" alt="SDRF layout" usemap="#sdrf_layout_Map"></div>
+
+ <p class="text">An experiment can be described in terms of a
+ graph, in which the graph nodes correspond to materials or data
+ files, and the graph edges (or arcs) correspond to
+ treatments. Each block in the diagram above would be represented
+ as a node in such a graph, with the treatments (protocols)
+ acting as edges. Each node block starts with a "Name" or "File"
+ column (e.g. "Extract Name", "Array Data File") identifying the
+ type of node, followed by a set of attribute columns. Each block
+ is separated from its predecessor by "<a
+ href="#protocol_section">Protocol REF</a>" graph edge
+ columns containing references to the "Protocol Name" values
+ defined in the IDF.</p>
+
+ <p class="text">A further set of columns is used to specify the
+ values for the variables ("experimental factors") within the
+ experiment. These <a href="#factorvalue_section">Factor
+ Value[]</a> columns reference the Experimental Factor Names
+ defined in the IDF, and should be placed after the hybridization
+ section (i.e., to the right of it, in or after the scanning,
+ normalization and data section in the image above). The contents
+ of these columns will usually duplicate those in a material <a
+ href="#characteristics">Characteristics</a> or a
+ protocol <a href="#parameter_value">Parameter Value</a>
+ column. See below for an example.</p>
+
+ <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="sdrfsections">Example SDRF sections</a></div>
+
+ <p class="text"><a name="protocol_section"
+ class="subhead">Protocols</a><br/> In most cases, each treatment
+ within an experiment will be represented simply by a <a
+ href="#protocol_ref">Protocol REF</a> column containing
+ references to the <a
+ href="idf.html#protoname">Protocol Names</a> defined in the
+ IDF. If multiple protocols need to be chained together this can
+ be achieved using multiple Protocol REF columns.</p>
+
+ <p class="text">Additionally, any <a
+ href="idf.html#protoparam">Protocol Parameters</a> associated
+ with the protocol (as defined in the IDF) should have their
+ values listed after the Protocol REF column. For example, with this in your IDF:</p>
+
+ <table class="example">
+ <tr><td><a href="idf.html#protoname" class="plain">Protocol Name</a></td><td>My Ext. Protocol</td><td>My Labeling Protocol</td></tr>
+ <tr><td><a href="idf.html#prototype" class="plain">Protocol Type</a></td><td>nucleic_acid_extraction</td><td>labeling</td></tr>
+ <tr><td><a href="idf.html#protoparam" class="plain">Protocol Parameters</a></td><td>amplification;RNA quality</td><td>amount of RNA used</td></tr>
+ </table>
+
+ <p class="text">The SDRF describing the use of these protocols
+ might look as follows:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#parameter_value" class="plain">Parameter Value [RNA quality]</a></td>
+ <td><a href="#parameter_value" class="plain">Parameter Value [amplification]</a></td>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#parameter_value" class="plain">Parameter Value [amount of RNA used]</a></td>
+ <td><a href="#unit" class="plain">Unit [MassUnit]</a></td>
+ </tr>
+ <tr><td>My Ext. Protocol</td><td>RIN 8</td><td>RNA polymerase</td><td>My Labeling Protocol</td><td>10</td><td>ug</td></tr>
+ </table>
+
+ <p class="text">Other columns which may be used to annotate
+ these Protocol REF columns are: <a
+ href="#performer">Performer</a>, <a href="#date">Date</a>, and <a
+ href="#comment">Comment</a>.</p>
+
+ <p class="text"><a name="source_section"
+ class="subhead">Sources</a><br/> Sources are the starting
+ material for the experiment. The section starts with a Source
+ Name column, which will typically be followed by several <a
+ href="#characteristics">Characteristics</a> columns and a <a
+ href="#material_type">Material Type</a> column:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#source_name" class="plain">Source Name</a></td>
+ <td><a href="#material_type" class="plain">Material Type</a></td>
+ <td><a href="#characteristics" class="plain">Characteristics [Organism]</a></td>
+ <td><a href="#characteristics" class="plain">Characteristics [OrganismPart]</a></td>
+ <td><a href="#characteristics" class="plain">Characteristics [DiseaseState]</a></td>
+ <td><a href="#characteristics" class="plain">Characteristics [BioSourceType]</a></td>
+ </tr>
+ <tr><td>Tumor 1</td><td>organism_part</td><td>Homo sapiens</td><td>mammary gland</td><td>invasive ductal carcinoma</td><td>frozen_sample</td></tr>
+ </table>
+
+ <p class="text">Additional columns which may be used to annotate
+ Sources are: <a href="#provider">Provider</a>, <a
+ href="#description">Description</a>, and <a
+ href="#comment">Comment</a>.</p>
+
+ <p class="text"><a name="sample_section"
+ class="subhead">Samples</a><br/> Samples represent steps in the
+ chain of treatments applied to the original Source. MAGE-TAB
+ allows you to create as many Sample steps as necessary:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#source_name" class="plain">Source Name</a></td>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#sample_name" class="plain">Sample Name</a></td>
+ <td><a href="#material_type" class="plain">Material Type</a></td>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#sample_name" class="plain">Sample Name</a></td>
+ <td><a href="#material_type" class="plain">Material Type</a></td>
+ <td><a href="#characteristics" class="plain">Characteristics [OrganismPart]</a></td>
+ </tr>
+ <tr><td>Young Rat 99</td><td>My growth protocol</td><td>Adult Rat 99</td><td>whole_organism</td><td>My dissect protocol</td><td>Adult Rat Liver 99</td><td>organism_part</td><td>liver</td></tr>
+ </table>
+
+ <p class="text">For ArrayExpress submissions, typically only one
+ or two Sample steps are needed. Columns which may be used to
+ annotate Samples are: <a
+ href="#characteristics">Characteristics[]</a>, <a
+ href="#material_type">Material Type</a>, <a
+ href="#description">Description</a>, and <a
+ href="#comment">Comment</a>.</p>
+
+ <p class="text"><a name="extract_section"
+ class="subhead">Extracts</a><br/> Extracts refer to the
+ extracted nucleic acid used in the experiment. Again, as many
+ Extract steps may be used as are necessary. For example, if you
+ need to represent separate nucleic acid extraction and chromatin
+ immunoprecipitation steps in your SDRF, we recommend that you
+ use two Extract steps. In most cases, however, a single Extract
+ Name column would suffice:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#sample_name" class="plain">Sample Name</a></td>
+ <td><a href="#material_type" class="plain">Material Type</a></td>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#extract_name" class="plain">Extract Name</a></td>
+ <td><a href="#material_type" class="plain">Material Type</a></td>
+ </tr>
+ <tr><td>Rat Liver 99</td><td>organism_part</td><td>My Ext. Protocol</td><td>Liver RNA 99</td><td>total_RNA</td></tr>
+ </table>
+
+ <p class="text">Columns which may be used to
+ annotate Extracts are: <a
+ href="#characteristics">Characteristics[]</a>, <a
+ href="#material_type">Material Type</a>, <a
+ href="#description">Description</a>, and <a
+ href="#comment">Comment</a>.</p>
+
+ <p class="text"><a name="labeledextract_section"
+ class="subhead">Labeled Extracts</a><br/> The Labeled Extracts
+ in an experiment are those materials which have been conjugated
+ to a label of some kind, prior to hybridization on an
+ array. Typically there is only one Labeled Extract step. For
+ submission to ArrayExpress, a <a href="#label">Label</a> column
+ must be included with the <a
+ href="#labeled_extract_name">Labeled Extract Name</a>
+ column to indicate which label (and therefore scanner channel)
+ corresponds to which sample:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#extract_name" class="plain">Extract Name</a></td>
+ <td><a href="#material_type" class="plain">Material Type</a></td>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#labeled_extract_name" class="plain">Labeled Extract Name</a></td>
+ <td><a href="#label" class="plain">Label</a></td>
+ <td><a href="#material_type" class="plain">Material Type</a></td>
+ </tr>
+ <tr><td>Liver RNA 99</td><td>total_RNA</td><td>My labeling protocol</td><td>Liver LE 99 Cy3</td><td>Cy3</td><td>synthetic_DNA</td></tr>
+ <tr><td>Kidney RNA 34</td><td>total_RNA</td><td>My labeling protocol</td><td>Kidney LE 34 Cy5</td><td>Cy5</td><td>synthetic_DNA</td></tr>
+ </table>
+
+ <p class="text">Note that it it wise to also include the Label
+ in the Labeled Extract Name itself, so that unique objects are
+ correctly created for each labeled extract. Columns which may be
+ used to annotate Labeled Extracts are: <a
+ href="#characteristics">Characteristics[]</a>, <a
+ href="#material_type">Material Type</a>, <a
+ href="#description">Description</a>, and <a
+ href="#comment">Comment</a>.</p>
+
+ <p class="text"><a name="hybridization_section"
+ class="subhead">Hybridizations</a><br/>The hybridization of
+ Labeled Extract to an array is a key step in the SDRF, since it
+ connects the "materials" section of the SDRF from the "data"
+ section. For submission to ArrayExpress, an <a
+ href="#array_design_ref">Array Design REF</a> column
+ must be included with the <a
+ href="#hybridization_name">Hybridization Name</a> column,
+ indicating which array design was used in the hybridization:
+ </p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#labeled_extract_name" class="plain">Labeled Extract Name</a></td>
+ <td><a href="#label" class="plain">Label</a></td>
+ <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+ <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+ </tr>
+ <tr><td>Liver LE 1</td><td>Cy3</td><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td></tr>
+ <tr><td>Kidney LE 1</td><td>Cy5</td><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td></tr>
+ </table>
+
+ <p class="text">It is also possible to use <a
+ href="#comment">Comment</a> columns to annotate both
+ Hybridization Name and Array Design REF
+ columns. Note that the values in Hybridization Name columns
+ may be used in <a
+ href="magetab_docs.html#datamatrix">Data Matrix</a> files
+ to link columns of data to individual hybridizations. </p>
+
+ <p class="text"><a name="scan_section"
+ class="subhead">Scans</a><br/>If desired, the act of scanning
+ the hybridized array may be represented as a distinct node in
+ the experimental graph, and encoded in the SDRF using <a
+ href="#scan_name">Scan Name</a> columns. These columns are
+ optional, but can be useful in cases where e.g. multiple scans
+ have been made of a single hybridized array, but where the data
+ files do not explicitly reflect this:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+ <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+ <td><a href="#scan_name" class="plain">Scan Name</a></td>
+ <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+ </tr>
+ <tr><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td><td>LK1 First Scan</td><td>Data1.txt</td></tr>
+ <tr><td>Liver vs. Kidney 1</td><td>A-MEXP-88</td><td>LK1 Second Scan</td><td>Data1.txt</td></tr>
+ </table>
+
+ <p class="text">Again, <a href="#comment">Comment</a> columns
+ may be used to further annotate Scan Name columns, where
+ appropriate. Note that the values in Scan Name columns may
+ be used in <a
+ href="magetab_docs.html#datamatrix">Data Matrix</a> files
+ to link columns of data to individual scanning events. </p>
+
+ <p class="text"><a name="arraydatafile_section"
+ class="subhead">Array Data Files</a><br/>The raw data files
+ generated by an investigation should be listed in an <a
+ href="#array_data_file">Array Data File</a> column
+ following the <a
+ href="#hybridization_name">Hybridization Name</a> and
+ (optional) <a href="#scan_name">Scan Name</a> columns:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+ <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+ <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+ <td><a href="#comment" class="plain">Comment [EXP]</a></td>
+ </tr>
+ <tr><td>Liver vs. Kidney 1</td><td>A-AFFY-33</td><td>Data1.CEL</td><td>Data1.EXP</td></tr>
+ </table>
+
+ <p class="text"><a href="#comment">Comment</a> columns can be
+ used to add information relating to Array Data Files. For
+ example, if you are coding an Affymetrix-based experiment and
+ you wish to include the EXP files in your submission, you should
+ list them in a "Comment[EXP]" column following the "Array Data
+ File" column, as shown above.</p>
+
+ <p class="text"><a name="normalization_section"
+ class="subhead">Normalizations</a><br/> Similarly to the use of
+ <a href="#scan_name">Scan Name</a> columns <a
+ href="#scan_section">above</a>, it is possible to represent the
+ act of normalizing your data independently from the listing of
+ data files themselves. This is done using the optional <a
+ href="#normalization_name">Normalization Name</a> column:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+ <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+ <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+ <td><a href="#normalization_name" class="plain">Normalization Name</a></td>
+ <td><a href="#derived_array_data_file" class="plain">Derived Array Data File</a></td>
+ </tr>
+ <tr><td>Liver vs. Kidney 1</td><td>A-AFFY-33</td><td>Data1.CEL</td><td>Norm 1</td><td>Data1.CHP</td></tr>
+ </table>
+
+ <p class="text">Again, <a href="#comment">Comment</a> columns
+ may be used to further annotate Normalization Name columns,
+ where appropriate. Note that the values in
+ Normalization Name columns may be used in <a
+ href="magetab_docs.html#datamatrix">Data Matrix</a> files
+ to link columns of data to individual normalization events. </p>
+
+ <p class="text"><a name="derivedarraydatafile_section"
+ class="subhead">Derived Array Data Files</a><br/>The processed
+ data files which have been derived from the raw data should be
+ listed in an <a
+ href="#derived_array_data_file">Derived Array Data File</a>
+ column. <em>Note</em> that this generally only applies to
+ processed data arranged into one file per hybridization (or
+ scan, or normalization). If your files contain processed data
+ columns for more than one hybridization, you should reformat
+ these into the <a href="magetab_docs.html#datamatrix">MAGE-TAB
+ Data Matrix format</a> and include them instead in a <a
+ href="#derived_array_data_matrix_file">Derived Array Data Matrix File</a>
+ column. Multiple steps of normalization can be captured:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+ <td><a href="#array_design_ref" class="plain">Array Design REF</a></td>
+ <td><a href="#array_data_file" class="plain">Array Data File</a></td>
+ <td><a href="#normalization_name" class="plain">Normalization Name</a></td>
+ <td><a href="#derived_array_data_file" class="plain">Derived Array Data File</a></td>
+ <td><a href="#normalization_name" class="plain">Normalization Name</a></td>
+ <td><a href="#derived_array_data_matrix_file" class="plain">Derived Array Data Matrix File</a></td>
+ <td><a href="#comment" class="plain">Comment [CDF]</a></td>
+ </tr>
+ <tr><td>Liver vs. Kidney 1</td><td>A-AFFY-33</td><td>Data1.CEL</td><td>MAS5 Norm 1</td><td>Data1.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+ <tr><td>Liver vs. Kidney 2</td><td>A-AFFY-33</td><td>Data2.CEL</td><td>MAS5 Norm 2</td><td>Data2.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+ <tr><td>Liver vs. Kidney 3</td><td>A-AFFY-33</td><td>Data3.CEL</td><td>MAS5 Norm 3</td><td>Data3.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+ <tr><td>Liver vs. Kidney 4</td><td>A-AFFY-33</td><td>Data4.CEL</td><td>MAS5 Norm 4</td><td>Data4.CHP</td><td>RMA Norm</td><td>RMANormData.txt</td><td>HG-U133A.cdf</td></tr>
+ </table>
+
+ <p class="text">In the above example, the columns from the
+ "RMANormData.txt" data matrix file could be linked to either the
+ Hybridization Names or the previous set of Normalization Names,
+ allowing for flexible representation of the flow of data through
+ the process. See the <a href="magetab_docs.html#datamatrix">Data
+ Matrix notes</a> for details of how these links are encoded in
+ the data matrix file header.</p>
+
+ <p class="text"><a href="#comment">Comment</a> columns may be
+ used to add information relating to processed data files. For
+ example, when coding an Affymetrix-based experiment with a Data
+ Matrix file, as in the example above, a Comment[CDF] column
+ should be used to indicate which Affymetrix library ("CDF") file
+ applies to these data.</p>
+
+ <p class="text"><a name="factorvalue_section"
+ class="subhead">Factor Values</a><br/> The Factor Values for an
+ experiment are the values of the variables under
+ investigation. For example, an experiment studying the effect of
+ different compounds on a cell culture would have "compound" as
+ an experimental variable. These variables are listed in the IDF
+ as "Experimental Factor Names" with associated Types:</p>
+
+ <table class="example">
+ <tr><td><a href="idf.html#factorname" class="plain">Experimental Factor Name</a></td><td>Cells</td><td>Drug</td></tr>
+ <tr><td><a href="idf.html#factortype" class="plain">Experimental Factor Type</a></td><td>cell_line</td><td>compound</td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td><a href="idf.html#protoname" class="plain">Protocol Name</a></td><td>All Treatments</td><td> </td></tr>
+ <tr><td><a href="idf.html#protoparams" class="plain">Protocol Parameters</a></td><td>drug compound</td><td> </td></tr>
+ </table>
+
+ <p class="text">Given the above definitions in the accompanying
+ IDF, the SDRF file can then reference these factors when we come
+ to list the factor values:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#source_name" class="plain">Source Name</a></td>
+ <td><a href="#characteristics" class="plain">Characteristics [CellLine]</a></td>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#parameter_value" class="plain">Parameter Value [drug compound]</a></td>
+ <td><a href="#hybridization_name" class="plain">Hybridization Name</a></td>
+ <td><a href="#factor_value" class="plain">Factor Value [Cells]</a></td>
+ <td><a href="#factor_value" class="plain">Factor Value [Drug]</a></td>
+ </tr>
+ <tr><td>Line 1</td><td>Jurkat</td><td>All Treatments</td><td>imatinib</td><td>Jurkat vs imatinib</td><td>Jurkat</td><td>imatinib</td></tr>
+ <tr><td>Line 1</td><td>Jurkat</td><td>All Treatments</td><td>lapatinib</td><td>Jurkat vs lapatinib</td><td>Jurkat</td><td>lapatinib</td></tr>
+ <tr><td>Line 2</td><td>RKO</td><td>All Treatments</td><td>imatinib</td><td>RKO vs imatinib</td><td>RKO</td><td>imatinib</td></tr>
+ <tr><td>Line 2</td><td>RKO</td><td>All Treatments</td><td>lapatinib</td><td>RKO vs lapatinib</td><td>RKO</td><td>lapatinib</td></tr>
+ </table>
+
+ <p class="text">Note that there is inevitably duplication
+ between Factor Values and values entered elsewhere in the
+ SDRF. It is particularly common to have the Factor Value column
+ duplicate either a <a
+ href="#characteristics">Characteristics[]</a> column or a <a
+ href="#parameter_value">Parameter Value[]</a> column.</p>
+
+ <p class="text">Factor Value columns may be placed anywhere
+ after the hybridization section of the SDRF, although they
+ should be placed to avoid disrupting any of the other
+ sections. This is most easily achieved by adding them at the end
+ (i.e., the far right) of the SDRF.</p>
+
+ <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="sdrftags">All valid SDRF column headings</a></div>
+
+ <p class="text"><a name="source_name" class="subhead">Source
+ Name</a><br/><font color="green">Used as an identifier within
+ the MAGE-TAB document.</font> This column contains user-defined names for the <a
+ href="#source_section">Source</a> materials. The following
+ columns can be used to annotate Source Name columns:</p>
+ <ul>
+ <li><a href="#characteristics">Characteristics[]</a></li>
+ <li><a href="#provider">Provider</a></li>
+ <li><a href="#material_type">Material Type</a></li>
+ <li><a href="#description">Description</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="sample_name" class="subhead">Sample
+ Name</a><br/> <font color="green">Used as an identifier within
+ the MAGE-TAB document.</font> This column contains user-defined
+ names for each <a href="#sample_section">Sample</a>
+ material. The following columns can be used to annotate Sample
+ Name columns:</p>
+
+ <ul>
+ <li><a href="#characteristics">Characteristics[]</a></li>
+ <li><a href="#material_type">Material Type</a></li>
+ <li><a href="#description">Description</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="extract_name" class="subhead">Extract
+ Name</a><br/> <font color="green">Used as an identifier within
+ the MAGE-TAB document.</font> This column contains user-defined
+ names for each <a href="#extract_section">Extract</a>
+ material. The following columns can be used to annotate Extract
+ Name columns:</p>
+
+ <ul>
+ <li><a href="#characteristics">Characteristics[]</a></li>
+ <li><a href="#material_type">Material Type</a></li>
+ <li><a href="#description">Description</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="labeled_extract_name"
+ class="subhead">Labeled Extract Name</a><br/> <font
+ color="green">Used as an identifier within the MAGE-TAB
+ document.</font> This column contains user-defined names for
+ each <a href="#labeledextract_section">Labeled Extract</a>
+ material. The following columns can be used to annotate Labeled
+ Extract Name columns:</p>
+
+ <ul>
+ <li><a href="#label">Label</a> (<font color="red">required</font>)</li>
+ <li><a href="#characteristics">Characteristics[]</a></li>
+ <li><a href="#material_type">Material Type</a></li>
+ <li><a href="#description">Description</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="hybridization_name"
+ class="subhead">Hybridization Name</a><br/> <font
+ color="green">Used as an identifier within the MAGE-TAB
+ document.</font>This column contains user-defined names for each
+ <a href="#hybridization_section">Hybridization</a>. The
+ following columns can be used to annotate Hybridization
+ Name columns:</p>
+
+ <ul>
+ <li><a href="#array_design_ref">Array Design REF</a> (<font color="red">required</font>)</li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="assay_name" class="subhead">Assay
+ Name</a><br/> <font color="green">Used as an identifier within
+ the MAGE-TAB document.</font>This column contains user-defined
+ names for each Assay. "Assay Name" may be used instead of
+ "Hybridization Name" to identify generic biological assays, such
+ as rtPCR. Note that this column should not be used for
+ submission of regular microarray experiments to
+ ArrayExpress. The following columns can be used to annotate
+ Assay Name columns:</p>
+
+ <ul>
+ <li><a href="#technology_type">Technology Type</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text">Note that as of MAGE-TAB version 1.1, all Assay
+ Name columns must be followed by a Technology Type column.</p>
+
+ <p class="text"><a name="scan_name" class="subhead">Scan
+ Name</a><br/> <font color="green">Used as an identifier within
+ the MAGE-TAB document.</font> This optional column contains
+ user-defined names for each <a href="#scan_section">Scan</a>
+ event. The following columns can be used to annotate Scan Name
+ columns:</p>
+
+ <ul>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="normalization_name"
+ class="subhead">Normalization Name</a><br/> <font
+ color="green">Used as an identifier within the MAGE-TAB
+ document.</font> This optional column contains user-defined
+ names for each <a
+ href="#normalization_section">Normalization</a> event. The
+ following columns can be used to annotate Normalization Name
+ columns:</p>
+
+ <ul>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="array_data_file" class="subhead">Array
+ Data File</a><br/> This column contains a list of <a
+ href="#arraydatafile_section">raw data files</a>, one for each
+ row of the SDRF file, linking these data files to their
+ respective hybridizations. The following columns can be used to
+ annotate Array Data File columns:</p>
+
+ <ul>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="derived_array_data_file"
+ class="subhead">Derived Array Data File</a><br/> This column
+ contains a list of <a
+ href="#derivedarraydatafile_section">processed data files</a>,
+ one for each row of the SDRF file, linking these data files to
+ their respective hybridizations. The following columns can be
+ used to annotate Derived Array Data File columns:</p>
+
+ <ul>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="array_data_matrix_file"
+ class="subhead">Array Data Matrix File</a><br/> This column
+ contains a list of raw data matrix files, where data from
+ multiple hybridizations is stored in a single file, and the data
+ mapped to each hybridization via the <a
+ href="magetab_docs.html#datamatrix">Data Matrix</a> format
+ itself. The following columns can be used to annotate Array Data
+ Matrix File columns:</p>
+
+ <ul>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="derived_array_data_matrix_file"
+ class="subhead">Derived Array Data Matrix File</a><br/> This
+ column contains a list of processed data matrix files, where
+ data from multiple hybridizations is stored in a single file,
+ and the data mapped to each hybridization (or scan, or
+ normalization) via the <a
+ href="magetab_docs.html#datamatrix">Data Matrix</a> format
+ itself. The following columns can be used to annotate Derived
+ Array Data Matrix File columns:</p>
+
+ <ul>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="image_file" class="subhead">Image
+ File</a><br/>This optional column contains a list of image
+ files, one for each row of the SDRF file, linking these image
+ files to their respective hybridizations. Note that ArrayExpress
+ does not store image data due to size constraints on the
+ database. If desired, you may use this column to include links
+ to image files stored on your local webserver. The following
+ columns can be used to annotate Derived Array Data File
+ columns:</p>
+
+ <ul>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text"><a name="array_design_ref" class="subhead">Array
+ Design REF</a><br/> This column contains references to the array
+ design used for each hybridization. For ArrayExpress submissions
+ this should be an ArrayExpress accession number,
+ e.g. "A-AFFY-33". The following columns can be used to annotate
+ Array Design REF columns:</p>
+
+ <ul>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text">The Term Source REF column here can be used to
+ point to the source of the array design referenced; however for
+ ArrayExpress submissions this should always be ArrayExpress
+ itself, and so this column is in effect ignored.</p>
+
+ <p class="text"><a name="protocol_ref" class="subhead">Protocol
+ REF</a><br/> This column contains references to Protocol Names
+ defined in the IDF, or accession numbers of protocols already
+ deposited with ArrayExpress. The following columns can be used
+ to annotate Protocol REF columns:</p>
+
+ <ul>
+ <li><a href="#parameter_value">Parameter Value[]</a></li>
+ <li><a href="#performer">Performer</a></li>
+ <li><a href="#date">Date</a></li>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text">The Term Source REF column here can be used to
+ point to the source of the protocol referenced, if it is not
+ contained within the IDF; for ArrayExpress submissions this
+ should always be ArrayExpress itself, and a suitable
+ ArrayExpress Term Source should be defined in the IDF.</p>
+
+ <p class="text"><a name="characteristics"
+ class="subhead">Characteristics[<category term>]</a><br/>
+ <font color="blue">Controlled vocabulary term or
+ measurement.</font> Used as an attribute column following <a
+ href="#source_name">Source Name</a>, <a
+ href="#sample_name">Sample Name</a>, <a
+ href="#extract_name">Extract Name</a>, or <a
+ href="#labeled_extract_name">Labeled Extract Name</a>. This
+ column contains terms describing each material according to the
+ characteristics category indicated in the column header. For
+ example, a column headed "Characteristics[OrganismPart]" would
+ contain individual OrganismPart terms. These terms may be
+ user-defined (the default), from an external ontology source
+ (indicated using a Term Source REF column), or a measurement
+ (indicated using a Unit[] column). </p>
+
+ <ul>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ <li><a href="#unit">Unit[]</a></li>
+ </ul>
+
+ <p class="text"><a name="provider"
+ class="subhead">Provider</a><br/> Used as an attribute column
+ following <a href="#source_name">Source Name</a>. A free-text
+ string identifying the organization or person from which the
+ Source was obtained.</p>
+
+ <p class="text"><a name="material_type" class="subhead">Material
+ Type</a><br/> <font color="blue">Controlled vocabulary
+ term.</font> Used as an attribute column following <a
+ href="#source_name">Source Name</a>, <a
+ href="#sample_name">Sample Name</a>, <a
+ href="#extract_name">Extract Name</a>, or <a
+ href="#labeled_extract_name">Labeled Extract Name</a>. This
+ column contains terms describing the type of each material. For
+ ArrayExpress submissions this term should be an instance of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType">MaterialType
+ from the MGED Ontology</a>. Examples: whole_organism,
+ organism_part, cell, total_RNA. The following columns can be
+ used to annotate Material Type columns:</p>
+
+ <ul>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ </ul>
+
+ <p class="text">The Term Source REF column in this case would
+ point to the ontology (defined in the IDF) from which the
+ Material Type terms are taken (the MGED Ontology in the example
+ above).</p>
+
+ <p class="text"><a name="label" class="subhead">Label</a><br/>
+ <font color="blue">Controlled vocabulary term.</font> Used as an
+ attribute column following <a
+ href="#labeled_extract_name">Labeled Extract Name</a>. The label
+ compound which is conjugated to an Extract to create the Labeled
+ Extract. For ArrayExpress submissions this term should be an
+ instance of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#LabelCompound">LabelCompound
+ from the MGED Ontology</a>. Examples: Cy3, Cy5, biotin,
+ alexa_546. The following columns can be used to annotate Label
+ columns:</p>
+
+ <ul>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ </ul>
+
+ <p class="text">The Term Source REF column in this case would
+ point to the ontology (defined in the IDF) from which the Label
+ terms are taken (the MGED Ontology in the example above).</p>
+
+ <p class="text"><a name="technology_type"
+ class="subhead">Technology Type</a><br/> <font
+ color="blue">Controlled vocabulary term.</font> Used as an
+ attribute column following <a href="#assay_name">Assay
+ Name</a>. This column contains terms describing the type of each
+ generic (non-hybridization) assay. Example: rtPCR. The following
+ columns can be used to annotate Technology Type columns:</p>
+
+ <ul>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ </ul>
+
+ <p class="text">The Term Source REF column in this case would
+ point to the ontology (defined in the IDF) from which the
+ Technology Type terms are taken.</p>
+
+ <p class="text"><a name="factor_value" class="subhead">Factor
+ Value[<experiment factor name>]</a><br/> <font
+ color="blue">Controlled vocabulary term or measurement.</font>
+ This column contains terms describing the experimental factor
+ values (i.e., variables) for each row of the SDRF. The
+ Experimental Factor Name to which it pertains (from the
+ accompanying IDF) should be indicated in the column heading. For
+ example, if you have this in your IDF:</p>
+
+ <table class="example">
+ <tr><td><a href="idf.html#factorname" class="plain">Experimental Factor Name</a></td><td>TissueEF</td></tr>
+ <tr><td><a href="idf.html#factortype" class="plain">Experimental Factor Type</a></td><td>organism_part</td></tr>
+ </table>
+
+ <p class="text">You could then use this factor in your SDRF
+ (assuming you had also defined the "Mouse Anatomy" term source
+ in your IDF):</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#factor_value" class="plain">Factor Value[TissueEF]</a></td>
+ <td><a href="#term_source_ref" class="plain">Term Source REF</a></td>
+ </tr>
+ <tr><td>brain</td><td>Mouse Anatomy</td></tr>
+ <tr><td>kidney</td><td>Mouse Anatomy</td></tr>
+ <tr><td>liver</td><td>Mouse Anatomy</td></tr>
+ <tr><td>intestine</td><td>Mouse Anatomy</td></tr>
+ <tr><td>pancreas</td><td>Mouse Anatomy</td></tr>
+ </table>
+
+ <p class="text">The terms in the column may be user-defined (the
+ default), from an external ontology source (indicated using a
+ Term Source REF column), or a measurement (indicated using a
+ Unit[] column).</p>
+
+ <p class="text">In the example above, the column terms would be
+ treated as describing organism parts. For more precise control
+ over the treatment of these terms, the optional form "Factor
+ Value [] ()" is available, e.g. "Factor Value [growth condition
+ EF] (Nutrients)".</p>
+
+ <p class="text"><a name="performer"
+ class="subhead">Performer</a><br/> Used as an attribute column
+ following <a href="#protocol_ref">Protocol REF</a>. The name of
+ the researcher who carried out the protocol.</p>
+
+ <p class="text"><a name="date" class="subhead">Date</a><br/>
+ Used as an attribute column following <a
+ href="#protocol_ref">Protocol REF</a>. The date (and time, where
+ available) upon which the protocol was performed, in the
+ following format: YYYY-MM-DD</p>
+
+ <p class="text"><a name="parameter_value"
+ class="subhead">Parameter Value[<protocol
+ parameter>]</a><br/> Used as an attribute column following <a
+ href="#protocol_ref">Protocol REF</a> columns. This column
+ contains values for the protocol parameters referenced in the
+ column header. The following columns can be used to annotate
+ Parameter Value[] columns:</p>
+
+ <ul>
+ <li><a href="#unit">Unit[]</a></li>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ <li><a href="#comment">Comment[]</a></li>
+ </ul>
+
+ <p class="text">For example, if a Protocol Name "Array
+ Hybridization" is defined in the accompanying IDF, with Protocol
+ Parameters "hyb temp;hyb volume", the following would be
+ valid:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#protocol_ref" class="plain">Protocol REF</a></td>
+ <td><a href="#parameter_value" class="plain">Parameter Value [hyb temp]</a></td>
+ <td><a href="#unit" class="plain">Unit [TemperatureUnit]</a></td>
+ <td><a href="#paramater_value" class="plain">Parameter Value [hyb volume]</a></td>
+ <td><a href="#unit" class="plain">Unit [VolumeUnit]</a></td>
+ </tr>
+ <tr><td>Array Hybridization</td><td>55</td><td>degrees_C</td><td>100</td><td>ul</td></tr>
+ </table>
+
+ <p class="text"><a name="unit" class="subhead">Unit[<unit
+ category>]</a><br/> <font color="blue">Controlled vocabulary
+ term.</font> Used as an attribute column following <a
+ href="#characteristics">Characteristics[]</a>, <a
+ href="#factor_value">Factor Value[]</a> or <a
+ href="#parameter_value">Parameter Value[]</a>. This column
+ contains terms describing the unit(s) to be applied to the
+ values in the preceding column. The type of unit is included in
+ the column heading, e.g. "Unit[TimeUnit]". These unit types
+ should correspond to <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#Unit">Unit
+ subclasses from the MGED Ontology</a>. The following columns can
+ be used to annotate Unit[] columns:</p>
+
+ <ul>
+ <li><a href="#term_source_ref">Term Source REF</a></li>
+ </ul>
+
+ <p class="text">The Term Source REF column in this case would
+ point to the ontology (defined in the IDF) from which the Unit
+ terms are taken.</p>
+
+ <p class="text"><a name="description"
+ class="subhead">Description</a><br/>Used as an attribute column
+ following <a href="#source_name">Source Name</a>, <a
+ href="#sample_name">Sample Name</a>, <a
+ href="#extract_name">Extract Name</a>, or <a
+ href="#labeled_extract_name">Labeled Extract Name</a>. A
+ free-text description to be attached to the corresponding
+ material. To be used sparingly, if at all - most annotations
+ should be provided using controlled vocabulary terms, using <a
+ href="#characteristics">Characteristics[]</a> columns.</p>
+
+ <p class="text"><a name="term_source_ref" class="subhead">Term
+ Source REF</a><br/> Used as an attribute column following any
+ controlled vocabulary column (e.g., <a
+ href="#characteristics">Characteristics[]</a>, or column
+ allowing reference of external entities (e.g., <a
+ href="#protocol_ref">Protocol REF</a>. This column contains
+ references to ontology or database Term Sources defined in the
+ IDF, and from which the values in the previous column were
+ taken. The following columns can be used to annotate Term Source
+ REF columns:</p>
+
+ <ul>
+ <li><a href="#term_accession_number">Term Accession Number</a></li>
+ </ul>
+
+ <p class="text"><a name="term_accession_number"
+ class="subhead">Term Accession Number</a><br/> Used as an
+ attribute column following <a href="#term_source_ref">Term
+ Source REF</a> columns. This column contains the accession
+ numbers from the term source used to identify the ontology or
+ database terms in question. For example:</p>
+
+ <table class="example">
+ <tr>
+ <td><a href="#source_name" class="plain">Source Name</a></td>
+ <td><a href="#characteristics" class="plain">Characteristics [DiseaseState]</a></td>
+ <td><a href="#term_source_ref" class="plain">Term Source REF</a></td>
+ <td><a href="#term_accession_number" class="plain">Term Accession Number</a></td>
+ </tr>
+ <tr><td>Sample 1</td><td>acute lymphocytic leukemia</td><td>NCI Metathesaurus</td><td>C0023449</td></tr>
+ </table>
+
+ <p class="text">(This example relies on the "NCI Metathesaurus"
+ Term Source having been pre-defined in the IDF accompanying the
+ SDRF.)</p>
+
+ <p class="text"><a name="comment"
+ class="subhead">Comment[<comment name>]</a><br/> This
+ column can be used to annotate the main graph node and edge
+ columns listed above. It is included as an extensibility
+ mechanism, and should not generally be used to encode meaningful
+ biological annotation. The column heading should contain a name
+ for the type of values included in the column.</p>
+
+ <p CLASS="sectionfoot"> [ <a href="javascript:%20history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <p class="text">Please see the <a href="http://www.mged.org/mage-tab/">
+ MAGE-TAB specification</a> for further information and examples.</p>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Mon Feb 25 18:06:43 GMT 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/sdrf_layout.png b/docs/sdrf_layout.png
new file mode 100644
index 0000000..639209b
Binary files /dev/null and b/docs/sdrf_layout.png differ
diff --git a/docs/spreadsheet.html b/docs/spreadsheet.html
new file mode 100644
index 0000000..e3277c7
--- /dev/null
+++ b/docs/spreadsheet.html
@@ -0,0 +1,625 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Spreadsheet construction</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css">
+ </head>
+
+ <body style="width:90%">
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+ <td class="pagetitle">Creating your spreadsheet</td>
+ </tr>
+ </table>
+ </div>
+
+ <p class="text">The Tab2MAGE spreadsheet structure is split into
+ three main sections: <a href="#experiment">Experiment</a>, <a
+ href="#protocol">Protocol</a> and <a
+ href="#hybridization">Hybridization</a>. The sections are
+ separated from each other by one or more blank
+ lines. <em>Please note</em> that blank lines are used to demarcate the ends
+ of sections, and as such they should not be used within the body
+ of any section. We will discuss each of these three sections in
+ turn.</p>
+
+ <p class="text">A series of <a href="#realexamples">real-world</a>
+ and <a href="#examples">conceptual</a> examples is given at the
+ end of this page.</p>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="experiment">Experiment section</a></div>
+
+ <p class="text">This section holds all of the top-level
+ information about an experiment. It consists of two columns; the
+ left column contains a series of predefined row tags, while the
+ right column contains the actual values pertaining to your
+ experiment. An example is shown below:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#exptsection">Experiment section</a></td><td> </td></tr>
+ <tr><td><a class="plain" href="detail.html#domain">domain</a></td><td>ebi.ac.uk</td></tr>
+ <tr><td><a class="plain" href="detail.html#accession">accession</a></td><td>E-EXML-1</td></tr>
+ <tr><td><a class="plain" href="detail.html#quality_control">quality_control</a></td><td>dye_swap_quality_control</td></tr>
+ <tr><td><a class="plain" href="detail.html#experiment_design_type">experiment_design_type</a></td><td>strain_or_line_design</td></tr>
+ <tr><td><a class="plain" href="detail.html#name">name</a></td><td><i><your experiment title></i></td></tr>
+ <tr><td><a class="plain" href="detail.html#description">description</a></td><td><i><short description of your experiment></i></td></tr>
+ <tr><td><a class="plain" href="detail.html#release_date">release_date</a></td><td>2004-08-30</td></tr>
+ <tr><td><a class="plain" href="detail.html#submission_date">submission_date</a></td><td>2004-07-28</td></tr>
+ <tr><td><a class="plain" href="detail.html#submitter">submitter</a></td><td>John Falstaff</td></tr>
+ <tr><td><a class="plain" href="detail.html#organization">organization</a></td><td>Windsor Laboratories</td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">publication_title</a></td><td><i><your manuscript title></i></td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">authors</a></td><td>John Falstaff; Robin Goodfellow</td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">journal</a></td><td>Nature Genetics</td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">volume</a></td><td>12</td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">issue</a></td><td>4</td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">pages</a></td><td>123-456</td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">year</a></td><td>2004</td></tr>
+ <tr><td><a class="plain" href="detail.html#publication">pubmed_id</a></td><td>12345678</td></tr>
+ </table>
+
+ <p CLASS="text">With the exception of the section header, all
+ the tags above are optional. However, if the <b>domain</b> or
+ <b>accession</b> tags are not present suitable placeholders will
+ be used instead. A <a href="detail.html#experiment">more
+ detailed explanation</a> of the meaning of each of these tags
+ may be obtained by clicking on them.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="protocol">Protocol section</a></div>
+
+ <p class="text">The Protocol section allows the user to define
+ the protocols used in the experiment. The section consists of
+ three required columns (<b>accession</b>, <b>text</b> and
+ <b>name</b>) with an optional <b>parameters</b> column. A fifth
+ column, "<b>type</b>" is available for adding custom
+ ProtocolType terms from the MGED Ontology to your protocols, but
+ its use is <em>not</em> recommended for the majority of
+ cases. This column should generally be omitted, and the tab2mage
+ script will use a set of default protocol types based on how the
+ protocols are used in the Hybridization section.</p>
+
+ <table class="example">
+ <tr><td style="width:110pt"><a class="plain" href="detail.html#protosection">Protocol section</a></td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#protoaccession">accession</a></td>
+ <td><a class="plain" href="detail.html#prototext">text</a></td>
+ <td><a class="plain" href="detail.html#protoname">name</a></td>
+ <td><a class="plain" href="detail.html#protoparams">parameters</a></td>
+ </tr>
+ <tr><td>P-EXML-1</td><td>Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8</td><td>Yeast growth</td><td>growth temperature (degree_C); pH</td></tr>
+ <tr><td>P-EXML-2</td><td><i><protocol text></i></td><td>Yeast cell harvesting</td><td>pellet weight (mg)</td></tr>
+ <tr><td>P-EXML-3</td><td><i><protocol text></i></td><td>Cell lysis and RNA prep</td><td> </td></tr>
+ <tr><td>P-EXML-4</td><td><i><protocol text></i></td><td>cDNA labeling</td><td> </td></tr>
+ <tr><td>P-EXML-5</td><td><i><protocol text></i></td><td>Hybridization</td><td>hyb temp (degree_C); hyb volume (uL)</td></tr>
+ <tr><td>P-EXML-6</td><td><i><protocol text></i></td><td>Scanning</td><td> </td></tr>
+ <tr><td>P-EXML-7</td><td><i><protocol text></i></td><td>Image analysis</td><td> </td></tr>
+ <tr><td>P-EXML-8</td><td><i><protocol text></i></td><td>Normalization</td><td> </td></tr>
+ </table>
+
+
+ <p CLASS="text">Note that you may omit protocols which have been
+ previously submitted to ArrayExpress, and simply refer to those
+ protocols directly in the <a href="#hybridization">Hybridization
+ section</a>. If no new protocols are needed then the entire Protocol
+ section may be omitted. A <a href="detail.html#protocol">more
+ detailed explanation</a> of the meaning of each of the fields in
+ this section may be obtained by clicking on the column
+ headings.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="hybridization">Hybridization section</a></div>
+
+ <p class="text">The Hybridization section describes how each
+ sample links to each hybridization, scanning and subsequent
+ normalization, and as such is the largest and most complex
+ section. There is a simple principle underlying this section,
+ however: <em>Everything which appears on a given line must be
+ related in some way.</em> </p>
+
+ <p class="text">The column headings available in this section
+ can be divided into several different types for the purpose of
+ clarity. Note that the various columns in this section may
+ appear in any order, and so you may find that re-ordering the
+ columns makes the spreadsheet more legible for your own
+ application.</p>
+
+ <ul>
+ <li><a href="#hnames">Names of materials and processes</a></li>
+ <li><a href="#htypes">Material types</a></li>
+ <li><a href="#hchars">Material characteristics</a></li>
+ <li><a href="#hproto">Links to defined protocols and parameters</a></li>
+ <li><a href="#hfacts">Experimental factors</a></li>
+ <li><a href="#hfiles">Data files and array information</a></li>
+ </ul>
+
+ <p class="text">For each of the following tables, a <a
+ href="detail.html#hybridization">more detailed explanation</a> of
+ the meaning of each of the column headings may be obtained by
+ clicking on them.</p>
+
+ <p class="text"><a name="hnames" class="subhead">Names of
+ materials and processes</a><br> These columns are provided so
+ that you can give unique names to each of the materials used in
+ your experiment (e.g., samples, extracts, labeled
+ extracts). Hybridization and normalization events are also given
+ names. The use of these names is entirely optional, as Tab2MAGE
+ will attempt to link all of these things together for you. If,
+ however, your experiment involves complex pooling or splitting
+ operations then it is recommended that you make use of the
+ names. Doing so will allow you a much greater degree of control
+ over the output MAGE-ML.</p>
+
+ <p class="text">The following table is an example of a simple
+ reference design in which individual samples are hybridized
+ against a common reference pool:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+ <tr><td><a class="plain" href="detail.html#biosource">BioSource</a>
+ </td><td><a class="plain" href="detail.html#sample">Sample</a>
+ </td><td><a class="plain" href="detail.html#extract">Extract</a>
+ </td><td><a class="plain" href="detail.html#label">LabeledExtract</a>
+ </td><td><a class="plain" href="detail.html#dye">Dye</a>
+ </td><td><a class="plain" href="detail.html#hyb">Hybridization</a>
+ </td><td><a class="plain" href="detail.html#norm">Normalization</a></td></tr>
+ <tr><td>S288C</td><td>S288C sample</td><td>S288C extract</td><td>S288C LE</td><td>Cy3</td><td>S288C Hyb</td><td>S288C Norm</td></tr>
+ <tr><td>S288C</td><td>S288C sample</td><td>Reference extract</td><td>Reference LE</td><td>Cy5</td><td>S288C Hyb</td><td>S288C Norm</td></tr>
+ <tr><td>Sigma1278b</td><td>Sigma1278b sample</td><td>Sigma1278b extract</td><td>Sigma1278b LE</td><td>Cy3</td><td>Sigma1278b Hyb</td><td>Sigma1278b Norm</td></tr>
+ <tr><td>Sigma1278b</td><td>Sigma1278b sample</td><td>Reference extract</td><td>Reference LE</td><td>Cy5</td><td>Sigma1278b Hyb</td><td>Sigma1278b Norm</td></tr>
+ <tr><td>W303a</td><td>W303a sample</td><td>W303a extract</td><td>W303a LE</td><td>Cy3</td><td>W303a Hyb</td><td>W303a Norm</td></tr>
+ <tr><td>W303a</td><td>W303a sample</td><td>Reference extract</td><td>Reference LE</td><td>Cy5</td><td>W303a Hyb</td><td>W303a Norm</td></tr>
+ </table>
+
+ <p class="text">Note that Tab2MAGE does not constrain you to use the standard experimental layout:</p>
+
+ <pre>
+biosource ----------> sample ----------> extract ----------> labeled extract ----------> hybridization event
+ [growth, treatment] [extraction] [labeling] [hybridization]
+</pre>
+
+ <p class="text">If, for instance, your experiment starts with a
+ series of extracts provided by an external supplier, the sample
+ and extract stages may be omitted:</p>
+
+ <pre>
+biosource ----------> labeled extract ----------> hybridization event
+ [labeling] [hybridization]
+</pre>
+
+ <p class="text"><a name="htypes" class="subhead">Material
+ types</a><br> These columns simply indicate the type of each
+ material used. The terms should all be instances of <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType">MaterialType</a>
+ from the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>. Typical terms are shown in this example:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#materialtype">BioSourceMaterial</a></td>
+ <td><a class="plain" href="detail.html#materialtype">SampleMaterial</a></td>
+ <td><a class="plain" href="detail.html#materialtype">ExtractMaterial</a></td>
+ <td><a class="plain" href="detail.html#materialtype">LabeledExtractMaterial</a></td></tr>
+ <tr><td>whole_organism</td><td>whole_organism</td><td>total_RNA</td><td>synthetic_DNA</td></tr>
+ <tr><td>whole_organism</td><td>organism_part</td><td>total_RNA</td><td>synthetic_RNA</td></tr>
+ <tr><td>organism_part</td><td>cell</td><td>polyA_RNA</td><td>synthetic_RNA</td></tr>
+ </table>
+
+ <p class="text">These columns are not absolutely required for
+ Tab2MAGE to run. However, the strictures of the MAGE model mean
+ that if these values are not given, then suitable default values
+ must be generated. It is therefore wise to use these columns
+ wherever possible.</p>
+
+ <p class="text"><a name="hchars" class="subhead">Material
+ characteristics</a><br> Each biosource used in the experiment
+ can have an arbitrary number of characteristics. The
+ "BioMaterialCharacteristics[]" heading provides a way to
+ define as many of these characteristics as are needed. The general form of this heading is: </p>
+
+ <pre>BioMaterialCharacteristics[<i><category></i>]</pre>
+
+ <p class="text">where <i><category></i> is a subclass of the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristics">BioMaterialCharacteristics</a> class within the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a>. Examples are given in the table below:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#bmc">BioMaterialCharacteristics[Genotype]</a></td>
+ <td><a class="plain" href="detail.html#bmc">BioMaterialCharacteristics[Organism]</a></td>
+ <td><a class="plain" href="detail.html#bmc">BioMaterialCharacteristics[StrainOrLine]</a></td></tr>
+ <tr><td>CAD1::myc9:TRP1</td><td>Saccharomyces cerevisiae</td><td>S288C</td></tr>
+ <tr><td>CAD1::myc9:TRP1</td><td>Saccharomyces cerevisiae</td><td>W303a</td></tr>
+ <tr><td>RTG3::myc18:TRP1</td><td>Saccharomyces cerevisiae</td><td>S288C</td></tr>
+ <tr><td>RTG3::myc18:TRP1</td><td>Saccharomyces cerevisiae</td><td>W303a</td></tr>
+ </table>
+
+ <p class="text">If the <a
+ href="detail.html#biosource">BioSource</a> name column is not
+ used, then the set of characteristics of each biosource is used
+ to determine how many biosources should be created. This means
+ that if all your biosources share identical characteristics, and
+ none are given names, then they will be treated as a single
+ biosource in the output MAGE-ML. To circumvent this behaviour,
+ please name your biosources.</p>
+
+ <p class="text"><a name="hproto" class="subhead">Links to
+ defined protocols and parameters</a><br> This set of columns
+ allows you to link the protocols defined in the <a
+ href="#protocol">previous section</a> to your hybridizations:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#pgrow">Protocol[grow]</a></td>
+ <td><a class="plain" href="detail.html#ptreat">Protocol[treatment]</a></td>
+ <td><a class="plain" href="detail.html#pextract">Protocol[extraction]</a></td>
+ <td><a class="plain" href="detail.html#plabel">Protocol[labeling]</a></td>
+ <td><a class="plain" href="detail.html#phyb">Protocol[hybridization]</a></td>
+ <td><a class="plain" href="detail.html#pscan">Protocol[scanning]</a></td></tr>
+ <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+ <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+ <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+ <tr><td>P-EXML-1</td><td>P-EXML-2</td><td>P-EXML-3</td><td>P-EXML-4</td><td>P-EXML-5</td><td>P-EXML-6</td></tr>
+ </table>
+
+ <p class="text">If you have assigned <a href="detail.html#protoparams">parameters</a> to your
+ protocols, you may set the parameter values here:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#param">Parameter[growth temperature]</a></td>
+ <td><a class="plain" href="detail.html#param">Parameter[pH]</a></td>
+ <td><a class="plain" href="detail.html#param">Parameter[pellet weight]</a></td>
+ <td><a class="plain" href="detail.html#param">Parameter[hyb temp]</a></td>
+ <td><a class="plain" href="detail.html#param">Parameter[hyb volume]</a></td></tr>
+ <tr><td>30</td><td>6</td><td>10</td><td>56</td><td>50</td></tr>
+ <tr><td>31</td><td>6</td><td>12</td><td>56</td><td>50</td></tr>
+ <tr><td>30</td><td>6</td><td>10</td><td>55</td><td>50</td></tr>
+ <tr><td>31</td><td>6</td><td>12</td><td>55</td><td>50</td></tr>
+ </table>
+
+ <p class="text">The general scheme linking protocols with
+ experimental stage is as shown below:</p>
+
+ <pre>
+biosource ----------> sample --------> extract -------> labeled extract -------> hybridization
+ [grow, treatment] [extraction] [labeling] [hybridization] |
+ |
+ |
+ ----------------------------------------------------------------------------------
+ |
+ |
+ v
+hybridization ------------> image -----------> raw data file -----------> normalized data file
+ [scanning] [image_analysis] [normalization]
+</pre>
+
+ <p class="text">Note that for protocols linked to the biological
+ materials, the existence of the protocol implies the existence
+ of the target material. For example, if a growth or treatment
+ protocol is used in a given row then a sample object will be
+ automatically created for that row in the output MAGE-ML.</p>
+
+ <p class="text"><a name="hfacts" class="subhead">Experimental
+ factors</a><br> Each of your hybridizations should have one or
+ more experimental factor values associated with it. These
+ factors refer to the different conditions used in the
+ preparation of each hybridization. They may reflect variation in
+ the starting biosources (<a
+ href="#hchars">BioMaterialCharacteristics</a>) or differences in
+ the way those biosources have been treated. The general form of this heading is: </p>
+
+ <pre>FactorValue[<i><category></i>]</pre>
+
+ <p class="text">where <i><category></i> is a subclass of
+ the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#ExperimentalFactorCategory">ExperimentalFactorCategory</a>
+ class within the <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php">MGED
+ ontology</a> (Note: you are likely to need to descend several
+ subclasses into the heirarchy to find a <a
+ href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BioMaterialCharacteristics">biologically
+ meaningful</a> category). Examples are given in the table
+ below:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#fv">FactorValue[Organism]</a></td>
+ <td><a class="plain" href="detail.html#fv">FactorValue[Sex]</a></td>
+ <td><a class="plain" href="detail.html#fv">FactorValue[OrganismPart]</a></td></tr>
+ <tr><td>Homo sapiens</td><td>male</td><td>liver</td></tr>
+ <tr><td>Homo sapiens</td><td>male</td><td>kidney</td></tr>
+ <tr><td>Homo sapiens</td><td>female</td><td>liver</td></tr>
+ <tr><td>Homo sapiens</td><td>female</td><td>kidney</td></tr>
+ <tr><td>Mus musculus</td><td>male</td><td>liver</td></tr>
+ <tr><td>Mus musculus</td><td>male</td><td>kidney</td></tr>
+ <tr><td>Mus musculus</td><td>female</td><td>liver</td></tr>
+ <tr><td>Mus musculus</td><td>female</td><td>kidney</td></tr>
+ </table>
+
+ <p class="text">Your spreadsheet may contain as many
+ "FactorValue[]" columns as necessary to fully describe your
+ experiment.</p>
+
+ <p class="text"><a name="hfiles" class="subhead">Data files and
+ array information</a><br>The final class of headings is used to
+ specify which data files relate to which hybridization. There
+ are also columns in this section for linking the data files to
+ the array design used. Here is an example of these columns for a
+ spotted array:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+ <td><a class="plain" href="detail.html#filenorm">File[normalized]</a></td>
+ <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+ <td><a class="plain" href="detail.html#arrayser">Array[serial]</a></td></tr>
+ <tr><td>Data1.txt</td><td>NormData1.txt</td><td>A-EXML-1</td><td>244532</td></tr>
+ <tr><td>Data2.txt</td><td>NormData2.txt</td><td>A-EXML-1</td><td>244533</td></tr>
+ <tr><td>Data3.txt</td><td>NormData3.txt</td><td>A-EXML-1</td><td>244534</td></tr>
+ <tr><td>Data4.txt</td><td>NormData4.txt</td><td>A-EXML-1</td><td>244535</td></tr>
+ </table>
+
+ <p class="text">Below is an example of what is required for an
+ Affymetrix submission. Note that for spreadsheet submissions to
+ ArrayExpress we do not require you to upload the actual CDF
+ library file, unless you are using a custom array. For such
+ submissions the "Array[serial]" column should contain Affymetrix
+ Chip Lot numbers:</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+ <td><a class="plain" href="detail.html#filenorm">File[normalized]</a></td>
+ <td><a class="plain" href="detail.html#fileexp">File[exp]</a></td>
+ <td><a class="plain" href="detail.html#filecdf">File[cdf]</a></td>
+ <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+ <td><a class="plain" href="detail.html#arrayser">Array[serial]</a></td></tr>
+ <tr><td>Data1.CEL</td><td>Data1.CHP</td><td>Data1.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2125576</td></tr>
+ <tr><td>Data2.CEL</td><td>Data2.CHP</td><td>Data2.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2103523</td></tr>
+ <tr><td>Data3.CEL</td><td>Data3.CHP</td><td>Data3.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2468372</td></tr>
+ <tr><td>Data4.CEL</td><td>Data4.CHP</td><td>Data4.EXP</td><td>YG_S98.cdf</td><td>A-AFFY-27</td><td>2542678</td></tr>
+ </table>
+
+ <p class="text">Currently supported data file formats include
+ GenePix, Affymetrix, Agilent, ScanArray/QuantArray, ScanAlyze and Arrayvision. The <a
+ href="http://www.ebi.ac.uk/miamexpress/help/datafile_help.html">MetaColumn/MetaRow
+ format</a> favoured by ArrayExpress is of course also
+ supported.</p>
+
+ <p class="text">The "Array[accession]" column is required in any
+ spreadsheets referencing data files. If Tab2MAGE is used in the
+ absence of data files then the array information may be omitted,
+ although such usage of Tab2MAGE will obviously not be acceptable
+ as an ArrayExpress data submission.</p>
+
+ <p class="text">If you prefer to process your normalized data in
+ a single file mapping to multiple hybridizations, Tab2MAGE also
+ supports including this data as a <a href="datafiles.html#final_data_matrix">Final Data Matrix</a>
+ file. This file can be included in an experiment using the
+ "File[transformed]" column. <em>Please note</em> that the final data
+ matrix file must be formatted as described in the <a
+ href="datafiles.html">data file notes</a>.</p>
+
+ <table class="example">
+ <tr><td><a class="plain" href="detail.html#hybsection">Hybridization section</a></td><td> </td><td> </td><td> </td></tr>
+ <tr>
+ <td><a class="plain" href="detail.html#fileraw">File[raw]</a></td>
+ <td><a class="plain" href="detail.html#filefgem">File[transformed]</a></td>
+ <td><a class="plain" href="detail.html#arrayacc">Array[accession]</a></td>
+ <td><a class="plain" href="detail.html#arrayser">Hybridization</a></td></tr>
+ <tr><td>Data1.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb1</td></tr>
+ <tr><td>Data2.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb2</td></tr>
+ <tr><td>Data3.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb3</td></tr>
+ <tr><td>Data4.txt</td><td>FinalDataMatrix.txt</td><td>A-EXML-1</td><td>Hyb4</td></tr>
+ </table>
+
+ <p class="text"></p>
+
+ <p class="text">Please see the <a
+ href="ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">MAGE
+ mappings</a> for other supported column headings, and further
+ information on how the spreadsheet information is used to create
+ the MAGE-ML document.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="realexamples">Real-world Examples</a></div>
+
+ <p class="text">Below is a selection of some of the
+ real-world curated Tab2MAGE spreadsheets submitted to
+ ArrayExpress over the past few months:</p>
+
+ <table border="1">
+
+ <tr>
+ <th>Spreadsheet</th>
+ <th>Graph</th>
+ <th>Experiment name</th>
+ <th>Platform</th>
+ <th>Link to experiment</th>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-16.txt">E-TABM-16.txt</a></td>
+ <td><a href="../examples/real/E-TABM-16.png">PNG</a></td>
+ <td>FDA-CDER MTS RNA reagent cross platform test</td>
+ <td>Multiple platforms</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-16">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-18.txt">E-TABM-18.txt</a></td>
+ <td><a href="../examples/real/E-TABM-18.png">PNG</a></td>
+ <td>Transcription profiling of 35 different Arabidopsis thaliana ecotypes</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-18">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-22.txt">E-TABM-22.txt</a></td>
+ <td><a href="../examples/real/E-TABM-22.png">PNG</a></td>
+ <td>Transcription profiling of human lung cancers and lung cancer cell lines miRNA expression</td>
+ <td>One-channel custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-22">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-33.txt">E-TABM-33.txt</a></td>
+ <td><a href="../examples/real/E-TABM-33.png">PNG</a></td>
+ <td>Transcription profiling of zebrafish development</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-33">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-35.txt">E-TABM-35.txt</a></td>
+ <td><a href="../examples/real/E-TABM-35.png">PNG</a></td>
+ <td>Comparative genomic hybridization of 25 Coxiella burnetii isolates relative to the Nine Mile (RSA493) reference isolate</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-35">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-54.txt">E-TABM-54.txt</a></td>
+ <td><a href="../examples/real/E-TABM-54.png">PNG</a></td>
+ <td>Comparative genome hybridization of 137 Bordetella pertussis strains</td>
+ <td>Two-color custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-54">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-66.txt">E-TABM-66.txt</a></td>
+ <td><a href="../examples/real/E-TABM-66.png">PNG</a></td>
+ <td>Transcription profiling of normal and malignant human breast epithelial cells</td>
+ <td>Multiple platforms</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-66">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-70.txt">E-TABM-70.txt</a></td>
+ <td><a href="../examples/real/E-TABM-70.png">PNG</a></td>
+ <td>Transcription profiling of human cell lines treated with cytochalasin D and nocodazole with the aim of characterising tetraploid clones</td>
+ <td>Agilent</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-70">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-102.txt">E-TABM-102.txt</a></td>
+ <td><a href="../examples/real/E-TABM-102.png">PNG</a></td>
+ <td>Transcription profiling of wild type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide over time</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-102">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-134.txt">E-TABM-134.txt</a></td>
+ <td><a href="../examples/real/E-TABM-134.png">PNG</a></td>
+ <td>WGA-LCM and Genomewide Survey of Lung Cancer</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-134">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-136.txt">E-TABM-136.txt</a></td>
+ <td><a href="../examples/real/E-TABM-136.png">PNG</a></td>
+ <td>Transcription profiling of human and chimpanzee heart, brain, testis and lymphblastoid cell lines to study functionality of intergenic transcription</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-136">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-140.txt">E-TABM-140.txt</a></td>
+ <td><a href="../examples/real/E-TABM-140.png">PNG</a></td>
+ <td>Chromatin immunoprecipitation (ChIP-chip) of human erythroleukemia cell line K-562 with anti-histone antibodies using an ENCODE array</td>
+ <td>Two-color custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-140">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-TABM-163.txt">E-TABM-163.txt</a></td>
+ <td><a href="../examples/real/E-TABM-163.png">PNG</a></td>
+ <td>Transcription profiling of murine presomitic mesoderms of 17 samples at various time points to identify cyclic genes of the mouse segmentation clock</td>
+ <td>Affymetrix</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-TABM-163">ArrayExpress</a></td>
+ </tr>
+
+ <tr>
+ <td><a href="../examples/real/E-MEXP-880.txt">E-MEXP-880.txt</a></td>
+ <td><a href="../examples/real/E-MEXP-880.png">PNG</a></td>
+ <td>Methylation profiling of normal and cancerous breast cells from human patients and cell lines in a 125 kB region of the HOXA cluster</td>
+ <td>Two-color custom array</td>
+ <td><a href="http://www.ebi.ac.uk/arrayexpress/experiments/E-MEXP-880">ArrayExpress</a></td>
+ </tr>
+ </table>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="examples">Conceptual Examples</a></div>
+
+ <p class="text">A series of very simple conceptual examples is given here:</p>
+
+ <ul>
+ <li><a href="../examples/two_color/">Simple dye-swap experiment</a></li>
+ <li><a href="../examples/affymetrix/">Experiment with Affymetrix data files</a></li>
+ <li><a href="../examples/reference/">Experiment using a pooled reference sample</a></li>
+ <li><a href="../examples/normalized_data/">How to include normalized data files</a></li>
+ <li><a href="../examples/final_data_matrix/">Using a final data matrix file</a></li>
+ <li><a href="../examples/chip_chip/">ChIP chip experiment</a></li>
+ <li><a href="../examples/parameters/">Experiment with user-defined parameters</a></li>
+ <li><a href="../examples/loop_design/">Simple loop design example</a></li>
+ <li><a href="../examples/illumina/">Illumina data file example</a></li>
+ </ul>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <p class="text">Please see the <a
+ href="expt_check.html">experiment checker</a> and <a
+ href="tab2mage.html">tab2mage.pl</a> help notes for more
+ information.</p>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Tue Feb 26 14:12:46 GMT 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/docs/style.css b/docs/style.css
new file mode 100644
index 0000000..6184814
--- /dev/null
+++ b/docs/style.css
@@ -0,0 +1,171 @@
+body {
+ font-size: 14pt;
+ margin-left: 1em;
+ margin-right: 1em;
+ width: 860px;
+ font-family: verdana, ariel, helvetica, sans-serif;
+ line-height: 140%;
+ background-color: #ceddff;
+}
+
+pre {
+ font-size: 10pt;
+ background: #dedede;
+ font-family: monospace;
+ padding: 2pt;
+ padding-left: 10pt;
+ margin-left: 2.5%;
+ margin-right: 2.5%;
+ border: 1px solid #bcbcbc;
+}
+
+tt {
+ font-size: 100%;
+ font-weight: bold;
+ color: #333388;
+}
+
+td {
+ border: solid 1px #acacac;
+}
+
+th {
+ border: solid 1px #acacac;
+}
+
+ul {
+ text-align: left;
+ line-height: 140%;
+}
+
+ol {
+ text-align: left;
+ line-height: 140%;
+}
+
+em {
+ color: #000000;
+ font-style: normal;
+ font-weight: bold;
+}
+
+th {
+ text-align: left;
+}
+
+a.plain {
+ text-decoration: none;
+}
+
+.text {
+ text-align: justify;
+}
+
+.imghead {
+ text-align: right;
+}
+
+.section {
+}
+
+.whitetitle {
+ font-weight: normal;
+ font-size: 150%;
+ text-decoration:none;
+/* color: #ffffff; */
+/* background-color: #0000a0; */
+ border-top: none;
+ border-left: none;
+ border-right: none;
+ border-bottom: solid 1px #0000a0;
+ font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+}
+
+.pagetitle {
+ font-size: 150%;
+ font-weight: bold;
+ color: #0000a0;
+ padding: 7px;
+ border-bottom: solid 1px #0000a0;
+ border-top: none;
+ border-left: none;
+ border-right: none;
+ font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+}
+
+.layout {
+ border: none;
+ width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.example {
+ font-size: 10pt;
+ background: #dedede;
+ font-family: monospace;
+ border: solid;
+ border-width: thin;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.leftexample {
+ font-size: 10pt;
+ background: #dedede;
+ font-family: monospace;
+ border: solid;
+ border-width: thin;
+}
+
+.information {
+ background: #eeeeee;
+ border: none;
+ border-width: thin;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.sectionhead {
+ border: 1px solid #0000A0;
+ background: #c0c0c0;
+ color: #0000A0;
+ padding: 7px;
+ text-align: left;
+ font-size: 140%;
+ font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+}
+
+.sectionfoot {
+ text-align:right;
+}
+
+.subhead {
+ text-align: left;
+ font-weight: bold;
+ font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+ font-size: 120%;
+}
+
+.picture {
+ display:block;
+ margin-left: auto;
+ margin-right: auto;
+ border-style: solid;
+ border-width: 2px;
+}
+
+.faqheading {
+ font-weight: bold;
+ font-size: 130%;
+}
+
+h1 {
+ font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+ font-size: 150%;
+}
+
+h2 {
+ font-family: "gill sans", verdana, ariel, helvetica, sans-serif;
+ font-size: 120%;
+}
diff --git a/docs/submit.css b/docs/submit.css
new file mode 100644
index 0000000..bebe4b7
--- /dev/null
+++ b/docs/submit.css
@@ -0,0 +1,91 @@
+body {
+ font-size: 12px;
+ font-family: verdana, geneva, arial, helvetica, sans-serif;
+ color: #111;
+ background-color: #ffffff;
+}
+
+input {
+ border-color: #b9cfd8;
+ border-style: solid;
+ border-width: 1px;
+ background-color: #ffffff;
+ padding: 4px 6px;
+ cursor: default;
+}
+
+.sidebar-title {
+ border-color: #dddddd;
+ border-style: solid;
+ border-width: 1px;
+ font-size: 150%;
+ padding: 4px 6px;
+ background-color: #ffffff;
+}
+
+.page-title {
+ font-size: 120%;
+}
+
+td {
+ background-color: #eeeeee;
+ padding: 4px;
+}
+
+th {
+ background-color: #dddddd;
+ padding: 4px;
+}
+
+.text {
+ width: 40em;
+ text-align: justify;
+}
+
+.navbar {
+ width: 30em;
+ padding: 4px;
+}
+
+.fieldWithErrors {
+ padding: 0px;
+ background-color: red;
+ display: table;
+}
+
+/* Set up the sidebar */
+div.link-list {
+ width:15%;
+ position:absolute;
+ top:0;
+ bottom:0;
+ font-size:100%;
+ padding-left:1%;
+ padding-right:1%;
+ padding-top:1%;
+ background-color: #eeeeee;
+}
+
+#main {
+ margin-left:15%;
+ padding-left:3%;
+}
+
+#sidebar {
+ left:0;
+ text-align: center;
+ line-height: 150%;
+}
+
+.notice {
+ font-size: 90%;
+ color: blue;
+}
+
+:link {
+ color: #3333aa;
+}
+
+:visited {
+ color: #7700aa;
+}
\ No newline at end of file
diff --git a/docs/t2m_visualize.html b/docs/t2m_visualize.html
new file mode 100644
index 0000000..f0ab4e2
--- /dev/null
+++ b/docs/t2m_visualize.html
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>t2m_visualize.pl - a rapid visualization script for Tab2MAGE
+spreadsheets.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="T2M_logo.png" border="0" height="50" alt=
+"Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Script detail: t2m_visualize.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>t2m_visualize.pl - a rapid visualization script for Tab2MAGE
+spreadsheets.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ t2m_visualize.pl -e <Tab2MAGE spreadsheet file>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script simply parses a Tab2MAGE spreadsheet into a format
+readable by the Graphviz ``dot'' application. If Graphviz has been
+installed the script then attempts to generate a PNG file. This PNG
+file contains a graph which represents the experiment which has
+been coded. Both the tab2mage.pl and expt_check.pl scripts include
+this functionality, but t2m_visualize.pl has two advantages: it
+does not need the data files to be present, and it works much
+faster.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2de_spreadsheet_filename" id=
+"item__2de_spreadsheet_filename"><strong>-e</strong>
+<code>spreadsheet filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to visualize.</p>
+</dd>
+<dt><strong><a name="item__2df_font_name" id=
+"item__2df_font_name"><strong>-f</strong> <code>font
+name</code></a></strong></dt>
+<dd>
+<p>Font to use in graph generation (optional).</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Print the version of the string.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Overwrite (``clobber'') existing files.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Prints a short help text.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2005.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/tab2mage.html b/docs/tab2mage.html
new file mode 100644
index 0000000..491ae06
--- /dev/null
+++ b/docs/tab2mage.html
@@ -0,0 +1,289 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content=
+"HTML Tidy for Mac OS X (vers 1 September 2005), see www.w3.org" />
+<title>tab2mage.pl - a script to produce valid MAGE-ML from a set
+of raw datafiles and a summary spreadsheet of defined
+format.</title>
+<link rel="stylesheet" href="/docs/style.css" type="text/css" />
+<meta http-equiv="content-type" content=
+"text/html; charset=us-ascii" />
+<link rev="made" href="mailto:rayner at ebi.ac.uk" />
+</head>
+<body>
+<p><a name="__index__"></a></p>
+<!-- INDEX BEGIN -->
+<!--
+
+<ul>
+
+ <li><a href="#name">NAME</a></li>
+ <li><a href="#synopsis">SYNOPSIS</a></li>
+ <li><a href="#description">DESCRIPTION</a></li>
+ <li><a href="#experiment_summary_file">EXPERIMENT SUMMARY FILE</a></li>
+ <ul>
+
+ <li><a href="#experiment_section">Experiment section</a></li>
+ <li><a href="#protocol_section">Protocol section</a></li>
+ <li><a href="#hybridization_section">Hybridization section</a></li>
+ </ul>
+
+ <li><a href="#options">OPTIONS</a></li>
+ <li><a href="#quantitationtype_file">QUANTITATIONTYPE FILE</a></li>
+ <li><a href="#author">AUTHOR</a></li>
+</ul>
+-->
+<!-- INDEX END -->
+<div><a name="top" id="top"></a>
+<table class="layout">
+<tr>
+<td class="whitetitle" width="100"><a href=
+"../index.html"><img src="T2M_logo.png" border="0" height="50" alt=
+"Tab2MAGE logo" /></a></td>
+<td class="pagetitle">Script detail: tab2mage.pl</td>
+</tr>
+</table>
+</div>
+<hr />
+<h1><a name="name" id="name">NAME</a></h1>
+<p>tab2mage.pl - a script to produce valid MAGE-ML from a set of
+raw datafiles and a summary spreadsheet of defined format.</p>
+<hr />
+<h1><a name="synopsis" id="synopsis">SYNOPSIS</a></h1>
+<pre>
+ tab2mage.pl -e <spreadsheet_filename> -t <target_directory>
+</pre>
+<hr />
+<h1><a name="description" id="description">DESCRIPTION</a></h1>
+<p>This script is designed to take a set of unprocessed raw
+datafiles and a spreadsheet providing the experiment metadata and
+generate valid MAGE-ML. The supported data file formats are listed
+in the accompanying documentation.</p>
+<p>Important: the data files must each contain a column heading
+line immediately preceding the start of the data. Typically, raw
+unprocessed data files have this as standard. Do not remove this
+line, as the script will use it to determine which
+QuantitationTypes to keep and which to discard.</p>
+<p>There are two auxilliary files which can be supplied alongside
+the data files. The first, experiment summary spreadsheet describes
+the way in which the experiment was performed. The second simply
+defines the QuantitationTypes which are to be extracted from the
+data files. This QuantitationType file is optional, however, as the
+script is supplied with a set of defaults determined by a survey of
+incoming QuantitationTypes by ArrayExpress curators. The script
+generates a log file in the target directory which details which
+columns have been ignored.</p>
+<hr />
+<h1><a name="experiment_summary_file" id=
+"experiment_summary_file">EXPERIMENT SUMMARY FILE</a></h1>
+<p>The experiment summary file has a flexible format based around a
+set of predefined column headers. Comments may be inserted using
+the '#' character at the start of any line. The file consists of
+three sections, each of which ends with a blank line:</p>
+<h2><a name="experiment_section" id="experiment_section">Experiment
+section</a></h2>
+<p>This section contains top-level information about the
+experiment, such as the title, description and accession number.
+The section is constructed in two columns, with row names as
+described in <a href=
+"ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">SUPPORTED
+HEADINGS in the ArrayExpress::Curator::MAGE::Definitions
+manpage</a>.</p>
+<h2><a name="protocol_section" id="protocol_section">Protocol
+section</a></h2>
+<p>Protocols are defined as needed in this section. The section is
+organized into rows, with column headings as described in <a href=
+"ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">SUPPORTED
+HEADINGS in the ArrayExpress::Curator::MAGE::Definitions
+manpage</a>. If all of the protocols used in the experiment have
+previously been loaded into ArrayExpress and given accession
+numbers, this whole section can be omitted.</p>
+<h2><a name="hybridization_section" id=
+"hybridization_section">Hybridization section</a></h2>
+<p>This section contains the bulk of the experiment information. At
+its simplest, each row describes the route taken from BioSource to
+output data file.</p>
+<p>Column headings for BioMaterialCharacteristics and FactorValue
+should be provided in the following form:</p>
+<dl>
+<dt><strong><a name=
+"item_biomaterialcharacteristics_5b_3cmged_ontology_cate" id=
+"item_biomaterialcharacteristics_5b_3cmged_ontology_cate">BioMaterialCharacteristics[<MGED
+Ontology Category>]</a></strong></dt>
+<dt><strong><a name=
+"item_factorvalue_5b_3cmged_ontology_category_3e_5d" id=
+"item_factorvalue_5b_3cmged_ontology_category_3e_5d">FactorValue[<MGED
+Ontology Category>]</a></strong></dt>
+</dl>
+<p>Each of these headings should contain a valid Category subclass
+from the MGED ontology. The values in the columns must likewise be
+valid ontology entry values for these subclasses.</p>
+<p>Multichannel (e.g., two-colour) data can be described by
+entering each channel as a separate line. Pooling can be described
+at multiple levels by using as many lines as necessary to describe
+all the relationships between upstream and downstream samples.</p>
+<p>In a sense the Hybridization table can be compared to an SQL
+database table in the way that it provides links between MAGE
+objects. Again, the recognized column headings for this section are
+described in <a href=
+"ArrayExpress/Curator/MAGE/Definitions.html#supported_headings">SUPPORTED
+HEADINGS in the ArrayExpress::Curator::MAGE::Definitions
+manpage</a>.</p>
+<hr />
+<h1><a name="options" id="options">OPTIONS</a></h1>
+<dl>
+<dt><strong><a name="item__2de_filename" id=
+"item__2de_filename"><strong>-e</strong>
+<code>filename</code></a></strong></dt>
+<dd>
+<p>The Tab2MAGE spreadsheet to be checked.</p>
+</dd>
+<dt><strong><a name="item__2dt_directory" id=
+"item__2dt_directory"><strong>-t</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>The target directory to be created. This directory will contain
+the MAGE-ML file and external data files ready for validation.</p>
+</dd>
+<dt><strong><a name="item__2dn_accession" id=
+"item__2dn_accession"><strong>-n</strong>
+<code>accession</code></a></strong></dt>
+<dd>
+<p>Normally the script uses the experiment accession number from
+the spreadsheet to be parsed. In cases where no accession has been
+entered in the spreadsheet, or you wish to override that accession,
+use this option.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option allows you to specify a
+custom QuantitationType definition file to override those defined
+as part of the Tab2MAGE package. See <a href=
+"ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dq_qt_filename" id=
+"item__2dq_qt_filename"><strong>-Q</strong> <code>QT
+filename</code></a></strong></dt>
+<dd>
+<p>QuantitationType file. This option will add the new
+QuantitationType definitions to those included with the Tab2MAGE
+package. See <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for more
+information.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-k</strong></a></strong></dt>
+<dd>
+<p>Keep all columns in the data files, regardless of whether they
+are recognized or not. Unrecognized QTs will be created as generic
+SpecializedQuantitationTypes in the output MAGE-ML.</p>
+</dd>
+<dt><strong><a name="item__2dk" id=
+"item__2dk"><strong>-K</strong></a></strong></dt>
+<dd>
+<p>If the autosubmissions system is configured, tab2mage.pl will
+automatically reassign protocol accessions to fit a local
+convention. Use the -K option to suppress this behaviour.</p>
+</dd>
+<dt><strong><a name="item__2ds" id=
+"item__2ds"><strong>-s</strong></a></strong></dt>
+<dd>
+<p>Standalone option. This prevents the script from attempting to
+connect to ArrayExpress to retrieve array information.</p>
+</dd>
+<dt><strong><a name="item__2dr_namespace" id=
+"item__2dr_namespace"><strong>-R</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>Reporter identifier prefix. By default the script uses the
+MIAMExpress convention for generating reporter identifiers. This
+option allows you to override this behaviour by supplying an
+alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dc_namespace" id=
+"item__2dc_namespace"><strong>-C</strong>
+<code>namespace</code></a></strong></dt>
+<dd>
+<p>CompositeSequence identifier prefix. By default the script uses
+the MIAMExpress convention for generating composite sequence
+identifiers. This option allows you to override this behaviour by
+supplying an alternate prefix for identifiers.</p>
+</dd>
+<dt><strong><a name="item__2dd_directory" id=
+"item__2dd_directory"><strong>-d</strong>
+<code>directory</code></a></strong></dt>
+<dd>
+<p>Source directory containing all the data files referenced in the
+tab2mage spreadsheet. If this is omitted, the current working
+directory will be searched for data files.</p>
+</dd>
+<dt><strong><a name="item__2dp" id=
+"item__2dp"><strong>-P</strong></a></strong></dt>
+<dd>
+<p>By default, native (usually binary) file formats are used for
+Affymetrix CEL files and all NimbleScan (NimbleGen) files. This
+encoding uses far less overhead and retains the files in their
+original formats; this is often appealing to end-users. When used
+for ArrayExpress submissions, this option allows for such files to
+be directly downloadable from the ArrayExpress web interface.
+However, in unusual circumstances when you might wish to use
+plain-text encoding for these datafile, use this option.</p>
+</dd>
+<dt><strong><a name="item__2dl" id=
+"item__2dl"><strong>-L</strong></a></strong></dt>
+<dd>
+<p>Ignore the data file size limit as configured in Config.yml
+(i.e., MAX_DATAFILE_SIZE).</p>
+</dd>
+<dt><strong><a name="item__2df_font_name" id=
+"item__2df_font_name"><strong>-f</strong> <code>font
+name</code></a></strong></dt>
+<dd>
+<p>Name of the font to be used for Graphviz-generated PNGs.</p>
+</dd>
+<dt><strong><a name="item__2dc" id=
+"item__2dc"><strong>-c</strong></a></strong></dt>
+<dd>
+<p>Overwrite preexisting files (``clobber'' option).</p>
+</dd>
+<dt><strong><a name="item__2dv" id=
+"item__2dv"><strong>-v</strong></a></strong></dt>
+<dd>
+<p>Prints the version number of the script.</p>
+</dd>
+<dt><strong><a name="item__2dh" id=
+"item__2dh"><strong>-h</strong></a></strong></dt>
+<dd>
+<p>Print a short help text summarizing these options.</p>
+</dd>
+</dl>
+<hr />
+<h1><a name="quantitationtype_file" id=
+"quantitationtype_file">QUANTITATIONTYPE FILE</a></h1>
+<p>Please see <a href="ArrayExpress/Datafile/QT_list.html">the
+ArrayExpress::Datafile::QT_list manpage</a> for a description of
+the format of this file.</p>
+<hr />
+<h1><a name="author" id="author">AUTHOR</a></h1>
+<p>Tim Rayner (<a href=
+"mailto:rayner at ebi.ac.uk">rayner at ebi.ac.uk</a>), ArrayExpress team,
+EBI, 2004.</p>
+<p>Acknowledgements go to the ArrayExpress curation team for
+feature requests, bug reports and other valuable comments.
+Particular credit goes to Ele Holloway, who was responsible for
+curating the lists of QuantitationTypes included with this
+script.</p>
+<hr />
+<a href="http://sourceforge.net"><img src=
+"http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+width="125" height="37" border="0" alt=
+"SourceForge.net Logo" /></a>
+</body>
+</html>
diff --git a/docs/usage.html b/docs/usage.html
new file mode 100644
index 0000000..d95db49
--- /dev/null
+++ b/docs/usage.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Typical usage</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="style.css">
+ </head>
+
+ <body>
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100"><a href="../index.html"><img src="T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"></a></td>
+ <td class="pagetitle">Typical usage</td>
+ </tr>
+ </table>
+ </div>
+
+ <p class="text">This page gives examples of the typical ways in
+ which the scripts in this package are invoked, and addresses some
+ common pitfalls.</p>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="columns">Unknown Data File Columns</a></div>
+
+ <p class="text">Once you have created your sample annotation spreadsheet, you
+ are ready to run the tab2mage.pl script. In an ideal world, you
+ could simply invoke the following command:</p>
+
+ <pre>tab2mage.pl -e <spreadsheet filename> -t <target directory to be created></pre>
+
+ <p class="text">This command should be executed from within the directory
+ containing the data files and the annotation spreadsheet.</p>
+
+ <p class="text">In reality, the situation is more
+ complicated. Manufacturers of scanning and image processing
+ software change the format of their output data files from time to
+ time. The most obvious example of this is when the column headings
+ (quantitation types) within a data file are altered. Additionally,
+ data files may have been modified in subsequent analyses, adding
+ customized columns. This creates a problem, because for accurate
+ encoding in MAGE-ML we need to know more information about these
+ quantitation types than just their name. Tab2MAGE stores this
+ information in a file named QT_list.txt within the installed
+ library directory. This file contains the information gathered so
+ far by ArrayExpress curators. A new version of this file is
+ released with each Tab2MAGE release as more information becomes
+ available. However, it is entirely possible that you will be using
+ data files which contain unknown quantitation types.</p>
+
+ <p class="text"><em>The default action for Tab2MAGE is to ignore unknown
+ quantitation types.</em> The package was designed like this to
+ allow very precise control of which data columns are exported to
+ MAGE-ML, and which are discarded. Tab2MAGE includes a mechanism to
+ customize its behavior so that a given site installation can set
+ up a default set of data columns which are then always exported.</p>
+
+ <p class="text">Any quantitation types which are discarded during Tab2MAGE
+ processing will be reported in the <tt>tab2mage.log</tt> file, created in
+ the target directory. To determine which columns are at risk
+ before running the tab2mage.pl script, we recommend using the <a
+ href="expt_check.html">experiment checker script</a> in Tab2MAGE mode:</p>
+
+ <pre>expt_check.pl -e <Tab2MAGE spreadsheet filename></pre>
+
+ <p class="text">This script will run some checks on the data files and generate
+ a series of log files. One of these, the <tt>expt_columnheadings.log</tt>
+ file, lists the unknown columns which would be discarded in a
+ Tab2MAGE run.</p>
+
+ <p class="text"><em>For Tab2MAGE to export unknown quantitation types</em>, you must
+ override the built-in quantitation type information using the <tt>-q</tt> or <tt>-Q</tt>
+ command-line options:</p>
+
+ <pre>expt_check.pl -q <new QT file> -e <spreadsheet filename></pre>
+ <pre>tab2mage.pl -q <new QT file> -e <spreadsheet filename> -t <target directory to be created></pre>
+
+ <p class="text"><em>Note</em> that using the script with the
+ <tt>-q</tt> option will ignore the default QT information supplied
+ with the script; you must, therefore, include all the QTs you wish
+ to export in your new QT file. Alternatively, you may wish to use
+ the <tt>-Q</tt> option, which will use the new QTs alongside those
+ embedded in the Tab2MAGE package (but see the <a
+ href="#one_sw_per_file">note below</a>). As a last resort in case
+ of difficulties, the tab2mage.pl script also accepts a <tt>-k</tt>
+ option, which will keep <em>all</em> the unknown QTs, including
+ them in the output MAGE-ML as generic
+ SpecializedQuantitationTypes.</p>
+
+ <p class="text">The format of the QT file is described in <a
+ href="ArrayExpress/Datafile/QT_list.html#quantitation_types">these
+ QT file format notes</a>, and the <a
+ href="../examples/other_QTs.txt">other_QTs.txt</a> file included
+ with the package provides a set of examples. At its simplest,
+ however, the file can be laid out in this form:</p>
+
+ <pre>>>>Software name[Manufacturer]
+QT name 1
+QT name 2
+QT name 3
+QT name 4
+QT name 5
+QT name 6...</pre>
+
+ <p class="text">(This example will create every quantitation type as a
+ SpecializedQuantitationType object).</p>
+
+ <p class="text">For correct creation of the appropriate
+ QuantitationType subclasses, with suitable associations and
+ attributes, please see the <a
+ href="ArrayExpress/Datafile/QT_list.html#quantitation_types">QT
+ file format notes</a>. </p>
+
+ <p class="text"><em><a
+ name="one_sw_per_file">Note:</a></em> scanning software packages
+ often use column headings which are ambiguous in their
+ origin. Some column headings (e.g., "Flags") are used by many
+ different software manufacturers, and this can lead to
+ ambiguities in assigning QTs to software type. As a result, the
+ Tab2MAGE scripts have to assume that there is only one software
+ type per data file. The desired quantitation types for a given
+ data file must all be grouped together under a single <a
+ href="ArrayExpress/Datafile/QT_list.html#quantitation_types">software
+ group</a>. This prevents the creation of incorrect quantitation
+ types for ambiguous column headings which are used by more than
+ one software type.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div class="section">
+
+ <div class="sectionhead"><a name="features">Feature Coordinate Checking</a></div>
+
+ <p class="text">It is not uncommon to encounter a situation in
+ which one or more data files contain features not represented in
+ the corresponding array design. The experiment checker script will
+ automatically check for this problem when run in Tab2MAGE or
+ MIAMExpress mode. However, if you have only a set of data files
+ and a suitable <a
+ href="http://www.ebi.ac.uk/miamexpress/help/adf/">Array Definition
+ File</a> (ADF) to check them against, you can run the script in
+ stand-alone mode:</p>
+
+ <pre>expt_check.pl -a <ADF filename> -s <list of data files></pre>
+
+ <p class="text">Please see the <a
+ href="expt_check.html">experiment checker</a> and <a
+ href="tab2mage.html">tab2mage.pl</a> help notes for more
+ information.</p>
+
+ <p CLASS="sectionfoot">[ <a href="javascript: history.back()">Back</a> ][ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <hr>
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Mon Jan 17 09:45:42 GMT 2005 -->
+<!-- hhmts start -->
+Last modified: Sun Dec 10 12:11:16 GMT 2006
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/examples/affymetrix/Data1.CEL b/examples/affymetrix/Data1.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/affymetrix/Data1.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093] test 1:CLS=4733 RWS=4733 XIN=3 YIN=3 VE=17 2.0 03/27/03 23:53:16 Test.1sq 6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X Y MEAN STDV NPIXELS
+ 0 0 158.0 28.9 36
+ 1 0 8875.5 1390.6 36
+ 2 0 160.0 25.3 36
+ 3 0 8552.5 1027.3 36
+ 4 0 78.8 15.6 36
+ 5 0 138.8 25.5 36
+ 6 0 8124.0 1124.9 36
+ 7 0 146.3 22.9 36
+ 8 0 7531.3 1038.5 36
+ 9 0 132.3 21.7 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X Y
+3 0
+6 0
+9 0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X Y ORIGMEAN
diff --git a/examples/affymetrix/Data1.CHP b/examples/affymetrix/Data1.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/affymetrix/Data1.CHP differ
diff --git a/examples/affymetrix/Data1.EXP b/examples/affymetrix/Data1.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data1.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/affymetrix/Data2.CEL b/examples/affymetrix/Data2.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/affymetrix/Data2.CEL differ
diff --git a/examples/affymetrix/Data2.CHP b/examples/affymetrix/Data2.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/affymetrix/Data2.CHP differ
diff --git a/examples/affymetrix/Data2.EXP b/examples/affymetrix/Data2.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data2.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/affymetrix/Data3.CEL b/examples/affymetrix/Data3.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/affymetrix/Data3.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093] test 1:CLS=4733 RWS=4733 XIN=3 YIN=3 VE=17 2.0 03/27/03 23:53:16 Test.1sq 6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X Y MEAN STDV NPIXELS
+ 0 0 158.0 28.9 36
+ 1 0 8875.5 1390.6 36
+ 2 0 160.0 25.3 36
+ 3 0 8552.5 1027.3 36
+ 4 0 78.8 15.6 36
+ 5 0 138.8 25.5 36
+ 6 0 8124.0 1124.9 36
+ 7 0 146.3 22.9 36
+ 8 0 7531.3 1038.5 36
+ 9 0 132.3 21.7 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X Y
+3 0
+6 0
+9 0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X Y ORIGMEAN
diff --git a/examples/affymetrix/Data3.CHP b/examples/affymetrix/Data3.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/affymetrix/Data3.CHP differ
diff --git a/examples/affymetrix/Data3.EXP b/examples/affymetrix/Data3.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data3.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/affymetrix/Data4.CEL b/examples/affymetrix/Data4.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/affymetrix/Data4.CEL differ
diff --git a/examples/affymetrix/Data4.CHP b/examples/affymetrix/Data4.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/affymetrix/Data4.CHP differ
diff --git a/examples/affymetrix/Data4.EXP b/examples/affymetrix/Data4.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/affymetrix/Data4.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/affymetrix/YG_S98.cdf b/examples/affymetrix/YG_S98.cdf
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/examples/affymetrix/YG_S98.cdf
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/examples/affymetrix/affymetrix.png b/examples/affymetrix/affymetrix.png
new file mode 100644
index 0000000..2256f5c
Binary files /dev/null and b/examples/affymetrix/affymetrix.png differ
diff --git a/examples/affymetrix/affymetrix.txt b/examples/affymetrix/affymetrix.txt
new file mode 100644
index 0000000..5f7a00c
--- /dev/null
+++ b/examples/affymetrix/affymetrix.txt
@@ -0,0 +1,36 @@
+# Simple example summary spreadsheet for tab2mage.pl script.
+# This file illustrates a simple Affymetrix experiment.
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-2
+quality_control biological_replicate
+experiment_design_type strain_or_line_design;co-expression_design
+name Transcriptome analysis of invasive verses non-invasive strains of budding yeast
+description An experiment was performed to...
+release_date 2004-08-30
+submission_date 2004-07-28
+submitter John Falstaff
+# The publication details below are fictitious
+publication_title Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast
+authors John Falstaff
+journal Nature Genetics
+volume 12
+issue 4
+pages 123-456
+year 2004
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-EXML-1 Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8 Yeast growth
+P-EXML-3 Your protocol text here Cell lysis and RNA prep
+P-EXML-4 Your protocol text here cDNA labeling
+
+Hybridization section
+File[raw] File[normalized] File[exp] File[cdf] Array[accession] Array[serial] Protocol[grow] Protocol[extraction] Protocol[labeling] BioSource BioSourceMaterial FactorValue[StrainOrLine] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Sex]
+Data1.CEL Data1.CHP Data1.EXP YG_S98.cdf A-AFFY-27 243455 P-EXML-1 P-EXML-3 P-EXML-4 S288C 1 whole_organism S288C Saccharomyces cerevisiae S288C mating_type_a
+Data2.CEL Data2.CHP Data2.EXP YG_S98.cdf A-AFFY-27 244530 P-EXML-1 P-EXML-3 P-EXML-4 Sigma1278b 1 whole_organism Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
+Data3.CEL Data3.CHP Data3.EXP YG_S98.cdf A-AFFY-27 348762 P-EXML-1 P-EXML-3 P-EXML-4 S288C 2 whole_organism S288C Saccharomyces cerevisiae S288C mating_type_a
+Data4.CEL Data4.CHP Data4.EXP YG_S98.cdf A-AFFY-27 234678 P-EXML-1 P-EXML-3 P-EXML-4 Sigma1278b 2 whole_organism Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
diff --git a/examples/chip_chip/Data1.txt b/examples/chip_chip/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/chip_chip/Data1.txt
@@ -0,0 +1,37 @@
+ATF 1.0
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 0.475 0.481 0.494 0.491 0.129 0.413 0.731 52 376 621 616 -1.074 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 0.494 0.496 0.500 0.599 0.409 0.433 0.777 120 778 1979 1904 -1.019 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 0.501 0.490 0.493 0.641 1.280 0.460 0.837 120 810 1379 1491 -0.998 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 0.780 0.782 0.819 1.372 3.252 0.675 0.722 120 796 1696 1663 -0.359 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 0.455 0.460 0.450 0.600 0.632 0.382 0.751 120 930 1334 1251 -1.137 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 0.420 0.431 0.473 0.696 2.121 0.362 0.589 120 914 426 438 -1.252 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 0.223 0.229 0.226 0.313 0.289 0.160 0.283 80 552 126 134 -2.163 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 0.525 0.503 0.522 0.741 1.710 0.445 0.754 120 816 1089 1052 -0.929 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 0.388 0.384 0.368 0.443 0.369 0.351 0.721 120 796 408 418 -1.367 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 0.885 0.912 0.923 1.477 3.031 0.751 0.698 120 810 1757 1648 -0.176 825 932 786 862 0
diff --git a/examples/chip_chip/Data2.txt b/examples/chip_chip/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/chip_chip/Data2.txt
@@ -0,0 +1,40 @@
+ATF 1.0
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 0.447 0.473 0.502 0.428 2.467 0.472 0.332 80 656 204 221 -1.162 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 0.530 0.484 0.477 0.503 3.009 0.418 0.611 120 820 1042 1177 -0.916 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 0.669 0.588 0.592 0.604 3.170 0.511 0.592 120 820 611 851 -0.579 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 0.742 0.684 0.755 0.675 3.312 0.576 0.412 120 820 385 608 -0.430 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 0.479 0.514 0.506 0.502 2.251 0.507 0.593 120 820 935 984 -1.061 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 0.506 0.525 0.546 0.488 3.047 0.532 0.398 120 810 244 241 -0.982 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 0.592 0.612 1.025 0.705 2.930 1.819 0.071 52 340 78 79 -0.757 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 0.497 0.506 0.543 0.522 3.195 0.482 0.645 120 820 834 845 -1.008 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 0.606 0.624 0.669 0.614 2.841 0.577 0.414 80 616 302 328 -0.722 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 0.584 0.503 0.457 0.508 2.361 0.481 0.581 120 930 507 708 -0.775 187 320 237 471 0
diff --git a/examples/chip_chip/chip_chip.png b/examples/chip_chip/chip_chip.png
new file mode 100644
index 0000000..758f3bc
Binary files /dev/null and b/examples/chip_chip/chip_chip.png differ
diff --git a/examples/chip_chip/chip_chip.txt b/examples/chip_chip/chip_chip.txt
new file mode 100644
index 0000000..9d6713e
--- /dev/null
+++ b/examples/chip_chip/chip_chip.txt
@@ -0,0 +1,39 @@
+# Simple example summary spreadsheet for tab2mage.pl script.
+# This file gives an example of a ChIP chip experiment.
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-3
+experiment_design_type strain_or_line_design;binding_site_identification_design
+name RTG3 and CAD1
+description An experiment was performed to...
+release_date 2004-08-30
+submission_date 2004-07-28
+submitter John Falstaff
+# The publication details below are fictitious
+publication_title Chromatin IP using myc-tagged RTG3 and CAD1 strains of Saccharomyces cerevisiae
+authors John Falstaff
+journal Nature Genetics
+volume 12
+issue 4
+pages 123-456
+year 2004
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-EXML-1 Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8 Yeast growth
+P-EXML-2 Your protocol text here Yeast cell harvesting
+P-EXML-3 Your protocol text here Cell lysis and RNA prep
+P-EXML-4 Your protocol text here cDNA labeling
+P-EXML-5 Your protocol text here Hybridization
+P-EXML-6 Your protocol text here Scanning
+P-EXML-7 Your protocol text here Chromatin immunoprecipitation
+
+Hybridization section
+File[raw] Array[accession] Array[serial] Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[immunoprecipitate] Protocol[labeling] Protocol[hybridization] Protocol[scanning] BioSource BioSourceMaterial SampleMaterial ExtractMaterial ImmunoprecipitateMaterial LabeledExtractMaterial Dye FactorValue[Genotype] BioMaterialCharacteristics[Genotype] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Sex]
+Data1.txt A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-7 P-EXML-4 P-EXML-5 P-EXML-6 CAD1 whole_organism whole_organism genomic_DNA genomic_DNA genomic_DNA Cy5 CAD1::myc9:TRP1 CAD1::myc9:TRP1 Saccharomyces cerevisiae S288C mating_type_a
+Data1.txt A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 CAD1 whole_organism whole_organism genomic_DNA genomic_DNA Cy3 CAD1::myc9:TRP1 CAD1::myc9:TRP1 Saccharomyces cerevisiae S288C mating_type_a
+Data2.txt A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-7 P-EXML-4 P-EXML-5 P-EXML-6 RTG3 whole_organism whole_organism genomic_DNA genomic_DNA genomic_DNA Cy5 RTG3::myc18:TRP1 RTG3::myc18:TRP1 Saccharomyces cerevisiae S288C mating_type_a
+Data2.txt A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 RTG3 whole_organism whole_organism genomic_DNA genomic_DNA Cy3 RTG3::myc18:TRP1 RTG3::myc18:TRP1 Saccharomyces cerevisiae S288C mating_type_a
diff --git a/examples/final_data_matrix/Data1.txt b/examples/final_data_matrix/Data1.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/final_data_matrix/Data1.txt
@@ -0,0 +1,37 @@
+ATF 1
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 52 376 621 616 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 120 778 1979 1904 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 120 810 1379 1491 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 120 796 1696 1663 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 120 930 1334 1251 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 120 914 426 438 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 80 552 126 134 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 120 816 1089 1052 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 120 796 408 418 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 120 810 1757 1648 825 932 786 862 0
diff --git a/examples/final_data_matrix/Data2.txt b/examples/final_data_matrix/Data2.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/final_data_matrix/Data2.txt
@@ -0,0 +1,40 @@
+ATF 1
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 80 656 204 221 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 120 820 1042 1177 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 120 820 611 851 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 120 820 385 608 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 120 820 935 984 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 120 810 244 241 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 52 340 78 79 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 120 820 834 845 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 80 616 302 328 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 120 930 507 708 187 320 237 471 0
diff --git a/examples/final_data_matrix/Data3.txt b/examples/final_data_matrix/Data3.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/final_data_matrix/Data3.txt
@@ -0,0 +1,37 @@
+ATF 1
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 52 376 621 616 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 120 778 1979 1904 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 120 810 1379 1491 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 120 796 1696 1663 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 120 930 1334 1251 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 120 914 426 438 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 80 552 126 134 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 120 816 1089 1052 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 120 796 408 418 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 120 810 1757 1648 825 932 786 862 0
diff --git a/examples/final_data_matrix/Data4.txt b/examples/final_data_matrix/Data4.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/final_data_matrix/Data4.txt
@@ -0,0 +1,40 @@
+ATF 1
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 80 656 204 221 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 120 820 1042 1177 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 120 820 611 851 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 120 820 385 608 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 120 820 935 984 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 120 810 244 241 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 52 340 78 79 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 120 820 834 845 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 80 616 302 328 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 120 930 507 708 187 320 237 471 0
diff --git a/examples/final_data_matrix/E-EXML-7.xml.dot.jpg b/examples/final_data_matrix/E-EXML-7.xml.dot.jpg
new file mode 100644
index 0000000..390886f
Binary files /dev/null and b/examples/final_data_matrix/E-EXML-7.xml.dot.jpg differ
diff --git a/examples/final_data_matrix/FGEM.txt b/examples/final_data_matrix/FGEM.txt
new file mode 100644
index 0000000..6bcb194
--- /dev/null
+++ b/examples/final_data_matrix/FGEM.txt
@@ -0,0 +1,11 @@
+Reporter Identifier RMA(Sigma1278b_1)(Sigma1278b_2) RMA(W303a_1)(W303a_2)
+A102340 0.147 0.473
+A102341 0.53 0.484
+A102342 0.169 0.188
+A102343 0.742 0.684
+A102344 0.479 0.514
+A102345 0.106 0.525
+A102346 0.592 0.612
+A102347 0.000 0.106
+A102348 0.606 0.624
+A102349 0.584 0.503
diff --git a/examples/final_data_matrix/NormData1.txt b/examples/final_data_matrix/NormData1.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/final_data_matrix/NormData1.txt
@@ -0,0 +1,11 @@
+MetaColumn MetaRow Column Row Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+1 1 1 1 A102340 0.475 0.481 0.494 0.491 0.129 0.413 0.731 -1.074
+1 1 2 1 A102341 0.494 0.496 0.5 0.599 0.409 0.433 0.777 -1.019
+1 1 3 1 A102342 0.501 0.49 0.493 0.641 1.28 0.46 0.837 -0.998
+1 1 4 1 A102343 0.78 0.782 0.819 1.372 3.252 0.675 0.722 -0.359
+1 1 5 1 A102344 0.455 0.46 0.45 0.6 0.632 0.382 0.751 -1.137
+1 1 6 1 A102345 0.42 0.431 0.473 0.696 2.121 0.362 0.589 -1.252
+1 1 7 1 A102346 0.223 0.229 0.226 0.313 0.289 0.16 0.283 -2.163
+1 1 8 1 A102347 0.525 0.503 0.522 0.741 1.71 0.445 0.754 -0.929
+1 1 9 1 A102348 0.388 0.384 0.368 0.443 0.369 0.351 0.721 -1.367
+1 1 10 1 A102349 0.885 0.912 0.923 1.477 3.031 0.751 0.698 -0.176
diff --git a/examples/final_data_matrix/NormData2.txt b/examples/final_data_matrix/NormData2.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/final_data_matrix/NormData2.txt
@@ -0,0 +1,11 @@
+Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+A102340 0.447 0.473 0.502 0.428 2.467 0.472 0.332 -1.162
+A102341 0.53 0.484 0.477 0.503 3.009 0.418 0.611 -0.916
+A102342 0.669 0.588 0.592 0.604 3.17 0.511 0.592 -0.579
+A102343 0.742 0.684 0.755 0.675 3.312 0.576 0.412 -0.43
+A102344 0.479 0.514 0.506 0.502 2.251 0.507 0.593 -1.061
+A102345 0.506 0.525 0.546 0.488 3.047 0.532 0.398 -0.982
+A102346 0.592 0.612 1.025 0.705 2.93 1.819 0.071 -0.757
+A102347 0.497 0.506 0.543 0.522 3.195 0.482 0.645 -1.008
+A102348 0.606 0.624 0.669 0.614 2.841 0.577 0.414 -0.722
+A102349 0.584 0.503 0.457 0.508 2.361 0.481 0.581 -0.775
diff --git a/examples/final_data_matrix/NormData3.txt b/examples/final_data_matrix/NormData3.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/final_data_matrix/NormData3.txt
@@ -0,0 +1,11 @@
+MetaColumn MetaRow Column Row Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+1 1 1 1 A102340 0.475 0.481 0.494 0.491 0.129 0.413 0.731 -1.074
+1 1 2 1 A102341 0.494 0.496 0.5 0.599 0.409 0.433 0.777 -1.019
+1 1 3 1 A102342 0.501 0.49 0.493 0.641 1.28 0.46 0.837 -0.998
+1 1 4 1 A102343 0.78 0.782 0.819 1.372 3.252 0.675 0.722 -0.359
+1 1 5 1 A102344 0.455 0.46 0.45 0.6 0.632 0.382 0.751 -1.137
+1 1 6 1 A102345 0.42 0.431 0.473 0.696 2.121 0.362 0.589 -1.252
+1 1 7 1 A102346 0.223 0.229 0.226 0.313 0.289 0.16 0.283 -2.163
+1 1 8 1 A102347 0.525 0.503 0.522 0.741 1.71 0.445 0.754 -0.929
+1 1 9 1 A102348 0.388 0.384 0.368 0.443 0.369 0.351 0.721 -1.367
+1 1 10 1 A102349 0.885 0.912 0.923 1.477 3.031 0.751 0.698 -0.176
diff --git a/examples/final_data_matrix/NormData4.txt b/examples/final_data_matrix/NormData4.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/final_data_matrix/NormData4.txt
@@ -0,0 +1,11 @@
+Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+A102340 0.447 0.473 0.502 0.428 2.467 0.472 0.332 -1.162
+A102341 0.53 0.484 0.477 0.503 3.009 0.418 0.611 -0.916
+A102342 0.669 0.588 0.592 0.604 3.17 0.511 0.592 -0.579
+A102343 0.742 0.684 0.755 0.675 3.312 0.576 0.412 -0.43
+A102344 0.479 0.514 0.506 0.502 2.251 0.507 0.593 -1.061
+A102345 0.506 0.525 0.546 0.488 3.047 0.532 0.398 -0.982
+A102346 0.592 0.612 1.025 0.705 2.93 1.819 0.071 -0.757
+A102347 0.497 0.506 0.543 0.522 3.195 0.482 0.645 -1.008
+A102348 0.606 0.624 0.669 0.614 2.841 0.577 0.414 -0.722
+A102349 0.584 0.503 0.457 0.508 2.361 0.481 0.581 -0.775
diff --git a/examples/final_data_matrix/final_data_matrix.png b/examples/final_data_matrix/final_data_matrix.png
new file mode 100644
index 0000000..943d949
Binary files /dev/null and b/examples/final_data_matrix/final_data_matrix.png differ
diff --git a/examples/final_data_matrix/final_data_matrix.txt b/examples/final_data_matrix/final_data_matrix.txt
new file mode 100644
index 0000000..5cdcdee
--- /dev/null
+++ b/examples/final_data_matrix/final_data_matrix.txt
@@ -0,0 +1,46 @@
+"# Simple example summary spreadsheet for tab2mage.pl script."
+"# This file illustrates how to incorporate normalized data into the spreadsheet."
+"# Lines starting with a '#' are treated as comments and are ignored."
+
+"Experiment section"
+"domain" "ebi.ac.uk"
+"accession" "E-EXML-7"
+"quality_control" "dye_swap_quality_control"
+"experiment_design_type" "strain_or_line_design;co-expression_design"
+"name" "Transcriptome analysis of invasive verses non-invasive strains of budding yeast "
+"description" "An experiment was performed to..."
+"release_date" "2004-08-30"
+"submission_date" "2004-07-28"
+"submitter" "John Falstaff"
+"# The publication details below are fictitious"
+"publication_title" "Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast "
+"authors" "John Falstaff; Robin Goodfellow"
+"journal" "Nature Genetics"
+"volume" 12
+"issue" 4
+"pages" "123-456"
+"year" 2004
+"pubmed_id" 12345678
+
+"# Protocol text may be formatted using standard HTML tags"
+"Protocol section"
+"accession" "text" "name"
+"P-EXML-1" "Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8" "Yeast growth"
+"P-EXML-2" "Your protocol text here" "Yeast cell harvesting"
+"P-EXML-3" "Your protocol text here" "Cell lysis and RNA prep"
+"P-EXML-4" "Your protocol text here" "cDNA labeling"
+"P-EXML-5" "Your protocol text here" "Hybridization"
+"P-EXML-6" "Your protocol text here" "Scanning"
+"P-EXML-7" "Your protocol text here" "Log Ratio Normalization"
+"P-EXML-8" "Your protocol text here" "RMA normalization"
+
+"Hybridization section"
+"File[raw]" "File[normalized]" "NormalizationType" "Protocol[normalization]" "Hybridization" "Scan" "Normalization" "File[transformed]" "TransformationType" "Protocol[transformation]" "Software[transformation]" "Array[accession]" "Array[serial]" "Protocol[grow]" "Protocol[treatment]" "Protocol[extraction]" "Protocol[labeling]" "Protocol[hybridization]" "Protocol[scanning]" "BioSource" "BioSourceMaterial" "SampleMaterial" "ExtractMaterial" "LabeledExtractMaterial" "Dye" "FactorValue[Strai [...]
+"Data1.txt" "NormData1.txt" "log_ratio" "P-EXML-7" "Sigma1278b_1" "Sigma1278b_1" "Sigma1278b_1" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244532 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "S288C" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy5" "S288C" "Saccharomyces cerevisiae" "S288C" "mating_type_a"
+"Data1.txt" "NormData1.txt" "log_ratio" "P-EXML-7" "Sigma1278b_1" "Sigma1278b_1" "Sigma1278b_1" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244532 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "Sigma1278b" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy3" "Sigma1278b" "Saccharomyces cerevisiae" "Sigma1278b" "mating_type_a"
+"Data2.txt" "NormData2.txt" "log_ratio" "P-EXML-7" "Sigma1278b_2" "Sigma1278b_2" "Sigma1278b_2" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244533 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "S288C" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy3" "S288C" "Saccharomyces cerevisiae" "S288C" "mating_type_a"
+"Data2.txt" "NormData2.txt" "log_ratio" "P-EXML-7" "Sigma1278b_2" "Sigma1278b_2" "Sigma1278b_2" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244533 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "Sigma1278b" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy5" "Sigma1278b" "Saccharomyces cerevisiae" "Sigma1278b" "mating_type_a"
+"Data3.txt" "NormData3.txt" "log_ratio" "P-EXML-7" "W303a_1" "W303a_1" "W303a_1" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244532 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "S288C" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy5" "S288C" "Saccharomyces cerevisiae" "S288C" "mating_type_a"
+"Data3.txt" "NormData3.txt" "log_ratio" "P-EXML-7" "W303a_1" "W303a_1" "W303a_1" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244532 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "W303a" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy3" "W303a" "Saccharomyces cerevisiae" "W303a" "mating_type_a"
+"Data4.txt" "NormData4.txt" "log_ratio" "P-EXML-7" "W303a_2" "W303a_2" "W303a_2" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244533 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "S288C" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy3" "S288C" "Saccharomyces cerevisiae" "S288C" "mating_type_a"
+"Data4.txt" "NormData4.txt" "log_ratio" "P-EXML-7" "W303a_2" "W303a_2" "W303a_2" "FGEM.txt" "RMA" "P-EXML-8" "BioConductor(1.6)" "A-EXML-1" 244533 "P-EXML-1" "P-EXML-2" "P-EXML-3" "P-EXML-4" "P-EXML-5" "P-EXML-6" "W303a" "whole_organism" "whole_organism" "total_RNA" "synthetic_DNA" "Cy5" "W303a" "Saccharomyces cerevisiae" "W303a" "mating_type_a"
diff --git a/examples/illumina/GeneGroupedData.txt b/examples/illumina/GeneGroupedData.txt
new file mode 100644
index 0000000..7aeca77
--- /dev/null
+++ b/examples/illumina/GeneGroupedData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID Tissue1.MIN_Signal Tissue1.AVG_Signal Tissue1.MAX_Signal Tissue1.NARRAYS Tissue1.ARRAY_STDEV Tissue1.BEAD_STDEV Tissue1.Avg_NBEADS Tissue1.Detection Tissue2.MIN_Signal Tissue2.AVG_Signal Tissue2.MAX_Signal Tissue2.NARRAYS Tissue2.ARRAY_STDEV Tissue2.BEAD_STDEV Tissue2.Avg_NBEADS Tissue2.Detection Mixture1.MIN_Signal Mixture1.AVG_Signal Mixture1.MAX_Signal Mixture1.NARRAYS Mixture1.ARRAY_STDEV Mixture1.BEAD_STDEV Mixture1.Avg_NBEADS Mixture1.Detection Mixture2.MIN_Signal Mixture2 [...]
+360450 163.6 192.6 215.3 5 22.896 10.220 29 0.99934 -4.8 -3.0 -1.2 5 1.655 4.069 40 0.41134 8.0 21.5 26.0 5 7.722 6.041 30 0.96902 0.5 9.9 18.7 5 8.033 6.284 30 0.79103
+1690139 12100.1 12690.8 13437.3 5 651.837 334.077 33 1.00000 17.4 47.5 80.8 5 28.639 11.742 36 0.95781 3713.4 3793.4 3842.4 5 56.041 99.723 33 1.00000 58.8 68.1 73.7 5 5.810 8.072 25 0.99604
+5420594 233.8 242.7 258.0 5 9.281 15.148 25 1.00000 321.2 330.7 351.8 5 12.739 21.817 28 0.99934 155.4 177.5 217.1 5 27.665 10.574 23 1.00000 422.2 440.1 484.5 5 26.092 24.009 19 1.00000
+3060411 407.1 438.3 466.4 5 21.578 16.360 29 1.00000 554.5 608.8 662.0 5 44.600 31.865 31 1.00000 154.9 192.4 227.6 5 30.232 8.826 34 1.00000 726.4 779.1 860.0 5 53.943 29.842 31 1.00000
+450341 2470.8 2566.6 2643.4 5 63.710 60.129 36 1.00000 1517.7 1579.3 1625.1 5 41.988 49.936 39 1.00000 1453.4 1500.6 1575.1 5 46.195 48.919 36 1.00000 2464.9 2529.3 2625.8 5 61.878 70.328 30 1.00000
+5420324 7.2 18.0 31.5 5 10.067 7.129 22 0.91628 2.5 11.4 23.2 5 8.795 5.271 29 0.75346 4.6 12.8 18.5 5 5.522 6.571 23 0.91430 253.5 267.7 288.8 5 14.313 20.951 19 1.00000
+730162 -5.3 3.1 7.8 5 5.348 4.983 31 0.63810 -11.5 -4.0 9.3 5 9.722 4.934 29 0.39156 -7.5 -1.6 1.8 5 3.514 5.049 27 0.53790 -7.9 0.4 7.9 5 5.624 5.104 29 0.54318
+4200739 53.9 59.2 64.5 5 4.617 6.342 30 0.99011 504.8 522.2 549.4 5 21.540 25.760 38 1.00000 132.9 158.6 170.5 5 15.093 8.087 33 1.00000 507.0 534.0 565.3 5 25.053 20.384 33 1.00000
+1090156 296.5 335.7 394.3 5 36.982 15.301 26 1.00000 -1.4 7.9 19.4 5 8.141 5.717 35 0.67172 62.8 69.2 77.7 5 5.786 7.231 33 0.99604 8.5 13.6 21.3 5 5.383 6.077 25 0.87607
+7050341 1.3 6.1 16.9 5 6.255 5.619 33 0.72577 4.2 13.2 20.5 5 6.862 6.073 35 0.78708 -0.9 6.3 13.9 5 5.681 5.052 34 0.78840 4.3 15.8 23.6 5 7.492 5.439 35 0.90112
diff --git a/examples/illumina/ProbeData.txt b/examples/illumina/ProbeData.txt
new file mode 100644
index 0000000..b29d4e6
--- /dev/null
+++ b/examples/illumina/ProbeData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID 1323910012_A.MIN_Signal 1323910012_A.AVG_Signal 1323910012_A.MAX_Signal 1323910012_A.NARRAYS 1323910012_A.ARRAY_STDEV 1323910012_A.BEAD_STDEV 1323910012_A.Avg_NBEADS 1323910012_A.Detection 1323910012_B.MIN_Signal 1323910012_B.AVG_Signal 1323910012_B.MAX_Signal 1323910012_B.NARRAYS 1323910012_B.ARRAY_STDEV 1323910012_B.BEAD_STDEV 1323910012_B.Avg_NBEADS 1323910012_B.Detection 1323910012_C.MIN_Signal 1323910012_C.AVG_Signal 1323910012_C.MAX_Signal 1323910012_C.NARRAYS 1323910012_C [...]
+360450 157.5 157.5 157.5 1 NaN 5.821 26 0.99868 207.8 207.8 207.8 1 NaN 12.980 27 0.99934 170.4 170.4 170.4 1 NaN 9.440 35 0.99934 206.7 206.7 206.7 1 NaN 11.886 21 0.99934 186.8 186.8 186.8 1 NaN 7.323 38 1.00000 -2.5 -2.5 -2.5 1 NaN 4.488 43 0.43243 -0.9 -0.9 -0.9 1 NaN 4.168 32 0.46210 -4.0 -4.0 -4.0 1 NaN 4.108 34 0.37640 -4.4 -4.4 -4.4 1 NaN 3.755 52 0.39288 -0.6 -0.6 -0.6 1 NaN 3.780 37 0.46473 8.6 8.6 8.6 1 NaN 2.680 25 0.79565 26.3 26.3 26.3 1 NaN 4.459 36 0.98220 26.2 26.2 26.2 [...]
+1690139 13230.6 13230.6 13230.6 1 NaN 325.577 29 1.00000 12232.2 12232.2 12232.2 1 NaN 357.805 33 1.00000 11960.1 11960.1 11960.1 1 NaN 225.731 34 1.00000 11912.3 11912.3 11912.3 1 NaN 322.376 35 1.00000 13139.1 13139.1 13139.1 1 NaN 389.941 33 1.00000 18.2 18.2 18.2 1 NaN 5.875 36 0.83125 23.4 23.4 23.4 1 NaN 5.492 36 0.85959 70.5 70.5 70.5 1 NaN 17.215 33 0.97627 42.9 42.9 42.9 1 NaN 7.071 38 0.95056 78.3 78.3 78.3 1 NaN 15.564 37 0.98220 3751.4 3751.4 3751.4 1 NaN 102.943 28 1.00000 3 [...]
+5420594 236.1 236.1 236.1 1 NaN 21.651 16 1.00000 232.5 232.5 232.5 1 NaN 9.196 26 1.00000 226.3 226.3 226.3 1 NaN 10.479 33 1.00000 250.7 250.7 250.7 1 NaN 18.603 19 1.00000 230.2 230.2 230.2 1 NaN 8.282 30 1.00000 342.6 342.6 342.6 1 NaN 29.768 17 0.99934 324.4 324.4 324.4 1 NaN 16.646 24 0.99934 312.9 312.9 312.9 1 NaN 19.538 32 0.99934 315.9 315.9 315.9 1 NaN 17.821 39 0.99736 312.0 312.0 312.0 1 NaN 19.734 28 0.99934 149.4 149.4 149.4 1 NaN 11.086 21 1.00000 209.8 209.8 209.8 1 NaN [...]
+3060411 419.2 419.2 419.2 1 NaN 14.573 25 1.00000 432.8 432.8 432.8 1 NaN 15.491 33 1.00000 396.3 396.3 396.3 1 NaN 13.728 29 1.00000 451.0 451.0 451.0 1 NaN 20.503 26 1.00000 428.8 428.8 428.8 1 NaN 14.104 31 1.00000 613.8 613.8 613.8 1 NaN 24.467 36 1.00000 553.9 553.9 553.9 1 NaN 33.553 29 1.00000 600.4 600.4 600.4 1 NaN 25.920 31 1.00000 540.8 540.8 540.8 1 NaN 30.170 27 1.00000 639.7 639.7 639.7 1 NaN 38.179 32 1.00000 205.9 205.9 205.9 1 NaN 12.051 28 1.00000 163.2 163.2 163.2 1 Na [...]
+450341 2542.7 2542.7 2542.7 1 NaN 57.743 27 1.00000 2419.4 2419.4 2419.4 1 NaN 43.567 46 1.00000 2501.8 2501.8 2501.8 1 NaN 62.710 28 1.00000 2509.3 2509.3 2509.3 1 NaN 62.196 48 1.00000 2584.8 2584.8 2584.8 1 NaN 65.386 32 1.00000 1549.3 1549.3 1549.3 1 NaN 51.455 41 1.00000 1596.1 1596.1 1596.1 1 NaN 49.101 37 1.00000 1525.7 1525.7 1525.7 1 NaN 58.618 36 1.00000 1482.3 1482.3 1482.3 1 NaN 44.746 33 1.00000 1576.9 1576.9 1576.9 1 NaN 37.999 47 1.00000 1414.2 1414.2 1414.2 1 NaN 54.880 2 [...]
+5420324 7.7 7.7 7.7 1 NaN 6.353 23 0.74621 31.6 31.6 31.6 1 NaN 7.836 20 0.96111 18.1 18.1 18.1 1 NaN 8.746 16 0.89057 24.6 24.6 24.6 1 NaN 6.910 28 0.93672 10.3 10.3 10.3 1 NaN 5.306 21 0.78840 17.5 17.5 17.5 1 NaN 5.599 39 0.81938 23.9 23.9 23.9 1 NaN 5.513 28 0.86486 11.6 11.6 11.6 1 NaN 4.647 31 0.72577 3.0 3.0 3.0 1 NaN 4.182 25 0.57086 4.0 4.0 4.0 1 NaN 6.170 21 0.57680 5.0 5.0 5.0 1 NaN 5.007 28 0.71457 16.6 16.6 16.6 1 NaN 7.569 20 0.94067 10.7 10.7 10.7 1 NaN 5.237 29 0.84113 16 [...]
+730162 -4.9 -4.9 -4.9 1 NaN 4.708 27 0.41266 8.4 8.4 8.4 1 NaN 5.663 41 0.74489 3.6 3.6 3.6 1 NaN 4.223 29 0.62162 8.2 8.2 8.2 1 NaN 6.101 28 0.74160 2.5 2.5 2.5 1 NaN 3.856 30 0.61833 -11.4 -11.4 -11.4 1 NaN 5.531 24 0.21885 -11.3 -11.3 -11.3 1 NaN 4.882 24 0.21028 9.9 9.9 9.9 1 NaN 4.898 35 0.69875 -9.5 -9.5 -9.5 1 NaN 4.025 33 0.25313 4.0 4.0 4.0 1 NaN 5.206 30 0.57680 0.5 0.5 0.5 1 NaN 5.029 25 0.58075 -0.8 -0.8 -0.8 1 NaN 5.831 30 0.54581 2.3 2.3 2.3 1 NaN 4.046 29 0.60976 -7.1 -7.1 [...]
+4200739 54.0 54.0 54.0 1 NaN 5.792 31 0.98879 62.9 62.9 62.9 1 NaN 5.879 33 0.99209 58.5 58.5 58.5 1 NaN 6.012 28 0.98879 61.3 61.3 61.3 1 NaN 7.225 29 0.98879 52.8 52.8 52.8 1 NaN 6.105 27 0.98813 494.0 494.0 494.0 1 NaN 25.935 44 1.00000 536.6 536.6 536.6 1 NaN 23.539 41 1.00000 490.2 490.2 490.2 1 NaN 30.217 30 1.00000 490.8 490.8 490.8 1 NaN 23.458 42 1.00000 529.4 529.4 529.4 1 NaN 21.175 35 1.00000 161.5 161.5 161.5 1 NaN 9.237 26 1.00000 158.0 158.0 158.0 1 NaN 6.707 39 1.00000 15 [...]
+1090156 384.2 384.2 384.2 1 NaN 11.330 18 1.00000 306.2 306.2 306.2 1 NaN 9.133 13 1.00000 334.4 334.4 334.4 1 NaN 15.539 33 1.00000 289.1 289.1 289.1 1 NaN 16.736 39 1.00000 320.0 320.0 320.0 1 NaN 19.402 25 1.00000 20.2 20.2 20.2 1 NaN 8.095 31 0.85234 4.0 4.0 4.0 1 NaN 5.022 34 0.56625 5.8 5.8 5.8 1 NaN 5.910 33 0.62030 13.3 13.3 13.3 1 NaN 4.399 41 0.76467 -0.8 -0.8 -0.8 1 NaN 4.292 38 0.45880 74.8 74.8 74.8 1 NaN 5.652 27 0.99802 63.4 63.4 63.4 1 NaN 12.553 28 0.99604 69.2 69.2 69.2 [...]
+7050341 1.9 1.9 1.9 1 NaN 4.078 31 0.60580 3.6 3.6 3.6 1 NaN 7.133 23 0.63612 6.6 6.6 6.6 1 NaN 5.750 40 0.70864 3.7 3.7 3.7 1 NaN 4.862 33 0.63942 17.6 17.6 17.6 1 NaN 5.800 39 0.89453 21.3 21.3 21.3 1 NaN 6.834 30 0.85695 16.3 16.3 16.3 1 NaN 6.212 36 0.79169 8.6 8.6 8.6 1 NaN 5.130 42 0.67963 18.6 18.6 18.6 1 NaN 6.243 40 0.83652 4.6 4.6 4.6 1 NaN 5.814 27 0.59130 14.7 14.7 14.7 1 NaN 5.893 28 0.89519 -0.3 -0.3 -0.3 1 NaN 4.062 35 0.55900 10.2 10.2 10.2 1 NaN 5.905 37 0.83191 3.9 3.9 [...]
diff --git a/examples/illumina/illumina.png b/examples/illumina/illumina.png
new file mode 100644
index 0000000..e49dbc3
Binary files /dev/null and b/examples/illumina/illumina.png differ
diff --git a/examples/illumina/illumina.txt b/examples/illumina/illumina.txt
new file mode 100644
index 0000000..d69b865
--- /dev/null
+++ b/examples/illumina/illumina.txt
@@ -0,0 +1,46 @@
+# Simple example spreadsheet for Tab2MAGE.
+# This file illustrates how to reference multi-hyb Illumina data files from within the spreadsheet.
+# Note that more protocols would typically be required for MIAME compliance.
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-9
+quality_control technical_replicate
+experiment_design_type array_platform_variation_design
+name MAQC Illumina data set (excerpt)
+description The MicroArray Quality Control (MAQC) project was initiated to address these concerns, as well as other performance and analysis issues. We demonstrate the consistency of results within a platform across test sites as well as the high level of cross-platform concordance in terms of genes identified as differentially expressed. The MAQC study provides a rich resource that will help build consensus on the use of microarrays in research, clinical and regulatory settings. Manuscr [...]
+release_date 2006-09-08
+submission_date 2006-08-02
+submitter Leming Shi
+publication_title The MicroArray Quality Control (MAQC) project demonstrates concordant results between gene expression technology platforms
+authors Leming Shi, Laura H. Reid, Richard Shippy, Janet A. Warrington, William Slikker Jr.
+journal Nature Biotechnology
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-EXML-1 Data was processed using Illumina BeadStudio version 1.5.0.34 software using background subtraction and cubic spline normalization BeadStudio normalization
+
+Hybridization section
+BioSource LabeledExtract Dye File[raw] Array[accession] Hybridization Normalization Protocol[transformation] File[transformed] FactorValue[Sample]
+A 1A biotin ProbeData.txt A-MEXP-524 1323910012_A Tissue1 P-EXML-1 GeneGroupedData.txt 1
+A 1A biotin ProbeData.txt A-MEXP-524 1323910012_B Tissue1 P-EXML-1 GeneGroupedData.txt 1
+A 1A biotin ProbeData.txt A-MEXP-524 1323910012_C Tissue1 P-EXML-1 GeneGroupedData.txt 1
+A 1A biotin ProbeData.txt A-MEXP-524 1323910012_D Tissue1 P-EXML-1 GeneGroupedData.txt 1
+A 1A biotin ProbeData.txt A-MEXP-524 1323910012_E Tissue1 P-EXML-1 GeneGroupedData.txt 1
+B 1B biotin ProbeData.txt A-MEXP-524 1323901020_A Tissue2 P-EXML-1 GeneGroupedData.txt 2
+B 1B biotin ProbeData.txt A-MEXP-524 1323901020_B Tissue2 P-EXML-1 GeneGroupedData.txt 2
+B 1B biotin ProbeData.txt A-MEXP-524 1323901020_C Tissue2 P-EXML-1 GeneGroupedData.txt 2
+B 1B biotin ProbeData.txt A-MEXP-524 1323901020_E Tissue2 P-EXML-1 GeneGroupedData.txt 2
+B 1B biotin ProbeData.txt A-MEXP-524 1323901020_F Tissue2 P-EXML-1 GeneGroupedData.txt 2
+C 1C biotin ProbeData.txt A-MEXP-524 1323901024_A Mixture1 P-EXML-1 GeneGroupedData.txt 3
+C 1C biotin ProbeData.txt A-MEXP-524 1323901024_B Mixture1 P-EXML-1 GeneGroupedData.txt 3
+C 1C biotin ProbeData.txt A-MEXP-524 1323901024_D Mixture1 P-EXML-1 GeneGroupedData.txt 3
+C 1C biotin ProbeData.txt A-MEXP-524 1323901024_E Mixture1 P-EXML-1 GeneGroupedData.txt 3
+C 1C biotin ProbeData.txt A-MEXP-524 1323901024_F Mixture1 P-EXML-1 GeneGroupedData.txt 3
+D 1D biotin ProbeData.txt A-MEXP-524 1323928007_A Mixture2 P-EXML-1 GeneGroupedData.txt 4
+D 1D biotin ProbeData.txt A-MEXP-524 1323928007_C Mixture2 P-EXML-1 GeneGroupedData.txt 4
+D 1D biotin ProbeData.txt A-MEXP-524 1323928007_D Mixture2 P-EXML-1 GeneGroupedData.txt 4
+D 1D biotin ProbeData.txt A-MEXP-524 1323928007_E Mixture2 P-EXML-1 GeneGroupedData.txt 4
+D 1D biotin ProbeData.txt A-MEXP-524 1323928007_F Mixture2 P-EXML-1 GeneGroupedData.txt 4
diff --git a/examples/loop_design/loop_design.png b/examples/loop_design/loop_design.png
new file mode 100644
index 0000000..c837dbf
Binary files /dev/null and b/examples/loop_design/loop_design.png differ
diff --git a/examples/loop_design/loop_design.txt b/examples/loop_design/loop_design.txt
new file mode 100644
index 0000000..5e636f2
--- /dev/null
+++ b/examples/loop_design/loop_design.txt
@@ -0,0 +1,55 @@
+# Simple example summary spreadsheet for tab2mage.pl script.
+# This file illustrates how to describe a loop design experiment in the spreadsheet.
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-8
+quality_control dye_swap_quality_control
+experiment_design_type organism_part_comparison_design, loop_design
+name Analysis of tissue-specific expression profile using a loop design
+description An experiment was performed to...
+release_date 2004-08-30
+submission_date 2004-07-28
+submitter John Falstaff
+# The publication details below are fictitious
+publication_title Analysis of tissue-specific expression profile using a loop design
+authors John Falstaff; Robin Goodfellow
+journal Genome Biol
+volume 12
+issue 4
+pages 123-456
+year 2004
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-EXML-1 Your protocol text here Mouse strain maintenance
+P-EXML-2 Your protocol text here Dissection
+P-EXML-3 Your protocol text here RNA Extraction
+P-EXML-4 Your protocol text here cDNA labeling
+P-EXML-5 Your protocol text here Hybridization
+P-EXML-6 Your protocol text here Scanning
+
+Hybridization section
+BioSource Sample Extract LabeledExtract Hybridization File[raw] Array[accession] Array[serial] Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[labeling] Protocol[hybridization] Protocol[scanning] BioSourceMaterial SampleMaterial ExtractMaterial LabeledExtractMaterial Dye FactorValue[OrganismPart] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Sex] BioMaterialCharacteristics[BioSourceType] BioMaterialCharacteristic [...]
+brain brain brain brain Cy3 spleen Cy5 vs brain Cy3 spleen_Cy5_brain_Cy3.txt A-EXML-1 10 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 brain Mus musculus C57BL/6 male frozen_sample brain
+brain brain brain brain Cy3 brain Cy3 vs heart Cy5 brain_Cy3_heart_Cy5.txt A-EXML-1 1 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 brain Mus musculus C57BL/6 male frozen_sample brain
+brain brain brain brain Cy5 spleen Cy3 vs brain Cy5 spleen_Cy3_brain_Cy5.txt A-EXML-1 9 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 brain Mus musculus C57BL/6 male frozen_sample brain
+brain brain brain brain Cy5 brain Cy5 vs heart Cy3 brain_Cy5_heart_Cy3.txt A-EXML-1 2 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 brain Mus musculus C57BL/6 male frozen_sample brain
+heart heart heart heart Cy3 brain Cy5 vs heart Cy3 brain_Cy5_heart_Cy3.txt A-EXML-1 2 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 heart Mus musculus C57BL/6 male frozen_sample heart
+heart heart heart heart Cy3 heart Cy3 vs kidney Cy5 heart_Cy3_kidney_Cy5.txt A-EXML-1 3 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 heart Mus musculus C57BL/6 male frozen_sample heart
+heart heart heart heart Cy5 brain Cy3 vs heart Cy5 brain_Cy3_heart_Cy5.txt A-EXML-1 1 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 heart Mus musculus C57BL/6 male frozen_sample heart
+heart heart heart heart Cy5 heart Cy5 vs kidney Cy3 heart_Cy5_kidney_Cy3.txt A-EXML-1 4 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 heart Mus musculus C57BL/6 male frozen_sample heart
+kidney kidney kidney kidney Cy3 heart Cy5 vs kidney Cy3 heart_Cy5_kidney_Cy3.txt A-EXML-1 4 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 kidney Mus musculus C57BL/6 male frozen_sample kidney
+kidney kidney kidney kidney Cy3 kidney Cy3 vs liver Cy5 kidney_Cy3_liver_Cy5.txt A-EXML-1 5 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 kidney Mus musculus C57BL/6 male frozen_sample kidney
+kidney kidney kidney kidney Cy5 heart Cy3 vs kidney Cy5 heart_Cy3_kidney_Cy5.txt A-EXML-1 3 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 kidney Mus musculus C57BL/6 male frozen_sample kidney
+kidney kidney kidney kidney Cy5 kidney Cy5 vs liver Cy3 kidney_Cy5_liver_Cy3.txt A-EXML-1 6 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 kidney Mus musculus C57BL/6 male frozen_sample kidney
+liver liver liver liver Cy3 kidney Cy5 vs liver Cy3 kidney_Cy5_liver_Cy3.txt A-EXML-1 6 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 liver Mus musculus C57BL/6 male frozen_sample liver
+liver liver liver liver Cy3 liver Cy3 vs spleen Cy5 liver_Cy3_spleen_Cy5.txt A-EXML-1 7 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 liver Mus musculus C57BL/6 male frozen_sample liver
+liver liver liver liver Cy5 kidney Cy3 vs liver Cy5 kidney_Cy3_liver_Cy5.txt A-EXML-1 5 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 liver Mus musculus C57BL/6 male frozen_sample liver
+liver liver liver liver Cy5 liver Cy5 vs spleen Cy3 liver_Cy5_spleen_Cy3.txt A-EXML-1 8 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 liver Mus musculus C57BL/6 male frozen_sample liver
+spleen spleen spleen spleen Cy3 liver Cy5 vs spleen Cy3 liver_Cy5_spleen_Cy3.txt A-EXML-1 8 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 spleen Mus musculus C57BL/6 male frozen_sample spleen
+spleen spleen spleen spleen Cy3 spleen Cy3 vs brain Cy5 spleen_Cy3_brain_Cy5.txt A-EXML-1 9 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy3 spleen Mus musculus C57BL/6 male frozen_sample spleen
+spleen spleen spleen spleen Cy5 spleen Cy5 vs brain Cy3 spleen_Cy5_brain_Cy3.txt A-EXML-1 10 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 spleen Mus musculus C57BL/6 male frozen_sample spleen
+spleen spleen spleen spleen Cy5 liver Cy3 vs spleen Cy5 liver_Cy3_spleen_Cy5.txt A-EXML-1 7 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 organism_part organism_part total_RNA synthetic_DNA Cy5 spleen Mus musculus C57BL/6 male frozen_sample spleen
diff --git a/examples/magetab/affymetrix/ATH1-121501.cdf b/examples/magetab/affymetrix/ATH1-121501.cdf
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/examples/magetab/affymetrix/ATH1-121501.cdf
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/examples/magetab/affymetrix/Data1.CEL b/examples/magetab/affymetrix/Data1.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/magetab/affymetrix/Data1.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093] test 1:CLS=4733 RWS=4733 XIN=3 YIN=3 VE=17 2.0 03/27/03 23:53:16 Test.1sq 6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X Y MEAN STDV NPIXELS
+ 0 0 158.0 28.9 36
+ 1 0 8875.5 1390.6 36
+ 2 0 160.0 25.3 36
+ 3 0 8552.5 1027.3 36
+ 4 0 78.8 15.6 36
+ 5 0 138.8 25.5 36
+ 6 0 8124.0 1124.9 36
+ 7 0 146.3 22.9 36
+ 8 0 7531.3 1038.5 36
+ 9 0 132.3 21.7 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X Y
+3 0
+6 0
+9 0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X Y ORIGMEAN
diff --git a/examples/magetab/affymetrix/Data1.CHP b/examples/magetab/affymetrix/Data1.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/magetab/affymetrix/Data1.CHP differ
diff --git a/examples/magetab/affymetrix/Data1.EXP b/examples/magetab/affymetrix/Data1.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data1.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/magetab/affymetrix/Data2.CEL b/examples/magetab/affymetrix/Data2.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/magetab/affymetrix/Data2.CEL differ
diff --git a/examples/magetab/affymetrix/Data2.CHP b/examples/magetab/affymetrix/Data2.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/magetab/affymetrix/Data2.CHP differ
diff --git a/examples/magetab/affymetrix/Data2.EXP b/examples/magetab/affymetrix/Data2.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data2.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/magetab/affymetrix/Data3.CEL b/examples/magetab/affymetrix/Data3.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/examples/magetab/affymetrix/Data3.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093] test 1:CLS=4733 RWS=4733 XIN=3 YIN=3 VE=17 2.0 03/27/03 23:53:16 Test.1sq 6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X Y MEAN STDV NPIXELS
+ 0 0 158.0 28.9 36
+ 1 0 8875.5 1390.6 36
+ 2 0 160.0 25.3 36
+ 3 0 8552.5 1027.3 36
+ 4 0 78.8 15.6 36
+ 5 0 138.8 25.5 36
+ 6 0 8124.0 1124.9 36
+ 7 0 146.3 22.9 36
+ 8 0 7531.3 1038.5 36
+ 9 0 132.3 21.7 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X Y
+3 0
+6 0
+9 0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X Y ORIGMEAN
diff --git a/examples/magetab/affymetrix/Data3.CHP b/examples/magetab/affymetrix/Data3.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/examples/magetab/affymetrix/Data3.CHP differ
diff --git a/examples/magetab/affymetrix/Data3.EXP b/examples/magetab/affymetrix/Data3.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data3.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/magetab/affymetrix/Data4.CEL b/examples/magetab/affymetrix/Data4.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/examples/magetab/affymetrix/Data4.CEL differ
diff --git a/examples/magetab/affymetrix/Data4.CHP b/examples/magetab/affymetrix/Data4.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/examples/magetab/affymetrix/Data4.CHP differ
diff --git a/examples/magetab/affymetrix/Data4.EXP b/examples/magetab/affymetrix/Data4.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/examples/magetab/affymetrix/Data4.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/examples/magetab/affymetrix/YG_S98.cdf b/examples/magetab/affymetrix/YG_S98.cdf
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/examples/magetab/affymetrix/YG_S98.cdf
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/examples/magetab/affymetrix/affy_dm.txt b/examples/magetab/affymetrix/affy_dm.txt
new file mode 100644
index 0000000..d793838
--- /dev/null
+++ b/examples/magetab/affymetrix/affy_dm.txt
@@ -0,0 +1,14 @@
+Normalization REF TK6 replicate 1 TK6 replicate 1 TK6 replicate 2 TK6 replicate 2 TK6MDR1 replicate 1 TK6MDR1 replicate 1 TK6MDR1 replicate 2 TK6MDR1 replicate 2
+Composite Element REF CHPSignal Call CHPSignal Call CHPSignal Call CHPSignal Call
+AFFX-BioB-5_at 172.05 A 141.47 P 103.52 A 634.15 P
+AFFX-BioB-M_at 17.43 A 40.69 P 50.78 P 242.73 P
+AFFX-BioB-3_at 1392.58 A 80.38 P 142.13 A 729.81 P
+AFFX-BioC-5_at 7.4 A 8.02 A 18.42 A 119.96 P
+AFFX-BioC-3_at 90.08 A 8.02 A 7.53 P 102.39 P
+AFFX-BioDn-5_at 13.24 A 4.45 A 6.56 A 93.37 P
+AFFX-BioDn-3_at 7.23 A 5.24 A 12.49 P 137.39 P
+AFFX-CreX-5_at 6.68 A 3.35 A 4.36 A 12.21 A
+AFFX-CreX-3_at 262.31 A 10.36 A 20.49 A 16.89 A
+AFFX-DapX-5_at 76.58 A 50.31 A 31.78 A 20.42 A
+AFFX-DapX-M_at 37.87 A 56.34 A 57.47 A 27.23 A
+AFFX-DapX-3_at 49.99 A 35.63 A 15.78 A 12.28 A
diff --git a/examples/magetab/affymetrix/affy_sdrf.txt b/examples/magetab/affymetrix/affy_sdrf.txt
new file mode 100644
index 0000000..df37e06
--- /dev/null
+++ b/examples/magetab/affymetrix/affy_sdrf.txt
@@ -0,0 +1,5 @@
+"Source Name" "Material Type" "Term Source REF" "Characteristics [Genotype]" "Characteristics[Organism]" "Term Source REF" "Characteristics[BioSourceType]" "Term Source REF" "Characteristics[CellType]" "Term Source REF" "Characteristics[CellLine]" "Protocol REF" "Parameter Value [media]" "Sample Name" "Protocol REF" "Parameter Value [Extracted Product]" "Parameter Value [Amplification]" "Extract Name" "Protocol REF" "Term Source REF" "Labeled Extract Name" "Label" "Protocol REF:Affymetri [...]
+"TK6 replicate 1" "cell" "MO" "wild_type" "Homo sapiens" "ncbitax" "fresh_sample" "MO" "B_lymphoblast" "CTO" "TK6" "GROWTHPRTCL10653" "RPMI 1640 medium supplemented with 10% horse serum" "TK6 replicate 1" "EXTPRTCL10654" "total RNA" "none" "TK6 replicate 1" "P-AFFY-2" "ArrayExpress" "TK6 replicate 1" "biotin" "H_TK6 replicate 1" "A-AFFY-44" "ArrayExpress" "TK6 replicate 1" "Data1.CEL" "Data1.EXP" "TK6 replicate 1" "Data1.CHP" "wild_type" "combined data" "affy_dm.txt" "HG-U133A_Plus_2.CDF"
+"TK6 replicate 2" "cell" "MO" "wild_type" "Homo sapiens" "ncbitax" "fresh_sample" "MO" "B_lymphoblast" "CTO" "TK6" "GROWTHPRTCL10653" "RPMI 1640 medium supplemented with 10% horse serum" "TK6 replicate 2" "EXTPRTCL10654" "total RNA" "none" "TK6 replicate 2" "P-AFFY-2" "ArrayExpress" "TK6 replicate 2" "biotin" "H_TK6 replicate 2" "A-AFFY-44" "ArrayExpress" "TK6 replicate 2" "Data2.CEL" "Data2.EXP" "TK6 replicate 2" "Data2.CHP" "wild_type" "combined data" "affy_dm.txt" "HG-U133A_Plus_2.CDF"
+"TK6MDR1 replicate 1" "cell" "MO" "SF91m3 oncoretrovirus" "Homo sapiens" "ncbitax" "fresh_sample" "MO" "B_lymphoblast" "CTO" "TK6" "GROWTHPRTCL10653" "RPMI 1640 medium supplemented with 10% horse serum" "TK6MDR1 replicate 1" "EXTPRTCL10654" "total RNA" "none" "TK6MDR1 replicate 1" "P-AFFY-2" "ArrayExpress" "TK6MDR1 replicate 1" "biotin" "Hybridization-EukGE-WS2v5" "H_TK6MDR1 replicate 1" "A-AFFY-44" "ArrayExpress" "Scan" "TK6MDR1 replicate 1" "Data3.CEL" "Data3.EXP" "TRANPRTCL10656" "TK6 [...]
+"TK6MDR1 replicate 2" "cell" "MO" "SF91m3 oncoretrovirus" "Homo sapiens" "ncbitax" "fresh_sample" "MO" "B_lymphoblast" "CTO" "TK6" "GROWTHPRTCL10653" "RPMI 1640 medium supplemented with 10% horse serum" "TK6MDR1 replicate 2" "EXTPRTCL10654" "total RNA" "none" "TK6MDR1 replicate 2" "P-AFFY-2" "ArrayExpress" "TK6MDR1 replicate 2" "biotin" "Hybridization-EukGE-WS2v5" "H_TK6MDR1 replicate 2" "A-AFFY-44" "ArrayExpress" "Scan" "TK6MDR1 replicate 2" "Data4.CEL" "Data4.EXP" "TRANPRTCL10656" "TK6 [...]
diff --git a/examples/magetab/affymetrix/affymetrix.idf b/examples/magetab/affymetrix/affymetrix.idf
new file mode 100644
index 0000000..586f50c
--- /dev/null
+++ b/examples/magetab/affymetrix/affymetrix.idf
@@ -0,0 +1,49 @@
+"Investigation Title" "University of Heidelberg H sapiens TK6"
+"Experimental Design" "genetic_modification_design"
+"Experimental Factor Name" "EF1" "EF2"
+"Experimental Factor Type" "genetic_modification" "compound"
+"Experimental Factor Term Source REF" "MO"
+
+"Person Last Name" "Maier" "Fleckenstein" "Li" "Laufs" "Zeller" "Freuhauf" "Herskind" "Wenz"
+"Person First Name" "Patrick" "Katharina" "Li" "Stephanie" "Jens" "Stephan" "Carsten" "Frederik"
+"Person Mid Initials" "W"
+"Person Email" "patrick.maier at radonk.ma.uni-heidelberg.de"
+"Person Phone" 491334787822
+"Person Fax" 123
+"Person Address" "Theodor-Kutzer-Ufer 1-3"
+"Person Affiliation" "Department of Radiation Oncology, University of Heidelberg" "Department of Radiation Oncology, University of Heidelberg" "University of Heidelberg, ZMF" "DKFZ, Heidelberg" "DKFZ, Heidelberg" "University of Heidelberg, Department of Int Med V" "Department of Radiation Oncology, University of Heidelberg" "Department of Radiation Oncology, University of Heidelberg"
+"Person Roles" "submitter;investigator" "investigator" "investigator" "investigator" "investigator" "investigator" "investigator" "investigator"
+"Person Roles Term Source REF" "MO"
+
+"Quality Control Type" "biological_replicate"
+"Quality Control Term Source REF" "MO"
+"Replicate Type" "biological_replicate"
+"Replicate Term Source REF" "MO"
+"Normalization Type" "whatever"
+"Normalization Term Source REF" "MO"
+"Date of Experiment" "2005-01-04"
+"Public Release Date" "2005-04-01"
+
+"PubMed ID" 16953664
+"Publication DOI" "doi: some number here"
+"Publication Author List" "Me, him and her"
+"Publication Title" "What I did on my holidays"
+"Publication Status" "Universally panned"
+"Publication Status Term Source REF" "MO"
+
+"Experiment Description" "Gene expression of TK6 cells transduced with an oncoretrovirus expressing MDR1 (TK6MDR1) was compared to untransduced TK6 cells and to TK6 cell transduced with an oncoretrovirus expressing the Neomycin resistance gene (TK6neo). Two biological replicates of each were generated and the expression profiles were determined using Affymetrix Human Genome U133 Plus2.0 GeneChip microarrays. Comparisons between the sample groups allow the identification of genes with exp [...]
+
+"Protocol Name" "GROWTHPRTCL10653" "EXTPRTCL10654" "TRANPRTCL10656"
+"Protocol Type" "grow" "nucleic_acid_extraction" "bioassay_data_transformation"
+"Protocol Description" "TK6 cells were grown in suspension cultures in RPMI 1640 medium supplemented with 10% horse serum (Invitrogen, Karlsruhe, Germany). The cells were routinely maintained at 37 C and 5% CO2." "Approximately 10^6 cells were lysed in RLT buffer (Qiagen).Total RNA was extracted from the cell lysate using an RNeasy kit (Qiagen)." "Mixed Model Normalization with SAS Micro Array Solutions (version 1.3)."
+"Protocol Parameters" "media" "Extracted Product; Amplification"
+"Protocol Term Source REF" "MO"
+"Protocol Hardware" "MyHardware"
+"Protocol Software" "MySoftware"
+"Protocol Contact" "MyContact"
+
+"SDRF File" "affy_sdrf.txt"
+
+"Term Source Name" "CTO" "MO" "ncbitax" "ArrayExpress"
+"Term Source File" "http://obo.sourceforge.net/cgi-bin/detail.cgi?cell" "http://mged.sourceforge.net/ontologies/MGEDontology.php" "http://www.ncbi.nlm.nih.gov/Taxonomy/taxonomyhome.html/" "http://www.ebi.ac.uk/arrayexpress/"
+"Term Source Version" "1.3.0.1"
diff --git a/examples/magetab/illumina/GeneGroupedData.txt b/examples/magetab/illumina/GeneGroupedData.txt
new file mode 100644
index 0000000..8540fdb
--- /dev/null
+++ b/examples/magetab/illumina/GeneGroupedData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID Tissue1.MIN_Signal Tissue1.AVG_Signal Tissue1.MAX_Signal Tissue1.NARRAYS Tissue1.ARRAY_STDEV Tissue1.BEAD_STDEV Tissue1.Avg_NBEADS Tissue1.Detection Tissue2.MIN_Signal Tissue2.AVG_Signal Tissue2.MAX_Signal Tissue2.NARRAYS Tissue2.ARRAY_STDEV Tissue2.BEAD_STDEV Tissue2.Avg_NBEADS Tissue2.Detection Mixture1.MIN_Signal Mixture1.AVG_Signal Mixture1.MAX_Signal Mixture1.NARRAYS Mixture1.ARRAY_STDEV Mixture1.BEAD_STDEV Mixture1.Avg_NBEADS Mixture1.Detection Mixture2.MIN_Signal Mixture2 [...]
+360450 163.6 192.6 215.3 5 22.896 10.220 29 0.99934 -4.8 -3.0 -1.2 5 1.655 4.069 40 0.41134 8.0 21.5 26.0 5 7.722 6.041 30 0.96902 0.5 9.9 18.7 5 8.033 6.284 30 0.79103
+1690139 12100.1 12690.8 13437.3 5 651.837 334.077 33 1.00000 17.4 47.5 80.8 5 28.639 11.742 36 0.95781 3713.4 3793.4 3842.4 5 56.041 99.723 33 1.00000 58.8 68.1 73.7 5 5.810 8.072 25 0.99604
+5420594 233.8 242.7 258.0 5 9.281 15.148 25 1.00000 321.2 330.7 351.8 5 12.739 21.817 28 0.99934 155.4 177.5 217.1 5 27.665 10.574 23 1.00000 422.2 440.1 484.5 5 26.092 24.009 19 1.00000
+3060411 407.1 438.3 466.4 5 21.578 16.360 29 1.00000 554.5 608.8 662.0 5 44.600 31.865 31 1.00000 154.9 192.4 227.6 5 30.232 8.826 34 1.00000 726.4 779.1 860.0 5 53.943 29.842 31 1.00000
+450341 2470.8 2566.6 2643.4 5 63.710 60.129 36 1.00000 1517.7 1579.3 1625.1 5 41.988 49.936 39 1.00000 1453.4 1500.6 1575.1 5 46.195 48.919 36 1.00000 2464.9 2529.3 2625.8 5 61.878 70.328 30 1.00000
+5420324 7.2 18.0 31.5 5 10.067 7.129 22 0.91628 2.5 11.4 23.2 5 8.795 5.271 29 0.75346 4.6 12.8 18.5 5 5.522 6.571 23 0.91430 253.5 267.7 288.8 5 14.313 20.951 19 1.00000
+730162 -5.3 3.1 7.8 5 5.348 4.983 31 0.63810 -11.5 -4.0 9.3 5 9.722 4.934 29 0.39156 -7.5 -1.6 1.8 5 3.514 5.049 27 0.53790 -7.9 0.4 7.9 5 5.624 5.104 29 0.54318
+4200739 53.9 59.2 64.5 5 4.617 6.342 30 0.99011 504.8 522.2 549.4 5 21.540 25.760 38 1.00000 132.9 158.6 170.5 5 15.093 8.087 33 1.00000 507.0 534.0 565.3 5 25.053 20.384 33 1.00000
+1090156 296.5 335.7 394.3 5 36.982 15.301 26 1.00000 -1.4 7.9 19.4 5 8.141 5.717 35 0.67172 62.8 69.2 77.7 5 5.786 7.231 33 0.99604 8.5 13.6 21.3 5 5.383 6.077 25 0.87607
+7050341 1.3 6.1 16.9 5 6.255 5.619 33 0.72577 4.2 13.2 20.5 5 6.862 6.073 35 0.78708 -0.9 6.3 13.9 5 5.681 5.052 34 0.78840 4.3 15.8 23.6 5 7.492 5.439 35 0.90112
diff --git a/examples/magetab/illumina/ProbeData.txt b/examples/magetab/illumina/ProbeData.txt
new file mode 100644
index 0000000..08ef278
--- /dev/null
+++ b/examples/magetab/illumina/ProbeData.txt
@@ -0,0 +1,18 @@
+Illumina Inc. BeadStudio version 3.2.3.27166
+Normalization = cubic spline
+Array Content = Human_WG-6.xml.xml
+Error Model = none
+DateTime = 4/7/2006 6:38 PM
+Local Settings = en-US
+
+PROBE_ID 1323910012_A.MIN_Signal 1323910012_A.AVG_Signal 1323910012_A.MAX_Signal 1323910012_A.NARRAYS 1323910012_A.ARRAY_STDEV 1323910012_A.BEAD_STDEV 1323910012_A.Avg_NBEADS 1323910012_A.Detection 1323910012_B.MIN_Signal 1323910012_B.AVG_Signal 1323910012_B.MAX_Signal 1323910012_B.NARRAYS 1323910012_B.ARRAY_STDEV 1323910012_B.BEAD_STDEV 1323910012_B.Avg_NBEADS 1323910012_B.Detection 1323910012_C.MIN_Signal 1323910012_C.AVG_Signal 1323910012_C.MAX_Signal 1323910012_C.NARRAYS 1323910012_C [...]
+360450 157.5 157.5 157.5 1 NaN 5.821 26 0.99868 207.8 207.8 207.8 1 NaN 12.980 27 0.99934 170.4 170.4 170.4 1 NaN 9.440 35 0.99934 206.7 206.7 206.7 1 NaN 11.886 21 0.99934 186.8 186.8 186.8 1 NaN 7.323 38 1.00000 -2.5 -2.5 -2.5 1 NaN 4.488 43 0.43243 -0.9 -0.9 -0.9 1 NaN 4.168 32 0.46210 -4.0 -4.0 -4.0 1 NaN 4.108 34 0.37640 -4.4 -4.4 -4.4 1 NaN 3.755 52 0.39288 -0.6 -0.6 -0.6 1 NaN 3.780 37 0.46473 8.6 8.6 8.6 1 NaN 2.680 25 0.79565 26.3 26.3 26.3 1 NaN 4.459 36 0.98220 26.2 26.2 26.2 [...]
+1690139 13230.6 13230.6 13230.6 1 NaN 325.577 29 1.00000 12232.2 12232.2 12232.2 1 NaN 357.805 33 1.00000 11960.1 11960.1 11960.1 1 NaN 225.731 34 1.00000 11912.3 11912.3 11912.3 1 NaN 322.376 35 1.00000 13139.1 13139.1 13139.1 1 NaN 389.941 33 1.00000 18.2 18.2 18.2 1 NaN 5.875 36 0.83125 23.4 23.4 23.4 1 NaN 5.492 36 0.85959 70.5 70.5 70.5 1 NaN 17.215 33 0.97627 42.9 42.9 42.9 1 NaN 7.071 38 0.95056 78.3 78.3 78.3 1 NaN 15.564 37 0.98220 3751.4 3751.4 3751.4 1 NaN 102.943 28 1.00000 3 [...]
+5420594 236.1 236.1 236.1 1 NaN 21.651 16 1.00000 232.5 232.5 232.5 1 NaN 9.196 26 1.00000 226.3 226.3 226.3 1 NaN 10.479 33 1.00000 250.7 250.7 250.7 1 NaN 18.603 19 1.00000 230.2 230.2 230.2 1 NaN 8.282 30 1.00000 342.6 342.6 342.6 1 NaN 29.768 17 0.99934 324.4 324.4 324.4 1 NaN 16.646 24 0.99934 312.9 312.9 312.9 1 NaN 19.538 32 0.99934 315.9 315.9 315.9 1 NaN 17.821 39 0.99736 312.0 312.0 312.0 1 NaN 19.734 28 0.99934 149.4 149.4 149.4 1 NaN 11.086 21 1.00000 209.8 209.8 209.8 1 NaN [...]
+3060411 419.2 419.2 419.2 1 NaN 14.573 25 1.00000 432.8 432.8 432.8 1 NaN 15.491 33 1.00000 396.3 396.3 396.3 1 NaN 13.728 29 1.00000 451.0 451.0 451.0 1 NaN 20.503 26 1.00000 428.8 428.8 428.8 1 NaN 14.104 31 1.00000 613.8 613.8 613.8 1 NaN 24.467 36 1.00000 553.9 553.9 553.9 1 NaN 33.553 29 1.00000 600.4 600.4 600.4 1 NaN 25.920 31 1.00000 540.8 540.8 540.8 1 NaN 30.170 27 1.00000 639.7 639.7 639.7 1 NaN 38.179 32 1.00000 205.9 205.9 205.9 1 NaN 12.051 28 1.00000 163.2 163.2 163.2 1 Na [...]
+450341 2542.7 2542.7 2542.7 1 NaN 57.743 27 1.00000 2419.4 2419.4 2419.4 1 NaN 43.567 46 1.00000 2501.8 2501.8 2501.8 1 NaN 62.710 28 1.00000 2509.3 2509.3 2509.3 1 NaN 62.196 48 1.00000 2584.8 2584.8 2584.8 1 NaN 65.386 32 1.00000 1549.3 1549.3 1549.3 1 NaN 51.455 41 1.00000 1596.1 1596.1 1596.1 1 NaN 49.101 37 1.00000 1525.7 1525.7 1525.7 1 NaN 58.618 36 1.00000 1482.3 1482.3 1482.3 1 NaN 44.746 33 1.00000 1576.9 1576.9 1576.9 1 NaN 37.999 47 1.00000 1414.2 1414.2 1414.2 1 NaN 54.880 2 [...]
+5420324 7.7 7.7 7.7 1 NaN 6.353 23 0.74621 31.6 31.6 31.6 1 NaN 7.836 20 0.96111 18.1 18.1 18.1 1 NaN 8.746 16 0.89057 24.6 24.6 24.6 1 NaN 6.910 28 0.93672 10.3 10.3 10.3 1 NaN 5.306 21 0.78840 17.5 17.5 17.5 1 NaN 5.599 39 0.81938 23.9 23.9 23.9 1 NaN 5.513 28 0.86486 11.6 11.6 11.6 1 NaN 4.647 31 0.72577 3.0 3.0 3.0 1 NaN 4.182 25 0.57086 4.0 4.0 4.0 1 NaN 6.170 21 0.57680 5.0 5.0 5.0 1 NaN 5.007 28 0.71457 16.6 16.6 16.6 1 NaN 7.569 20 0.94067 10.7 10.7 10.7 1 NaN 5.237 29 0.84113 16 [...]
+730162 -4.9 -4.9 -4.9 1 NaN 4.708 27 0.41266 8.4 8.4 8.4 1 NaN 5.663 41 0.74489 3.6 3.6 3.6 1 NaN 4.223 29 0.62162 8.2 8.2 8.2 1 NaN 6.101 28 0.74160 2.5 2.5 2.5 1 NaN 3.856 30 0.61833 -11.4 -11.4 -11.4 1 NaN 5.531 24 0.21885 -11.3 -11.3 -11.3 1 NaN 4.882 24 0.21028 9.9 9.9 9.9 1 NaN 4.898 35 0.69875 -9.5 -9.5 -9.5 1 NaN 4.025 33 0.25313 4.0 4.0 4.0 1 NaN 5.206 30 0.57680 0.5 0.5 0.5 1 NaN 5.029 25 0.58075 -0.8 -0.8 -0.8 1 NaN 5.831 30 0.54581 2.3 2.3 2.3 1 NaN 4.046 29 0.60976 -7.1 -7.1 [...]
+4200739 54.0 54.0 54.0 1 NaN 5.792 31 0.98879 62.9 62.9 62.9 1 NaN 5.879 33 0.99209 58.5 58.5 58.5 1 NaN 6.012 28 0.98879 61.3 61.3 61.3 1 NaN 7.225 29 0.98879 52.8 52.8 52.8 1 NaN 6.105 27 0.98813 494.0 494.0 494.0 1 NaN 25.935 44 1.00000 536.6 536.6 536.6 1 NaN 23.539 41 1.00000 490.2 490.2 490.2 1 NaN 30.217 30 1.00000 490.8 490.8 490.8 1 NaN 23.458 42 1.00000 529.4 529.4 529.4 1 NaN 21.175 35 1.00000 161.5 161.5 161.5 1 NaN 9.237 26 1.00000 158.0 158.0 158.0 1 NaN 6.707 39 1.00000 15 [...]
+1090156 384.2 384.2 384.2 1 NaN 11.330 18 1.00000 306.2 306.2 306.2 1 NaN 9.133 13 1.00000 334.4 334.4 334.4 1 NaN 15.539 33 1.00000 289.1 289.1 289.1 1 NaN 16.736 39 1.00000 320.0 320.0 320.0 1 NaN 19.402 25 1.00000 20.2 20.2 20.2 1 NaN 8.095 31 0.85234 4.0 4.0 4.0 1 NaN 5.022 34 0.56625 5.8 5.8 5.8 1 NaN 5.910 33 0.62030 13.3 13.3 13.3 1 NaN 4.399 41 0.76467 -0.8 -0.8 -0.8 1 NaN 4.292 38 0.45880 74.8 74.8 74.8 1 NaN 5.652 27 0.99802 63.4 63.4 63.4 1 NaN 12.553 28 0.99604 69.2 69.2 69.2 [...]
+7050341 1.9 1.9 1.9 1 NaN 4.078 31 0.60580 3.6 3.6 3.6 1 NaN 7.133 23 0.63612 6.6 6.6 6.6 1 NaN 5.750 40 0.70864 3.7 3.7 3.7 1 NaN 4.862 33 0.63942 17.6 17.6 17.6 1 NaN 5.800 39 0.89453 21.3 21.3 21.3 1 NaN 6.834 30 0.85695 16.3 16.3 16.3 1 NaN 6.212 36 0.79169 8.6 8.6 8.6 1 NaN 5.130 42 0.67963 18.6 18.6 18.6 1 NaN 6.243 40 0.83652 4.6 4.6 4.6 1 NaN 5.814 27 0.59130 14.7 14.7 14.7 1 NaN 5.893 28 0.89519 -0.3 -0.3 -0.3 1 NaN 4.062 35 0.55900 10.2 10.2 10.2 1 NaN 5.905 37 0.83191 3.9 3.9 [...]
diff --git a/examples/magetab/illumina/illumina.idf b/examples/magetab/illumina/illumina.idf
new file mode 100755
index 0000000..5f619b2
--- /dev/null
+++ b/examples/magetab/illumina/illumina.idf
@@ -0,0 +1,28 @@
+Investigation Title MAQC Illumina data set (excerpt)
+Experimental Design array_platform_variation_design
+Experimental Factor Name SAMPLE
+Experimental Factor Type Sample
+Experimental Factor Type Term Source REF MGED Ontology
+
+Person Last Name Shi
+Person First Name Leming
+Person Roles submitter
+Person Roles Term Source REF MGED Ontology
+
+Quality Control Types technical_replicate
+Quality Control Types Term Source REF MGED Ontology
+Public Release Date 08/09/2006
+Comment[ArrayExpressSubmissionDate] 02/08/2006
+Publication Author List Leming Shi, Laura H. Reid, Richard Shippy, Janet A. Warrington, William Slikker Jr.
+Publication Title The MicroArray Quality Control (MAQC) project demonstrates concordant results between gene expression technology platforms
+Experiment Description The MicroArray Quality Control (MAQC) project was initiated to address these concerns, as well as other performance and analysis issues. We demonstrate the consistency of results within a platform across test sites as well as the high level of cross-platform concordance in terms of genes identified as differentially expressed. The MAQC study provides a rich resource that will help build consensus on the use of microarrays in research, clinical and regulatory settin [...]
+
+Protocol Name P-EXML-1
+Protocol Type transformation
+Protocol Description Data was processed using Illumina BeadStudio version 1.5.0.34 software using background subtraction and cubic spline normalization
+Protocol Term Source REF MGED Ontology
+
+SDRF File illumina_sdrf.txt
+
+Term Source Name MGED Ontology
+Term Source File http://mged.sourceforge.net/ontologies/MGEDontology.php
diff --git a/examples/magetab/illumina/illumina_sdrf.txt b/examples/magetab/illumina/illumina_sdrf.txt
new file mode 100755
index 0000000..477a3c6
--- /dev/null
+++ b/examples/magetab/illumina/illumina_sdrf.txt
@@ -0,0 +1,21 @@
+Source Name Material Type Term Source REF Labeled Extract Name Material Type Term Source REF Label Term Source REF Hybridization Name Array Design REF Scan Name Normalization Name Protocol REF Array Data File Derived Array Data File Factor Value[SAMPLE](Sample)
+A whole_organism MGED Ontology 1A.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323910012_A A-MEXP-524 1323910012_A Tissue1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 1
+A whole_organism MGED Ontology 1A.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323910012_B A-MEXP-524 1323910012_B Tissue1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 1
+A whole_organism MGED Ontology 1A.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323910012_C A-MEXP-524 1323910012_C Tissue1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 1
+A whole_organism MGED Ontology 1A.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323910012_D A-MEXP-524 1323910012_D Tissue1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 1
+A whole_organism MGED Ontology 1A.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323910012_E A-MEXP-524 1323910012_E Tissue1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 1
+B whole_organism MGED Ontology 1B.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901020_A A-MEXP-524 1323901020_A Tissue2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 2
+B whole_organism MGED Ontology 1B.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901020_B A-MEXP-524 1323901020_B Tissue2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 2
+B whole_organism MGED Ontology 1B.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901020_C A-MEXP-524 1323901020_C Tissue2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 2
+B whole_organism MGED Ontology 1B.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901020_E A-MEXP-524 1323901020_E Tissue2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 2
+B whole_organism MGED Ontology 1B.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901020_F A-MEXP-524 1323901020_F Tissue2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 2
+C whole_organism MGED Ontology 1C.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901024_A A-MEXP-524 1323901024_A Mixture1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 3
+C whole_organism MGED Ontology 1C.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901024_B A-MEXP-524 1323901024_B Mixture1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 3
+C whole_organism MGED Ontology 1C.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901024_D A-MEXP-524 1323901024_D Mixture1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 3
+C whole_organism MGED Ontology 1C.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901024_E A-MEXP-524 1323901024_E Mixture1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 3
+C whole_organism MGED Ontology 1C.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323901024_F A-MEXP-524 1323901024_F Mixture1 P-EXML-1 ProbeData.txt GeneGroupedData.txt 3
+D whole_organism MGED Ontology 1D.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323928007_A A-MEXP-524 1323928007_A Mixture2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 4
+D whole_organism MGED Ontology 1D.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323928007_C A-MEXP-524 1323928007_C Mixture2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 4
+D whole_organism MGED Ontology 1D.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323928007_D A-MEXP-524 1323928007_D Mixture2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 4
+D whole_organism MGED Ontology 1D.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323928007_E A-MEXP-524 1323928007_E Mixture2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 4
+D whole_organism MGED Ontology 1D.biotin synthetic_DNA MGED Ontology biotin MGED Ontology 1323928007_F A-MEXP-524 1323928007_F Mixture2 P-EXML-1 ProbeData.txt GeneGroupedData.txt 4
diff --git a/examples/magetab/real/E-MEXP-880.idf b/examples/magetab/real/E-MEXP-880.idf
new file mode 100644
index 0000000..08c68f8
--- /dev/null
+++ b/examples/magetab/real/E-MEXP-880.idf
@@ -0,0 +1,27 @@
+"Investigation Title" BF-H.sapiens-HOXAmethylation
+"Experimental Design" disease_state_design
+"Experimental Factor Name" DISEASESTATE CELLLINE MATERIALTYPE
+"Experimental Factor Type" disease_state cell_line material_type
+
+"Person Last Name" Novak
+"Person First Name" Petr
+"Person Mid Initial"
+"Person Email" futscherlab at azcc.arizona.edu
+"Person Address" "1515 N Campbell Ave, Tucson, AZ, 85722, USA"
+"Person Affiliation" "University of Arizona"
+"Person Roles" submitter
+
+"Public Release Date" 2006-09-30
+
+Comment[ArrayExpressSubmissionDate] 2006-10-04
+
+"Publication Title" "Epigenetic Inactivation of the HOXA Gene Cluster in Breast Cancer"
+"Experiment Description" "To evaluate DNA methylation profile associated with breast cancer in 125 kB region of HOXA cluster we analyzed DNA from normal cancerous breast specimens and cell lines. Analysis was performed on the tiling array covering entire HOXA region with 500bp resolution. Immunoprecipitation with anti-methylcytosine antibody was for target preparation."
+
+"Protocol Name" BF_CGI_hyb HMEC CCL-231-BT549 BREAST_TISSUE MeCIP_Cy3 BF_CGI_Scan BF-MeCIP MeCIP_Cy5 BF-HOXA_transf
+"Protocol Type" hybridization specified_biomaterial_action specified_biomaterial_action specified_biomaterial_action labeling image_acquisition nucleic_acid_extraction labeling bioassay_data_transformation
+"Protocol Description" "Cy3 and Cy5 labeled extracts were mixed and purified using Qiagen PCR purification kit according manufacturer's instruction.Hybridization Protocol:Slide Preparation Protocol:1. Remove all slides that you need from N2 storage and label accordingly. On the back side of the slide, scratch an outline of the corners of the array using a diamond-tipped scribe. The front is defined as the side the array is printed on and should have a label on it.Note: Only label with a [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" E-MEXP-880_sdrf.txt
diff --git a/examples/magetab/real/E-MEXP-880_sdrf.txt b/examples/magetab/real/E-MEXP-880_sdrf.txt
new file mode 100644
index 0000000..4c8bd12
--- /dev/null
+++ b/examples/magetab/real/E-MEXP-880_sdrf.txt
@@ -0,0 +1,41 @@
+"Source Name" "Material Type" "Characteristics [BioSourceProvider]" "Characteristics [BioSourceType]" "Characteristics [CellLine]" "Characteristics [ClinicalHistory]" "Characteristics [DevelopmentalStage]" "Characteristics [DiseaseState]" "Characteristics [Individual]" "Characteristics [OrganismPart]" "Characteristics [Organism]" "Characteristics [Sex]" "Characteristics [TargetedCellType]" "Protocol REF" "Sample Name" "Material Type" "Protocol REF" "Sample Name" "Material Type" "Extract [...]
+1139T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 1139 "Lymph node metastasis" adult "Lymph node metastasis" 1139 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 1139T organism_part BF-MeCIP E_1139T genomic_DNA "L_1139T IP" molecular_mixture MeCIP_Cy3 "L_1139T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_1139T_1139T_1 A-MEXP-568 BF_CGI_Scan 1139T 1139T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 1139 "Lymph node metastasis" molecular_mixture
+1139T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 1139 "Lymph node metastasis" adult "Lymph node metastasis" 1139 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 1139T organism_part BF-MeCIP E_1139T genomic_DNA "L_1139T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_1139T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_1139T_1139T_1 A-MEXP-568 BF_CGI_Scan 1139T 1139T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 1139 "Lymph node metastasis" [...]
+120T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 120 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 120 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 120T organism_part BF-MeCIP E_120T genomic_DNA "L_120T IP" molecular_mixture MeCIP_Cy3 "L_120T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_120T_120T_1 A-MEXP-568 BF_CGI_Scan 120T 120T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 120 "Infiltrating ductal carcinoma" [...]
+120T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 120 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 120 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 120T organism_part BF-MeCIP E_120T genomic_DNA "L_120 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_120T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_120T_120T_1 A-MEXP-568 BF_CGI_Scan 120T 120T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 120 "Infiltrating ductal [...]
+2845T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 2845 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 2845 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 2845T organism_part BF-MeCIP E_2845T genomic_DNA "L_2845T IP" molecular_mixture MeCIP_Cy3 "L_2845T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_2845T_2845T_1 A-MEXP-568 BF_CGI_Scan 2845T 2845T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 2845 "Infiltrating ductal [...]
+2845T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 2845 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 2845 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 2845T organism_part BF-MeCIP E_2845T genomic_DNA "L_2845T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_2845T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_2845T_2845T_1 A-MEXP-568 BF_CGI_Scan 2845T 2845T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 2845 "Infilt [...]
+5256T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 5256 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 5256 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 5256T organism_part BF-MeCIP E_5256 genomic_DNA "L_5256T IP" molecular_mixture MeCIP_Cy3 "L_5256T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_5256T_5256T_1 A-MEXP-568 BF_CGI_Scan 5256T 5256T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 5256 "Infiltrating ductal [...]
+5256T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 5256 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 5256 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 5256T organism_part BF-MeCIP E_5256 genomic_DNA "L_5256T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_5256T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_5256T_5256T_1 A-MEXP-568 BF_CGI_Scan 5256T 5256T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 5256 "Infiltr [...]
+5343N whole_organism "University Medical Center, Tucson,AZ" frozen_sample 5343 adult Normal 5343 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 5343N organism_part BF-MeCIP E_5343N genomic_DNA "L_5343N IP" molecular_mixture MeCIP_Cy3 "L_5343N Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_5343N_5343N_1 A-MEXP-568 BF_CGI_Scan 5343N 5343N.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 5343 normal molecular_mixture
+5343N whole_organism "University Medical Center, Tucson,AZ" frozen_sample 5343 adult Normal 5343 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 5343N organism_part BF-MeCIP E_5343N genomic_DNA "L_5343N sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_5343N_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_5343N_5343N_1 A-MEXP-568 BF_CGI_Scan 5343N 5343N.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 5343 normal genomic_DNA
+5799T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 5799 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 5799 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 5799T organism_part BF-MeCIP E_5799T genomic_DNA "L_5799T IP" molecular_mixture MeCIP_Cy3 "L_5799T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_5799T_5799T_1 A-MEXP-568 BF_CGI_Scan 5799T 5799T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 5799 "Infiltrating ductal [...]
+5799T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 5799 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 5799 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 5799T organism_part BF-MeCIP E_5799T genomic_DNA "L_5799T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_5799T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_5799T_5799T_1 A-MEXP-568 BF_CGI_Scan 5799T 5799T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 5799 "Infilt [...]
+6245T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 6245 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 6245 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 6245T organism_part BF-MeCIP E_6245T genomic_DNA "L_6245T IP" molecular_mixture MeCIP_Cy3 "L_6245T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_6245T_6245T_1 A-MEXP-568 BF_CGI_Scan 6245T 6245T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 6245 "Infiltrating ductal [...]
+6245T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 6245 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 6245 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 6245T organism_part BF-MeCIP E_6245T genomic_DNA "L_6245T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_6245T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_6245T_6245T_1 A-MEXP-568 BF_CGI_Scan 6245T 6245T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 6245 "Infilt [...]
+6333N whole_organism "University Medical Center, Tucson,AZ" frozen_sample 6333 adult Normal 6333 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 6333N organism_part BF-MeCIP E_6333N genomic_DNA "L_6333N IP" molecular_mixture MeCIP_Cy3 "L_6333N Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_6333N_6333N_1 A-MEXP-568 BF_CGI_Scan 6333N 6333N.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 6333 normal molecular_mixture
+6333N whole_organism "University Medical Center, Tucson,AZ" frozen_sample 6333 adult Normal 6333 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 6333N organism_part BF-MeCIP E_6333N genomic_DNA "L_6333N sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_6333N_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_6333N_6333N_1 A-MEXP-568 BF_CGI_Scan 6333N 6333N.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 6333 normal genomic_DNA
+7732N whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7732 adult Normal 7732 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7732N organism_part BF-MeCIP E_7732N genomic_DNA "L_7732N IP" molecular_mixture MeCIP_Cy3 "L_7732N Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_7732N_7732N_1 A-MEXP-568 BF_CGI_Scan 7732N 7732N.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7732 normal molecular_mixture
+7732N whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7732 adult Normal 7732 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7732N organism_part BF-MeCIP E_7732N genomic_DNA "L_7732N sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_7732N_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_7732N_7732N_1 A-MEXP-568 BF_CGI_Scan 7732N 7732N.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7732 normal genomic_DNA
+7732T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7732 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 7732 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7732T organism_part BF-MeCIP E_7732T genomic_DNA "L_7732T IP" molecular_mixture MeCIP_Cy3 "L_7732T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_7732T_7732T_1 A-MEXP-568 BF_CGI_Scan 7732T 7732T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7732 "Infiltrating ductal [...]
+7732T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7732 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 7732 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7732T organism_part BF-MeCIP E_7732T genomic_DNA "L_7732T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_7732T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_7732T_7732T_1 A-MEXP-568 BF_CGI_Scan 7732T 7732T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7732 "Infilt [...]
+7768T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7768 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 7768 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7768T organism_part BF-MeCIP E_7768T genomic_DNA "L_7768T IP" molecular_mixture MeCIP_Cy3 "L_7768T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_7768T_7768T_1 A-MEXP-568 BF_CGI_Scan 7768T 7768T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7768 "Infiltrating ductal [...]
+7768T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7768 "Infiltrating ductal carcinoma" adult "Infiltrating ductal carcinoma" 7768 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7768T organism_part BF-MeCIP E_7768T genomic_DNA "L_7768T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_7768T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_7768T_7768T_1 A-MEXP-568 BF_CGI_Scan 7768T 7768T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7768 "Infilt [...]
+7788T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7788 "Infiltrating lobular carcinoma" adult "Infiltrating lobular carcinoma" 7788 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7788T organism_part BF-MeCIP E_7788T genomic_DNA "L_7788T IP" molecular_mixture MeCIP_Cy3 "L_7788T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_7788T_7788T_1 A-MEXP-568 BF_CGI_Scan 7788T 7788T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7788 "Infiltrating lobu [...]
+7788T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 7788 "Infiltrating lobular carcinoma" adult "Infiltrating lobular carcinoma" 7788 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 7788T organism_part BF-MeCIP E_7788T genomic_DNA "L_7788T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_7788T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_7788T_7788T_1 A-MEXP-568 BF_CGI_Scan 7788T 7788T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 7788 "Infi [...]
+9663T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 9663 "Lymph node metastasis" adult "Lymph node metastasis" 9663 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 9663T organism_part BF-MeCIP E_9663T genomic_DNA "L_9663T IP" molecular_mixture MeCIP_Cy3 "L_9663T Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_9663T_9663T_1 A-MEXP-568 BF_CGI_Scan 9663T 9663T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 9663 "Lymph node metastasis" molecular_mixture
+9663T whole_organism "University Medical Center, Tucson,AZ" frozen_sample 9663 "Lymph node metastasis" adult "Lymph node metastasis" 9663 "mammary gland" "Homo sapiens" female Epithelial BREAST_TISSUE 9663T organism_part BF-MeCIP E_9663T genomic_DNA "L_9663T sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_9663T_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_9663T_9663T_1 A-MEXP-568 BF_CGI_Scan 9663T 9663T.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt 9663 "Lymph node metastasis" [...]
+"BT 549-1" whole_organism "American Type Culture Collection (Rockville, MD)" "BT 549" "Breast Cancer Cell line" adult "Breast Cancer Cell line" "BT 549" "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 "BT 549-1" organism_part BF-MeCIP "E_BT 549-1" genomic_DNA "L_BT 549-1 IP" molecular_mixture MeCIP_Cy3 "L_BT 549-1 Cy3" synthetic_DNA Cy3 BF_CGI_hyb "H_BT 549-1_BT 549-1_1" A-MEXP-568 BF_CGI_Scan BT549-1 BT549-1.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.t [...]
+"BT 549-1" whole_organism "American Type Culture Collection (Rockville, MD)" "BT 549" "Breast Cancer Cell line" adult "Breast Cancer Cell line" "BT 549" "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 "BT 549-1" organism_part BF-MeCIP "E_BT 549-1" genomic_DNA "L_BT 549-1 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_BT 549-1_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb "H_BT 549-1_BT 549-1_1" A-MEXP-568 BF_CGI_Scan BT549-1 BT549-1.gpr BF-HOXA_transf combined_data_HOXA.txt combine [...]
+"BT 549-2" whole_organism "American Type Culture Collection (Rockville, MD)" "BT 549" "Breast Cancer Cell line" adult "Breast Cancer Cell line" "BT 549" "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 "BT 549-2" organism_part BF-MeCIP "E_BT 549-2" genomic_DNA "L_BT 549-2 IP" molecular_mixture MeCIP_Cy3 "L_BT 549-2 Cy3" synthetic_DNA Cy3 BF_CGI_hyb "H_BT 549-2_BT 549-2_1" A-MEXP-568 BF_CGI_Scan BT549-2 BT549-2.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.t [...]
+"BT 549-2" whole_organism "American Type Culture Collection (Rockville, MD)" "BT 549" "Breast Cancer Cell line" adult "Breast Cancer Cell line" "BT 549" "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 "BT 549-2" organism_part BF-MeCIP "E_BT 549-2" genomic_DNA "L_BT 549-2 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_BT 549-2_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb "H_BT 549-2_BT 549-2_1" A-MEXP-568 BF_CGI_Scan BT549-2 BT549-2.gpr BF-HOXA_transf combined_data_HOXA.txt combine [...]
+HMEC1 whole_organism "Clonetics, San Diego, CA" HMEC "Human Mammary Epithelial Cell" adult Normal HMEC "mammary gland" "Homo sapiens" female Epithelial HMEC HMEC1 organism_part BF-MeCIP E_HMEC1 genomic_DNA "L_HMEC1 IP" molecular_mixture MeCIP_Cy3 "L_HMEC1 Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_HMEC1_HMEC1_1 A-MEXP-568 BF_CGI_Scan HMEC1 HMEC1.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt HMEC normal molecular_mixture
+HMEC1 whole_organism "Clonetics, San Diego, CA" HMEC "Human Mammary Epithelial Cell" adult Normal HMEC "mammary gland" "Homo sapiens" female Epithelial HMEC HMEC1 organism_part BF-MeCIP E_HMEC1 genomic_DNA "L_HMEC1 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_HMEC1_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_HMEC1_HMEC1_1 A-MEXP-568 BF_CGI_Scan HMEC1 HMEC1.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt HMEC normal genomic_DNA
+HMEC2 whole_organism "Clonetics, San Diego, CA" HMEC "Human Mammary Epithelial Cell" adult Normal HMEC "mammary gland" "Homo sapiens" female Epithelial HMEC HMEC2 organism_part BF-MeCIP E_HMEC2 genomic_DNA "L_HMEC2 IP" molecular_mixture MeCIP_Cy3 "L_HMEC2 Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_HMEC2_HMEC2_1 A-MEXP-568 BF_CGI_Scan HMEC2 HMEC2.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt HMEC normal molecular_mixture
+HMEC2 whole_organism "Clonetics, San Diego, CA" HMEC "Human Mammary Epithelial Cell" adult Normal HMEC "mammary gland" "Homo sapiens" female Epithelial HMEC HMEC2 organism_part BF-MeCIP E_HMEC2 genomic_DNA "L_HMEC2 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_HMEC2_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_HMEC2_HMEC2_1 A-MEXP-568 BF_CGI_Scan HMEC2 HMEC2.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt HMEC normal genomic_DNA
+HMEC3 whole_organism "Clonetics, San Diego, CA" HMEC "Human Mammary Epithelial Cell" adult Normal HMEC "mammary gland" "Homo sapiens" female Epithelial HMEC HMEC3 organism_part BF-MeCIP E_HMEC3 genomic_DNA "L_HMEC3 IP" molecular_mixture MeCIP_Cy3 "L_HMEC3 Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_HMEC3_HMEC3_1 A-MEXP-568 BF_CGI_Scan HMEC3 HMEC3.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt HMEC normal molecular_mixture
+HMEC3 whole_organism "Clonetics, San Diego, CA" HMEC "Human Mammary Epithelial Cell" adult Normal HMEC "mammary gland" "Homo sapiens" female Epithelial HMEC HMEC3 organism_part BF-MeCIP E_HMEC3 genomic_DNA "L_HMEC3 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_HMEC3_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_HMEC3_HMEC3_1 A-MEXP-568 BF_CGI_Scan HMEC3 HMEC3.gpr BF-HOXA_transf combined_data_HOXA.txt combined_data_HOXA.txt HMEC normal genomic_DNA
+MB-MDA-231-1 whole_organism "American Type Culture Collection (Rockville, MD)" MB-MDA-231 "Breast Cancer Cell line" adult "Breast Cancer Cell line" MB-MDA-231 "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 MB-MDA-231-1 organism_part BF-MeCIP E_MB-MDA-231-1 genomic_DNA "L_MB-MDA-231-1 IP" molecular_mixture MeCIP_Cy3 "L_MB-MDA-231-1 Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_MB-MDA-231-1_MB-MDA-231-1_1 A-MEXP-568 BF_CGI_Scan MDA-MB-231-1 MDA-MB-231-1.gpr BF-HOXA_transf combined_d [...]
+MB-MDA-231-1 whole_organism "American Type Culture Collection (Rockville, MD)" MB-MDA-231 "Breast Cancer Cell line" adult "Breast Cancer Cell line" MB-MDA-231 "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 MB-MDA-231-1 organism_part BF-MeCIP E_MB-MDA-231-1 genomic_DNA "L_MB-MDA-231-1 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_MB-MDA-231-1_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_MB-MDA-231-1_MB-MDA-231-1_1 A-MEXP-568 BF_CGI_Scan MDA-MB-231-1 MDA-MB-231-1.gpr BF-HOXA_tran [...]
+MB-MDA-231-2 whole_organism "American Type Culture Collection (Rockville, MD)" MB-MDA-231 "Breast Cancer Cell line" adult "Breast Cancer Cell line" MB-MDA-231 "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 MB-MDA-231-2 organism_part BF-MeCIP E_MB-MDA-231-2 genomic_DNA "L_MB-MDA-231-2 IP" molecular_mixture MeCIP_Cy3 "L_MB-MDA-231-2 Cy3" synthetic_DNA Cy3 BF_CGI_hyb H_MB-MDA-231-2_MB-MDA-231-2_1 A-MEXP-568 BF_CGI_Scan MDA-MB-231-2 MDA-MB-231-2.gpr BF-HOXA_transf combined_d [...]
+MB-MDA-231-2 whole_organism "American Type Culture Collection (Rockville, MD)" MB-MDA-231 "Breast Cancer Cell line" adult "Breast Cancer Cell line" MB-MDA-231 "mammary gland" "Homo sapiens" female Epithelial CCL-231-BT549 MB-MDA-231-2 organism_part BF-MeCIP E_MB-MDA-231-2 genomic_DNA "L_MB-MDA-231-2 sonicated input DNA" genomic_DNA MeCIP_Cy5 "L_MB-MDA-231-2_1 Cy5" synthetic_DNA Cy5 BF_CGI_hyb H_MB-MDA-231-2_MB-MDA-231-2_1 A-MEXP-568 BF_CGI_Scan MDA-MB-231-2 MDA-MB-231-2.gpr BF-HOXA_tran [...]
diff --git a/examples/magetab/real/E-TABM-102.idf b/examples/magetab/real/E-TABM-102.idf
new file mode 100644
index 0000000..7163233
--- /dev/null
+++ b/examples/magetab/real/E-TABM-102.idf
@@ -0,0 +1,33 @@
+"Investigation Title" "Transcription profiling of wild-type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide after 1, 2, 4, 8 and 24 hours"
+"Experimental Design" compound_treatment_design genetic_modification_design time_series_design
+"Experimental Design Term Source REF" "MGED Ontology" "MGED Ontology" "MGED Ontology"
+"Experimental Factor Name" COMPOUND DOSE TIME GENOTYPE
+"Experimental Factor Type" compound dose time genotype
+
+"Person Last Name" Marzolf
+"Person First Name" Bruz
+"Person Mid Initial"
+"Person Affiliation" "Institute for Systems Biology"
+"Person Roles" submitter
+"Person Roles Term Source REF" "MGED Ontology"
+
+"Quality Control Types" biological_replicate
+"Quality Control Term Source REF" "MGED Ontology"
+"Public Release Date" "2006-05-30"
+
+Comment[ArrayExpressSubmissionDate] "2006-05-30"
+
+"Publication Author List" "Mark Gilchrist; Vesteinn Thorsson; Bin Li; Alistair G. Rust; Martin Korb; Kathleen Kennedy; Tsonwin Hai; Hamid Bolouri and Alan Aderem"
+"Publication Title" "Systems biology approaches identify ATF3 as a negative regulator of Toll-like receptor 4"
+"Experiment Description" "Time course data from lipopolysaccharide-stimulated wild-type and ATF3 -/- mouse bone marrow macrophages were collected to investigate the transcriptional network in Toll-like recepter 4-activated macrophages."
+
+"Protocol Name" P-TABM-gilchrist1 P-TABM-gilchrist2
+"Protocol Type" grow specified_biomaterial_action
+"Protocol Description" "Flush femurs from C57BL/6 mice (Jackson Laboratories) with complete RPMI (RPMI 1640 supplemented with 10% FCS, 2mM L-glutamine, 100 IU/mL penicillin and 100ug/mL streptomycin, all from Cellgro, Mediatech, except the FCS which is from from Hyclone). Plate bone marrow cells on non-tissue culture treated plastic in complete RPMI supplemented with recombinant human M-CSF (rhM-CSF) at 50 ng/mL (Chiron). On day 3, wash the cells two times with complete RPMI and then gro [...]
+"Protocol Term Source REF" "MGED Ontology" "MGED Ontology"
+
+"SDRF File" E-TABM-102_sdrf.txt
+
+"Term Source Name" "MGED Ontology" "ArrayExpress" "Cell Type Ontology"
+"Term Source File" http://mged.sourceforge.net/ontologies/MGEDontology.php http://www.ebi.ac.uk/arrayexpress/ http://obo.cvs.sourceforge.net/*checkout*/obo/obo/ontology/anatomy/cell_type/cell.obo
+"Term Source Version" "1.3.1.1"
diff --git a/examples/magetab/real/E-TABM-102_sdrf.txt b/examples/magetab/real/E-TABM-102_sdrf.txt
new file mode 100644
index 0000000..1eb41a7
--- /dev/null
+++ b/examples/magetab/real/E-TABM-102_sdrf.txt
@@ -0,0 +1,31 @@
+"Source Name" "Material Type" "Term Source REF" "Characteristics [CellType]" "Term Source REF" "Term Accession Number" "Characteristics [GeneticModification]" "Term Source REF" "Characteristics [Genotype]" "Term Source REF" "Characteristics [Organism]" "Characteristics [Sex]" "Term Source REF" "Characteristics [StrainOrLine]" "Protocol REF" "Protocol REF" "Sample Name" "Material Type" "Term Source REF" "Protocol REF" "Term Source REF" "Extract Name" "Material Type" "Term Source REF" "Pro [...]
+"LPS 0 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 0 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 0 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 0 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040603_03_LPS1-0 A-AFFY-45 ArrayExpress 20040603_03_LPS1-0 20040603_03_LPS1-0 [...]
+"LPS 0 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 0 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 0 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 0 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040621_01_LPS2-0 A-AFFY-45 ArrayExpress 20040621_01_LPS2-0 20040621_01_LPS2-0 [...]
+"LPS 0 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 0 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 0 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 0 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040622_01_LPS3-0 A-AFFY-45 ArrayExpress 20040622_01_LPS3-0 20040622_01_LPS3-0 [...]
+"LPS 1 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 1 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 1 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 1 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040609_03_LPS1-60 A-AFFY-45 ArrayExpress 20040609_03_LPS1-60 20040609_03_LPS1 [...]
+"LPS 1 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 1 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 1 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 1 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040621_04_LPS2-60 A-AFFY-45 ArrayExpress 20040621_04_LPS2-60 20040621_04_LPS2 [...]
+"LPS 1 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 1 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 1 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 1 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040622_04_LPS3-60 A-AFFY-45 ArrayExpress 20040622_04_LPS3-60 20040622_04_LPS3 [...]
+"LPS 2 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 2 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 2 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 2 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040603_04_LPS1-120 A-AFFY-45 ArrayExpress 20040603_04_LPS1-120 20040603_04_LP [...]
+"LPS 2 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 2 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 2 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 2 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040621_06_LPS2-120 A-AFFY-45 ArrayExpress 20040621_06_LPS2-120 20040621_06_LP [...]
+"LPS 2 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 2 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 2 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 2 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20040622_06_LPS3-120 A-AFFY-45 ArrayExpress 20040622_06_LPS3-120 20040622_06_LP [...]
+"LPS 4 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 4 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 4 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 4 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050310_03_C57_A-240 A-AFFY-45 ArrayExpress 20050310_03_C57_A-240 20050310_03_ [...]
+"LPS 4 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 4 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 4 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 4 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050505_09_C57_LPS_B_240 A-AFFY-45 ArrayExpress 20050505_09_C57_LPS_B_240 2005 [...]
+"LPS 4 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 4 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 4 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 4 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050623_03_C57_LPS_C_240min A-AFFY-45 ArrayExpress 20050623_03_C57_LPS_C_240mi [...]
+"LPS 8 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 8 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 8 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 8 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050622_09_LPS_A1_8_hr A-AFFY-45 ArrayExpress 20050622_09_LPS_A1_8_hr 20050622 [...]
+"LPS 8 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 8 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 8 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 8 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050622_11_LPS_B1_8_hr A-AFFY-45 ArrayExpress 20050622_11_LPS_B1_8_hr 20050622 [...]
+"LPS 8 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 8 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 8 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 8 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050623_01_LPS_C1_8hr A-AFFY-45 ArrayExpress 20050623_01_LPS_C1_8hr 20050623_0 [...]
+"LPS 24 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 24 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 24 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 24 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050622_10_LPS_A1_24_hr A-AFFY-45 ArrayExpress 20050622_10_LPS_A1_24_hr 20 [...]
+"LPS 24 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 24 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 24 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 24 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050622_12_LPS_B1_24_hr A-AFFY-45 ArrayExpress 20050622_12_LPS_B1_24_hr 20 [...]
+"LPS 24 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 wild_type "MGED Ontology" "Mus musculus" mixed_sex "MGED Ontology" C57BL/6 P-TABM-gilchrist1 P-TABM-gilchrist2 "LPS 24 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "LPS 24 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "LPS 24 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050623_04_LPS_C2_24hr A-AFFY-45 ArrayExpress 20050623_04_LPS_C2_24hr 2005 [...]
+"ATF3 -/- LPS 0 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 0 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 0 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 0 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050310_04_ATF3- [...]
+"ATF3 -/- LPS 0 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 0 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 0 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 0 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050331_08_ATF3- [...]
+"ATF3 -/- LPS 0 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 0 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 0 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 0 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050504_09_ATF3_ [...]
+"ATF3 -/- LPS 1 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 1 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 1 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 1 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050310_05_ATF3- [...]
+"ATF3 -/- LPS 1 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 1 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 1 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 1 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050411_04_ATF3- [...]
+"ATF3 -/- LPS 1 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 1 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 1 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 1 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050504_10_ATF3_ [...]
+"ATF3 -/- LPS 2 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 2 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 2 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 2 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050310_06_ATF3- [...]
+"ATF3 -/- LPS 2 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 2 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 2 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 2 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050411_05_ATF3- [...]
+"ATF3 -/- LPS 2 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 2 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 2 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 2 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050504_11_ATF3_ [...]
+"ATF3 -/- LPS 4 hr Mouse 1" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 4 hr Mouse 1" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 4 hr Mouse 1" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 4 hr Mouse 1 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050310_07_ATF3- [...]
+"ATF3 -/- LPS 4 hr Mouse 2" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 4 hr Mouse 2" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 4 hr Mouse 2" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 4 hr Mouse 2 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050623_02_ATF3_ [...]
+"ATF3 -/- LPS 4 hr Mouse 3" cell "MGED Ontology" macrophage "Cell Type Ontology" CL:0000235 gene_knock_out "MGED Ontology" "ATF3 -/-" "Mus musculus" mixed_sex "MGED Ontology" "ATF3 -/- C57BL/6" P-TABM-gilchrist1 P-TABM-gilchrist2 "ATF3 -/- LPS 4 hr Mouse 3" organism_part "MGED Ontology" P-AFFY-1 ArrayExpress "ATF3 -/- LPS 4 hr Mouse 3" total_RNA "MGED Ontology" P-AFFY-2 ArrayExpress "ATF3 -/- LPS 4 hr Mouse 3 biotin" synthetic_DNA "MGED Ontology" biotin "MGED Ontology" 20050504_12_ATF3_ [...]
diff --git a/examples/magetab/real/E-TABM-134.idf b/examples/magetab/real/E-TABM-134.idf
new file mode 100644
index 0000000..a8b0141
--- /dev/null
+++ b/examples/magetab/real/E-TABM-134.idf
@@ -0,0 +1,27 @@
+"Investigation Title" "WGA-LCM and Genomewide Survey of Lung Cancer"
+"Experimental Design" "genotyping_design disease_state_design optimization_design"
+"Experimental Factor Name" DISEASESTAGING INDIVIDUAL DISEASESTATE LINEAR_AMPLIFICATION
+"Experimental Factor Type" disease_staging individual disease_state linear_amplification
+
+"Person Last Name" Bird
+"Person First Name" Helen
+"Person Mid Initial"
+"Person Email" H.E.Bird at warwick.ac.uk
+"Person Affiliation" "Warwick University"
+"Person Roles" submitter
+
+"Public Release Date" "2006-10-01"
+
+Comment[ArrayExpressSubmissionDate] "2006-08-11"
+
+"Publication Author List" "Bird H; Snead DRJ; Hey Y; Brown PE; Davis SA; James S; Knowles MA; Hurst CD; Blanks A; Pepper SD & Snead DRJ; Brown PE; James S; Davis SA; Suortamo S; Pandey S; Blanks A; Grammatopoulos D; Vohra H; Bird H"
+"Publication Title" "Validation of a method for processing laser captures microdissected samples for analysis using high density single nucleotide polymorphism arrays & Genomewide loss of heterozygosity in small cell and non-small cell lung cancer and precursor bronchial epithelium"
+"Experiment Description" "A series of experiments were performed to validate whole genome amplification of genomic DNA from laser captured microdissectates and the technique was then applied to serial samples of developing lung cancers to identify early events. 10K Affymetrix SNP arrays were used."
+
+"Protocol Name" P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-6 P-EXML-8 P-EXML-9 P-EXML-10
+"Protocol Type" grow specified_biomaterial_action nucleic_acid_extraction labeling hybridization hybridization image_acquisition
+"Protocol Description" "Resected tumour, tissue of bronchial origin and lymph node were snap frozen in liquid nitrogen. Frozen sections were cut and stained with haematoxylin and eosin." "LCM was performed using the Nikon Slᄉ cut instrument (Nikon UK Ltd.) on frozen sections which had been stored at -80ᄎC. Target lesions were visualised directly, excised and captured on to the adhesive cap of the LCM tubes (Nikon UK Ltd.). Captured cells were stored at -80ᄎC prior to WGA and microarray [...]
+"Protocol Parameters" amount_of_starting_material;amplification_method;processing_centre
+"Protocol Software"
+
+"SDRF File" E-TABM-134_sdrf.txt
diff --git a/examples/magetab/real/E-TABM-134_sdrf.txt b/examples/magetab/real/E-TABM-134_sdrf.txt
new file mode 100644
index 0000000..18555bf
--- /dev/null
+++ b/examples/magetab/real/E-TABM-134_sdrf.txt
@@ -0,0 +1,26 @@
+"Source Name" "Material Type" "Characteristics [Age]" "Characteristics [ClinicalHistory]" "Characteristics [DiseaseStaging]" "Characteristics [DiseaseState]" "Characteristics [Individual]" "Characteristics [OrganismPart]" "Characteristics [Organism]" "Characteristics [Sex]" "Characteristics [TimeUnit]" "Characteristics [TumourGrading]" "Protocol REF" "Protocol REF" "Sample Name" "Material Type" "Protocol REF" "Parameter Value [amount_of_starting_material]" "Parameter Value [amplification [...]
+nJ82_P cell_line "tumour cell line" "patient J82" "Homo sapiens" nJ82_P organism_part P-EXML-4 "250ng DNA" none PICR nJ82_P total_RNA P-EXML-6 "nJ82_P biotin" synthetic_DNA biotin P-EXML-8 nJ82_P A-AFFY-64 P-EXML-10 nJ82_P nJ82_P.CEL nJ82_P nJ82_P.CHP Mapping10K_Xba131.CDF "not applicable" "tumour cell line" "patient J82" none
+nJ82ebv_P cell_line "immortalised cell line" "patient J82" "Homo sapiens" nJ82ebv_P organism_part P-EXML-4 "250ng DNA" none PICR nJ82ebv_P total_RNA P-EXML-6 "nJ82ebv_P biotin" synthetic_DNA biotin P-EXML-8 nJ82ebv_P A-AFFY-64 P-EXML-10 nJ82ebv_P nJ82ebv_P.CEL nJ82ebv_P nJ82ebv_P.CHP Mapping10K_Xba131.CDF "not applicable" "immortalised cell line" "patient J82" none
+aJ82_1_W cell_line "tumour cell line" "patient J82" "Homo sapiens" aJ82_1_W organism_part P-EXML-4 "1ng DNA" WGA Warwick aJ82_1_W total_RNA P-EXML-6 "aJ82_1_W biotin" synthetic_DNA biotin P-EXML-8 aJ82_1_W A-AFFY-64 P-EXML-10 aJ82_1_W aJ82_1_W.CEL aJ82_1_W aJ82_1_W.CHP Mapping10K_Xba131.CDF "not applicable" "tumour cell line" "patient J82" WGA
+aJ82ebv_1_W cell_line "immortalised cell line" "patient J82" "Homo sapiens" aJ82ebv_1_W organism_part P-EXML-4 "1ng DNA" WGA Warwick aJ82ebv_1_W total_RNA P-EXML-6 "aJ82ebv_1_W biotin" synthetic_DNA biotin P-EXML-8 aJ82ebv_1_W A-AFFY-64 P-EXML-10 aJ82ebv_1_W aJ82ebv_1_W.CEL aJ82ebv_1_W aJ82ebv_1_W.CHP Mapping10K_Xba131.CDF "not applicable" "immortalised cell line" "patient J82" WGA
+aJ82_3_W cell_line "tumour cell line" "patient J82" "Homo sapiens" aJ82_3_W organism_part P-EXML-4 "3ng DNA" WGA Warwick aJ82_3_W total_RNA P-EXML-6 "aJ82_3_W biotin" synthetic_DNA biotin P-EXML-8 aJ82_3_W A-AFFY-64 P-EXML-10 aJ82_3_W aJ82_3_W.CEL aJ82_3_W aJ82_3_W.CHP Mapping10K_Xba131.CDF "not applicable" "tumour cell line" "patient J82" WGA
+nJ82_W cell_line "tumour cell line" "patient J82" "Homo sapiens" nJ82_W organism_part P-EXML-4 "250ng DNA" none Warwick nJ82_W total_RNA P-EXML-6 "nJ82_W biotin" synthetic_DNA biotin P-EXML-8 nJ82_W A-AFFY-64 P-EXML-10 nJ82_W nJ82_W.CEL nJ82_W nJ82_W.CHP Mapping10K_Xba131.CDF "not applicable" "tumour cell line" "patient J82" none
+0904_MK18_10K_90J82 cell_line "mixed source cell line" "patient J82" "Homo sapiens" 0904_MK18_10K_90J82 organism_part P-EXML-4 "250ng DNA" none PICR 0904_MK18_10K_90J82 total_RNA P-EXML-6 "0904_MK18_10K_90J82 biotin" synthetic_DNA biotin P-EXML-8 0904_MK18_10K_90J82 A-AFFY-64 P-EXML-10 0904_MK18_10K_90J82 0904_MK18_10K_90J82.CEL 0904_MK18_10K_90J82 0904_MK18_10K_90J82.CHP Mapping10K_Xba131.CDF "not applicable" "mixed source cell line" "patient J82" none
+0904_MK19_10K_80J82 cell_line "mixed source cell line" "patient J82" "Homo sapiens" 0904_MK19_10K_80J82 organism_part P-EXML-4 "250ng DNA" none PICR 0904_MK19_10K_80J82 total_RNA P-EXML-6 "0904_MK19_10K_80J82 biotin" synthetic_DNA biotin P-EXML-8 0904_MK19_10K_80J82 A-AFFY-64 P-EXML-10 0904_MK19_10K_80J82 0904_MK19_10K_80J82.CEL 0904_MK19_10K_80J82 0904_MK19_10K_80J82.CHP Mapping10K_Xba131.CDF "not applicable" "mixed source cell line" "patient J82" none
+0904_MK20_10K_70J82 cell_line "mixed source cell line" "patient J82" "Homo sapiens" 0904_MK20_10K_70J82 organism_part P-EXML-4 "250ng DNA" none PICR 0904_MK20_10K_70J82 total_RNA P-EXML-6 "0904_MK20_10K_70J82 biotin" synthetic_DNA biotin P-EXML-8 0904_MK20_10K_70J82 A-AFFY-64 P-EXML-10 0904_MK20_10K_70J82 0904_MK20_10K_70J82.CEL 0904_MK20_10K_70J82 0904_MK20_10K_70J82.CHP Mapping10K_Xba131.CDF "not applicable" "mixed source cell line" "patient J82" none
+Case1_aLN organism_part 65 "smoking history: 50 pack years" aLN "squamous cell carcinoma" "case 1" "lymph node" "Homo sapiens" male years "T2, N0, stage 1b" P-EXML-2 P-EXML-3 Case1_aLN organism_part P-EXML-4 "1035 cells" WGA Warwick Case1_aLN total_RNA P-EXML-6 "Case1_aLN biotin" synthetic_DNA biotin P-EXML-8 Case1_aLN A-AFFY-64 P-EXML-10 Case1_aLN Case1_aLN.CEL Case1_aLN Case1_aLN.CHP Mapping10K_Xba131.CDF aLN "squamous cell carcinoma" "case 1" WGA
+Case1_aHYP organism_part 65 "smoking history: 50 pack years" aHYP "squamous cell carcinoma" "case 1" lung "Homo sapiens" male years "T2, N0, stage 1b" P-EXML-2 P-EXML-3 Case1_aHYP organism_part P-EXML-4 "816 cells" WGA Warwick Case1_aHYP total_RNA P-EXML-6 "Case1_aHYP biotin" synthetic_DNA biotin P-EXML-8 Case1_aHYP A-AFFY-64 P-EXML-10 Case1_aHYP Case1_aHYP.CEL Case1_aHYP Case1_aHYP.CHP Mapping10K_Xba131.CDF aHYP "squamous cell carcinoma" "case 1" WGA
+Case1_aTUM organism_part 65 "smoking history: 50 pack years" aTUM "squamous cell carcinoma" "case 1" lung "Homo sapiens" male years "T2, N0, stage 1b" P-EXML-2 P-EXML-3 Case1_aTUM organism_part P-EXML-4 "1136 cells" WGA Warwick Case1_aTUM total_RNA P-EXML-6 "Case1_aTUM biotin" synthetic_DNA biotin P-EXML-8 Case1_aTUM A-AFFY-64 P-EXML-10 Case1_aTUM Case1_aTUM.CEL Case1_aTUM Case1_aTUM.CHP Mapping10K_Xba131.CDF aTUM "squamous cell carcinoma" "case 1" WGA
+Case2_aAVM organism_part 70 "smoking history: 30 pack years" aAVM "large cell neuroendocrine carcinoma" "case 2" macrophage "Homo sapiens" female years "T2, N0, M0, stage 1b" P-EXML-2 P-EXML-3 Case2_aAVM organism_part P-EXML-4 "558 cells" WGA Warwick Case2_aAVM total_RNA P-EXML-6 "Case2_aAVM biotin" synthetic_DNA biotin P-EXML-8 Case2_aAVM A-AFFY-64 P-EXML-10 Case2_aAVM Case2_aAVM.CEL Case2_aAVM Case2_aAVM.CHP Mapping10K_Xba131.CDF aAVM "large cell neuroendocrine carcinoma" "case 2" WGA
+Case2_aBE organism_part 70 "smoking history: 30 pack years" aBE "large cell neuroendocrine carcinoma" "case 2" lung "Homo sapiens" female years "T2, N0, M0, stage 1b" P-EXML-2 P-EXML-3 Case2_aBE organism_part P-EXML-4 nk WGA Warwick Case2_aBE total_RNA P-EXML-6 "Case2_aBE biotin" synthetic_DNA biotin P-EXML-8 Case2_aBE A-AFFY-64 P-EXML-10 Case2_aBE Case2_aBE.CEL Case2_aBE Case2_aBE.CHP Mapping10K_Xba131.CDF aBE "large cell neuroendocrine carcinoma" "case 2" WGA
+Case2_aOE organism_part 70 "smoking history: 30 pack years" aOE "large cell neuroendocrine carcinoma" "case 2" lung "Homo sapiens" female years "T2, N0, M0, stage 1b" P-EXML-2 P-EXML-3 Case2_aOE organism_part P-EXML-4 "419 cells" WGA Warwick Case2_aOE total_RNA P-EXML-6 "Case2_aOE biotin" synthetic_DNA biotin P-EXML-8 Case2_aOE A-AFFY-64 P-EXML-10 Case2_aOE Case2_aOE.CEL Case2_aOE Case2_aOE.CHP Mapping10K_Xba131.CDF aOE "large cell neuroendocrine carcinoma" "case 2" WGA
+Case2_aTUM1 organism_part 70 "smoking history: 30 pack years" aTUM1 "large cell neuroendocrine carcinoma" "case 2" lung "Homo sapiens" female years "T2, N0, M0, stage 1b" P-EXML-2 P-EXML-3 Case2_aTUM1 organism_part P-EXML-4 "446 cells" WGA Warwick Case2_aTUM1 total_RNA P-EXML-6 "Case2_aTUM1 biotin" synthetic_DNA biotin P-EXML-8 Case2_aTUM1 A-AFFY-64 P-EXML-10 Case2_aTUM1 Case2_aTUM1.CEL Case2_aTUM1 Case2_aTUM1.CHP Mapping10K_Xba131.CDF aTUM1 "large cell neuroendocrine carcinoma" "case 2" WGA
+Case2_aTUM2 organism_part 70 "smoking history: 30 pack years" aTUM2 "large cell neuroendocrine carcinoma" "case 2" lung "Homo sapiens" female years "T2, N0, M0, stage 1b" P-EXML-2 P-EXML-3 Case2_aTUM2 organism_part P-EXML-4 "498 cells" WGA Warwick Case2_aTUM2 total_RNA P-EXML-6 "Case2_aTUM2 biotin" synthetic_DNA biotin P-EXML-9 Case2_aTUM2 A-AFFY-65 P-EXML-10 Case2_aTUM2 Case2_aTUM2.CEL Case2_aTUM2 Case2_aTUM2.CHP Mapping10K_Xba142.CDF aTUM2 "large cell neuroendocrine carcinoma" "case 2" WGA
+Case3_nLN organism_part 47 "smoking history: 30 pack years" nLN "small cell lung cancer" "case 3" "lymph node" "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 Case3_nLN organism_part P-EXML-4 "250ng DNA" none Warwick Case3_nLN total_RNA P-EXML-6 "Case3_nLN biotin" synthetic_DNA biotin P-EXML-8 Case3_nLN A-AFFY-64 P-EXML-10 Case3_nLN Case3_nLN.CEL Case3_nLN Case3_nLN.CHP Mapping10K_Xba131.CDF nLN "small cell lung cancer" "case 3" none
+Case3_aLN organism_part 47 "smoking history: 30 pack years" aLN "small cell lung cancer" "case 3" "lymph node" "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 P-EXML-3 Case3_aLN organism_part P-EXML-4 "545 cells" WGA Warwick Case3_aLN total_RNA P-EXML-6 "Case3_aLN biotin" synthetic_DNA biotin P-EXML-8 Case3_aLN A-AFFY-64 P-EXML-10 Case3_aLN Case3_aLN.CEL Case3_aLN Case3_aLN.CHP Mapping10K_Xba131.CDF aLN "small cell lung cancer" "case 3" WGA
+Case3_aBE organism_part 47 "smoking history: 30 pack years" aBE "small cell lung cancer" "case 3" lung "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 P-EXML-3 Case3_aBE organism_part P-EXML-4 "843 cells" WGA Warwick Case3_aBE total_RNA P-EXML-6 "Case3_aBE biotin" synthetic_DNA biotin P-EXML-8 Case3_aBE A-AFFY-64 P-EXML-10 Case3_aBE Case3_aBE.CEL Case3_aBE Case3_aBE.CHP Mapping10K_Xba131.CDF aBE "small cell lung cancer" "case 3" WGA
+Case3_aOE organism_part 47 "smoking history: 30 pack years" aOE "small cell lung cancer" "case 3" lung "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 P-EXML-3 Case3_aOE organism_part P-EXML-4 "200 cells" WGA Warwick Case3_aOE total_RNA P-EXML-6 "Case3_aOE biotin" synthetic_DNA biotin P-EXML-9 Case3_aOE A-AFFY-65 P-EXML-10 Case3_aOE Case3_aOE.CEL Case3_aOE Case3_aOE.CHP Mapping10K_Xba142.CDF aOE "small cell lung cancer" "case 3" WGA
+Case3_nTB organism_part 47 "smoking history: 30 pack years" nTB "small cell lung cancer" "case 3" lung "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 Case3_nTB organism_part P-EXML-4 "250ng DNA" none Warwick Case3_nTB total_RNA P-EXML-6 "Case3_nTB biotin" synthetic_DNA biotin P-EXML-8 Case3_nTB A-AFFY-64 P-EXML-10 Case3_nTB Case3_nTB.CEL Case3_nTB Case3_nTB.CHP Mapping10K_Xba131.CDF nTB "small cell lung cancer" "case 3" none
+Case3_aTUM1 organism_part 47 "smoking history: 30 pack years" aTUM1 "small cell lung cancer" "case 3" lung "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 P-EXML-3 Case3_aTUM1 organism_part P-EXML-4 "1300 cells" WGA Warwick Case3_aTUM1 total_RNA P-EXML-6 "Case3_aTUM1 biotin" synthetic_DNA biotin P-EXML-8 Case3_aTUM1 A-AFFY-64 P-EXML-10 Case3_aTUM1 Case3_aTUM1.CEL Case3_aTUM1 Case3_aTUM1.CHP Mapping10K_Xba131.CDF aTUM1 "small cell lung cancer" "case 3" WGA
+Case3_aTUM2 organism_part 47 "smoking history: 30 pack years" aTUM2 "small cell lung cancer" "case 3" lung "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 P-EXML-3 Case3_aTUM2 organism_part P-EXML-4 "997 cells" WGA Warwick Case3_aTUM2 total_RNA P-EXML-6 "Case3_aTUM2 biotin" synthetic_DNA biotin P-EXML-8 Case3_aTUM2 A-AFFY-64 P-EXML-10 Case3_aTUM2 Case3_aTUM2.CEL Case3_aTUM2 Case3_aTUM2.CHP Mapping10K_Xba131.CDF aTUM2 "small cell lung cancer" "case 3" WGA
+Case3_aTUM3 organism_part 47 "smoking history: 30 pack years" aTUM3 "small cell lung cancer" "case 3" lung "Homo sapiens" female years "T1, N2, M0, stage 3" P-EXML-2 P-EXML-3 Case3_aTUM3 organism_part P-EXML-4 "350 cells" WGA Warwick Case3_aTUM3 total_RNA P-EXML-6 "Case3_aTUM3 biotin" synthetic_DNA biotin P-EXML-8 Case3_aTUM3 A-AFFY-64 P-EXML-10 Case3_aTUM3 Case3_aTUM3.CEL Case3_aTUM3 Case3_aTUM3.CHP Mapping10K_Xba131.CDF aTUM3 "small cell lung cancer" "case 3" WGA
diff --git a/examples/magetab/real/E-TABM-136.idf b/examples/magetab/real/E-TABM-136.idf
new file mode 100644
index 0000000..9a31ee5
--- /dev/null
+++ b/examples/magetab/real/E-TABM-136.idf
@@ -0,0 +1,24 @@
+"Investigation Title" "Functionality of Intergenic Transcription: An Evolutionary Comparison"
+"Experimental Design" organism_part_comparison_design species_comparison_design
+"Experimental Factor Name" ORGANISM ORGANISMPART "LYMPHOBLASTOID CELLLINE"
+"Experimental Factor Type" organism organism_part "lymphoblastoid cell_line"
+
+"Person Last Name" Khaitovich
+"Person First Name" Philipp
+"Person Mid Initial"
+"Person Email" khaitovich at eva.mpg.de
+"Person Roles" submitter
+
+"Quality Control Types" biological_replicate_design
+"Public Release Date" "2006-09-30"
+
+Comment[ArrayExpressSubmissionDate] "2006-08-18"
+
+"Publication Author List" "Philipp Khaitovich; Janet Kelso; Henriette Franz; Johann Visagie; Thomas Giger; Sabrina Joerchel; Ekkehard Petzold; Richard E. Green; Michael Lachmann; Svante P��bo"
+"Publication Title" "Functionality of Intergenic Transcription: An Evolutionary Comparison"
+"Experiment Description" "Although a large proportion of human transcription occurs outside the boundaries of known genes, the functional significance of this transcription remains unknown. We have compared the expression patterns of known genes as well as intergenic transcripts within the ENCODE regions between humans and chimpanzees in brain, heart, testis and lymphoblastoid cell lines. We find that intergenic transcripts show patterns of tissue-specific conservation of their expressio [...]
+
+"SDRF File" E-TABM-136_sdrf.txt
+
+"Term Source Name" ArrayExpress
+"Term Source File" http://www.ebi.ac.uk/arrayexpress/
diff --git a/examples/magetab/real/E-TABM-136_sdrf.txt b/examples/magetab/real/E-TABM-136_sdrf.txt
new file mode 100644
index 0000000..dbe3d26
--- /dev/null
+++ b/examples/magetab/real/E-TABM-136_sdrf.txt
@@ -0,0 +1,81 @@
+"Source Name" "Material Type" "Characteristics [Age(years)]" "Characteristics [OrganismStatus]" "Characteristics [Organism]" "Characteristics [Sex]" "Material Type" "Sample Name" "Characteristics [OrganismPart]" "Material Type" "Protocol REF" "Term Source REF" "Extract Name" "Material Type" "Protocol REF" "Term Source REF" "Labeled Extract Name" "Material Type" Label "Protocol REF:Affymetrix:Protocol:" "Hybridization Name" "Array Design REF" "Protocol REF" "Term Source REF" "Scan Name" " [...]
+c01 organism_part 7 postmortem "Pan troglodytes" male whole_organism c01: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c01: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c01: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB1F A-AFFY-61 P-TABM-46 ArrayExpress CB1F CB1F.CEL CB1F.EXP brain "Pan troglodytes"
+c02 organism_part 12 postmortem "Pan troglodytes" male whole_organism c02: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c02: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c02: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB2F A-AFFY-61 P-TABM-46 ArrayExpress CB2F CB2F.CEL CB2F.EXP brain "Pan troglodytes"
+c03 organism_part 12 postmortem "Pan troglodytes" male whole_organism c03: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c03: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c03: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB3F A-AFFY-61 P-TABM-46 ArrayExpress CB3F CB3F.CEL CB3F.EXP brain "Pan troglodytes"
+c04 organism_part 12 postmortem "Pan troglodytes" male whole_organism c04: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c04: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c04: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB4F A-AFFY-61 P-TABM-46 ArrayExpress CB4F CB4F.CEL CB4F.EXP brain "Pan troglodytes"
+c05 organism_part >40 postmortem "Pan troglodytes" male whole_organism c05: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c05: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c05: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB5F A-AFFY-61 P-TABM-46 ArrayExpress CB5F CB5F.CEL CB5F.EXP brain "Pan troglodytes"
+c06 organism_part 19 postmortem "Pan troglodytes" female whole_organism c06: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c06: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c06: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC1F A-AFFY-61 P-TABM-46 ArrayExpress CC1F CC1F.CEL CC1F.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c07 organism_part 20 postmortem "Pan troglodytes" female whole_organism c07: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c07: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c07: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC2F A-AFFY-61 P-TABM-46 ArrayExpress CC2F CC2F.CEL CC2F.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c08 organism_part 32 postmortem "Pan troglodytes" female whole_organism c08: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c08: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c08: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC3F A-AFFY-61 P-TABM-46 ArrayExpress CC3F CC3F.CEL CC3F.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c09 organism_part 12 postmortem "Pan troglodytes" female whole_organism c09: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c09: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c09: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC4F A-AFFY-61 P-TABM-46 ArrayExpress CC4F CC4F.CEL CC4F.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c10 organism_part 37 postmortem "Pan troglodytes" female whole_organism c10: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c10: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c10: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC5F A-AFFY-61 P-TABM-46 ArrayExpress CC5F CC5F.CEL CC5F.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c01 organism_part 7 postmortem "Pan troglodytes" male whole_organism c01: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c01: heart muscle total_RNA P-TABM-47 ArrayExpress c01: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH1F A-AFFY-61 P-TABM-46 ArrayExpress CH1F CH1F.CEL CH1F.EXP heart "Pan troglodytes"
+c11 organism_part 34 postmortem "Pan troglodytes" male whole_organism c11: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c11: heart muscle total_RNA P-TABM-47 ArrayExpress c11: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH2F A-AFFY-61 P-TABM-46 ArrayExpress CH2F CH2F.CEL CH2F.EXP heart "Pan troglodytes"
+c03 organism_part 12 postmortem "Pan troglodytes" male whole_organism c03: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c03: heart muscle total_RNA P-TABM-47 ArrayExpress c03: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH3F A-AFFY-61 P-TABM-46 ArrayExpress CH3F CH3F.CEL CH3F.EXP heart "Pan troglodytes"
+c12 organism_part 26 postmortem "Pan troglodytes" male whole_organism c12: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c12: heart muscle total_RNA P-TABM-47 ArrayExpress c12: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH4F A-AFFY-61 P-TABM-46 ArrayExpress CH4F CH4F.CEL CH4F.EXP heart "Pan troglodytes"
+c02 organism_part 12 postmortem "Pan troglodytes" male whole_organism c02: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c02: heart muscle total_RNA P-TABM-47 ArrayExpress c02: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH5F A-AFFY-61 P-TABM-46 ArrayExpress CH5F CH5F.CEL CH5F.EXP heart "Pan troglodytes"
+c13 organism_part 20 postmortem "Pan troglodytes" male whole_organism c13: testis testis organism_part P-TABM-44 ArrayExpress c13: testis total_RNA P-TABM-47 ArrayExpress c13: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT1F A-AFFY-61 P-TABM-46 ArrayExpress CT1F CT1F.CEL CT1F.EXP testis "Pan troglodytes"
+c02 organism_part 12 postmortem "Pan troglodytes" male whole_organism c02: testis testis organism_part P-TABM-44 ArrayExpress c02: testis total_RNA P-TABM-47 ArrayExpress c02: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT2F A-AFFY-61 P-TABM-46 ArrayExpress CT2F CT2F.CEL CT2F.EXP testis "Pan troglodytes"
+c03 organism_part 12 postmortem "Pan troglodytes" male whole_organism c03: testis testis organism_part P-TABM-44 ArrayExpress c03: testis total_RNA P-TABM-47 ArrayExpress c03: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT3F A-AFFY-61 P-TABM-46 ArrayExpress CT3F CT3F.CEL CT3F.EXP testis "Pan troglodytes"
+c04 organism_part 12 postmortem "Pan troglodytes" male whole_organism c04: testis testis organism_part P-TABM-44 ArrayExpress c04: testis total_RNA P-TABM-47 ArrayExpress c04: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT4F A-AFFY-61 P-TABM-46 ArrayExpress CT4F CT4F.CEL CT4F.EXP testis "Pan troglodytes"
+c05 organism_part >40 postmortem "Pan troglodytes" male whole_organism c05: testis testis organism_part P-TABM-44 ArrayExpress c05: testis total_RNA P-TABM-47 ArrayExpress c05: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT5F A-AFFY-61 P-TABM-46 ArrayExpress CT5F CT5F.CEL CT5F.EXP testis "Pan troglodytes"
+h01 organism_part 45 postmortem "Homo sapiens" male whole_organism h01: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h01: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h01: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB1F A-AFFY-61 P-TABM-46 ArrayExpress HB1F HB1F.CEL HB1F.EXP brain "Homo sapiens"
+h02 organism_part 45 postmortem "Homo sapiens" male whole_organism h02: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h02: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h02: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB2F A-AFFY-61 P-TABM-46 ArrayExpress HB2F HB2F.CEL HB2F.EXP brain "Homo sapiens"
+h03 organism_part 48 postmortem "Homo sapiens" male whole_organism h03: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h03: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h03: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB3F A-AFFY-61 P-TABM-46 ArrayExpress HB3F HB3F.CEL HB3F.EXP brain "Homo sapiens"
+h04 organism_part 56 postmortem "Homo sapiens" male whole_organism h04: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h04: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h04: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB4F A-AFFY-61 P-TABM-46 ArrayExpress HB4F HB4F.CEL HB4F.EXP brain "Homo sapiens"
+h05 organism_part 67 postmortem "Homo sapiens" male whole_organism h05: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h05: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h05: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB5F A-AFFY-61 P-TABM-46 ArrayExpress HB5F HB5F.CEL HB5F.EXP brain "Homo sapiens"
+h06 organism_part 13 postmortem "Homo sapiens" female whole_organism h06: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h06: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h06: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC1F A-AFFY-61 P-TABM-46 ArrayExpress HC1F HC1F.CEL HC1F.EXP "Homo sapiens" "lymphoblastoid cell line"
+h07 organism_part 12 postmortem "Homo sapiens" female whole_organism h07: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h07: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h07: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC2F A-AFFY-61 P-TABM-46 ArrayExpress HC2F HC2F.CEL HC2F.EXP "Homo sapiens" "lymphoblastoid cell line"
+h08 organism_part 20 postmortem "Homo sapiens" female whole_organism h08: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h08: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h08: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC3F A-AFFY-61 P-TABM-46 ArrayExpress HC3F HC3F.CEL HC3F.EXP "Homo sapiens" "lymphoblastoid cell line"
+h09 organism_part 22 postmortem "Homo sapiens" female whole_organism h09: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h09: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h09: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC4F A-AFFY-61 P-TABM-46 ArrayExpress HC4F HC4F.CEL HC4F.EXP "Homo sapiens" "lymphoblastoid cell line"
+h10 organism_part 32 postmortem "Homo sapiens" female whole_organism h10: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h10: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h10: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC5F A-AFFY-61 P-TABM-46 ArrayExpress HC5F HC5F.CEL HC5F.EXP "Homo sapiens" "lymphoblastoid cell line"
+h11 organism_part 71 postmortem "Homo sapiens" male whole_organism h11: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h11: heart muscle total_RNA P-TABM-47 ArrayExpress h11: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH1F A-AFFY-61 P-TABM-46 ArrayExpress HH1F HH1F.CEL HH1F.EXP heart "Homo sapiens"
+h12 organism_part 74 postmortem "Homo sapiens" male whole_organism h12: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h12: heart muscle total_RNA P-TABM-47 ArrayExpress h12: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH2F A-AFFY-61 P-TABM-46 ArrayExpress HH2F HH2F.CEL HH2F.EXP heart "Homo sapiens"
+h13 organism_part 57 postmortem "Homo sapiens" male whole_organism h13: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h13: heart muscle total_RNA P-TABM-47 ArrayExpress h13: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH3F A-AFFY-61 P-TABM-46 ArrayExpress HH3F HH3F.CEL HH3F.EXP heart "Homo sapiens"
+h14 organism_part 77 postmortem "Homo sapiens" male whole_organism h14: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h14: heart muscle total_RNA P-TABM-47 ArrayExpress h14: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH4F A-AFFY-61 P-TABM-46 ArrayExpress HH4F HH4F.CEL HH4F.EXP heart "Homo sapiens"
+h15 organism_part 51 postmortem "Homo sapiens" male whole_organism h15: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h15: heart muscle total_RNA P-TABM-47 ArrayExpress h15: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH5F A-AFFY-61 P-TABM-46 ArrayExpress HH5F HH5F.CEL HH5F.EXP "Homo sapiens" "lymphoblastoid cell line"
+h16 organism_part 27 postmortem "Homo sapiens" male whole_organism h16: testis testis organism_part P-TABM-44 ArrayExpress h16: testis total_RNA P-TABM-47 ArrayExpress h16: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT1F A-AFFY-61 P-TABM-46 ArrayExpress HT1F HT1F.CEL HT1F.EXP testis "Homo sapiens"
+h17 organism_part 32 postmortem "Homo sapiens" male whole_organism h17: testis testis organism_part P-TABM-44 ArrayExpress h17: testis total_RNA P-TABM-47 ArrayExpress h17: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT2F A-AFFY-61 P-TABM-46 ArrayExpress HT2F HT2F.CEL HT2F.EXP testis "Homo sapiens"
+h18 organism_part 25 postmortem "Homo sapiens" male whole_organism h18: testis testis organism_part P-TABM-44 ArrayExpress h18: testis total_RNA P-TABM-47 ArrayExpress h18: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT3F A-AFFY-61 P-TABM-46 ArrayExpress HT3F HT3F.CEL HT3F.EXP testis "Homo sapiens"
+h19 organism_part 41 postmortem "Homo sapiens" male whole_organism h19: testis testis organism_part P-TABM-44 ArrayExpress h19: testis total_RNA P-TABM-47 ArrayExpress h19: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT4F A-AFFY-61 P-TABM-46 ArrayExpress HT4F HT4F.CEL HT4F.EXP testis "Homo sapiens"
+h20 organism_part 27 postmortem "Homo sapiens" male whole_organism h20: testis testis organism_part P-TABM-44 ArrayExpress h20: testis total_RNA P-TABM-47 ArrayExpress h20: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT5F A-AFFY-61 P-TABM-46 ArrayExpress HT5F HT5F.CEL HT5F.EXP testis "Homo sapiens"
+c01 organism_part 7 postmortem "Pan troglodytes" male whole_organism c01: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c01: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c01: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB1R A-AFFY-67 P-TABM-46 ArrayExpress CB1R CB1R.CEL CB1R.EXP brain "Pan troglodytes"
+c02 organism_part 12 postmortem "Pan troglodytes" male whole_organism c02: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c02: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c02: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB2R A-AFFY-67 P-TABM-46 ArrayExpress CB2R CB2R.CEL CB2R.EXP brain "Pan troglodytes"
+c03 organism_part 12 postmortem "Pan troglodytes" male whole_organism c03: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c03: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c03: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB3R A-AFFY-67 P-TABM-46 ArrayExpress CB3R CB3R.CEL CB3R.EXP brain "Pan troglodytes"
+c04 organism_part 12 postmortem "Pan troglodytes" male whole_organism c04: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c04: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c04: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB4R A-AFFY-67 P-TABM-46 ArrayExpress CB4R CB4R.CEL CB4R.EXP brain "Pan troglodytes"
+c05 organism_part >40 postmortem "Pan troglodytes" male whole_organism c05: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress c05: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress c05: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CB5R A-AFFY-67 P-TABM-46 ArrayExpress CB5R CB5R.CEL CB5R.EXP brain "Pan troglodytes"
+c06 organism_part 19 postmortem "Pan troglodytes" female whole_organism c06: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c06: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c06: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC1R A-AFFY-67 P-TABM-46 ArrayExpress CC1R CC1R.CEL CC1R.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c07 organism_part 20 postmortem "Pan troglodytes" female whole_organism c07: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c07: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c07: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC2R A-AFFY-67 P-TABM-46 ArrayExpress CC2R CC2R.CEL CC2R.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c08 organism_part 32 postmortem "Pan troglodytes" female whole_organism c08: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c08: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c08: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC3R A-AFFY-67 P-TABM-46 ArrayExpress CC3R CC3R.CEL CC3R.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c09 organism_part 12 postmortem "Pan troglodytes" female whole_organism c09: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c09: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c09: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC4R A-AFFY-67 P-TABM-46 ArrayExpress CC4R CC4R.CEL CC4R.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c10 organism_part 37 postmortem "Pan troglodytes" female whole_organism c10: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress c10: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress c10: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CC5R A-AFFY-67 P-TABM-46 ArrayExpress CC5R CC5R.CEL CC5R.EXP "Pan troglodytes" "lymphoblastoid cell line"
+c01 organism_part 7 postmortem "Pan troglodytes" male whole_organism c01: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c01: heart muscle total_RNA P-TABM-47 ArrayExpress c01: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH1R A-AFFY-67 P-TABM-46 ArrayExpress CH1R CH1R.CEL CH1R.EXP heart "Pan troglodytes"
+c11 organism_part 34 postmortem "Pan troglodytes" male whole_organism c11: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c11: heart muscle total_RNA P-TABM-47 ArrayExpress c11: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH2R A-AFFY-67 P-TABM-46 ArrayExpress CH2R CH2R.CEL CH2R.EXP heart "Pan troglodytes"
+c03 organism_part 12 postmortem "Pan troglodytes" male whole_organism c03: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c03: heart muscle total_RNA P-TABM-47 ArrayExpress c03: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH3R A-AFFY-67 P-TABM-46 ArrayExpress CH3R CH3R.CEL CH3R.EXP heart "Pan troglodytes"
+c12 organism_part 26 postmortem "Pan troglodytes" male whole_organism c12: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c12: heart muscle total_RNA P-TABM-47 ArrayExpress c12: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH4R A-AFFY-67 P-TABM-46 ArrayExpress CH4R CH4R.CEL CH4R.EXP heart "Pan troglodytes"
+c02 organism_part 12 postmortem "Pan troglodytes" male whole_organism c02: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress c02: heart muscle total_RNA P-TABM-47 ArrayExpress c02: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CH5R A-AFFY-67 P-TABM-46 ArrayExpress CH5R CH5R.CEL CH5R.EXP heart "Pan troglodytes"
+c13 organism_part 20 postmortem "Pan troglodytes" male whole_organism c13: testis testis organism_part P-TABM-44 ArrayExpress c13: testis total_RNA P-TABM-47 ArrayExpress c13: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT1R A-AFFY-67 P-TABM-46 ArrayExpress CT1R CT1R.CEL CT1R.EXP testis "Pan troglodytes"
+c02 organism_part 12 postmortem "Pan troglodytes" male whole_organism c02: testis testis organism_part P-TABM-44 ArrayExpress c02: testis total_RNA P-TABM-47 ArrayExpress c02: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT2R A-AFFY-67 P-TABM-46 ArrayExpress CT2R CT2R.CEL CT2R.EXP testis "Pan troglodytes"
+c03 organism_part 12 postmortem "Pan troglodytes" male whole_organism c03: testis testis organism_part P-TABM-44 ArrayExpress c03: testis total_RNA P-TABM-47 ArrayExpress c03: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT3R A-AFFY-67 P-TABM-46 ArrayExpress CT3R CT3R.CEL CT3R.EXP testis "Pan troglodytes"
+c04 organism_part 12 postmortem "Pan troglodytes" male whole_organism c04: testis testis organism_part P-TABM-44 ArrayExpress c04: testis total_RNA P-TABM-47 ArrayExpress c04: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT4R A-AFFY-67 P-TABM-46 ArrayExpress CT4R CT4R.CEL CT4R.EXP testis "Pan troglodytes"
+c05 organism_part >40 postmortem "Pan troglodytes" male whole_organism c05: testis testis organism_part P-TABM-44 ArrayExpress c05: testis total_RNA P-TABM-47 ArrayExpress c05: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV CT5R A-AFFY-67 P-TABM-46 ArrayExpress CT5R CT5R.CEL CT5R.EXP testis "Pan troglodytes"
+h01 organism_part 45 postmortem "Homo sapiens" male whole_organism h01: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h01: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h01: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB1R A-AFFY-67 P-TABM-46 ArrayExpress HB1R HB1R.CEL HB1R.EXP brain "Homo sapiens"
+h02 organism_part 45 postmortem "Homo sapiens" male whole_organism h02: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h02: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h02: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB2R A-AFFY-67 P-TABM-46 ArrayExpress HB2R HB2R.CEL HB2R.EXP brain "Homo sapiens"
+h03 organism_part 48 postmortem "Homo sapiens" male whole_organism h03: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h03: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h03: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB3R A-AFFY-67 P-TABM-46 ArrayExpress HB3R HB3R.CEL HB3R.EXP brain "Homo sapiens"
+h04 organism_part 56 postmortem "Homo sapiens" male whole_organism h04: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h04: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h04: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB4R A-AFFY-67 P-TABM-46 ArrayExpress HB4R HB4R.CEL HB4R.EXP brain "Homo sapiens"
+h05 organism_part 67 postmortem "Homo sapiens" male whole_organism h05: brain, prefrontal cortex "brain, prefrontal cortex" organism_part P-TABM-44 ArrayExpress h05: brain, prefrontal cortex total_RNA P-TABM-47 ArrayExpress h05: brain, prefrontal cortex synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HB5R A-AFFY-67 P-TABM-46 ArrayExpress HB5R HB5R.CEL HB5R.EXP brain "Homo sapiens"
+h06 organism_part 13 postmortem "Homo sapiens" female whole_organism h06: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h06: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h06: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC1R A-AFFY-67 P-TABM-46 ArrayExpress HC1R HC1R.CEL HC1R.EXP "Homo sapiens" "lymphoblastoid cell line"
+h07 organism_part 12 postmortem "Homo sapiens" female whole_organism h07: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h07: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h07: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC2R A-AFFY-67 P-TABM-46 ArrayExpress HC2R HC2R.CEL HC2R.EXP "Homo sapiens" "lymphoblastoid cell line"
+h08 organism_part 20 postmortem "Homo sapiens" female whole_organism h08: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h08: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h08: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC3R A-AFFY-67 P-TABM-46 ArrayExpress HC3R HC3R.CEL HC3R.EXP "Homo sapiens" "lymphoblastoid cell line"
+h09 organism_part 22 postmortem "Homo sapiens" female whole_organism h09: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h09: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h09: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC4R A-AFFY-67 P-TABM-46 ArrayExpress HC4R HC4R.CEL HC4R.EXP "Homo sapiens" "lymphoblastoid cell line"
+h10 organism_part 32 postmortem "Homo sapiens" female whole_organism h10: lymphoblastoid cell line "lymphoblastoid cell line" organism_part P-TABM-44 ArrayExpress h10: lymphoblastoid cell line total_RNA P-TABM-47 ArrayExpress h10: lymphoblastoid cell line synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HC5R A-AFFY-67 P-TABM-46 ArrayExpress HC5R HC5R.CEL HC5R.EXP "Homo sapiens" "lymphoblastoid cell line"
+h11 organism_part 71 postmortem "Homo sapiens" male whole_organism h11: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h11: heart muscle total_RNA P-TABM-47 ArrayExpress h11: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH1R A-AFFY-67 P-TABM-46 ArrayExpress HH1R HH1R.CEL HH1R.EXP heart "Homo sapiens"
+h12 organism_part 74 postmortem "Homo sapiens" male whole_organism h12: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h12: heart muscle total_RNA P-TABM-47 ArrayExpress h12: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH2R A-AFFY-67 P-TABM-46 ArrayExpress HH2R HH2R.CEL HH2R.EXP heart "Homo sapiens"
+h13 organism_part 57 postmortem "Homo sapiens" male whole_organism h13: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h13: heart muscle total_RNA P-TABM-47 ArrayExpress h13: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH3R A-AFFY-67 P-TABM-46 ArrayExpress HH3R HH3R.CEL HH3R.EXP heart "Homo sapiens"
+h14 organism_part 77 postmortem "Homo sapiens" male whole_organism h14: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h14: heart muscle total_RNA P-TABM-47 ArrayExpress h14: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH4R A-AFFY-67 P-TABM-46 ArrayExpress HH4R HH4R.CEL HH4R.EXP heart "Homo sapiens"
+h15 organism_part 51 postmortem "Homo sapiens" male whole_organism h15: heart muscle "heart muscle" organism_part P-TABM-44 ArrayExpress h15: heart muscle total_RNA P-TABM-47 ArrayExpress h15: heart muscle synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HH5R A-AFFY-67 P-TABM-46 ArrayExpress HH5R HH5R.CEL HH5R.EXP heart "Homo sapiens"
+h16 organism_part 27 postmortem "Homo sapiens" male whole_organism h16: testis testis organism_part P-TABM-44 ArrayExpress h16: testis total_RNA P-TABM-47 ArrayExpress h16: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT1R A-AFFY-67 P-TABM-46 ArrayExpress HT1R HT1R.CEL HT1R.EXP testis "Homo sapiens"
+h17 organism_part 32 postmortem "Homo sapiens" male whole_organism h17: testis testis organism_part P-TABM-44 ArrayExpress h17: testis total_RNA P-TABM-47 ArrayExpress h17: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT2R A-AFFY-67 P-TABM-46 ArrayExpress HT2R HT2R.CEL HT2R.EXP testis "Homo sapiens"
+h18 organism_part 25 postmortem "Homo sapiens" male whole_organism h18: testis testis organism_part P-TABM-44 ArrayExpress h18: testis total_RNA P-TABM-47 ArrayExpress h18: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT3R A-AFFY-67 P-TABM-46 ArrayExpress HT3R HT3R.CEL HT3R.EXP testis "Homo sapiens"
+h19 organism_part 41 postmortem "Homo sapiens" male whole_organism h19: testis testis organism_part P-TABM-44 ArrayExpress h19: testis total_RNA P-TABM-47 ArrayExpress h19: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT4R A-AFFY-67 P-TABM-46 ArrayExpress HT4R HT4R.CEL HT4R.EXP testis "Homo sapiens"
+h20 organism_part 27 postmortem "Homo sapiens" male whole_organism h20: testis testis organism_part P-TABM-44 ArrayExpress h20: testis total_RNA P-TABM-47 ArrayExpress h20: testis synthetic_DNA biotin Hybridization-MES_EukGE-WS2v5_450-DEV HT5R A-AFFY-67 P-TABM-46 ArrayExpress HT5R HT5R.CEL HT5R.EXP testis "Homo sapiens"
diff --git a/examples/magetab/real/E-TABM-140.idf b/examples/magetab/real/E-TABM-140.idf
new file mode 100644
index 0000000..33fb7ae
--- /dev/null
+++ b/examples/magetab/real/E-TABM-140.idf
@@ -0,0 +1,32 @@
+"Investigation Title" "Chromatin immunoprecipitation microarray (ChIP-chip) study using human erythroleukemia cell line K-562 and H3K4me2 (ab7766); H3K4me3 (ab8580); H3ac (06-599); H4ac (06-866): Histone H2B(ab1790) and Histone H3 (ab1791) antibodies (ENCODE)."
+"Experimental Design" binding_site_identification_design
+"Experimental Factor Name" STRAINORLINE IMMUNOPRECIPITATE
+"Experimental Factor Type" strain_or_line immunoprecipitate
+
+"Person Last Name" Vetrie
+"Person First Name" David
+"Person Mid Initial"
+"Person Email" scd at sanger.ac.uk
+"Person Address" "Wellcome Trust Genome Campus;Hinxton; Cambridge; CB10 1SA; UK"
+"Person Affiliation" "The Wellcome Trust Sanger Institute"
+"Person Roles" investigator;submitter
+
+"Quality Control Types" biological_replicate
+"Public Release Date" "2006-09-01"
+
+Comment[ArrayExpressSubmissionDate] "2006-08-18"
+
+"Publication Author List" "Christoph M. Koch; Robert M. Andrews; Paul Flicek; Shane C. Dillon; Ula Kara�z; Gayle K. Clelland; Sarah Wilcox; David M. Beare; Joanna C. Fowler; Phillippe Couttet; Keith D. James; Gregory C. Lefebvre; Alexander W. Bruce; Oliver M. Dovey; Peter D. Ellis; Pawandeep Dhami; Cordelia F. Langford; Zhiping Weng; Ewan Birney; Nigel P. Carter; David Vetrie; Ian Dunham"
+"Publication Title" "The Landscape of Activating Histone Modifications across 1% of the Human Genome."
+"Experiment Description" "ENCODE ChIP-chip study using human myelogenous leukemia cell line K-562 and anti histone H3K4me2 (Abcam; ab7766); H3K4me3 (Abcam; ab8580); H3ac (Upstate; 06-599); H4ac (Upstate; 06-866); Histone H2B (Abcam: ab1790) and Histone H3 (Abcam: ab1791) antibodies. Each antibody experiment was conducted in three biological replicates, with two technical replicates performed for each biological replicate"
+
+"Protocol Name" GROW_K-562 CROSSLINK_K-562_1 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION
+"Protocol Type" grow specified_biomaterial_action specified_biomaterial_action nucleic_acid_extraction hybridization image_acquisition bioassay_data_transformation
+"Protocol Description" "Cells were grown in DMEM supplemented with 10% FBS and 1% Penicillin/Streptomycin to a total number of ~ 3x10^8 cells. Dilute cell density to 3 x10^5/ml every two to three days. Split and feed 16 hours before cross-linking the cells." "Day 1 Cross linking cells and chromatin extraction/preparation<br>1. Grow or harvest 1x10^8 cells. Collect the cells by centrifuging at 1200 rpm for 5-8 minutes.<br>2. Resuspend the cells in pre-warmed 50 ml serum free media in a gl [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" E-TABM-140_sdrf.txt
+
+"Term Source Name" ArrayExpress
+"Term Source File" http://www.ebi.ac.uk/arrayexpress/
diff --git a/examples/magetab/real/E-TABM-140_sdrf.txt b/examples/magetab/real/E-TABM-140_sdrf.txt
new file mode 100644
index 0000000..8defa7c
--- /dev/null
+++ b/examples/magetab/real/E-TABM-140_sdrf.txt
@@ -0,0 +1,73 @@
+"Source Name" "Material Type" "Characteristics [BioSourceType]" "Characteristics [CellType]" "Characteristics [Organism]" "Characteristics [Sex]" "Characteristics [StrainOrLine]" "Growth Protocol REF" "Chromatin Extraction Protocol REF" "Sample Name" "Material Type" "Immunoprecipitation Protocol REF" "Extract Name" "Material Type" "Labeling Protocol REF" Term Source REF "Labeled Extract Name" "Material Type" Label "Hybridization Protocol REF" "Hybridization Name" "Array Design REF" "Scan [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-16_raw 1106-16_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-16_raw 1106-16_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-17_raw 1106-17_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-17_raw 1106-17_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-6_raw 1140-6_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-6_raw 1140-6_raw.txt TRANSFORMATION FGED [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-7_raw 1140-7_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-7_raw 1140-7_raw.txt TRANSFORMATION FGED [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-12_raw 1140-12_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-12_raw 1140-12_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-13_raw 1140-13_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-13_raw 1140-13_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-16_raw 1023-16_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-16_raw 1023-16_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-17_raw 1023-17_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-17_raw 1023-17_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-10_raw 1106-10_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-10_raw 1106-10_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-11_raw 1106-11_raw.tx [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-11_raw 1106-11_raw.txt TRANSFORMATION FG [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1163-8_raw 1163-8_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1163-8_raw 1163-8_raw.txt TRANSFORMATION FGED [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1163-9_raw 1163-9_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1163-9_raw 1163-9_raw.txt TRANSFORMATION FGED [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H3ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-13_raw 1023-13_raw.txt TR [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H3ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-13_raw 1023-13_raw.txt TRANSFORMATION FGEDM [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H3ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-18_raw 1023-18_raw.txt TR [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H3ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1023-18_raw 1023-18_raw.txt TRANSFORMATION FGEDM [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-2_raw 1106-2_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-2_raw 1106-2_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-3_raw 1106-3_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-3_raw 1106-3_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-2_raw 1140-2_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-2_raw 1140-2_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-3_raw 1140-3_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H3ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-3_raw 1140-3_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H4ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1043-6_raw 1043-6_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep1 ChIP H4ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1043-6_raw 1043-6_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H4ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1043-7_raw 1043-7_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP1 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1_techrep2 ChIP H4ac hyb" A-MEXP-156 SCANARRAYEXPRESS-2 1043-7_raw 1043-7_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-4_raw 1106-4_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep1 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-4_raw 1106-4_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-5_raw 1106-5_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP2 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2_techrep2 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1106-5_raw 1106-5_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-20_raw 1140-20_raw.txt TR [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep1 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep1 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1140-20_raw 1140-20_raw.txt TRANSFORMATION FGEDM [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1163-2_raw 1163-2_raw.txt TRAN [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_1 K-562_BIOREP3 genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3_techrep2 genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3 Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3_techrep2 ChIP H4ac hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1163-2_raw 1163-2_raw.txt TRANSFORMATION FGEDM_c [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep1a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep1a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-84_raw 1604-84_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep1a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep1a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-84_raw 1604-84_raw.txt TRANSFORMATION [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep2a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep2a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-89_raw 1604-89_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep2a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep2a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-89_raw 1604-89_raw.txt TRANSFORMATION [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep1a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep1a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-97_raw 1604-97_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep1a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep1a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-97_raw 1604-97_raw.txt TRANSFORMATION [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep2a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep2a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-98_raw 1604-98_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep2a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep2a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-98_raw 1604-98_raw.txt TRANSFORMATION [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep1a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep1a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-88_raw 1604-88_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep1a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep1a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-88_raw 1604-88_raw.txt TRANSFORMATION [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep2a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep2a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-99_raw 1604-99_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep2a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep2a ChIP H2B hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-99_raw 1604-99_raw.txt TRANSFORMATION [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep1a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep1a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-85_raw 1604-85_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep1a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep1a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-85_raw 1604-85_raw.txt TRANSFORMATION F [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep2a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep2a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-91_raw 1604-91_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP1a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP1a_techrep2a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP1a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP1a_techrep2a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-91_raw 1604-91_raw.txt TRANSFORMATION F [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep1a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep1a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-95_raw 1604-95_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep1a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep1a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-95_raw 1604-95_raw.txt TRANSFORMATION F [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep2a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep2a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-96_raw 1604-96_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP2a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP2a_techrep2a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP2a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP2a_techrep2a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-96_raw 1604-96_raw.txt TRANSFORMATION F [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep1a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep1a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-90_raw 1604-90_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep1a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep1a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-90_raw 1604-90_raw.txt TRANSFORMATION F [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep2a genomic_DNA P-MEXP-7423 ArrayExpress "IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791) Cy3" synthetic_DNA Cy3 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep2a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-94_raw 1604-94_raw.txt [...]
+K-562 cell frozen_sample "human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis" "Homo sapiens" female K-562 GROW_K-562 CROSSLINK_K-562_2 K-562_BIOREP3a genomic_DNA CHROMATIN_IMMUNOPRECIPITATION K-562_BIOREP3a_techrep2a genomic_DNA P-MEXP-7422 ArrayExpress "Input control DNA of K-562_BIOREP3a Cy5" synthetic_DNA Cy5 HYBRIDISATION_TECAN "K-562 BIOREP3a_techrep2a ChIP H3 hyb" A-MEXP-215 SCANARRAYEXPRESS-2 1604-94_raw 1604-94_raw.txt TRANSFORMATION F [...]
diff --git a/examples/magetab/real/E-TABM-16.idf b/examples/magetab/real/E-TABM-16.idf
new file mode 100644
index 0000000..d3db90f
--- /dev/null
+++ b/examples/magetab/real/E-TABM-16.idf
@@ -0,0 +1,26 @@
+"Investigation Title" "FDA-CDER MTS RNA reagent cross platform test"
+"Experimental Design" "quality_control_testing_design"
+"Experimental Factor Name" "PLATFORM" "INDIVIDUAL" "MIX" "OPERATOR"
+"Experimental Factor Type" "platform" "individual" "mix" "operator"
+
+"Person Last Name" "Pine"
+"Person First Name" "Scott"
+"Person Mid Initial"
+"Person Affiliation" "FDA-CDER"
+"Person Roles" "submitter"
+
+"Public Release Date" "2005-11-23"
+
+"Comment[ArrayExpressSubmissionDate]" "2005-06-01"
+
+"Publication Author List"
+"Publication Title"
+"Experiment Description" "This experiment is a test of a reagent that can provide a basis for performance assessments on Affymetrix RAE230A, CodeLink UniSet Rat I, and Agilent G4130A rat oligonucleotide arrays. The reagent is a set of two mixed tissue RNA samples formulated to contain different ratios of RNA for each of four rat tissues (brain, liver, kidney, and testes). The analytes in this assay bind to a subset of gene probes that were matched across platforms to the same UniGen [...]
+
+"Protocol Name" 6377 6380 "6396-mix1" "6396-mix2" 6407 6409 6486 6489 6491 6552 6553 6672 6673 "6841-mix1" "6841-mix2" 7671 7672 8188 8189 8190 8309 8532 8534 8535 8536 8537 8538 8573 8588 8589 8590
+"Protocol Type" "grow" "nucleic_acid_extraction" "pool" "pool" "image_aquisition" "image_aquisition" "labeling" "labeling" "hybridization" "bioassay_data_transformation" "bioassay_data_transformation" "nucleic_acid_extraction" "grow" "pool" "pool" "labeling" "labeling" "hybridization" "hybridization" "image_aquisition" "image_aquisition" "labeling" "labeling" "labeling" "labeling" "labeling" "labeling" "hybridization" "hybridization" "hybridization" "image_aquisition"
+"Protocol Description" "Rodents received certified rodent diet #5002C ad lib and drinking water purified by reverse osmosis. The animals were acclimated for 6 days prior to euthanasia. The animals were on a 12 hr light/dark cycle and euthanasia was performed consistently within 4-6 hr after the start of the light cycle. Each batch was made up of 8 animals from the same shipment. " "After euthanasia in a slow charged CO2 chamber, the rats were immediately decapitated to allow for rap [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" "E-TABM-16_sdrf.txt"
diff --git a/examples/magetab/real/E-TABM-163.idf b/examples/magetab/real/E-TABM-163.idf
new file mode 100644
index 0000000..d7be75d
--- /dev/null
+++ b/examples/magetab/real/E-TABM-163.idf
@@ -0,0 +1,27 @@
+"Investigation Title" "Identification of cyclic genes of the mouse segmentation clock"
+"Experimental Design" time_series_design development_or_differentiation_design co_expression_design
+"Experimental Factor Name" ORGANISMPART INDIVIDUAL TIME
+"Experimental Factor Type" organism_part individual time
+
+"Person Last Name" "Dequツant"
+"Person First Name" Mary-Lee
+"Person Mid Initial"
+"Person Affiliation" "Stowers Institute For Medical Research"
+"Person Roles" submitter
+
+"Quality Control Types" biological_replicate
+"Public Release Date" "2006-10-30"
+
+Comment[ArrayExpressSubmissionDate] "2006-10-30"
+
+"Publication Author List" "Mary-Lee Dequツant, Earl Glynn, Karin Gaudenz, Matthias Wahl, Jie Chen, Arcady Mushegian, Olivier Pourquiツ"
+"Publication Title" "A complex oscillating network of signaling genes underlies the mouse segmentation clock"
+"Experiment Description" "A microarray time series was generated to identify cyclic genes of the segmentation clock in the mouse. The right posterior half presomitic mesoderms (PSM) from 17 mouse embryos were dissected while the contralateral side of the embryo containing the left PSM was immediately fixed to be analyzed by in situ hybridization using a Lfng probe to order the samples along the segmentation clock oscillation cycle. Probes were produced from RNA extracted from the 17 diss [...]
+
+"Protocol Name" P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+"Protocol Type" specified_biomaterial_action nucleic_acid_extraction labeling hybridization image_acquisition feature_extraction bioassay_data_transformation
+"Protocol Description" "CD-1 mouse embryos (9.0 days post-coitus [dpc]) were dissected in cold Tyrode's solution prepared in RNAse-free conditions. Mouse embryos from each litter were dissected one by one, while the rest of the embryos were kept on ice. No more than three embryos per litter were dissected in order to limit the time between removal of the uterus and embryo dissection to reduce the risk of RNA degradation. The time spent for dissection per embryo was limited to 15-20 min. [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" E-TABM-163_sdrf.txt
diff --git a/examples/magetab/real/E-TABM-163_sdrf.txt b/examples/magetab/real/E-TABM-163_sdrf.txt
new file mode 100644
index 0000000..a44d562
--- /dev/null
+++ b/examples/magetab/real/E-TABM-163_sdrf.txt
@@ -0,0 +1,24 @@
+"Source Name" "Material Type" "Characteristics [Age(days)]" "Characteristics [DevelopmentalStage]" "Characteristics [Individual]" "Characteristics [OrganismPart]" "Characteristics [Organism]" "Characteristics [StrainOrLine]" "Protocol REF" "Sample Name" "Material Type" "Protocol REF" "Extract Name" "Material Type" "Protocol REF" "Labeled Extract Name" "Material Type" Label "Protocol REF" "Hybridization Name" "Array Design REF" "Protocol REF" "Scan Name" "Protocol REF" "Array Data File" C [...]
+"Charles River # 022" organism_part 9.5 embryo "Embryo 1" "right or left presomitic mesoderm (posterior half)(a)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 022" organism_part P-MLD6-2 "Charles River # 022" total_RNA P-MLD6-3 "Charles River # 022 biotin" synthetic_RNA biotin P-MLD6-4 Emb1a-5D2_MGU74Av2 A-AFFY-6 P-MLD6-5 Emb1a-5D2_MGU74Av2 P-MLD6-6 Emb1a-5D2_MGU74Av2.CEL Emb1a-5D2_MGU74Av2.EXP P-MLD6-7 Emb1a-5D2_MGU74Av2 Emb1a-5D2_MGU74Av2.CHP MG_U74Av2.CDF "Embryo 1" "right or left pr [...]
+"Charles River # 023" organism_part 9.5 embryo "Embryo 1" "right or left presomitic mesoderm (posterior half)(b)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 023" organism_part P-MLD6-2 "Charles River # 023" total_RNA P-MLD6-3 "Charles River # 023 biotin" synthetic_RNA biotin P-MLD6-4 Emb1b-5G1_MGU74Av2 A-AFFY-6 P-MLD6-5 Emb1b-5G1_MGU74Av2 P-MLD6-6 Emb1b-5G1_MGU74Av2.CEL Emb1b-5G1_MGU74Av2.EXP P-MLD6-7 Emb1b-5G1_MGU74Av2 Emb1b-5G1_MGU74Av2.CHP MG_U74Av2.CDF "Embryo 1" "right or left pr [...]
+"Charles River # 024" organism_part 9.5 embryo "Embryo 2" "right or left presomitic mesoderm (posterior half)(a)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 024" organism_part P-MLD6-2 "Charles River # 024" total_RNA P-MLD6-3 "Charles River # 024 biotin" synthetic_RNA biotin P-MLD6-4 Emb2a-3_MGU74Av2 A-AFFY-6 P-MLD6-5 Emb2a-3_MGU74Av2 P-MLD6-6 Emb2a-3_MGU74Av2.CEL Emb2a-3_MGU74Av2.EXP P-MLD6-7 Emb2a-3_MGU74Av2 Emb2a-3_MGU74Av2.CHP MG_U74Av2.CDF "Embryo 2" "right or left presomitic mes [...]
+"Charles River # 025" organism_part 9.5 embryo "Embryo 2" "right or left presomitic mesoderm (posterior half)(b)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 025" organism_part P-MLD6-2 "Charles River # 025" total_RNA P-MLD6-3 "Charles River # 025 biotin" synthetic_RNA biotin P-MLD6-4 Emb2b-4_MGU74Av2 A-AFFY-6 P-MLD6-5 Emb2b-4_MGU74Av2 P-MLD6-6 Emb2b-4_MGU74Av2.CEL Emb2b-4_MGU74Av2.EXP P-MLD6-7 Emb2b-4_MGU74Av2 Emb2b-4_MGU74Av2.CHP MG_U74Av2.CDF "Embryo 2" "right or left presomitic mes [...]
+"Charles River # 026" organism_part 9.5 embryo "Embryo 3" "right or left presomitic mesoderm (posterior half)(a)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 026" organism_part P-MLD6-2 "Charles River # 026" total_RNA P-MLD6-3 "Charles River # 026 biotin" synthetic_RNA biotin P-MLD6-4 Emb3a-D_MGU74Av2 A-AFFY-6 P-MLD6-5 Emb3a-D_MGU74Av2 P-MLD6-6 Emb3a-D_MGU74Av2.CEL Emb3a-D_MGU74Av2.EXP P-MLD6-7 Emb3a-D_MGU74Av2 Emb3a-D_MGU74Av2.CHP MG_U74Av2.CDF "Embryo 3" "right or left presomitic mes [...]
+"Charles River # 027" organism_part 9.5 embryo "Embryo 3" "right or left presomitic mesoderm (posterior half)(b)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 027" organism_part P-MLD6-2 "Charles River # 027" total_RNA P-MLD6-3 "Charles River # 027 biotin" synthetic_RNA biotin P-MLD6-4 Emb3b-E_MGU74Av2 A-AFFY-6 P-MLD6-5 Emb3b-E_MGU74Av2 P-MLD6-6 Emb3b-E_MGU74Av2.CEL Emb3b-E_MGU74Av2.EXP P-MLD6-7 Emb3b-E_MGU74Av2 Emb3b-E_MGU74Av2.CHP MG_U74Av2.CDF "Embryo 3" "right or left presomitic mes [...]
+"Charles River # 028" organism_part 9.5 embryo "Embryo 01" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 028" organism_part P-MLD6-2 "Charles River # 028" total_RNA P-MLD6-3 "Charles River # 028 biotin" synthetic_RNA biotin P-MLD6-4 Emb01-6.3_MOE430A A-AFFY-23 P-MLD6-5 Emb01-6.3_MOE430A P-MLD6-6 Emb01-6.3_MOE430A.CEL Emb01-6.3_MOE430A.EXP P-MLD6-7 Emb01-6.3_MOE430A Emb01-6.3_MOE430A.CHP MOE430A.CDF "Embryo 01" "right presomitic mesoderm (poste [...]
+"Charles River # 029" organism_part 9.5 embryo "Embryo 02" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 029" organism_part P-MLD6-2 "Charles River # 029" total_RNA P-MLD6-3 "Charles River # 029 biotin" synthetic_RNA biotin P-MLD6-4 Emb02-10.3_MOE430A A-AFFY-23 P-MLD6-5 Emb02-10.3_MOE430A P-MLD6-6 Emb02-10.3_MOE430A.CEL Emb02-10.3_MOE430A.EXP P-MLD6-7 Emb02-10.3_MOE430A Emb02-10.3_MOE430A.CHP MOE430A.CDF "Embryo 02" "right presomitic mesoderm [...]
+"Charles River # 030" organism_part 9.5 embryo "Embryo 03" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 030" organism_part P-MLD6-2 "Charles River # 030" total_RNA P-MLD6-3 "Charles River # 030 biotin" synthetic_RNA biotin P-MLD6-4 Emb03-7.3_MOE430A A-AFFY-23 P-MLD6-5 Emb03-7.3_MOE430A P-MLD6-6 Emb03-7.3_MOE430A.CEL Emb03-7.3_MOE430A.EXP P-MLD6-7 Emb03-7.3_MOE430A Emb03-7.3_MOE430A.CHP MOE430A.CDF "Embryo 03" "right presomitic mesoderm (poste [...]
+"Charles River # 031" organism_part 9.5 embryo "Embryo 04" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 031" organism_part P-MLD6-2 "Charles River # 031" total_RNA P-MLD6-3 "Charles River # 031 biotin" synthetic_RNA biotin P-MLD6-4 Emb04-11.3_MOE430A A-AFFY-23 P-MLD6-5 Emb04-11.3_MOE430A P-MLD6-6 Emb04-11.3_MOE430A.CEL Emb04-11.3_MOE430A.EXP P-MLD6-7 Emb04-11.3_MOE430A Emb04-11.3_MOE430A.CHP MOE430A.CDF "Embryo 04" "right presomitic mesoderm [...]
+"Charles River # 032" organism_part 9.5 embryo "Embryo 05" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 032" organism_part P-MLD6-2 "Charles River # 032" total_RNA P-MLD6-3 "Charles River # 032 biotin" synthetic_RNA biotin P-MLD6-4 Emb05-12.1_MOE430A A-AFFY-23 P-MLD6-5 Emb05-12.1_MOE430A P-MLD6-6 Emb05-12.1_MOE430A.CEL Emb05-12.1_MOE430A.EXP P-MLD6-7 Emb05-12.1_MOE430A Emb05-12.1_MOE430A.CHP MOE430A.CDF "Embryo 05" "right presomitic mesoderm [...]
+"Charles River # 033" organism_part 9.5 embryo "Embryo 06" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 033" organism_part P-MLD6-2 "Charles River # 033" total_RNA P-MLD6-3 "Charles River # 033 biotin" synthetic_RNA biotin P-MLD6-4 Emb06-19.1_MOE430A A-AFFY-23 P-MLD6-5 Emb06-19.1_MOE430A P-MLD6-6 Emb06-19.1_MOE430A.CEL Emb06-19.1_MOE430A.EXP P-MLD6-7 Emb06-19.1_MOE430A Emb06-19.1_MOE430A.CHP MOE430A.CDF "Embryo 06" "right presomitic mesoderm [...]
+"Charles River # 034" organism_part 9.5 embryo "Embryo 07" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 034" organism_part P-MLD6-2 "Charles River # 034" total_RNA P-MLD6-3 "Charles River # 034 biotin" synthetic_RNA biotin P-MLD6-4 Emb07-20.1_MOE430A A-AFFY-23 P-MLD6-5 Emb07-20.1_MOE430A P-MLD6-6 Emb07-20.1_MOE430A.CEL Emb07-20.1_MOE430A.EXP P-MLD6-7 Emb07-20.1_MOE430A Emb07-20.1_MOE430A.CHP MOE430A.CDF "Embryo 07" "right presomitic mesoderm [...]
+"Charles River # 035" organism_part 9.5 embryo "Embryo 08" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 035" organism_part P-MLD6-2 "Charles River # 035" total_RNA P-MLD6-3 "Charles River # 035 biotin" synthetic_RNA biotin P-MLD6-4 Emb08-4.1_MOE430A A-AFFY-23 P-MLD6-5 Emb08-4.1_MOE430A P-MLD6-6 Emb08-4.1_MOE430A.CEL Emb08-4.1_MOE430A.EXP P-MLD6-7 Emb08-4.1_MOE430A Emb08-4.1_MOE430A.CHP MOE430A.CDF "Embryo 08" "right presomitic mesoderm (poste [...]
+"Charles River # 036" organism_part 9.5 embryo "Embryo 09" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 036" organism_part P-MLD6-2 "Charles River # 036" total_RNA P-MLD6-3 "Charles River # 036 biotin" synthetic_RNA biotin P-MLD6-4 Emb09-22.1_MOE430A A-AFFY-23 P-MLD6-5 Emb09-22.1_MOE430A P-MLD6-6 Emb09-22.1_MOE430A.CEL Emb09-22.1_MOE430A.EXP P-MLD6-7 Emb09-22.1_MOE430A Emb09-22.1_MOE430A.CHP MOE430A.CDF "Embryo 09" "right presomitic mesoderm [...]
+"Charles River # 037" organism_part 9.5 embryo "Embryo 10" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 037" organism_part P-MLD6-2 "Charles River # 037" total_RNA P-MLD6-3 "Charles River # 037 biotin" synthetic_RNA biotin P-MLD6-4 Emb10-9.1_MOE430A A-AFFY-23 P-MLD6-5 Emb10-9.1_MOE430A P-MLD6-6 Emb10-9.1_MOE430A.CEL Emb10-9.1_MOE430A.EXP P-MLD6-7 Emb10-9.1_MOE430A Emb10-9.1_MOE430A.CHP MOE430A.CDF "Embryo 10" "right presomitic mesoderm (poste [...]
+"Charles River # 038" organism_part 9.5 embryo "Embryo 11" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 038" organism_part P-MLD6-2 "Charles River # 038" total_RNA P-MLD6-3 "Charles River # 038 biotin" synthetic_RNA biotin P-MLD6-4 Emb11-23.2_MOE430A A-AFFY-23 P-MLD6-5 Emb11-23.2_MOE430A P-MLD6-6 Emb11-23.2_MOE430A.CEL Emb11-23.2_MOE430A.EXP P-MLD6-7 Emb11-23.2_MOE430A Emb11-23.2_MOE430A.CHP MOE430A.CDF "Embryo 11" "right presomitic mesoderm [...]
+"Charles River # 039" organism_part 9.5 embryo "Embryo 12" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 039" organism_part P-MLD6-2 "Charles River # 039" total_RNA P-MLD6-3 "Charles River # 039 biotin" synthetic_RNA biotin P-MLD6-4 Emb12-13.2_MOE430A A-AFFY-23 P-MLD6-5 Emb12-13.2_MOE430A P-MLD6-6 Emb12-13.2_MOE430A.CEL Emb12-13.2_MOE430A.EXP P-MLD6-7 Emb12-13.2_MOE430A Emb12-13.2_MOE430A.CHP MOE430A.CDF "Embryo 12" "right presomitic mesoderm [...]
+"Charles River # 040" organism_part 9.5 embryo "Embryo 13" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 040" organism_part P-MLD6-2 "Charles River # 040" total_RNA P-MLD6-3 "Charles River # 040 biotin" synthetic_RNA biotin P-MLD6-4 Emb13-14.2_MOE430A A-AFFY-23 P-MLD6-5 Emb13-14.2_MOE430A P-MLD6-6 Emb13-14.2_MOE430A.CEL Emb13-14.2_MOE430A.EXP P-MLD6-7 Emb13-14.2_MOE430A Emb13-14.2_MOE430A.CHP MOE430A.CDF "Embryo 13" "right presomitic mesoderm [...]
+"Charles River # 041" organism_part 9.5 embryo "Embryo 14" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 041" organism_part P-MLD6-2 "Charles River # 041" total_RNA P-MLD6-3 "Charles River # 041 biotin" synthetic_RNA biotin P-MLD6-4 Emb14-17.2_MOE430A A-AFFY-23 P-MLD6-5 Emb14-17.2_MOE430A P-MLD6-6 Emb14-17.2_MOE430A.CEL Emb14-17.2_MOE430A.EXP P-MLD6-7 Emb14-17.2_MOE430A Emb14-17.2_MOE430A.CHP MOE430A.CDF "Embryo 14" "right presomitic mesoderm [...]
+"Charles River # 042" organism_part 9.5 embryo "Embryo 15" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 042" organism_part P-MLD6-2 "Charles River # 042" total_RNA P-MLD6-3 "Charles River # 042 biotin" synthetic_RNA biotin P-MLD6-4 Emb15-16.2_MOE430A A-AFFY-23 P-MLD6-5 Emb15-16.2_MOE430A P-MLD6-6 Emb15-16.2_MOE430A.CEL Emb15-16.2_MOE430A.EXP P-MLD6-7 Emb15-16.2_MOE430A Emb15-16.2_MOE430A.CHP MOE430A.CDF "Embryo 15" "right presomitic mesoderm [...]
+"Charles River # 043" organism_part 9.5 embryo "Embryo 16" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 043" organism_part P-MLD6-2 "Charles River # 043" total_RNA P-MLD6-3 "Charles River # 043 biotin" synthetic_RNA biotin P-MLD6-4 Emb16-15.2_MOE430A A-AFFY-23 P-MLD6-5 Emb16-15.2_MOE430A P-MLD6-6 Emb16-15.2_MOE430A.CEL Emb16-15.2_MOE430A.EXP P-MLD6-7 Emb16-15.2_MOE430A Emb16-15.2_MOE430A.CHP MOE430A.CDF "Embryo 16" "right presomitic mesoderm [...]
+"Charles River # 044" organism_part 9.5 embryo "Embryo 17" "right presomitic mesoderm (posterior half)" "Mus musculus" CD-1 P-MLD6-1 "Charles River # 044" organism_part P-MLD6-2 "Charles River # 044" total_RNA P-MLD6-3 "Charles River # 044 biotin" synthetic_RNA biotin P-MLD6-4 Emb17-18.2_MOE430A A-AFFY-23 P-MLD6-5 Emb17-18.2_MOE430A P-MLD6-6 Emb17-18.2_MOE430A.CEL Emb17-18.2_MOE430A.EXP P-MLD6-7 Emb17-18.2_MOE430A Emb17-18.2_MOE430A.CHP MOE430A.CDF "Embryo 17" "right presomitic mesoderm [...]
diff --git a/examples/magetab/real/E-TABM-16_sdrf.txt b/examples/magetab/real/E-TABM-16_sdrf.txt
new file mode 100644
index 0000000..5baa0e2
--- /dev/null
+++ b/examples/magetab/real/E-TABM-16_sdrf.txt
@@ -0,0 +1,105 @@
+"Source Name" "Material Type" "Characteristics [Age]" "Characteristics [BioSourceProvider]" "Characteristics [Organism]" "Characteristics [Sex]" "Characteristics [StrainOrLine]" "Protocol REF" "Protocol REF" "Sample Name" "Material Type" "Protocol REF" "Extract Name" "Material Type" "Protocol REF" "Labeled Extract Name" "Material Type" Label "Protocol REF" "Hybridization Name" "Array Design REF" "Protocol REF" "Scan Name" "Array Data File" Comment[EXP] "Protocol REF" "Normalization Name" [...]
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 6486 "MTS Site1 Batch1 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch1 Mix1" A-AFFY-25 6407 Site1_Batch1_Mix1 Site1_Batch1_Mix1.CEL Site1_Batch1_Mix1.EXP 6553 Site1_Batch1_Mix1_PLIER Site1_Batch1_Mix1_PLIER.CHP RAE230A.CDF Batch1 Mix1 Site1 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 6486 "MTS Site1 Batch1 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch1 Mix2" A-AFFY-25 6407 Site1_Batch1_Mix2 Site1_Batch1_Mix2.CEL Site1_Batch1_Mix2.EXP 6553 Site1_Batch1_Mix2_PLIER Site1_Batch1_Mix2_PLIER.CHP RAE230A.CDF Batch1 Mix2 Site1 Affymetrix
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 6486 "MTS Site1 Batch2 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch2 Mix1" A-AFFY-25 6407 Site1_Batch2_Mix1 Site1_Batch2_Mix1.CEL Site1_Batch2_Mix1.EXP 6553 Site1_Batch2_Mix1_PLIER Site1_Batch2_Mix1_PLIER.CHP RAE230A.CDF Batch2 Mix1 Site1 Affymetrix
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 6486 "MTS Site1 Batch2 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch2 Mix2" A-AFFY-25 6407 Site1_Batch2_Mix2 Site1_Batch2_Mix2.CEL Site1_Batch2_Mix2.EXP 6553 Site1_Batch2_Mix2_PLIER Site1_Batch2_Mix2_PLIER.CHP RAE230A.CDF Batch2 Mix2 Site1 Affymetrix
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 6486 "MTS Site1 Batch3 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch3 Mix1" A-AFFY-25 6407 Site1_Batch3_Mix1 Site1_Batch3_Mix1.CEL Site1_Batch3_Mix1.EXP 6553 Site1_Batch3_Mix1_PLIER Site1_Batch3_Mix1_PLIER.CHP RAE230A.CDF Batch3 Mix1 Site1 Affymetrix
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 6486 "MTS Site1 Batch3 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch3 Mix2" A-AFFY-25 6407 Site1_Batch3_Mix2 Site1_Batch3_Mix2.CEL Site1_Batch3_Mix2.EXP 6553 Site1_Batch3_Mix2_PLIER Site1_Batch3_Mix2_PLIER.CHP RAE230A.CDF Batch3 Mix2 Site1 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 6489 "MTS Site2 Batch1 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch1 Mix1" A-AFFY-25 6407 Site2_Batch1_Mix1 Site2_Batch1_Mix1.CEL Site2_Batch1_Mix1.EXP 6553 Site2_Batch1_Mix1_PLIER Site2_Batch1_Mix1_PLIER.CHP RAE230A.CDF Batch1 Mix1 Site2 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 6489 "MTS Site2 Batch1 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch1 Mix2" A-AFFY-25 6407 Site2_Batch1_Mix2 Site2_Batch1_Mix2.CEL Site2_Batch1_Mix2.EXP 6553 Site2_Batch1_Mix2_PLIER Site2_Batch1_Mix2_PLIER.CHP RAE230A.CDF Batch1 Mix2 Site2 Affymetrix
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 6489 "MTS Site2 Batch2 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch2 Mix1" A-AFFY-25 6407 Site2_Batch2_Mix1 Site2_Batch2_Mix1.CEL Site2_Batch2_Mix1.EXP 6553 Site2_Batch2_Mix1_PLIER Site2_Batch2_Mix1_PLIER.CHP RAE230A.CDF Batch2 Mix1 Site2 Affymetrix
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 6489 "MTS Site2 Batch2 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch2 Mix2" A-AFFY-25 6407 Site2_Batch2_Mix2 Site2_Batch2_Mix2.CEL Site2_Batch2_Mix2.EXP 6553 Site2_Batch2_Mix2_PLIER Site2_Batch2_Mix2_PLIER.CHP RAE230A.CDF Batch2 Mix2 Site2 Affymetrix
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 6489 "MTS Site2 Batch3 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch3 Mix1" A-AFFY-25 6407 Site2_Batch3_Mix1 Site2_Batch3_Mix1.CEL Site2_Batch3_Mix1.EXP 6553 Site2_Batch3_Mix1_PLIER Site2_Batch3_Mix1_PLIER.CHP RAE230A.CDF Batch3 Mix1 Site2 Affymetrix
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 6489 "MTS Site2 Batch3 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch3 Mix2" A-AFFY-25 6407 Site2_Batch3_Mix2 Site2_Batch3_Mix2.CEL Site2_Batch3_Mix2.EXP 6553 Site2_Batch3_Mix2_PLIER Site2_Batch3_Mix2_PLIER.CHP RAE230A.CDF Batch3 Mix2 Site2 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 6486 "MTS Site3 Batch1 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch1 Mix1" A-AFFY-25 6409 Site3_Batch1_Mix1 Site3_Batch1_Mix1.CEL Site3_Batch1_Mix1.EXP 6553 Site3_Batch1_Mix1_PLIER Site3_Batch1_Mix1_PLIER.CHP RAE230A.CDF Batch1 Mix1 Site3 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 6486 "MTS Site3 Batch1 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch1 Mix2" A-AFFY-25 6409 Site3_Batch1_Mix2 Site3_Batch1_Mix2.CEL Site3_Batch1_Mix2.EXP 6553 Site3_Batch1_Mix2_PLIER Site3_Batch1_Mix2_PLIER.CHP RAE230A.CDF Batch1 Mix2 Site3 Affymetrix
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 6486 "MTS Site3 Batch2 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch2 Mix1" A-AFFY-25 6409 Site3_Batch2_Mix1 Site3_Batch2_Mix1.CEL Site3_Batch2_Mix1.EXP 6553 Site3_Batch2_Mix1_PLIER Site3_Batch2_Mix1_PLIER.CHP RAE230A.CDF Batch2 Mix1 Site3 Affymetrix
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 6486 "MTS Site3 Batch2 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch2 Mix2" A-AFFY-25 6409 Site3_Batch2_Mix2 Site3_Batch2_Mix2.CEL Site3_Batch2_Mix2.EXP 6553 Site3_Batch2_Mix2_PLIER Site3_Batch2_Mix2_PLIER.CHP RAE230A.CDF Batch2 Mix2 Site3 Affymetrix
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 6486 "MTS Site3 Batch3 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch3 Mix1" A-AFFY-25 6409 Site3_Batch3_Mix1 Site3_Batch3_Mix1.CEL Site3_Batch3_Mix1.EXP 6553 Site3_Batch3_Mix1_PLIER Site3_Batch3_Mix1_PLIER.CHP RAE230A.CDF Batch3 Mix1 Site3 Affymetrix
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 6486 "MTS Site3 Batch3 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch3 Mix2" A-AFFY-25 6409 Site3_Batch3_Mix2 Site3_Batch3_Mix2.CEL Site3_Batch3_Mix2.EXP 6553 Site3_Batch3_Mix2_PLIER Site3_Batch3_Mix2_PLIER.CHP RAE230A.CDF Batch3 Mix2 Site3 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 6486 "MTS Site1 Batch1 Mix1 1YR biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch1 Mix1 1YR" A-AFFY-25 6407 Site1_Batch1_Mix1_1YR Site1_Batch1_Mix1_1YR.CEL Site1_Batch1_Mix1_1YR.EXP 6553 Site1_Batch1_Mix1_1YR_PLIER Site1_Batch1_Mix1_1YR_PLIER.CHP RAE230A.CDF Batch1 Mix1 Site1 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 6486 "MTS Site1 Batch1 Mix2 1YR biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch1 Mix2 1YR" A-AFFY-25 6407 Site1_Batch1_Mix2_1YR Site1_Batch1_Mix2_1YR.CEL Site1_Batch1_Mix2_1YR.EXP 6553 Site1_Batch1_Mix2_1YR_PLIER Site1_Batch1_Mix2_1YR_PLIER.CHP RAE230A.CDF Batch1 Mix2 Site1 Affymetrix
+"MTS Batch4" total_RNA "Ambion (Austin, TX)" "Rattus norvegicus" unknown_sex Sprague-Dawley 6673 6672 "MTS Batch4" organism_part 6841-mix1 "MTS Batch4 Mix1" total_RNA 6486 "MTS Site1 Batch4 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch4 Mix1" A-AFFY-25 6407 Site1_Batch4_Mix1 Site1_Batch4_Mix1.CEL Site1_Batch4_Mix1.EXP 6553 Site1_Batch4_Mix1_PLIER Site1_Batch4_Mix1_PLIER.CHP RAE230A.CDF Batch4 Mix1 Site1 Affymetrix
+"MTS Batch4" total_RNA "Ambion (Austin, TX)" "Rattus norvegicus" unknown_sex Sprague-Dawley 6673 6672 "MTS Batch4" organism_part 6841-mix2 "MTS Batch4 Mix2" total_RNA 6486 "MTS Site1 Batch4 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch4 Mix2" A-AFFY-25 6407 Site1_Batch4_Mix2 Site1_Batch4_Mix2.CEL Site1_Batch4_Mix2.EXP 6553 Site1_Batch4_Mix2_PLIER Site1_Batch4_Mix2_PLIER.CHP RAE230A.CDF Batch4 Mix2 Site1 Affymetrix
+"MTS Batch5" total_RNA "Stratagene (La Jolla, CA)" "Rattus norvegicus" unknown_sex Sprague-Dawley 6673 6672 "MTS Batch5" organism_part 6841-mix1 "MTS Batch5 Mix1" total_RNA 6486 "MTS Site1 Batch5 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch5 Mix1" A-AFFY-25 6407 Site1_Batch5_Mix1 Site1_Batch5_Mix1.CEL Site1_Batch5_Mix1.EXP 6553 Site1_Batch5_Mix1_PLIER Site1_Batch5_Mix1_PLIER.CHP RAE230A.CDF Batch5 Mix1 Site1 Affymetrix
+"MTS Batch5" total_RNA "Stratagene (La Jolla, CA)" "Rattus norvegicus" unknown_sex Sprague-Dawley 6673 6672 "MTS Batch5" organism_part 6841-mix2 "MTS Batch5 Mix2" total_RNA 6486 "MTS Site1 Batch5 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch5 Mix2" A-AFFY-25 6407 Site1_Batch5_Mix2 Site1_Batch5_Mix2.CEL Site1_Batch5_Mix2.EXP 6553 Site1_Batch5_Mix2_PLIER Site1_Batch5_Mix2_PLIER.CHP RAE230A.CDF Batch5 Mix2 Site1 Affymetrix
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 7672 "MTS Site4 Batch1 Mix1 biotin" synthetic_DNA biotin 8188 "MTS Site4 Batch1 Mix1" A-MEXP-124 8309 Site4_Batch1_Mix1_Data Site4_Batch1_Mix1_Data.txt Site4_Batch1_Mix1_Data Batch1 Mix1 Site4 Codelink
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 7672 "MTS Site4 Batch1 Mix2 biotin" synthetic_DNA biotin 8188 "MTS Site4 Batch1 Mix2" A-MEXP-124 8309 Site4_Batch1_Mix2_Data Site4_Batch1_Mix2_Data.txt Site4_Batch1_Mix2_Data Batch1 Mix2 Site4 Codelink
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 7672 "MTS Site4 Batch2 Mix1 biotin" synthetic_DNA biotin 8188 "MTS Site4 Batch2 Mix1" A-MEXP-124 8309 Site4_Batch2_Mix1_Data Site4_Batch2_Mix1_Data.txt Site4_Batch2_Mix1_Data Batch2 Mix1 Site4 Codelink
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 7672 "MTS Site4 Batch2 Mix2 biotin" synthetic_DNA biotin 8188 "MTS Site4 Batch2 Mix2" A-MEXP-124 8309 Site4_Batch2_Mix2_Data Site4_Batch2_Mix2_Data.txt Site4_Batch2_Mix2_Data Batch2 Mix2 Site4 Codelink
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 7672 "MTS Site4 Batch3 Mix1 biotin" synthetic_DNA biotin 8188 "MTS Site4 Batch3 Mix1" A-MEXP-124 8309 Site4_Batch3_Mix1_Data Site4_Batch3_Mix1_Data.txt Site4_Batch3_Mix1_Data Batch3 Mix1 Site4 Codelink
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 7672 "MTS Site4 Batch3 Mix2 biotin" synthetic_DNA biotin 8188 "MTS Site4 Batch3 Mix2" A-MEXP-124 8309 Site4_Batch3_Mix2_Data Site4_Batch3_Mix2_Data.txt Site4_Batch3_Mix2_Data Batch3 Mix2 Site4 Codelink
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 7671 "MTS Site5 Batch1 Mix1 biotin" synthetic_DNA biotin 8189 "MTS Site5 Batch1 Mix1" A-MEXP-124 8190 Site5_Batch1_Mix1_Data Site5_Batch1_Mix1_Data.txt Site5_Batch1_Mix1_Data Batch1 Mix1 Site5 Codelink
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 7671 "MTS Site5 Batch1 Mix2 biotin" synthetic_DNA biotin 8189 "MTS Site5 Batch1 Mix2" A-MEXP-124 8190 Site5_Batch1_Mix2_Data Site5_Batch1_Mix2_Data.txt Site5_Batch1_Mix2_Data Batch1 Mix2 Site5 Codelink
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 7671 "MTS Site5 Batch2 Mix1 biotin" synthetic_DNA biotin 8189 "MTS Site5 Batch2 Mix1" A-MEXP-124 8190 Site5_Batch2_Mix1_Data Site5_Batch2_Mix1_Data.txt Site5_Batch2_Mix1_Data Batch2 Mix1 Site5 Codelink
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 7671 "MTS Site5 Batch2 Mix2 biotin" synthetic_DNA biotin 8189 "MTS Site5 Batch2 Mix2" A-MEXP-124 8190 Site5_Batch2_Mix2_Data Site5_Batch2_Mix2_Data.txt Site5_Batch2_Mix2_Data Batch2 Mix2 Site5 Codelink
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 7671 "MTS Site5 Batch3 Mix1 biotin" synthetic_DNA biotin 8189 "MTS Site5 Batch3 Mix1" A-MEXP-124 8190 Site5_Batch3_Mix1_Data Site5_Batch3_Mix1_Data.txt Site5_Batch3_Mix1_Data Batch3 Mix1 Site5 Codelink
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 7671 "MTS Site5 Batch3 Mix2 biotin" synthetic_DNA biotin 8189 "MTS Site5 Batch3 Mix2" A-MEXP-124 8190 Site5_Batch3_Mix2_Data Site5_Batch3_Mix2_Data.txt Site5_Batch3_Mix2_Data Batch3 Mix2 Site5 Codelink
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8532 "MTS Site7 Batch1 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8573 "MTS Site7 Batch1 rMix1 gMix2" A-AGIL-6 8590 Site7_Batch1_rMix1_gMix2 Site7_Batch1_rMix1_gMix2.txt Site7_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site7 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8534 "MTS Site7 Batch1 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8573 "MTS Site7 Batch1 rMix1 gMix2" A-AGIL-6 8590 Site7_Batch1_rMix1_gMix2 Site7_Batch1_rMix1_gMix2.txt Site7_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site7 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8534 "MTS Site7 Batch1 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8573 "MTS Site7 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site7_Batch1_rMix2_gMix1 Site7_Batch1_rMix2_gMix1.txt Site7_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site7 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8532 "MTS Site7 Batch1 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8573 "MTS Site7 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site7_Batch1_rMix2_gMix1 Site7_Batch1_rMix2_gMix1.txt Site7_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site7 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8534 "MTS Site7 Batch2 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8573 "MTS Site7 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site7_Batch2_rMix1_gMix2 Site7_Batch2_rMix1_gMix2.txt Site7_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site7 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8532 "MTS Site7 Batch2 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8573 "MTS Site7 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site7_Batch2_rMix1_gMix2 Site7_Batch2_rMix1_gMix2.txt Site7_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site7 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8534 "MTS Site7 Batch2 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8573 "MTS Site7 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site7_Batch2_rMix2_gMix1 Site7_Batch2_rMix2_gMix1.txt Site7_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site7 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8532 "MTS Site7 Batch2 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8573 "MTS Site7 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site7_Batch2_rMix2_gMix1 Site7_Batch2_rMix2_gMix1.txt Site7_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site7 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8532 "MTS Site7 Batch3 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8573 "MTS Site7 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site7_Batch3_rMix1_gMix2 Site7_Batch3_rMix1_gMix2.txt Site7_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site7 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8534 "MTS Site7 Batch3 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8573 "MTS Site7 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site7_Batch3_rMix1_gMix2 Site7_Batch3_rMix1_gMix2.txt Site7_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site7 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8534 "MTS Site7 Batch3 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8573 "MTS Site7 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site7_Batch3_rMix2_gMix1 Site7_Batch3_rMix2_gMix1.txt Site7_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site7 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8532 "MTS Site7 Batch3 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8573 "MTS Site7 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site7_Batch3_rMix2_gMix1 Site7_Batch3_rMix2_gMix1.txt Site7_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site7 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8535 "MTS Site8 Batch1 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8588 "MTS Site8 Batch1 rMix1 g Mix2" A-AGIL-6 8590 Site8_Batch1_rMix1_gMix2 Site8_Batch1_rMix1_gMix2.txt Site8_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site8 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8536 "MTS Site8 Batch1 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8588 "MTS Site8 Batch1 rMix1 g Mix2" A-AGIL-6 8590 Site8_Batch1_rMix1_gMix2 Site8_Batch1_rMix1_gMix2.txt Site8_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site8 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8536 "MTS Site8 Batch1 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8588 "MTS Site8 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site8_Batch1_rMix2_gMix1 Site8_Batch1_rMix2_gMix1.txt Site8_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site8 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8535 "MTS Site8 Batch1 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8588 "MTS Site8 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site8_Batch1_rMix2_gMix1 Site8_Batch1_rMix2_gMix1.txt Site8_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site8 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8535 "MTS Site8 Batch2 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8588 "MTS Site8 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site8_Batch2_rMix1_gMix2 Site8_Batch2_rMix1_gMix2.txt Site8_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site8 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8536 "MTS Site8 Batch2 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8588 "MTS Site8 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site8_Batch2_rMix1_gMix2 Site8_Batch2_rMix1_gMix2.txt Site8_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site8 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8536 "MTS Site8 Batch2 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8588 "MTS Site8 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site8_Batch2_rMix2_gMix1 Site8_Batch2_rMix2_gMix1.txt Site8_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site8 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8535 "MTS Site8 Batch2 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8588 "MTS Site8 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site8_Batch2_rMix2_gMix1 Site8_Batch2_rMix2_gMix1.txt Site8_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site8 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8536 "MTS Site8 Batch3 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8588 "MTS Site8 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site8_Batch3_rMix1_gMix2 Site8_Batch3_rMix1_gMix2.txt Site8_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site8 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8535 "MTS Site8 Batch3 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8588 "MTS Site8 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site8_Batch3_rMix1_gMix2 Site8_Batch3_rMix1_gMix2.txt Site8_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site8 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8536 "MTS Site8 Batch3 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8588 "MTS Site8 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site8_Batch3_rMix2_gMix1 Site8_Batch3_rMix2_gMix1.txt Site8_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site8 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8535 "MTS Site8 Batch3 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8588 "MTS Site8 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site8_Batch3_rMix2_gMix1 Site8_Batch3_rMix2_gMix1.txt Site8_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site8 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8537 "MTS Site9 Batch1 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site9 Batch1 rMix1 gMix2" A-AGIL-6 8590 Site9_Batch1_rMix1_gMix2 Site9_Batch1_rMix1_gMix2.txt Site9_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site9 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8538 "MTS Site9 Batch1 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site9 Batch1 rMix1 gMix2" A-AGIL-6 8590 Site9_Batch1_rMix1_gMix2 Site9_Batch1_rMix1_gMix2.txt Site9_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site9 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8538 "MTS Site9 Batch1 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site9 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site9_Batch1_rMix2_gMix1 Site9_Batch1_rMix2_gMix1.txt Site9_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site9 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8537 "MTS Site9 Batch1 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site9 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site9_Batch1_rMix2_gMix1 Site9_Batch1_rMix2_gMix1.txt Site9_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site9 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8537 "MTS Site9 Batch2 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site9 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site9_Batch2_rMix1_gMix2 Site9_Batch2_rMix1_gMix2.txt Site9_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site9 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8538 "MTS Site9 Batch2 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site9 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site9_Batch2_rMix1_gMix2 Site9_Batch2_rMix1_gMix2.txt Site9_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site9 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8537 "MTS Site9 Batch2 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site9 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site9_Batch2_rMix2_gMix1 Site9_Batch2_rMix2_gMix1.txt Site9_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site9 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8538 "MTS Site9 Batch2 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site9 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site9_Batch2_rMix2_gMix1 Site9_Batch2_rMix2_gMix1.txt Site9_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site9 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8537 "MTS Site9 Batch3 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site9 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site9_Batch3_rMix1_gMix2 Site9_Batch3_rMix1_gMix2.txt Site9_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site9 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8538 "MTS Site9 Batch3 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site9 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site9_Batch3_rMix1_gMix2 Site9_Batch3_rMix1_gMix2.txt Site9_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site9 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8538 "MTS Site9 Batch3 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site9 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site9_Batch3_rMix2_gMix1 Site9_Batch3_rMix2_gMix1.txt Site9_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site9 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8537 "MTS Site9 Batch3 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site9 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site9_Batch3_rMix2_gMix1 Site9_Batch3_rMix2_gMix1.txt Site9_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site9 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8538 "MTS Site3 Batch1 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site3 Batch1 rMix1 gMix2" A-AGIL-6 8590 Site3_Batch1_rMix1_gMix2 Site3_Batch1_rMix1_gMix2.txt Site3_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site3 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8537 "MTS Site3 Batch1 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site3 Batch1 rMix1 gMix2" A-AGIL-6 8590 Site3_Batch1_rMix1_gMix2 Site3_Batch1_rMix1_gMix2.txt Site3_Batch1_rMix1_gMix2 Batch1 Mix1;Mix2 Site3 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix2 "MTS Batch1 Mix2" total_RNA 8538 "MTS Site3 Batch1 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site3 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site3_Batch1_rMix2_gMix1 Site3_Batch1_rMix2_gMix1.txt Site3_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site3 Agilent
+"MTS Batch1" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch1" organism_part 6396-mix1 "MTS Batch1 Mix1" total_RNA 8537 "MTS Site3 Batch1 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site3 Batch1 rMix2 gMix1" A-AGIL-6 8590 Site3_Batch1_rMix2_gMix1 Site3_Batch1_rMix2_gMix1.txt Site3_Batch1_rMix2_gMix1 Batch1 Mix1;Mix2 Site3 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8537 "MTS Site3 Batch2 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site3 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site3_Batch2_rMix1_gMix2 Site3_Batch2_rMix1_gMix2.txt Site3_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site3 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8538 "MTS Site3 Batch2 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site3 Batch2 rMix1 gMix2" A-AGIL-6 8590 Site3_Batch2_rMix1_gMix2 Site3_Batch2_rMix1_gMix2.txt Site3_Batch2_rMix1_gMix2 Batch2 Mix1;Mix2 Site3 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix2 "MTS Batch2 Mix2" total_RNA 8538 "MTS Site3 Batch2 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site3 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site3_Batch2_rMix2_gMix1 Site3_Batch2_rMix2_gMix1.txt Site3_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site3 Agilent
+"MTS Batch2" whole_organism "6-7 weeks" "Charles River, Raleigh, NC" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch2" organism_part 6396-mix1 "MTS Batch2 Mix1" total_RNA 8537 "MTS Site3 Batch2 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site3 Batch2 rMix2 gMix1" A-AGIL-6 8590 Site3_Batch2_rMix2_gMix1 Site3_Batch2_rMix2_gMix1.txt Site3_Batch2_rMix2_gMix1 Batch2 Mix1;Mix2 Site3 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8537 "MTS Site3 Batch3 Mix2 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site3 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site3_Batch3_rMix1_gMix2 Site3_Batch3_rMix1_gMix2.txt Site3_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site3 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8538 "MTS Site3 Batch3 Mix1 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site3 Batch3 rMix1 gMix2" A-AGIL-6 8590 Site3_Batch3_rMix1_gMix2 Site3_Batch3_rMix1_gMix2.txt Site3_Batch3_rMix1_gMix2 Batch3 Mix1;Mix2 Site3 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix1 "MTS Batch3 Mix1" total_RNA 8537 "MTS Site3 Batch3 Mix1 (Cy3) Cy3" synthetic_DNA Cy3 8589 "MTS Site3 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site3_Batch3_rMix2_gMix1 Site3_Batch3_rMix2_gMix1.txt Site3_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site3 Agilent
+"MTS Batch3" whole_organism "6-7 weeks" "Harlan, Frederick, MD" "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch3" organism_part 6396-mix2 "MTS Batch3 Mix2" total_RNA 8538 "MTS Site3 Batch3 Mix2 (Cy5) Cy5" synthetic_DNA Cy5 8589 "MTS Site3 Batch3 rMix2 gMix1" A-AGIL-6 8590 Site3_Batch3_rMix2_gMix1 Site3_Batch3_rMix2_gMix1.txt Site3_Batch3_rMix2_gMix1 Batch3 Mix1;Mix2 Site3 Agilent
+"MTS Batch6" whole_organism "6-7 weeks" Site3 "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch6" organism_part 6396-mix1 "MTS Batch6 Mix1" total_RNA 6486 "MTS Site3 Batch6 Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch6 Mix1" A-AFFY-25 6407 Site1_Batch6_Mix1 Site1_Batch6_Mix1.CEL Site1_Batch6_Mix1.EXP "MTS Site1 Batch6 Mix1" RAE230A.CDF Batch6 Mix1 Site1 Affymetrix
+"MTS Batch6" whole_organism "6-7 weeks" Site3 "Rattus norvegicus" male Sprague-Dawley 6377 6380 "MTS Batch6" organism_part 6396-mix2 "MTS Batch6 Mix2" total_RNA 6486 "MTS Site1 Batch6 Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch6 Mix2" A-AFFY-25 6407 Site1_Batch6_Mix2 Site1_Batch6_Mix2.CEL Site1_Batch6_Mix2.EXP "MTS Site1 Batch6 Mix2" RAE230A.CDF Batch6 Mix2 Site1 Affymetrix
+Site1_Batch1_Mix1 whole_organism Site1_Batch1_Mix1 organism_part Site1_Batch1_Mix1 total_RNA "Site1_Batch1_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch1 Mix1" A-AFFY-25 6407 Site1_Batch1_Mix1 Site1_Batch1_Mix1.CEL Site1_Batch1_Mix1.EXP 6552 Site1_Batch1_Mix1_MAS5 Site1_Batch1_Mix1_MAS5.CHP RAE230A.CDF Batch1 Mix1 Site1 Affymetrix
+Site1_Batch1_Mix2 whole_organism Site1_Batch1_Mix2 organism_part Site1_Batch1_Mix2 total_RNA "Site1_Batch1_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch1 Mix2" A-AFFY-25 6407 Site1_Batch1_Mix2 Site1_Batch1_Mix2.CEL Site1_Batch1_Mix2.EXP 6552 Site1_Batch1_Mix2_MAS5 Site1_Batch1_Mix2_MAS5.CHP RAE230A.CDF Batch1 Mix2 Site1 Affymetrix
+Site1_Batch2_Mix1 whole_organism Site1_Batch2_Mix1 organism_part Site1_Batch2_Mix1 total_RNA "Site1_Batch2_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch2 Mix1" A-AFFY-25 6407 Site1_Batch2_Mix1 Site1_Batch2_Mix1.CEL Site1_Batch2_Mix1.EXP 6552 Site1_Batch2_Mix1_MAS5 Site1_Batch2_Mix1_MAS5.CHP RAE230A.CDF Batch2 Mix1 Site1 Affymetrix
+Site1_Batch2_Mix2 whole_organism Site1_Batch2_Mix2 organism_part Site1_Batch2_Mix2 total_RNA "Site1_Batch2_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch2 Mix2" A-AFFY-25 6407 Site1_Batch2_Mix2 Site1_Batch2_Mix2.CEL Site1_Batch2_Mix2.EXP 6552 Site1_Batch2_Mix2_MAS5 Site1_Batch2_Mix2_MAS5.CHP RAE230A.CDF Batch2 Mix2 Site1 Affymetrix
+Site1_Batch3_Mix1 whole_organism Site1_Batch3_Mix1 organism_part Site1_Batch3_Mix1 total_RNA "Site1_Batch3_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch3 Mix1" A-AFFY-25 6407 Site1_Batch3_Mix1 Site1_Batch3_Mix1.CEL Site1_Batch3_Mix1.EXP 6552 Site1_Batch3_Mix1_MAS5 Site1_Batch3_Mix1_MAS5.CHP RAE230A.CDF Batch3 Mix1 Site1 Affymetrix
+Site1_Batch3_Mix2 whole_organism Site1_Batch3_Mix2 organism_part Site1_Batch3_Mix2 total_RNA "Site1_Batch3_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site1 Batch3 Mix2" A-AFFY-25 6407 Site1_Batch3_Mix2 Site1_Batch3_Mix2.CEL Site1_Batch3_Mix2.EXP 6552 Site1_Batch3_Mix2_MAS5 Site1_Batch3_Mix2_MAS5.CHP RAE230A.CDF Batch3 Mix2 Site1 Affymetrix
+Site2_Batch1_Mix1 whole_organism Site2_Batch1_Mix1 organism_part Site2_Batch1_Mix1 total_RNA "Site2_Batch1_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch1 Mix1" A-AFFY-25 6407 Site2_Batch1_Mix1 Site2_Batch1_Mix1.CEL Site2_Batch1_Mix1.EXP 6552 Site2_Batch1_Mix1_MAS5 Site2_Batch1_Mix1_MAS5.CHP RAE230A.CDF Batch1 Mix1 Site2 Affymetrix
+Site2_Batch1_Mix2 whole_organism Site2_Batch1_Mix2 organism_part Site2_Batch1_Mix2 total_RNA "Site2_Batch1_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch1 Mix2" A-AFFY-25 6407 Site2_Batch1_Mix2 Site2_Batch1_Mix2.CEL Site2_Batch1_Mix2.EXP 6552 Site2_Batch1_Mix2_MAS5 Site2_Batch1_Mix2_MAS5.CHP RAE230A.CDF Batch1 Mix2 Site2 Affymetrix
+Site2_Batch2_Mix1 whole_organism Site2_Batch2_Mix1 organism_part Site2_Batch2_Mix1 total_RNA "Site2_Batch2_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch2 Mix1" A-AFFY-25 6407 Site2_Batch2_Mix1 Site2_Batch2_Mix1.CEL Site2_Batch2_Mix1.EXP 6552 Site2_Batch2_Mix1_MAS5 Site2_Batch2_Mix1_MAS5.CHP RAE230A.CDF Batch2 Mix1 Site2 Affymetrix
+Site2_Batch2_Mix2 whole_organism Site2_Batch2_Mix2 organism_part Site2_Batch2_Mix2 total_RNA "Site2_Batch2_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch2 Mix2" A-AFFY-25 6407 Site2_Batch2_Mix2 Site2_Batch2_Mix2.CEL Site2_Batch2_Mix2.EXP 6552 Site2_Batch2_Mix2_MAS5 Site2_Batch2_Mix2_MAS5.CHP RAE230A.CDF Batch2 Mix2 Site2 Affymetrix
+Site2_Batch3_Mix1 whole_organism Site2_Batch3_Mix1 organism_part Site2_Batch3_Mix1 total_RNA "Site2_Batch3_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch3 Mix1" A-AFFY-25 6407 Site2_Batch3_Mix1 Site2_Batch3_Mix1.CEL Site2_Batch3_Mix1.EXP 6552 Site2_Batch3_Mix1_MAS5 Site2_Batch3_Mix1_MAS5.CHP RAE230A.CDF Batch3 Mix1 Site2 Affymetrix
+Site2_Batch3_Mix2 whole_organism Site2_Batch3_Mix2 organism_part Site2_Batch3_Mix2 total_RNA "Site2_Batch3_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site2 Batch3 Mix2" A-AFFY-25 6407 Site2_Batch3_Mix2 Site2_Batch3_Mix2.CEL Site2_Batch3_Mix2.EXP 6552 Site2_Batch3_Mix2_MAS5 Site2_Batch3_Mix2_MAS5.CHP RAE230A.CDF Batch3 Mix2 Site2 Affymetrix
+Site3_Batch1_Mix1 whole_organism Site3_Batch1_Mix1 organism_part Site3_Batch1_Mix1 total_RNA "Site3_Batch1_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch1 Mix1" A-AFFY-25 6409 Site3_Batch1_Mix1 Site3_Batch1_Mix1.CEL Site3_Batch1_Mix1.EXP 6552 Site3_Batch1_Mix1_MAS5 Site3_Batch1_Mix1_MAS5.CHP RAE230A.CDF Batch1 Mix1 Site3 Affymetrix
+Site3_Batch1_Mix2 whole_organism Site3_Batch1_Mix2 organism_part Site3_Batch1_Mix2 total_RNA "Site3_Batch1_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch1 Mix2" A-AFFY-25 6409 Site3_Batch1_Mix2 Site3_Batch1_Mix2.CEL Site3_Batch1_Mix2.EXP 6552 Site3_Batch1_Mix2_MAS5 Site3_Batch1_Mix2_MAS5.CHP RAE230A.CDF Batch1 Mix2 Site3 Affymetrix
+Site3_Batch2_Mix1 whole_organism Site3_Batch2_Mix1 organism_part Site3_Batch2_Mix1 total_RNA "Site3_Batch2_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch2 Mix1" A-AFFY-25 6409 Site3_Batch2_Mix1 Site3_Batch2_Mix1.CEL Site3_Batch2_Mix1.EXP 6552 Site3_Batch2_Mix1_MAS5 Site3_Batch2_Mix1_MAS5.CHP RAE230A.CDF Batch2 Mix1 Site3 Affymetrix
+Site3_Batch2_Mix2 whole_organism Site3_Batch2_Mix2 organism_part Site3_Batch2_Mix2 total_RNA "Site3_Batch2_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch2 Mix2" A-AFFY-25 6409 Site3_Batch2_Mix2 Site3_Batch2_Mix2.CEL Site3_Batch2_Mix2.EXP 6552 Site3_Batch2_Mix2_MAS5 Site3_Batch2_Mix2_MAS5.CHP RAE230A.CDF Batch2 Mix2 Site3 Affymetrix
+Site3_Batch3_Mix1 whole_organism Site3_Batch3_Mix1 organism_part Site3_Batch3_Mix1 total_RNA "Site3_Batch3_Mix1 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch3 Mix1" A-AFFY-25 6409 Site3_Batch3_Mix1 Site3_Batch3_Mix1.CEL Site3_Batch3_Mix1.EXP 6552 Site3_Batch3_Mix1_MAS5 Site3_Batch3_Mix1_MAS5.CHP RAE230A.CDF Batch3 Mix1 Site3 Affymetrix
+Site3_Batch3_Mix2 whole_organism Site3_Batch3_Mix2 organism_part Site3_Batch3_Mix2 total_RNA "Site3_Batch3_Mix2 biotin" synthetic_DNA biotin 6491 "MTS Site3 Batch3 Mix2" A-AFFY-25 6409 Site3_Batch3_Mix2 Site3_Batch3_Mix2.CEL Site3_Batch3_Mix2.EXP 6552 Site3_Batch3_Mix2_MAS5 Site3_Batch3_Mix2_MAS5.CHP RAE230A.CDF Batch3 Mix2 Site3 Affymetrix
diff --git a/examples/magetab/real/E-TABM-18.idf b/examples/magetab/real/E-TABM-18.idf
new file mode 100644
index 0000000..47b9cca
--- /dev/null
+++ b/examples/magetab/real/E-TABM-18.idf
@@ -0,0 +1,26 @@
+"Investigation Title" Ecotype
+"Experimental Design" strain_or_line_design
+"Experimental Factor Name" ECOTYPE
+"Experimental Factor Type" ecotype
+
+"Person Last Name" Lempe
+"Person First Name" Janne
+"Person Mid Initial"
+"Person Affiliation" "Max Planck Institute for Developmental Biology, Department of Molecular Biology, Detlef Weigel"
+"Person Roles" submitter
+
+"Public Release Date" "2005-06-15"
+
+Comment[ArrayExpressSubmissionDate]
+
+"Publication Author List" "Janne Lempe;Sureshkumar Balasubramanian;Sridevi Sureshkumar;Anandita Singh;Markus Schmid;Detlef Weigel"
+"Publication Title" "Diversity of flowering responses in wild Arabidopsis thaliana strains"
+"Experiment Description" "Seedlings of 35 different Arabidopsis thaliana ecotypes were compared. Triplicates were performed of 10 ecotpyes, single arrays of 25 ecotypes. "
+
+"Protocol Name" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4
+"Protocol Type" growth extraction pooling labeling
+"Protocol Description" "Plants were grown on soil for 4 days under continuous light at 23ᄚC and 65% relative humidity. Most replicates were grown at the same time, exceptions to this are indicated by the 'replicate timepoint' parameter." "pdf of the protocol (Probe preparation Development) can be downloaded at http://www.weigelworld.org/resources/microarray/AtGenExpress/; also at TAIR http://www.arabidopsis.org Protocol:501712557" "above soil plant material of 15 seedlings was pooled p [...]
+"Protocol Parameters" "replicate timepoint"
+"Protocol Software"
+
+"SDRF File" E-TABM-18_sdrf.txt
diff --git a/examples/magetab/real/E-TABM-18_sdrf.txt b/examples/magetab/real/E-TABM-18_sdrf.txt
new file mode 100644
index 0000000..f12178d
--- /dev/null
+++ b/examples/magetab/real/E-TABM-18_sdrf.txt
@@ -0,0 +1,56 @@
+"Source Name" "Material Type" "Characteristics [Ecotype]" "Characteristics [OrganismPart]" "Characteristics [Organism]" "Characteristics [nasc]" "Protocol REF" "Parameter Value [replicate timepoint]" "Sample Name" "Material Type" "Protocol REF" "Protocol REF" "Extract Name" "Material Type" "Protocol REF" "Labeled Extract Name" "Material Type" Label "Hybridization Name" "Array Design REF" "Scan Name" "Array Data File" Comment[EXP] "Normalization Name" "Derived Array Data File" Comment[CDF [...]
+AtGE_111_A organism_part Bay-0 "seedling, aerial parts" "Arabidopsis thaliana" CS954 P-TABM-Janne5 A AtGE_111_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_111_A total_RNA P-TABM-Janne4 "AtGE_111_A biotin" synthetic_DNA biotin ATGE_111_A A-AFFY-2 ATGE_111_A ATGE_111_A.CEL ATGE_111_A.EXP ATGE_111_A ATGE_111_A.CHP ATH1-121501.CDF Bay-0
+AtGE_111_B organism_part Bay-0 "seedling, aerial parts" "Arabidopsis thaliana" CS954 P-TABM-Janne5 A AtGE_111_B organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_111_B total_RNA P-TABM-Janne4 "AtGE_111_B biotin" synthetic_DNA biotin ATGE_111_B A-AFFY-2 ATGE_111_B ATGE_111_B.CEL ATGE_111_B.EXP ATGE_111_B ATGE_111_B.CHP ATH1-121501.CDF Bay-0
+AtGE_111_C organism_part Bay-0 "seedling, aerial parts" "Arabidopsis thaliana" CS954 P-TABM-Janne5 A AtGE_111_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_111_C total_RNA P-TABM-Janne4 "AtGE_111_C biotin" synthetic_DNA biotin ATGE_111_C A-AFFY-2 ATGE_111_C ATGE_111_C.CEL ATGE_111_C.EXP ATGE_111_C ATGE_111_C.CHP ATH1-121501.CDF Bay-0
+AtGE_112_A organism_part C24 "seedling, aerial parts" "Arabidopsis thaliana" CS906 P-TABM-Janne5 A AtGE_112_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_112_A total_RNA P-TABM-Janne4 "AtGE_112_A biotin" synthetic_DNA biotin ATGE_112_A A-AFFY-2 ATGE_112_A ATGE_112_A.CEL ATGE_112_A.EXP ATGE_112_A ATGE_112_A.CHP ATH1-121501.CDF C24
+AtGE_112_C organism_part C24 "seedling, aerial parts" "Arabidopsis thaliana" CS906 P-TABM-Janne5 A AtGE_112_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_112_C total_RNA P-TABM-Janne4 "AtGE_112_C biotin" synthetic_DNA biotin ATGE_112_C A-AFFY-2 ATGE_112_C ATGE_112_C.CEL ATGE_112_C.EXP ATGE_112_C ATGE_112_C.CHP ATH1-121501.CDF C24
+AtGE_112_D organism_part C24 "seedling, aerial parts" "Arabidopsis thaliana" CS906 P-TABM-Janne5 A AtGE_112_D organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_112_D total_RNA P-TABM-Janne4 "AtGE_112_D biotin" synthetic_DNA biotin ATGE_112_D A-AFFY-2 ATGE_112_D ATGE_112_D.CEL ATGE_112_D.EXP ATGE_112_D ATGE_112_D.CHP ATH1-121501.CDF C24
+AtGE_113_A organism_part Col-0 "seedling, aerial parts" "Arabidopsis thaliana" N1093 P-TABM-Janne5 A AtGE_113_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_113_A total_RNA P-TABM-Janne4 "AtGE_113_A biotin" synthetic_DNA biotin ATGE_113_A A-AFFY-2 ATGE_113_A ATGE_113_A.CEL ATGE_113_A.EXP ATGE_113_A ATGE_113_A.CHP ATH1-121501.CDF Col-0
+AtGE_113_C organism_part Col-0 "seedling, aerial parts" "Arabidopsis thaliana" N1093 P-TABM-Janne5 A AtGE_113_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_113_C total_RNA P-TABM-Janne4 "AtGE_113_C biotin" synthetic_DNA biotin ATGE_113_C A-AFFY-2 ATGE_113_C ATGE_113_C.CEL ATGE_113_C.EXP ATGE_113_C ATGE_113_C.CHP ATH1-121501.CDF Col-0
+AtGE_113_D organism_part Col-0 "seedling, aerial parts" "Arabidopsis thaliana" N1093 P-TABM-Janne5 B AtGE_113_D organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_113_D total_RNA P-TABM-Janne4 "AtGE_113_D biotin" synthetic_DNA biotin ATGE_113_D A-AFFY-2 ATGE_113_D ATGE_113_D.CEL ATGE_113_D.EXP ATGE_113_D ATGE_113_D.CHP ATH1-121501.CDF Col-0
+AtGE_114_A organism_part Cvi "seedling, aerial parts" "Arabidopsis thaliana" CS8580 P-TABM-Janne5 A AtGE_114_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_114_A total_RNA P-TABM-Janne4 "AtGE_114_A biotin" synthetic_DNA biotin ATGE_114_A A-AFFY-2 ATGE_114_A ATGE_114_A.CEL ATGE_114_A.EXP ATGE_114_A ATGE_114_A.CHP ATH1-121501.CDF Cvi
+AtGE_114_B organism_part Cvi "seedling, aerial parts" "Arabidopsis thaliana" CS8580 P-TABM-Janne5 A AtGE_114_B organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_114_B total_RNA P-TABM-Janne4 "AtGE_114_B biotin" synthetic_DNA biotin ATGE_114_B A-AFFY-2 ATGE_114_B ATGE_114_B.CEL ATGE_114_B.EXP ATGE_114_B ATGE_114_B.CHP ATH1-121501.CDF Cvi
+AtGE_114_C organism_part Cvi "seedling, aerial parts" "Arabidopsis thaliana" CS8580 P-TABM-Janne5 A AtGE_114_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_114_C total_RNA P-TABM-Janne4 "AtGE_114_C biotin" synthetic_DNA biotin ATGE_114_C A-AFFY-2 ATGE_114_C ATGE_114_C.CEL ATGE_114_C.EXP ATGE_114_C ATGE_114_C.CHP ATH1-121501.CDF Cvi
+AtGE_115_A organism_part Est "seedling, aerial parts" "Arabidopsis thaliana" CS6173 P-TABM-Janne5 A AtGE_115_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_115_A total_RNA P-TABM-Janne4 "AtGE_115_A biotin" synthetic_DNA biotin ATGE_115_A A-AFFY-2 ATGE_115_A ATGE_115_A.CEL ATGE_115_A.EXP ATGE_115_A ATGE_115_A.CHP ATH1-121501.CDF Est
+AtGE_115_B organism_part Est "seedling, aerial parts" "Arabidopsis thaliana" CS6173 P-TABM-Janne5 A AtGE_115_B organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_115_B total_RNA P-TABM-Janne4 "AtGE_115_B biotin" synthetic_DNA biotin ATGE_115_B A-AFFY-2 ATGE_115_B ATGE_115_B.CEL ATGE_115_B.EXP ATGE_115_B ATGE_115_B.CHP ATH1-121501.CDF Est
+AtGE_115_D organism_part Est "seedling, aerial parts" "Arabidopsis thaliana" CS6173 P-TABM-Janne5 B AtGE_115_D organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_115_D total_RNA P-TABM-Janne4 "AtGE_115_D biotin" synthetic_DNA biotin ATGE_115_D A-AFFY-2 ATGE_115_D ATGE_115_D.CEL ATGE_115_D.EXP ATGE_115_D ATGE_115_D.CHP ATH1-121501.CDF Est
+AtGE_116_A organism_part Kin-0 "seedling, aerial parts" "Arabidopsis thaliana" CS6755 P-TABM-Janne5 A AtGE_116_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_116_A total_RNA P-TABM-Janne4 "AtGE_116_A biotin" synthetic_DNA biotin ATGE_116_A A-AFFY-2 ATGE_116_A ATGE_116_A.CEL ATGE_116_A.EXP ATGE_116_A ATGE_116_A.CHP ATH1-121501.CDF Kin-0
+AtGE_116_B organism_part Kin-0 "seedling, aerial parts" "Arabidopsis thaliana" CS6755 P-TABM-Janne5 A AtGE_116_B organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_116_B total_RNA P-TABM-Janne4 "AtGE_116_B biotin" synthetic_DNA biotin ATGE_116_B A-AFFY-2 ATGE_116_B ATGE_116_B.CEL ATGE_116_B.EXP ATGE_116_B ATGE_116_B.CHP ATH1-121501.CDF Kin-0
+AtGE_116_C organism_part Kin-0 "seedling, aerial parts" "Arabidopsis thaliana" CS6755 P-TABM-Janne5 A AtGE_116_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_116_C total_RNA P-TABM-Janne4 "AtGE_116_C biotin" synthetic_DNA biotin ATGE_116_C A-AFFY-2 ATGE_116_C ATGE_116_C.CEL ATGE_116_C.EXP ATGE_116_C ATGE_116_C.CHP ATH1-121501.CDF Kin-0
+AtGE_117_B organism_part Ler "seedling, aerial parts" "Arabidopsis thaliana" CS8581 P-TABM-Janne5 A AtGE_117_B organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_117_B total_RNA P-TABM-Janne4 "AtGE_117_B biotin" synthetic_DNA biotin ATGE_117_B A-AFFY-2 ATGE_117_B ATGE_117_B.CEL ATGE_117_B.EXP ATGE_117_B ATGE_117_B.CHP ATH1-121501.CDF Ler
+AtGE_117_C organism_part Ler "seedling, aerial parts" "Arabidopsis thaliana" CS8581 P-TABM-Janne5 A AtGE_117_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_117_C total_RNA P-TABM-Janne4 "AtGE_117_C biotin" synthetic_DNA biotin ATGE_117_C A-AFFY-2 ATGE_117_C ATGE_117_C.CEL ATGE_117_C.EXP ATGE_117_C ATGE_117_C.CHP ATH1-121501.CDF Ler
+AtGE_117_D organism_part Ler "seedling, aerial parts" "Arabidopsis thaliana" CS8581 P-TABM-Janne5 B AtGE_117_D organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_117_D total_RNA P-TABM-Janne4 "AtGE_117_D biotin" synthetic_DNA biotin ATGE_117_D A-AFFY-2 ATGE_117_D ATGE_117_D.CEL ATGE_117_D.EXP ATGE_117_D ATGE_117_D.CHP ATH1-121501.CDF Ler
+AtGE_118_A organism_part Nd-1 "seedling, aerial parts" "Arabidopsis thaliana" CS1636 P-TABM-Janne5 A AtGE_118_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_118_A total_RNA P-TABM-Janne4 "AtGE_118_A biotin" synthetic_DNA biotin ATGE_118_A A-AFFY-2 ATGE_118_A ATGE_118_A.CEL ATGE_118_A.EXP ATGE_118_A ATGE_118_A.CHP ATH1-121501.CDF Nd-1
+AtGE_118_B organism_part Nd-1 "seedling, aerial parts" "Arabidopsis thaliana" CS1636 P-TABM-Janne5 A AtGE_118_B organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_118_B total_RNA P-TABM-Janne4 "AtGE_118_B biotin" synthetic_DNA biotin ATGE_118_B A-AFFY-2 ATGE_118_B ATGE_118_B.CEL ATGE_118_B.EXP ATGE_118_B ATGE_118_B.CHP ATH1-121501.CDF Nd-1
+AtGE_118_C organism_part Nd-1 "seedling, aerial parts" "Arabidopsis thaliana" CS1636 P-TABM-Janne5 A AtGE_118_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_118_C total_RNA P-TABM-Janne4 "AtGE_118_C biotin" synthetic_DNA biotin ATGE_118_C A-AFFY-2 ATGE_118_C ATGE_118_C.CEL ATGE_118_C.EXP ATGE_118_C ATGE_118_C.CHP ATH1-121501.CDF Nd-1
+AtGE_119_A organism_part Shahdara "seedling, aerial parts" "Arabidopsis thaliana" CS929 P-TABM-Janne5 A AtGE_119_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_119_A total_RNA P-TABM-Janne4 "AtGE_119_A biotin" synthetic_DNA biotin ATGE_119_A A-AFFY-2 ATGE_119_A ATGE_119_A.CEL ATGE_119_A.EXP ATGE_119_A ATGE_119_A.CHP ATH1-121501.CDF Shahdara
+AtGE_119_C organism_part Shahdara "seedling, aerial parts" "Arabidopsis thaliana" CS929 P-TABM-Janne5 A AtGE_119_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_119_C total_RNA P-TABM-Janne4 "AtGE_119_C biotin" synthetic_DNA biotin ATGE_119_C A-AFFY-2 ATGE_119_C ATGE_119_C.CEL ATGE_119_C.EXP ATGE_119_C ATGE_119_C.CHP ATH1-121501.CDF Shahdara
+AtGE_119_D organism_part Shahdara "seedling, aerial parts" "Arabidopsis thaliana" CS929 P-TABM-Janne5 A AtGE_119_D organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_119_D total_RNA P-TABM-Janne4 "AtGE_119_D biotin" synthetic_DNA biotin ATGE_119_D A-AFFY-2 ATGE_119_D ATGE_119_D.CEL ATGE_119_D.EXP ATGE_119_D ATGE_119_D.CHP ATH1-121501.CDF Shahdara
+AtGE_120_A organism_part Van-0 "seedling, aerial parts" "Arabidopsis thaliana" CS6884 P-TABM-Janne5 A AtGE_120_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_120_A total_RNA P-TABM-Janne4 "AtGE_120_A biotin" synthetic_DNA biotin ATGE_120_A A-AFFY-2 ATGE_120_A ATGE_120_A.CEL ATGE_120_A.EXP ATGE_120_A ATGE_120_A.CHP ATH1-121501.CDF Van-0
+AtGE_120_B organism_part Van-0 "seedling, aerial parts" "Arabidopsis thaliana" CS6884 P-TABM-Janne5 A AtGE_120_B organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_120_B total_RNA P-TABM-Janne4 "AtGE_120_B biotin" synthetic_DNA biotin ATGE_120_B A-AFFY-2 ATGE_120_B ATGE_120_B.CEL ATGE_120_B.EXP ATGE_120_B ATGE_120_B.CHP ATH1-121501.CDF Van-0
+AtGE_120_C organism_part Van-0 "seedling, aerial parts" "Arabidopsis thaliana" CS6884 P-TABM-Janne5 A AtGE_120_C organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_120_C total_RNA P-TABM-Janne4 "AtGE_120_C biotin" synthetic_DNA biotin ATGE_120_C A-AFFY-2 ATGE_120_C ATGE_120_C.CEL ATGE_120_C.EXP ATGE_120_C ATGE_120_C.CHP ATH1-121501.CDF Van-0
+AtGE_121_A organism_part Ak-1 "seedling, aerial parts" "Arabidopsis thaliana" "N939 " P-TABM-Janne5 AtGE_121_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_121_A total_RNA P-TABM-Janne4 "AtGE_121_A biotin" synthetic_DNA biotin ATGE_121_A A-AFFY-2 ATGE_121_A ATGE_121_A.CEL ATGE_121_A.EXP ATGE_121_A ATGE_121_A.CHP ATH1-121501.CDF Ak-1
+AtGE_124_A organism_part Bla-5 "seedling, aerial parts" "Arabidopsis thaliana" "N6620 " P-TABM-Janne5 AtGE_124_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_124_A total_RNA P-TABM-Janne4 "AtGE_124_A biotin" synthetic_DNA biotin ATGE_124_A A-AFFY-2 ATGE_124_A ATGE_124_A.CEL ATGE_124_A.EXP ATGE_124_A ATGE_124_A.CHP ATH1-121501.CDF Bla-5
+AtGE_125_A organism_part Can-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1065 " P-TABM-Janne5 AtGE_125_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_125_A total_RNA P-TABM-Janne4 "AtGE_125_A biotin" synthetic_DNA biotin ATGE_125_A A-AFFY-2 ATGE_125_A ATGE_125_A.CEL ATGE_125_A.EXP ATGE_125_A ATGE_125_A.CHP ATH1-121501.CDF Can-0
+AtGE_126_A organism_part Cen-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1067 " P-TABM-Janne5 AtGE_126_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_126_A total_RNA P-TABM-Janne4 "AtGE_126_A biotin" synthetic_DNA biotin ATGE_126_A A-AFFY-2 ATGE_126_A ATGE_126_A.CEL ATGE_126_A.EXP ATGE_126_A ATGE_126_A.CHP ATH1-121501.CDF Cen-0
+AtGE_127_A organism_part CIBC10 "seedling, aerial parts" "Arabidopsis thaliana" "N22229 " P-TABM-Janne5 AtGE_127_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_127_A total_RNA P-TABM-Janne4 "AtGE_127_A biotin" synthetic_DNA biotin ATGE_127_A A-AFFY-2 ATGE_127_A ATGE_127_A.CEL ATGE_127_A.EXP ATGE_127_A ATGE_127_A.CHP ATH1-121501.CDF CIBC10
+AtGE_128_A organism_part Dra-1 "seedling, aerial parts" "Arabidopsis thaliana" "N1119 " P-TABM-Janne5 AtGE_128_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_128_A total_RNA P-TABM-Janne4 "AtGE_128_A biotin" synthetic_DNA biotin ATGE_128_A A-AFFY-2 ATGE_128_A ATGE_128_A.CEL ATGE_128_A.EXP ATGE_128_A ATGE_128_A.CHP ATH1-121501.CDF Dra-1
+AtGE_129_A organism_part Enkheim-T "seedling, aerial parts" "Arabidopsis thaliana" "N6176 " P-TABM-Janne5 AtGE_129_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_129_A total_RNA P-TABM-Janne4 "AtGE_129_A biotin" synthetic_DNA biotin ATGE_129_A A-AFFY-2 ATGE_129_A ATGE_129_A.CEL ATGE_129_A.EXP ATGE_129_A ATGE_129_A.CHP ATH1-121501.CDF Enkheim-T
+AtGE_130_A organism_part Er-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1143 " P-TABM-Janne5 AtGE_130_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_130_A total_RNA P-TABM-Janne4 "AtGE_130_A biotin" synthetic_DNA biotin ATGE_130_A A-AFFY-2 ATGE_130_A ATGE_130_A.CEL ATGE_130_A.EXP ATGE_130_A ATGE_130_A.CHP ATH1-121501.CDF Er-0
+AtGE_131_A organism_part Fr-2 "seedling, aerial parts" "Arabidopsis thaliana" "N1169 " P-TABM-Janne5 AtGE_131_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_131_A total_RNA P-TABM-Janne4 "AtGE_131_A biotin" synthetic_DNA biotin ATGE_131_A A-AFFY-2 ATGE_131_A ATGE_131_A.CEL ATGE_131_A.EXP ATGE_131_A ATGE_131_A.CHP ATH1-121501.CDF Fr-2
+AtGE_132_A organism_part GOT1 "seedling, aerial parts" "Arabidopsis thaliana" "N22277 " P-TABM-Janne5 AtGE_132_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_132_A total_RNA P-TABM-Janne4 "AtGE_132_A biotin" synthetic_DNA biotin ATGE_132_A A-AFFY-2 ATGE_132_A ATGE_132_A.CEL ATGE_132_A.EXP ATGE_132_A ATGE_132_A.CHP ATH1-121501.CDF GOT1
+AtGE_133_A organism_part HR-5 "seedling, aerial parts" "Arabidopsis thaliana" "N22205 " P-TABM-Janne5 AtGE_133_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_133_A total_RNA P-TABM-Janne4 "AtGE_133_A biotin" synthetic_DNA biotin ATGE_133_A A-AFFY-2 ATGE_133_A ATGE_133_A.CEL ATGE_133_A.EXP ATGE_133_A ATGE_133_A.CHP ATH1-121501.CDF HR-5
+AtGE_134_A organism_part Is-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1241 " P-TABM-Janne5 AtGE_134_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_134_A total_RNA P-TABM-Janne4 "AtGE_134_A biotin" synthetic_DNA biotin ATGE_134_A A-AFFY-2 ATGE_134_A ATGE_134_A.CEL ATGE_134_A.EXP ATGE_134_A ATGE_134_A.CHP ATH1-121501.CDF Is-0
+AtGE_136_A organism_part Li-2:1 "seedling, aerial parts" "Arabidopsis thaliana" "N1315 " P-TABM-Janne5 AtGE_136_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_136_A total_RNA P-TABM-Janne4 "AtGE_136_A biotin" synthetic_DNA biotin ATGE_136_A A-AFFY-2 ATGE_136_A ATGE_136_A.CEL ATGE_136_A.EXP ATGE_136_A ATGE_136_A.CHP ATH1-121501.CDF Li-2:1
+AtGE_137_A organism_part M7323S "seedling, aerial parts" "Arabidopsis thaliana" "N6184 " P-TABM-Janne5 AtGE_137_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_137_A total_RNA P-TABM-Janne4 "AtGE_137_A biotin" synthetic_DNA biotin ATGE_137_A A-AFFY-2 ATGE_137_A ATGE_137_A.CEL ATGE_137_A.EXP ATGE_137_A ATGE_137_A.CHP ATH1-121501.CDF M7323S
+AtGE_138_A organism_part Ms-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1377 " P-TABM-Janne5 AtGE_138_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_138_A total_RNA P-TABM-Janne4 "AtGE_138_A biotin" synthetic_DNA biotin ATGE_138_A A-AFFY-2 ATGE_138_A ATGE_138_A.CEL ATGE_138_A.EXP ATGE_138_A ATGE_138_A.CHP ATH1-121501.CDF Ms-0
+AtGE_139_A organism_part Nc-1 "seedling, aerial parts" "Arabidopsis thaliana" "N1389 " P-TABM-Janne5 AtGE_139_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_139_A total_RNA P-TABM-Janne4 "AtGE_139_A biotin" synthetic_DNA biotin ATGE_139_A A-AFFY-2 ATGE_139_A ATGE_139_A.CEL ATGE_139_A.EXP ATGE_139_A ATGE_139_A.CHP ATH1-121501.CDF Nc-1
+AtGE_140_A organism_part NFE1 "seedling, aerial parts" "Arabidopsis thaliana" "N22163 " P-TABM-Janne5 AtGE_140_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_140_A total_RNA P-TABM-Janne4 "AtGE_140_A biotin" synthetic_DNA biotin ATGE_140_A A-AFFY-2 ATGE_140_A ATGE_140_A.CEL ATGE_140_A.EXP ATGE_140_A ATGE_140_A.CHP ATH1-121501.CDF NFE1
+AtGE_141_A organism_part Nok-1 "seedling, aerial parts" "Arabidopsis thaliana" "N1401 " P-TABM-Janne5 AtGE_141_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_141_A total_RNA P-TABM-Janne4 "AtGE_141_A biotin" synthetic_DNA biotin ATGE_141_A A-AFFY-2 ATGE_141_A ATGE_141_A.CEL ATGE_141_A.EXP ATGE_141_A ATGE_141_A.CHP ATH1-121501.CDF Nok-1
+AtGE_142_A organism_part Nw-1 "seedling, aerial parts" "Arabidopsis thaliana" "N1411 " P-TABM-Janne5 AtGE_142_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_142_A total_RNA P-TABM-Janne4 "AtGE_142_A biotin" synthetic_DNA biotin ATGE_142_A A-AFFY-2 ATGE_142_A ATGE_142_A.CEL ATGE_142_A.EXP ATGE_142_A ATGE_142_A.CHP ATH1-121501.CDF Nw-1
+AtGE_144_A organism_part Old-2 "seedling, aerial parts" "Arabidopsis thaliana" "N1429 " P-TABM-Janne5 AtGE_144_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_144_A total_RNA P-TABM-Janne4 "AtGE_144_A biotin" synthetic_DNA biotin ATGE_144_A A-AFFY-2 ATGE_144_A ATGE_144_A.CEL ATGE_144_A.EXP ATGE_144_A ATGE_144_A.CHP ATH1-121501.CDF Old-2
+AtGE_145_A organism_part Ove-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1435 " P-TABM-Janne5 AtGE_145_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_145_A total_RNA P-TABM-Janne4 "AtGE_145_A biotin" synthetic_DNA biotin ATGE_145_A A-AFFY-2 ATGE_145_A ATGE_145_A.CEL ATGE_145_A.EXP ATGE_145_A ATGE_145_A.CHP ATH1-121501.CDF Ove-0
+AtGE_146_A organism_part Se-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1503 " P-TABM-Janne5 AtGE_146_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_146_A total_RNA P-TABM-Janne4 "AtGE_146_A biotin" synthetic_DNA biotin ATGE_146_A A-AFFY-2 ATGE_146_A ATGE_146_A.CEL ATGE_146_A.EXP ATGE_146_A ATGE_146_A.CHP ATH1-121501.CDF Se-0
+AtGE_147_A organism_part Sf-2e "seedling, aerial parts" "Arabidopsis thaliana" "N1675 " P-TABM-Janne5 AtGE_147_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_147_A total_RNA P-TABM-Janne4 "AtGE_147_A biotin" synthetic_DNA biotin ATGE_147_A A-AFFY-2 ATGE_147_A ATGE_147_A.CEL ATGE_147_A.EXP ATGE_147_A ATGE_147_A.CHP ATH1-121501.CDF Sf-2e
+AtGE_148_A organism_part Ta-0 "seedling, aerial parts" "Arabidopsis thaliana" "N1549 " P-TABM-Janne5 AtGE_148_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_148_A total_RNA P-TABM-Janne4 "AtGE_148_A biotin" synthetic_DNA biotin ATGE_148_A A-AFFY-2 ATGE_148_A ATGE_148_A.CEL ATGE_148_A.EXP ATGE_148_A ATGE_148_A.CHP ATH1-121501.CDF Ta-0
+AtGE_149_A organism_part Uk-3 "seedling, aerial parts" "Arabidopsis thaliana" "N1577 " P-TABM-Janne5 AtGE_149_A organism_part P-TABM-Janne2 P-TABM-Janne6 AtGE_149_A total_RNA P-TABM-Janne4 "AtGE_149_A biotin" synthetic_DNA biotin ATGE_149_A A-AFFY-2 ATGE_149_A ATGE_149_A.CEL ATGE_149_A.EXP ATGE_149_A ATGE_149_A.CHP ATH1-121501.CDF Uk-3
diff --git a/examples/magetab/real/E-TABM-22.idf b/examples/magetab/real/E-TABM-22.idf
new file mode 100644
index 0000000..0e6ee6f
--- /dev/null
+++ b/examples/magetab/real/E-TABM-22.idf
@@ -0,0 +1,24 @@
+"Investigation Title" "Lung cancers"
+"Experimental Design" cell_type_comparison_design stimulus_or_stress_design compound_treatment_design disease_state_design
+"Experimental Factor Name" COMPOUND DISEASESTATE DISEASESTAGING CELLLINE INDIVIDUAL
+"Experimental Factor Type" compound disease_state disease_staging cell_line individual
+
+"Person Last Name" Yanaihara
+"Person First Name" Nozomu
+"Person Mid Initial"
+"Person Affiliation" "Laboratory of Human Carcinogenesis, NCI, NIH"
+"Person Roles" submitter
+
+"Public Release Date" "2006-07-01"
+
+Comment[ArrayExpressSubmissionDate] "2005-06-20"
+
+"Experiment Description" "Distinct miRNA expression significance in human lung cancers and lung cancer cell lines."
+
+"Protocol Name" P-MEXP-9141 P-MEXP-9142 P-MEXP-9143 P-MEXP-9144 P-MEXP-9145 P-MEXP-9146 P-MEXP-9147
+"Protocol Type" grow compound_based_treatment nucleic_acid_extraction labeling hybridization image_acquisition bioassay_data_transformation
+"Protocol Description" "Human NSCLC cell lines were cultured with 10% FCS containing RPMI 1640 at 37C with 5% CO2." "For the first 48h, cells were incubated with medium containing 1.0 uM 5-aza-dC, and then for another 24h with the addition of 1.0 uM TSA." "Total RNA was isolated with TRIzol according to the manufacturer's protocol." "Five micrograms of total RNA were separately added to reaction mix in a final volume of 12 ul, containing 1 ug of [3'-(N)8-(A)12-biotin-(A)12-biotin-5_] oli [...]
+"Protocol Parameters" medium compound "RNA labelled" "RNA hybridized; Hyb temperature"
+"Protocol Software"
+
+"SDRF File" E-TABM-22_sdrf.txt
diff --git a/examples/magetab/real/E-TABM-22_sdrf.txt b/examples/magetab/real/E-TABM-22_sdrf.txt
new file mode 100644
index 0000000..2287653
--- /dev/null
+++ b/examples/magetab/real/E-TABM-22_sdrf.txt
@@ -0,0 +1,252 @@
+"Source Name" "Material Type" "Characteristics [Age]" "Characteristics [BioSourceType]" "Characteristics [CellLine]" "Characteristics [DiseaseStaging]" "Characteristics [DiseaseState]" "Characteristics [Individual]" "Characteristics [Organism]" "Characteristics [Sex]" "Characteristics [TimeUnit]" "Protocol REF" "Parameter Value [medium]" "Protocol REF" "Parameter Value [compound]" "Sample Name" "Material Type" "Protocol REF" "Extract Name" "Material Type" "Protocol REF" "Parameter Value [...]
+A549 cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" A549 cell P-MEXP-9143 A549 total_RNA P-MEXP-9144 5 ug "A549 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1791_A549 A-MEXP-86 P-MEXP-9146 R1791_A549 R1791_A549.txt A549 "Non-Small Cell Lung Carcinoma"
+A-427 cell fresh_sample A-427 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" A-427 cell P-MEXP-9143 A-427 total_RNA P-MEXP-9144 5 ug "A-427 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1790_A427 A-MEXP-86 P-MEXP-9146 R1790_A427 R1790_A427.txt A-427 "Non-Small Cell Lung Carcinoma"
+A2182 cell fresh_sample A2182 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" A2182 cell P-MEXP-9143 A2182 total_RNA P-MEXP-9144 5 ug "A2182 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1792_A2182 A-MEXP-86 P-MEXP-9146 R1792_A2182 R1792_A2182.txt A2182 "Non-Small Cell Lung Carcinoma"
+Calu-1 cell fresh_sample Calu-1 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" Calu-1 cell P-MEXP-9143 Calu-1 total_RNA P-MEXP-9144 5 ug "Calu-1 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1793_Calu1 A-MEXP-86 P-MEXP-9146 R1793_Calu1 R1793_Calu1.txt Calu-1 "Non-Small Cell Lung Carcinoma"
+Calu-6 cell fresh_sample Calu-6 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" Calu-6 cell P-MEXP-9143 Calu-6 total_RNA P-MEXP-9144 5 ug "Calu-6 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1794_Calu6 A-MEXP-86 P-MEXP-9146 R1794_Calu6 R1794_Calu6.txt Calu-6 "Non-Small Cell Lung Carcinoma"
+DMS92 cell fresh_sample DMS92 "Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" DMS92 cell P-MEXP-9143 DMS92 total_RNA P-MEXP-9144 5 ug "DMS92 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1795_DMS92 A-MEXP-86 P-MEXP-9146 R1795_DMS92 R1795_DMS92.txt DMS92 "Small Cell Lung Carcinoma"
+NCI-H82 cell fresh_sample NCI-H82 "Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" NCI-H82 cell P-MEXP-9143 NCI-H82 total_RNA P-MEXP-9144 5 ug "NCI-H82 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1796_NCIH82 A-MEXP-86 P-MEXP-9146 R1796_NCIH82 R1796_NCIH82.txt NCI-H82 "Small Cell Lung Carcinoma"
+NCI-H146 cell fresh_sample NCI-H146 "Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" NCI-H146 cell P-MEXP-9143 NCI-H146 total_RNA P-MEXP-9144 5 ug "NCI-H146 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1797_NCIH146 A-MEXP-86 P-MEXP-9146 R1797_NCIH146 R1797_NCIH146.txt NCI-H146 "Small Cell Lung Carcinoma"
+NCI-H157 cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" NCI-H157 cell P-MEXP-9143 NCI-H157 total_RNA P-MEXP-9144 5 ug "NCI-H157 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1798_NCIH157 A-MEXP-86 P-MEXP-9146 R1798_NCIH157 R1798_NCIH157.txt NCI-H157 "Non-Small Cell Lung Carcinoma"
+NCI-H446 cell fresh_sample NCI-H446 "Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" NCI-H446 cell P-MEXP-9143 NCI-H446 total_RNA P-MEXP-9144 5 ug "NCI-H446 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1805_NCIH446 A-MEXP-86 P-MEXP-9146 R1805_NCIH446 R1805_NCIH446.txt NCI-H446 "Small Cell Lung Carcinoma"
+NCI-H1155 cell fresh_sample NCI-H1155 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" NCI-H1155 cell P-MEXP-9143 NCI-H1155 total_RNA P-MEXP-9144 5 ug "NCI-H1155 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1807_NCIH1155 A-MEXP-86 P-MEXP-9146 R1807_NCIH1155 R1807_NCIH1155.txt NCI-H1155 "Non-Small Cell Lung Carcinoma"
+NCI-N417 cell fresh_sample NCI-N417 "Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" NCI-N417 cell P-MEXP-9143 NCI-N417 total_RNA P-MEXP-9144 5 ug "NCI-N417 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1808_NCIN417 A-MEXP-86 P-MEXP-9146 R1808_NCIN417 R1808_NCIN417.txt NCI-N417 "Small Cell Lung Carcinoma"
+NCI-H596 cell fresh_sample NCI-H596 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" NCI-H596 cell P-MEXP-9143 NCI-H596 total_RNA P-MEXP-9144 5 ug "NCI-H596 biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1806_NCIH596 A-MEXP-86 P-MEXP-9146 R1806_NCIH596 R1806_NCIH596.txt NCI-H596 "Non-Small Cell Lung Carcinoma"
+A549_ex1_nontreat cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 none A549_ex1_nontreat cell P-MEXP-9143 A549_ex1_nontreat total_RNA P-MEXP-9144 5 ug "A549_ex1_nontreat biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1811_A5491-1 A-MEXP-86 P-MEXP-9146 R1811_A5491-1 R1811_A5491-1.txt A549 null "Non-Small Cell Lung Carcinoma"
+A549_ex1_aza cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza A549_ex1_aza cell P-MEXP-9143 A549_ex1_aza total_RNA P-MEXP-9144 5 ug "A549_ex1_aza biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1812_A5491-2 A-MEXP-86 P-MEXP-9146 R1812_A5491-2 R1812_A5491-2.txt A549 Aza "Non-Small Cell Lung Carcinoma"
+A549_ex1_tsa cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 TSA A549_ex1_tsa cell P-MEXP-9143 A549_ex1_tsa total_RNA P-MEXP-9144 5 ug "A549_ex1_tsa biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1813_A5491-3 A-MEXP-86 P-MEXP-9146 R1813_A5491-3 R1813_A5491-3.txt A549 TSA "Non-Small Cell Lung Carcinoma"
+A549_ex1_both cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA A549_ex1_both cell P-MEXP-9143 A549_ex1_both total_RNA P-MEXP-9144 5 ug "A549_ex1_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1814_A5491-4 A-MEXP-86 P-MEXP-9146 R1814_A5491-4 R1814_A5491-4.txt A549 Aza "Non-Small Cell Lung Carcinoma"
+A549_ex1_both cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA A549_ex1_both cell P-MEXP-9143 A549_ex1_both total_RNA P-MEXP-9144 5 ug "A549_ex1_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1814_A5491-4 A-MEXP-86 P-MEXP-9146 R1814_A5491-4 R1814_A5491-4.txt A549 TSA "Non-Small Cell Lung Carcinoma"
+A549_ex2_nontreat cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 none A549_ex2_nontreat cell P-MEXP-9143 A549_ex2_nontreat total_RNA P-MEXP-9144 5 ug "A549_ex2_nontreat biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1815_A5492-1 A-MEXP-86 P-MEXP-9146 R1815_A5492-1 R1815_A5492-1.txt A549 null "Non-Small Cell Lung Carcinoma"
+A549_ex2_aza cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza A549_ex2_aza cell P-MEXP-9143 A549_ex2_aza total_RNA P-MEXP-9144 5 ug "A549_ex2_aza biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1816_A5492-2 A-MEXP-86 P-MEXP-9146 R1816_A5492-2 R1816_A5492-2.txt A549 Aza "Non-Small Cell Lung Carcinoma"
+A549_ex2_tsa cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 TSA A549_ex2_tsa cell P-MEXP-9143 A549_ex2_tsa total_RNA P-MEXP-9144 5 ug "A549_ex2_tsa biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1817_A5492-3 A-MEXP-86 P-MEXP-9146 R1817_A5492-3 R1817_A5492-3.txt A549 TSA "Non-Small Cell Lung Carcinoma"
+A549_ex2_both cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA A549_ex2_both cell P-MEXP-9143 A549_ex2_both total_RNA P-MEXP-9144 5 ug "A549_ex2_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1818_A5492-4 A-MEXP-86 P-MEXP-9146 R1818_A5492-4 R1818_A5492-4.txt A549 Aza "Non-Small Cell Lung Carcinoma"
+A549_ex2_both cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA A549_ex2_both cell P-MEXP-9143 A549_ex2_both total_RNA P-MEXP-9144 5 ug "A549_ex2_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1818_A5492-4 A-MEXP-86 P-MEXP-9146 R1818_A5492-4 R1818_A5492-4.txt A549 TSA "Non-Small Cell Lung Carcinoma"
+A549_ex3_nontreat cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 none A549_ex3_nontreat cell P-MEXP-9143 A549_ex3_nontreat total_RNA P-MEXP-9144 5 ug "A549_ex3_nontreat biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1819_A5493-1 A-MEXP-86 P-MEXP-9146 R1819_A5493-1 R1819_A5493-1.txt A549 null "Non-Small Cell Lung Carcinoma"
+A549_ex3_aza cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza A549_ex3_aza cell P-MEXP-9143 A549_ex3_aza total_RNA P-MEXP-9144 5 ug "A549_ex3_aza biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1820_A5493-2 A-MEXP-86 P-MEXP-9146 R1820_A5493-2 R1820_A5493-2.txt A549 Aza "Non-Small Cell Lung Carcinoma"
+A549_ex3_tsa cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 TSA A549_ex3_tsa cell P-MEXP-9143 A549_ex3_tsa total_RNA P-MEXP-9144 5 ug "A549_ex3_tsa biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1821_A5493-3 A-MEXP-86 P-MEXP-9146 R1821_A5493-3 R1821_A5493-3.txt A549 TSA "Non-Small Cell Lung Carcinoma"
+A549_ex3_both cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA A549_ex3_both cell P-MEXP-9143 A549_ex3_both total_RNA P-MEXP-9144 5 ug "A549_ex3_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1822_A5493-4 A-MEXP-86 P-MEXP-9146 R1822_A5493-4 R1822_A5493-4.txt A549 Aza "Non-Small Cell Lung Carcinoma"
+A549_ex3_both cell fresh_sample A549 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA A549_ex3_both cell P-MEXP-9143 A549_ex3_both total_RNA P-MEXP-9144 5 ug "A549_ex3_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1822_A5493-4 A-MEXP-86 P-MEXP-9146 R1822_A5493-4 R1822_A5493-4.txt A549 TSA "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex1_nontreat cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 none NCI-H157_ex1_nontreat cell P-MEXP-9143 NCI-H157_ex1_nontreat total_RNA P-MEXP-9144 5 ug "NCI-H157_ex1_nontreat biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1835_NCIH1571-1 A-MEXP-86 P-MEXP-9146 R1835_NCIH1571-1 R1835_NCIH1571-1.txt NCI-H157 null "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex1_aza cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza NCI-H157_ex1_aza cell P-MEXP-9143 NCI-H157_ex1_aza total_RNA P-MEXP-9144 5 ug "NCI-H157_ex1_aza biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1836_NCIH1571-2 A-MEXP-86 P-MEXP-9146 R1836_NCIH1571-2 R1836_NCIH1571-2.txt NCI-H157 Aza "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex1_tsa cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 TSA NCI-H157_ex1_tsa cell P-MEXP-9143 NCI-H157_ex1_tsa total_RNA P-MEXP-9144 5 ug "NCI-H157_ex1_tsa biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1837_NCIH1571-3 A-MEXP-86 P-MEXP-9146 R1837_NCIH1571-3 R1837_NCIH1571-3.txt NCI-H157 TSA "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex1_both cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA NCI-H157_ex1_both cell P-MEXP-9143 NCI-H157_ex1_both total_RNA P-MEXP-9144 5 ug "NCI-H157_ex1_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1838_NCIH1571-4 A-MEXP-86 P-MEXP-9146 R1838_NCIH1571-4 R1838_NCIH1571-4.txt NCI-H157 Aza "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex1_both cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA NCI-H157_ex1_both cell P-MEXP-9143 NCI-H157_ex1_both total_RNA P-MEXP-9144 5 ug "NCI-H157_ex1_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1838_NCIH1571-4 A-MEXP-86 P-MEXP-9146 R1838_NCIH1571-4 R1838_NCIH1571-4.txt NCI-H157 TSA "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex2_nontreat cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 none NCI-H157_ex2_nontreat cell P-MEXP-9143 NCI-H157_ex2_nontreat total_RNA P-MEXP-9144 5 ug "NCI-H157_ex2_nontreat biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1839_NCIH1572-1 A-MEXP-86 P-MEXP-9146 R1839_NCIH1572-1 R1839_NCIH1572-1.txt NCI-H157 null "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex2_aza cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza NCI-H157_ex2_aza cell P-MEXP-9143 NCI-H157_ex2_aza total_RNA P-MEXP-9144 5 ug "NCI-H157_ex2_aza biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1840_NCIH1572-2 A-MEXP-86 P-MEXP-9146 R1840_NCIH1572-2 R1840_NCIH1572-2.txt NCI-H157 Aza "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex2_tsa cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 TSA NCI-H157_ex2_tsa cell P-MEXP-9143 NCI-H157_ex2_tsa total_RNA P-MEXP-9144 5 ug "NCI-H157_ex2_tsa biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1841_NCIH1572-3 A-MEXP-86 P-MEXP-9146 R1841_NCIH1572-3 R1841_NCIH1572-3.txt NCI-H157 TSA "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex2_both cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA NCI-H157_ex2_both cell P-MEXP-9143 NCI-H157_ex2_both total_RNA P-MEXP-9144 5 ug "NCI-H157_ex2_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1842_NCIH1572-4 A-MEXP-86 P-MEXP-9146 R1842_NCIH1572-4 R1842_NCIH1572-4.txt NCI-H157 Aza "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex2_both cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA NCI-H157_ex2_both cell P-MEXP-9143 NCI-H157_ex2_both total_RNA P-MEXP-9144 5 ug "NCI-H157_ex2_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1842_NCIH1572-4 A-MEXP-86 P-MEXP-9146 R1842_NCIH1572-4 R1842_NCIH1572-4.txt NCI-H157 TSA "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex3_nontreat cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 none NCI-H157_ex3_nontreat cell P-MEXP-9143 NCI-H157_ex3_nontreat total_RNA P-MEXP-9144 5 ug "NCI-H157_ex3_nontreat biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1843_NCIH1573-1 A-MEXP-86 P-MEXP-9146 R1843_NCIH1573-1 R1843_NCIH1573-1.txt NCI-H157 null "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex3_aza cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza NCI-H157_ex3_aza cell P-MEXP-9143 NCI-H157_ex3_aza total_RNA P-MEXP-9144 5 ug "NCI-H157_ex3_aza biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1844_NCIH1573-2 A-MEXP-86 P-MEXP-9146 R1844_NCIH1573-2 R1844_NCIH1573-2.txt NCI-H157 Aza "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex3_tsa cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 TSA NCI-H157_ex3_tsa cell P-MEXP-9143 NCI-H157_ex3_tsa total_RNA P-MEXP-9144 5 ug "NCI-H157_ex3_tsa biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1845_NCIH1573-3 A-MEXP-86 P-MEXP-9146 R1845_NCIH1573-3 R1845_NCIH1573-3.txt NCI-H157 TSA "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex3_both cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA NCI-H157_ex3_both cell P-MEXP-9143 NCI-H157_ex3_both total_RNA P-MEXP-9144 5 ug "NCI-H157_ex3_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1846_NCIH1573-4 A-MEXP-86 P-MEXP-9146 R1846_NCIH1573-4 R1846_NCIH1573-4.txt NCI-H157 Aza "Non-Small Cell Lung Carcinoma"
+NCI-H157_ex3_both cell fresh_sample NCI-H157 "Non-Small Cell Lung Carcinoma" "Homo sapiens" P-MEXP-9141 "RPMI 1640" P-MEXP-9142 Aza+TSA NCI-H157_ex3_both cell P-MEXP-9143 NCI-H157_ex3_both total_RNA P-MEXP-9144 5 ug "NCI-H157_ex3_both biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1846_NCIH1573-4 A-MEXP-86 P-MEXP-9146 R1846_NCIH1573-4 R1846_NCIH1573-4.txt NCI-H157 TSA "Non-Small Cell Lung Carcinoma"
+12902T organism_part 74 fresh_sample "Stage II" Adenocarcinoma 12902N "Homo sapiens" male years 12902T organism_part P-MEXP-9143 12902T total_RNA P-MEXP-9144 5 ug "12902T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1595_12902T A-MEXP-86 P-MEXP-9146 R1595_12902T R1595_12902T.txt "Stage II" Adenocarcinoma 12902N
+12902N organism_part 74 fresh_sample normal 12902N "Homo sapiens" male years 12902N organism_part P-MEXP-9143 12902N total_RNA P-MEXP-9144 5 ug "12902N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1594_12902N A-MEXP-86 P-MEXP-9146 R1594_12902N R1594_12902N.txt normal 12902N
+12822T organism_part 66 fresh_sample "Stage I" Adenocarcinoma 12822N "Homo sapiens" male years 12822T organism_part P-MEXP-9143 12822T total_RNA P-MEXP-9144 5 ug "12822T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1591_12822T A-MEXP-86 P-MEXP-9146 R1591_12822T R1591_12822T.txt "Stage I" Adenocarcinoma 12822N
+12822N organism_part 66 fresh_sample normal 12822N "Homo sapiens" male years 12822N organism_part P-MEXP-9143 12822N total_RNA P-MEXP-9144 5 ug "12822N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1590_12822N A-MEXP-86 P-MEXP-9146 R1590_12822N R1590_12822N.txt normal 12822N
+12821T organism_part 68 fresh_sample "Stage II" Adenocarcinoma 12821N "Homo sapiens" male years 12821T organism_part P-MEXP-9143 12821T total_RNA P-MEXP-9144 5 ug "12821T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1583_12821T A-MEXP-86 P-MEXP-9146 R1583_12821T R1583_12821T.txt "Stage II" Adenocarcinoma 12821N
+12821N organism_part 68 fresh_sample normal 12821N "Homo sapiens" male years 12821N organism_part P-MEXP-9143 12821N total_RNA P-MEXP-9144 5 ug "12821N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1582_12821N A-MEXP-86 P-MEXP-9146 R1582_12821N R1582_12821N.txt normal 12821N
+12745T organism_part 71 fresh_sample "Stage I" Adenocarcinoma 12745N "Homo sapiens" male years 12745T organism_part P-MEXP-9143 12745T total_RNA P-MEXP-9144 5 ug "12745T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1581_12745T A-MEXP-86 P-MEXP-9146 R1581_12745T R1581_12745T.txt "Stage I" Adenocarcinoma 12745N
+12745N organism_part 71 fresh_sample normal 12745N "Homo sapiens" male years 12745N organism_part P-MEXP-9143 12745N total_RNA P-MEXP-9144 5 ug "12745N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1580_12745N A-MEXP-86 P-MEXP-9146 R1580_12745N R1580_12745N.txt normal 12745N
+12681T organism_part 75 fresh_sample "Stage I" Adenocarcinoma 12681N "Homo sapiens" male years 12681T organism_part P-MEXP-9143 12681T total_RNA P-MEXP-9144 5 ug "12681T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1579_12681T A-MEXP-86 P-MEXP-9146 R1579_12681T R1579_12681T.txt "Stage I" Adenocarcinoma 12681N
+12681N organism_part 75 fresh_sample normal 12681N "Homo sapiens" male years 12681N organism_part P-MEXP-9143 12681N total_RNA P-MEXP-9144 5 ug "12681N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1578_12681N A-MEXP-86 P-MEXP-9146 R1578_12681N R1578_12681N.txt normal 12681N
+12606T organism_part 66 fresh_sample "Stage I" Adenocarcinoma 12606N "Homo sapiens" male years 12606T organism_part P-MEXP-9143 12606T total_RNA P-MEXP-9144 5 ug "12606T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1569_12606T A-MEXP-86 P-MEXP-9146 R1569_12606T R1569_12606T.txt "Stage I" Adenocarcinoma 12606N
+12606N organism_part 66 fresh_sample normal 12606N "Homo sapiens" male years 12606N organism_part P-MEXP-9143 12606N total_RNA P-MEXP-9144 5 ug "12606N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1568_12606N A-MEXP-86 P-MEXP-9146 R1568_12606N R1568_12606N.txt normal 12606N
+12577T organism_part 71 fresh_sample "Stage I" Adenocarcinoma 12577N "Homo sapiens" male years 12577T organism_part P-MEXP-9143 12577T total_RNA P-MEXP-9144 5 ug "12577T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1567_12577T A-MEXP-86 P-MEXP-9146 R1567_12577T R1567_12577T.txt "Stage I" Adenocarcinoma 12577N
+12577N organism_part 71 fresh_sample normal 12577N "Homo sapiens" male years 12577N organism_part P-MEXP-9143 12577N total_RNA P-MEXP-9144 5 ug "12577N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1566_12577N A-MEXP-86 P-MEXP-9146 R1566_12577N R1566_12577N.txt normal 12577N
+12520T organism_part 49 fresh_sample "Stage I" Adenocarcinoma 12520N "Homo sapiens" male years 12520T organism_part P-MEXP-9143 12520T total_RNA P-MEXP-9144 5 ug "12520T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1563_12520T A-MEXP-86 P-MEXP-9146 R1563_12520T R1563_12520T.txt "Stage I" Adenocarcinoma 12520N
+12520N organism_part 49 fresh_sample normal 12520N "Homo sapiens" male years 12520N organism_part P-MEXP-9143 12520N total_RNA P-MEXP-9144 5 ug "12520N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1562_12520N A-MEXP-86 P-MEXP-9146 R1562_12520N R1562_12520N.txt normal 12520N
+12503T organism_part 75 fresh_sample "Stage I" Adenocarcinoma 12503N "Homo sapiens" male years 12503T organism_part P-MEXP-9143 12503T total_RNA P-MEXP-9144 5 ug "12503T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1559_12503T A-MEXP-86 P-MEXP-9146 R1559_12503T R1559_12503T.txt "Stage I" Adenocarcinoma 12503N
+12503N organism_part 75 fresh_sample normal 12503N "Homo sapiens" male years 12503N organism_part P-MEXP-9143 12503N total_RNA P-MEXP-9144 5 ug "12503N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1558_12503N A-MEXP-86 P-MEXP-9146 R1558_12503N R1558_12503N.txt normal 12503N
+12490T organism_part 57 fresh_sample "Stage III or IV" Adenocarcinoma 12490N "Homo sapiens" male years 12490T organism_part P-MEXP-9143 12490T total_RNA P-MEXP-9144 5 ug "12490T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1547_12490T A-MEXP-86 P-MEXP-9146 R1547_12490T R1547_12490T.txt "Stage III or IV" Adenocarcinoma 12490N
+12490N organism_part 57 fresh_sample normal 12490N "Homo sapiens" male years 12490N organism_part P-MEXP-9143 12490N total_RNA P-MEXP-9144 5 ug "12490N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1546_12490N A-MEXP-86 P-MEXP-9146 R1546_12490N R1546_12490N.txt normal 12490N
+12394T organism_part 51 fresh_sample "Stage I" Adenocarcinoma 12394N "Homo sapiens" female years 12394T organism_part P-MEXP-9143 12394T total_RNA P-MEXP-9144 5 ug "12394T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1539_12394T A-MEXP-86 P-MEXP-9146 R1539_12394T R1539_12394T.txt "Stage I" Adenocarcinoma 12394N
+12394N organism_part 51 fresh_sample normal 12394N "Homo sapiens" female years 12394N organism_part P-MEXP-9143 12394N total_RNA P-MEXP-9144 5 ug "12394N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1538_12394N A-MEXP-86 P-MEXP-9146 R1538_12394N R1538_12394N.txt normal 12394N
+12374T organism_part 68 fresh_sample "Stage I" Adenocarcinoma 12374N "Homo sapiens" male years 12374T organism_part P-MEXP-9143 12374T total_RNA P-MEXP-9144 5 ug "12374T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1537_12374T A-MEXP-86 P-MEXP-9146 R1537_12374T R1537_12374T.txt "Stage I" Adenocarcinoma 12374N
+12374N organism_part 68 fresh_sample normal 12374N "Homo sapiens" male years 12374N organism_part P-MEXP-9143 12374N total_RNA P-MEXP-9144 5 ug "12374N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1536_12374N A-MEXP-86 P-MEXP-9146 R1536_12374N R1536_12374N.txt normal 12374N
+12251T organism_part 63 fresh_sample "Stage I" Adenocarcinoma 12251N "Homo sapiens" female years 12251T organism_part P-MEXP-9143 12251T total_RNA P-MEXP-9144 5 ug "12251T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1527_12251T A-MEXP-86 P-MEXP-9146 R1527_12251T R1527_12251T.txt "Stage I" Adenocarcinoma 12251N
+12251N organism_part 63 fresh_sample normal 12251N "Homo sapiens" female years 12251N organism_part P-MEXP-9143 12251N total_RNA P-MEXP-9144 5 ug "12251N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1526_12251N A-MEXP-86 P-MEXP-9146 R1526_12251N R1526_12251N.txt normal 12251N
+12189T organism_part 61 fresh_sample "Stage I" Adenocarcinoma 12189N "Homo sapiens" male years 12189T organism_part P-MEXP-9143 12189T total_RNA P-MEXP-9144 5 ug "12189T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1525_12189T A-MEXP-86 P-MEXP-9146 R1525_12189T R1525_12189T.txt "Stage I" Adenocarcinoma 12189N
+12189N organism_part 61 fresh_sample normal 12189N "Homo sapiens" male years 12189N organism_part P-MEXP-9143 12189N total_RNA P-MEXP-9144 5 ug "12189N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1524_12189N A-MEXP-86 P-MEXP-9146 R1524_12189N R1524_12189N.txt normal 12189N
+12178T organism_part 63 fresh_sample "Stage II" Adenocarcinoma 12178N "Homo sapiens" male years 12178T organism_part P-MEXP-9143 12178T total_RNA P-MEXP-9144 5 ug "12178T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1521_12178T A-MEXP-86 P-MEXP-9146 R1521_12178T R1521_12178T.txt "Stage II" Adenocarcinoma 12178N
+12178N organism_part 63 fresh_sample normal 12178N "Homo sapiens" male years 12178N organism_part P-MEXP-9143 12178N total_RNA P-MEXP-9144 5 ug "12178N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1520_12178N A-MEXP-86 P-MEXP-9146 R1520_12178N R1520_12178N.txt normal 12178N
+12170T organism_part 50 fresh_sample "Stage I" Adenocarcinoma 12170N "Homo sapiens" female years 12170T organism_part P-MEXP-9143 12170T total_RNA P-MEXP-9144 5 ug "12170T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1519_12170T A-MEXP-86 P-MEXP-9146 R1519_12170T R1519_12170T.txt "Stage I" Adenocarcinoma 12170N
+12170N organism_part 50 fresh_sample normal 12170N "Homo sapiens" female years 12170N organism_part P-MEXP-9143 12170N total_RNA P-MEXP-9144 5 ug "12170N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1518_12170N A-MEXP-86 P-MEXP-9146 R1518_12170N R1518_12170N.txt normal 12170N
+12167T organism_part 71 fresh_sample "Stage I" Adenocarcinoma 12167N "Homo sapiens" male years 12167T organism_part P-MEXP-9143 12167T total_RNA P-MEXP-9144 5 ug "12167T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1517_12167T A-MEXP-86 P-MEXP-9146 R1517_12167T R1517_12167T.txt "Stage I" Adenocarcinoma 12167N
+12167N organism_part 71 fresh_sample normal 12167N "Homo sapiens" male years 12167N organism_part P-MEXP-9143 12167N total_RNA P-MEXP-9144 5 ug "12167N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1516_12167N A-MEXP-86 P-MEXP-9146 R1516_12167N R1516_12167N.txt normal 12167N
+12165T organism_part 38 fresh_sample "Stage III or IV" Adenocarcinoma 12165N "Homo sapiens" female years 12165T organism_part P-MEXP-9143 12165T total_RNA P-MEXP-9144 5 ug "12165T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1778_12165T A-MEXP-86 P-MEXP-9146 R1778_12165T R1778_12165T.txt "Stage III or IV" Adenocarcinoma 12165N
+12165N organism_part 38 fresh_sample normal 12165N "Homo sapiens" female years 12165N organism_part P-MEXP-9143 12165N total_RNA P-MEXP-9144 5 ug "12165N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1777_12165N A-MEXP-86 P-MEXP-9146 R1777_12165N R1777_12165N.txt normal 12165N
+12118T organism_part 57 fresh_sample "Stage I" Adenocarcinoma 12118N "Homo sapiens" male years 12118T organism_part P-MEXP-9143 12118T total_RNA P-MEXP-9144 5 ug "12118T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1513_12118T A-MEXP-86 P-MEXP-9146 R1513_12118T R1513_12118T.txt "Stage I" Adenocarcinoma 12118N
+12118N organism_part 57 fresh_sample normal 12118N "Homo sapiens" male years 12118N organism_part P-MEXP-9143 12118N total_RNA P-MEXP-9144 5 ug "12118N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1512_12118N A-MEXP-86 P-MEXP-9146 R1512_12118N R1512_12118N.txt normal 12118N
+12084T organism_part 75 fresh_sample "Stage I" Adenocarcinoma 12084N "Homo sapiens" male years 12084T organism_part P-MEXP-9143 12084T total_RNA P-MEXP-9144 5 ug "12084T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1507_12084T A-MEXP-86 P-MEXP-9146 R1507_12084T R1507_12084T.txt "Stage I" Adenocarcinoma 12084N
+12084N organism_part 75 fresh_sample normal 12084N "Homo sapiens" male years 12084N organism_part P-MEXP-9143 12084N total_RNA P-MEXP-9144 5 ug "12084N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1500_12084N A-MEXP-86 P-MEXP-9146 R1500_12084N R1500_12084N.txt normal 12084N
+12012T organism_part 61 fresh_sample "Stage I" Adenocarcinoma 12012N "Homo sapiens" male years 12012T organism_part P-MEXP-9143 12012T total_RNA P-MEXP-9144 5 ug "12012T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1099_12012T A-MEXP-86 P-MEXP-9146 R1099_12012T R1099_12012T.txt "Stage I" Adenocarcinoma 12012N
+12012N organism_part 61 fresh_sample normal 12012N "Homo sapiens" male years 12012N organism_part P-MEXP-9143 12012N total_RNA P-MEXP-9144 5 ug "12012N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1098_12012N A-MEXP-86 P-MEXP-9146 R1098_12012N R1098_12012N.txt normal 12012N
+11922T organism_part 80 fresh_sample "Stage III or IV" Adenocarcinoma 11922N "Homo sapiens" male years 11922T organism_part P-MEXP-9143 11922T total_RNA P-MEXP-9144 5 ug "11922T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1091_11922T A-MEXP-86 P-MEXP-9146 R1091_11922T R1091_11922T.txt "Stage III or IV" Adenocarcinoma 11922N
+11922N organism_part 80 fresh_sample normal 11922N "Homo sapiens" male years 11922N organism_part P-MEXP-9143 11922N total_RNA P-MEXP-9144 5 ug "11922N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1090_11922N A-MEXP-86 P-MEXP-9146 R1090_11922N R1090_11922N.txt normal 11922N
+11908T organism_part 57 fresh_sample "Stage I" Adenocarcinoma 11908N "Homo sapiens" female years 11908T organism_part P-MEXP-9143 11908T total_RNA P-MEXP-9144 5 ug "11908T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1776_11908T A-MEXP-86 P-MEXP-9146 R1776_11908T R1776_11908T.txt "Stage I" Adenocarcinoma 11908N
+11908N organism_part 57 fresh_sample normal 11908N "Homo sapiens" female years 11908N organism_part P-MEXP-9143 11908N total_RNA P-MEXP-9144 5 ug "11908N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1775_11908N A-MEXP-86 P-MEXP-9146 R1775_11908N R1775_11908N.txt normal 11908N
+11898T organism_part 84 fresh_sample "Stage III or IV" Adenocarcinoma 11898N "Homo sapiens" female years 11898T organism_part P-MEXP-9143 11898T total_RNA P-MEXP-9144 5 ug "11898T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1089_11898T A-MEXP-86 P-MEXP-9146 R1089_11898T R1089_11898T.txt "Stage III or IV" Adenocarcinoma 11898N
+11898N organism_part 84 fresh_sample normal 11898N "Homo sapiens" female years 11898N organism_part P-MEXP-9143 11898N total_RNA P-MEXP-9144 5 ug "11898N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1088_11898N A-MEXP-86 P-MEXP-9146 R1088_11898N R1088_11898N.txt normal 11898N
+11891T organism_part 69 fresh_sample "Stage III or IV" Adenocarcinoma 11891N "Homo sapiens" female years 11891T organism_part P-MEXP-9143 11891T total_RNA P-MEXP-9144 5 ug "11891T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1087_11891T A-MEXP-86 P-MEXP-9146 R1087_11891T R1087_11891T.txt "Stage III or IV" Adenocarcinoma 11891N
+11891N organism_part 69 fresh_sample normal 11891N "Homo sapiens" female years 11891N organism_part P-MEXP-9143 11891N total_RNA P-MEXP-9144 5 ug "11891N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1085_11891N A-MEXP-86 P-MEXP-9146 R1085_11891N R1085_11891N.txt normal 11891N
+11885T organism_part 65 fresh_sample "Stage I" Adenocarcinoma 11885N "Homo sapiens" male years 11885T organism_part P-MEXP-9143 11885T total_RNA P-MEXP-9144 5 ug "11885T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1084_11885T A-MEXP-86 P-MEXP-9146 R1084_11885T R1084_11885T.txt "Stage I" Adenocarcinoma 11885N
+11885N organism_part 65 fresh_sample normal 11885N "Homo sapiens" male years 11885N organism_part P-MEXP-9143 11885N total_RNA P-MEXP-9144 5 ug "11885N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1083_11885N A-MEXP-86 P-MEXP-9146 R1083_11885N R1083_11885N.txt normal 11885N
+11823T organism_part 67 fresh_sample "Stage I" Adenocarcinoma 11823N "Homo sapiens" male years 11823T organism_part P-MEXP-9143 11823T total_RNA P-MEXP-9144 5 ug "11823T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R896_11823T A-MEXP-86 P-MEXP-9146 R896_11823T R896_11823T.txt "Stage I" Adenocarcinoma 11823N
+11823N organism_part 67 fresh_sample normal 11823N "Homo sapiens" male years 11823N organism_part P-MEXP-9143 11823N total_RNA P-MEXP-9144 5 ug "11823N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R895_11823N A-MEXP-86 P-MEXP-9146 R895_11823N R895_11823N.txt normal 11823N
+11812T organism_part 67 fresh_sample "Stage III or IV" Adenocarcinoma 11812N "Homo sapiens" male years 11812T organism_part P-MEXP-9143 11812T total_RNA P-MEXP-9144 5 ug "11812T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R894_11812T A-MEXP-86 P-MEXP-9146 R894_11812T R894_11812T.txt "Stage III or IV" Adenocarcinoma 11812N
+11812N organism_part 67 fresh_sample normal 11812N "Homo sapiens" male years 11812N organism_part P-MEXP-9143 11812N total_RNA P-MEXP-9144 5 ug "11812N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R893_11812N A-MEXP-86 P-MEXP-9146 R893_11812N R893_11812N.txt normal 11812N
+11807T organism_part 73 fresh_sample "Stage I" Adenocarcinoma 11807N "Homo sapiens" female years 11807T organism_part P-MEXP-9143 11807T total_RNA P-MEXP-9144 5 ug "11807T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R890_11807T A-MEXP-86 P-MEXP-9146 R890_11807T R890_11807T.txt "Stage I" Adenocarcinoma 11807N
+11807N organism_part 73 fresh_sample normal 11807N "Homo sapiens" female years 11807N organism_part P-MEXP-9143 11807N total_RNA P-MEXP-9144 5 ug "11807N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R889_11807N A-MEXP-86 P-MEXP-9146 R889_11807N R889_11807N.txt normal 11807N
+11777T organism_part 72 fresh_sample "Stage I" Adenocarcinoma 11777N "Homo sapiens" female years 11777T organism_part P-MEXP-9143 11777T total_RNA P-MEXP-9144 5 ug "11777T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R888_11777T A-MEXP-86 P-MEXP-9146 R888_11777T R888_11777T.txt "Stage I" Adenocarcinoma 11777N
+11777N organism_part 72 fresh_sample normal 11777N "Homo sapiens" female years 11777N organism_part P-MEXP-9143 11777N total_RNA P-MEXP-9144 5 ug "11777N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R887_11777N A-MEXP-86 P-MEXP-9146 R887_11777N R887_11777N.txt normal 11777N
+11671T organism_part 53 fresh_sample "Stage I" Adenocarcinoma 11671N "Homo sapiens" male years 11671T organism_part P-MEXP-9143 11671T total_RNA P-MEXP-9144 5 ug "11671T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R882_11671T A-MEXP-86 P-MEXP-9146 R882_11671T R882_11671T.txt "Stage I" Adenocarcinoma 11671N
+11671N organism_part 53 fresh_sample normal 11671N "Homo sapiens" male years 11671N organism_part P-MEXP-9143 11671N total_RNA P-MEXP-9144 5 ug "11671N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R881_11671N A-MEXP-86 P-MEXP-9146 R881_11671N R881_11671N.txt normal 11671N
+11669T organism_part 67 fresh_sample "Stage I" Adenocarcinoma 11669N "Homo sapiens" female years 11669T organism_part P-MEXP-9143 11669T total_RNA P-MEXP-9144 5 ug "11669T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R880_11669T A-MEXP-86 P-MEXP-9146 R880_11669T R880_11669T.txt "Stage I" Adenocarcinoma 11669N
+11669N organism_part 67 fresh_sample normal 11669N "Homo sapiens" female years 11669N organism_part P-MEXP-9143 11669N total_RNA P-MEXP-9144 5 ug "11669N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R879_11669N A-MEXP-86 P-MEXP-9146 R879_11669N R879_11669N.txt normal 11669N
+11616T organism_part 65 fresh_sample "Stage III or IV" Adenocarcinoma 11616N "Homo sapiens" male years 11616T organism_part P-MEXP-9143 11616T total_RNA P-MEXP-9144 5 ug "11616T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R876_11616T A-MEXP-86 P-MEXP-9146 R876_11616T R876_11616T.txt "Stage III or IV" Adenocarcinoma 11616N
+11616N organism_part 65 fresh_sample normal 11616N "Homo sapiens" male years 11616N organism_part P-MEXP-9143 11616N total_RNA P-MEXP-9144 5 ug "11616N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R875_11616N A-MEXP-86 P-MEXP-9146 R875_11616N R875_11616N.txt normal 11616N
+11547T organism_part 74 fresh_sample "Stage III or IV" Adenocarcinoma 11547N "Homo sapiens" female years 11547T organism_part P-MEXP-9143 11547T total_RNA P-MEXP-9144 5 ug "11547T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1074_11547T A-MEXP-86 P-MEXP-9146 R1074_11547T R1074_11547T.txt "Stage III or IV" Adenocarcinoma 11547N
+11547N organism_part 74 fresh_sample normal 11547N "Homo sapiens" female years 11547N organism_part P-MEXP-9143 11547N total_RNA P-MEXP-9144 5 ug "11547N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1073_11547N A-MEXP-86 P-MEXP-9146 R1073_11547N R1073_11547N.txt normal 11547N
+11541T organism_part 70 fresh_sample "Stage I" Adenocarcinoma 11541N "Homo sapiens" male years 11541T organism_part P-MEXP-9143 11541T total_RNA P-MEXP-9144 5 ug "11541T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1072_11541T A-MEXP-86 P-MEXP-9146 R1072_11541T R1072_11541T.txt "Stage I" Adenocarcinoma 11541N
+11541N organism_part 70 fresh_sample normal 11541N "Homo sapiens" male years 11541N organism_part P-MEXP-9143 11541N total_RNA P-MEXP-9144 5 ug "11541N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1071_11541N A-MEXP-86 P-MEXP-9146 R1071_11541N R1071_11541N.txt normal 11541N
+11502T organism_part 61 fresh_sample "Stage III or IV" Adenocarcinoma 11502N "Homo sapiens" female years 11502T organism_part P-MEXP-9143 11502T total_RNA P-MEXP-9144 5 ug "11502T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1070_11502T A-MEXP-86 P-MEXP-9146 R1070_11502T R1070_11502T.txt "Stage III or IV" Adenocarcinoma 11502N
+11502N organism_part 61 fresh_sample normal 11502N "Homo sapiens" female years 11502N organism_part P-MEXP-9143 11502N total_RNA P-MEXP-9144 5 ug "11502N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1069_11502N A-MEXP-86 P-MEXP-9146 R1069_11502N R1069_11502N.txt normal 11502N
+11447T organism_part 52 fresh_sample "Stage I" Adenocarcinoma 11447N "Homo sapiens" female years 11447T organism_part P-MEXP-9143 11447T total_RNA P-MEXP-9144 5 ug "11447T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1068_11447T A-MEXP-86 P-MEXP-9146 R1068_11447T R1068_11447T.txt "Stage I" Adenocarcinoma 11447N
+11447N organism_part 52 fresh_sample normal 11447N "Homo sapiens" female years 11447N organism_part P-MEXP-9143 11447N total_RNA P-MEXP-9144 5 ug "11447N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1067_11447N A-MEXP-86 P-MEXP-9146 R1067_11447N R1067_11447N.txt normal 11447N
+11404T organism_part 38 fresh_sample "Stage I" Adenocarcinoma 11404N "Homo sapiens" female years 11404T organism_part P-MEXP-9143 11404T total_RNA P-MEXP-9144 5 ug "11404T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R900_11404T A-MEXP-86 P-MEXP-9146 R900_11404T R900_11404T.txt "Stage I" Adenocarcinoma 11404N
+11404N organism_part 38 fresh_sample normal 11404N "Homo sapiens" female years 11404N organism_part P-MEXP-9143 11404N total_RNA P-MEXP-9144 5 ug "11404N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R899_11404N A-MEXP-86 P-MEXP-9146 R899_11404N R899_11404N.txt normal 11404N
+11343T organism_part 68 fresh_sample "Stage II" Adenocarcinoma 11343N "Homo sapiens" male years 11343T organism_part P-MEXP-9143 11343T total_RNA P-MEXP-9144 5 ug "11343T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1050_11343T A-MEXP-86 P-MEXP-9146 R1050_11343T R1050_11343T.txt "Stage II" Adenocarcinoma 11343N
+11343N organism_part 68 fresh_sample normal 11343N "Homo sapiens" male years 11343N organism_part P-MEXP-9143 11343N total_RNA P-MEXP-9144 5 ug "11343N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1049_11343N A-MEXP-86 P-MEXP-9146 R1049_11343N R1049_11343N.txt normal 11343N
+11231T organism_part 56 fresh_sample "Stage III or IV" Adenocarcinoma 11231N "Homo sapiens" male years 11231T organism_part P-MEXP-9143 11231T total_RNA P-MEXP-9144 5 ug "11231T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1048_11231T A-MEXP-86 P-MEXP-9146 R1048_11231T R1048_11231T.txt "Stage III or IV" Adenocarcinoma 11231N
+11231N organism_part 56 fresh_sample normal 11231N "Homo sapiens" male years 11231N organism_part P-MEXP-9143 11231N total_RNA P-MEXP-9144 5 ug "11231N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1047_11231N A-MEXP-86 P-MEXP-9146 R1047_11231N R1047_11231N.txt normal 11231N
+11191T organism_part 47 fresh_sample "Stage I" Adenocarcinoma 11191N "Homo sapiens" male years 11191T organism_part P-MEXP-9143 11191T total_RNA P-MEXP-9144 5 ug "11191T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1042_11191T A-MEXP-86 P-MEXP-9146 R1042_11191T R1042_11191T.txt "Stage I" Adenocarcinoma 11191N
+11191N organism_part 47 fresh_sample normal 11191N "Homo sapiens" male years 11191N organism_part P-MEXP-9143 11191N total_RNA P-MEXP-9144 5 ug "11191N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1041_11191N A-MEXP-86 P-MEXP-9146 R1041_11191N R1041_11191N.txt normal 11191N
+11184T organism_part 53 fresh_sample "Stage I" Adenocarcinoma 11184N "Homo sapiens" female years 11184T organism_part P-MEXP-9143 11184T total_RNA P-MEXP-9144 5 ug "11184T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1040_11184T A-MEXP-86 P-MEXP-9146 R1040_11184T R1040_11184T.txt "Stage I" Adenocarcinoma 11184N
+11184N organism_part 53 fresh_sample normal 11184N "Homo sapiens" female years 11184N organism_part P-MEXP-9143 11184N total_RNA P-MEXP-9144 5 ug "11184N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1039_11184N A-MEXP-86 P-MEXP-9146 R1039_11184N R1039_11184N.txt normal 11184N
+11139T organism_part 49 fresh_sample "Stage I" Adenocarcinoma 11139N "Homo sapiens" male years 11139T organism_part P-MEXP-9143 11139T total_RNA P-MEXP-9144 5 ug "11139T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1036_11139T A-MEXP-86 P-MEXP-9146 R1036_11139T R1036_11139T.txt "Stage I" Adenocarcinoma 11139N
+11139N organism_part 49 fresh_sample normal 11139N "Homo sapiens" male years 11139N organism_part P-MEXP-9143 11139N total_RNA P-MEXP-9144 5 ug "11139N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1035_11139N A-MEXP-86 P-MEXP-9146 R1035_11139N R1035_11139N.txt normal 11139N
+11019T organism_part 59 fresh_sample "Stage III or IV" Adenocarcinoma 11019N "Homo sapiens" male years 11019T organism_part P-MEXP-9143 11019T total_RNA P-MEXP-9144 5 ug "11019T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1026_11019T A-MEXP-86 P-MEXP-9146 R1026_11019T R1026_11019T.txt "Stage III or IV" Adenocarcinoma 11019N
+11019N organism_part 59 fresh_sample normal 11019N "Homo sapiens" male years 11019N organism_part P-MEXP-9143 11019N total_RNA P-MEXP-9144 5 ug "11019N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1025_11019N A-MEXP-86 P-MEXP-9146 R1025_11019N R1025_11019N.txt normal 11019N
+11005T organism_part 76 fresh_sample "Stage I" Adenocarcinoma 11005N "Homo sapiens" female years 11005T organism_part P-MEXP-9143 11005T total_RNA P-MEXP-9144 5 ug "11005T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1020_11005T A-MEXP-86 P-MEXP-9146 R1020_11005T R1020_11005T.txt "Stage I" Adenocarcinoma 11005N
+11005N organism_part 76 fresh_sample normal 11005N "Homo sapiens" female years 11005N organism_part P-MEXP-9143 11005N total_RNA P-MEXP-9144 5 ug "11005N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1019_11005N A-MEXP-86 P-MEXP-9146 R1019_11005N R1019_11005N.txt normal 11005N
+10622T organism_part 57 fresh_sample "Stage I" Adenocarcinoma 10622N "Homo sapiens" male years 10622T organism_part P-MEXP-9143 10622T total_RNA P-MEXP-9144 5 ug "10622T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R991_10622T A-MEXP-86 P-MEXP-9146 R991_10622T R991_10622T.txt "Stage I" Adenocarcinoma 10622N
+10622N organism_part 57 fresh_sample normal 10622N "Homo sapiens" male years 10622N organism_part P-MEXP-9143 10622N total_RNA P-MEXP-9144 5 ug "10622N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R990_10622N A-MEXP-86 P-MEXP-9146 R990_10622N R990_10622N.txt normal 10622N
+10605T organism_part 62 fresh_sample "Stage I" Adenocarcinoma 10605N "Homo sapiens" male years 10605T organism_part P-MEXP-9143 10605T total_RNA P-MEXP-9144 5 ug "10605T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R989_10605T A-MEXP-86 P-MEXP-9146 R989_10605T R989_10605T.txt "Stage I" Adenocarcinoma 10605N
+10605N organism_part 62 fresh_sample normal 10605N "Homo sapiens" male years 10605N organism_part P-MEXP-9143 10605N total_RNA P-MEXP-9144 5 ug "10605N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R988_10605N A-MEXP-86 P-MEXP-9146 R988_10605N R988_10605N.txt normal 10605N
+10455T organism_part 66 fresh_sample "Stage III or IV" Adenocarcinoma 10455N "Homo sapiens" female years 10455T organism_part P-MEXP-9143 10455T total_RNA P-MEXP-9144 5 ug "10455T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R983_10455T A-MEXP-86 P-MEXP-9146 R983_10455T R983_10455T.txt "Stage III or IV" Adenocarcinoma 10455N
+10455N organism_part 66 fresh_sample normal 10455N "Homo sapiens" female years 10455N organism_part P-MEXP-9143 10455N total_RNA P-MEXP-9144 5 ug "10455N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R982_10455N A-MEXP-86 P-MEXP-9146 R982_10455N R982_10455N.txt normal 10455N
+10419T organism_part 72 fresh_sample "Stage I" Adenocarcinoma 10419N "Homo sapiens" female years 10419T organism_part P-MEXP-9143 10419T total_RNA P-MEXP-9144 5 ug "10419T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R977_10419T A-MEXP-86 P-MEXP-9146 R977_10419T R977_10419T.txt "Stage I" Adenocarcinoma 10419N
+10419N organism_part 72 fresh_sample normal 10419N "Homo sapiens" female years 10419N organism_part P-MEXP-9143 10419N total_RNA P-MEXP-9144 5 ug "10419N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R976_10419N A-MEXP-86 P-MEXP-9146 R976_10419N R976_10419N.txt normal 10419N
+10213T organism_part 72 fresh_sample "Stage III or IV" Adenocarcinoma 10213N "Homo sapiens" male years 10213T organism_part P-MEXP-9143 10213T total_RNA P-MEXP-9144 5 ug "10213T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R868_10213T A-MEXP-86 P-MEXP-9146 R868_10213T R868_10213T.txt "Stage III or IV" Adenocarcinoma 10213N
+10213N organism_part 72 fresh_sample normal 10213N "Homo sapiens" male years 10213N organism_part P-MEXP-9143 10213N total_RNA P-MEXP-9144 5 ug "10213N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R867_10213N A-MEXP-86 P-MEXP-9146 R867_10213N R867_10213N.txt normal 10213N
+10210T organism_part 79 fresh_sample "Stage I" Adenocarcinoma 10210N "Homo sapiens" male years 10210T organism_part P-MEXP-9143 10210T total_RNA P-MEXP-9144 5 ug "10210T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R866_10210T A-MEXP-86 P-MEXP-9146 R866_10210T R866_10210T.txt "Stage I" Adenocarcinoma 10210N
+10210N organism_part 79 fresh_sample normal 10210N "Homo sapiens" male years 10210N organism_part P-MEXP-9143 10210N total_RNA P-MEXP-9144 5 ug "10210N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R865_10210N A-MEXP-86 P-MEXP-9146 R865_10210N R865_10210N.txt normal 10210N
+10018T organism_part 63 fresh_sample "Stage III or IV" Adenocarcinoma 10018N "Homo sapiens" female years 10018T organism_part P-MEXP-9143 10018T total_RNA P-MEXP-9144 5 ug "10018T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1749_10018T A-MEXP-86 P-MEXP-9146 R1749_10018T R1749_10018T.txt "Stage III or IV" Adenocarcinoma 10018N
+10018N organism_part 63 fresh_sample normal 10018N "Homo sapiens" female years 10018N organism_part P-MEXP-9143 10018N total_RNA P-MEXP-9144 5 ug "10018N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1748_10018N A-MEXP-86 P-MEXP-9146 R1748_10018N R1748_10018N.txt normal 10018N
+1822T organism_part 61 fresh_sample "Stage III or IV" Adenocarcinoma 1822N "Homo sapiens" male years 1822T organism_part P-MEXP-9143 1822T total_RNA P-MEXP-9144 5 ug "1822T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1747_1822T A-MEXP-86 P-MEXP-9146 R1747_1822T R1747_1822T.txt "Stage III or IV" Adenocarcinoma 1822N
+1822N organism_part 61 fresh_sample normal 1822N "Homo sapiens" male years 1822N organism_part P-MEXP-9143 1822N total_RNA P-MEXP-9144 5 ug "1822N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1746_1822N A-MEXP-86 P-MEXP-9146 R1746_1822N R1746_1822N.txt normal 1822N
+1818T organism_part 55 fresh_sample "Stage I" Adenocarcinoma 1818N "Homo sapiens" male years 1818T organism_part P-MEXP-9143 1818T total_RNA P-MEXP-9144 5 ug "1818T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1743_1818T A-MEXP-86 P-MEXP-9146 R1743_1818T R1743_1818T.txt "Stage I" Adenocarcinoma 1818N
+1818N organism_part 55 fresh_sample normal 1818N "Homo sapiens" male years 1818N organism_part P-MEXP-9143 1818N total_RNA P-MEXP-9144 5 ug "1818N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1742_1818N A-MEXP-86 P-MEXP-9146 R1742_1818N R1742_1818N.txt normal 1818N
+1729T organism_part 73 fresh_sample "Stage I" Adenocarcinoma 1729N "Homo sapiens" female years 1729T organism_part P-MEXP-9143 1729T total_RNA P-MEXP-9144 5 ug "1729T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1731_1729T A-MEXP-86 P-MEXP-9146 R1731_1729T R1731_1729T.txt "Stage I" Adenocarcinoma 1729N
+1729N organism_part 73 fresh_sample normal 1729N "Homo sapiens" female years 1729N organism_part P-MEXP-9143 1729N total_RNA P-MEXP-9144 5 ug "1729N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1730_1729N A-MEXP-86 P-MEXP-9146 R1730_1729N R1730_1729N.txt normal 1729N
+1718T organism_part 58 fresh_sample "Stage II" Adenocarcinoma 1718N "Homo sapiens" female years 1718T organism_part P-MEXP-9143 1718T total_RNA P-MEXP-9144 5 ug "1718T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1729_1718T A-MEXP-86 P-MEXP-9146 R1729_1718T R1729_1718T.txt "Stage II" Adenocarcinoma 1718N
+1718N organism_part 58 fresh_sample normal 1718N "Homo sapiens" female years 1718N organism_part P-MEXP-9143 1718N total_RNA P-MEXP-9144 5 ug "1718N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1728_1718N A-MEXP-86 P-MEXP-9146 R1728_1718N R1728_1718N.txt normal 1718N
+1610T organism_part 69 fresh_sample "Stage II" Adenocarcinoma 1610N "Homo sapiens" female years 1610T organism_part P-MEXP-9143 1610T total_RNA P-MEXP-9144 5 ug "1610T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1723_1610T A-MEXP-86 P-MEXP-9146 R1723_1610T R1723_1610T.txt "Stage II" Adenocarcinoma 1610N
+1610N organism_part 69 fresh_sample normal 1610N "Homo sapiens" female years 1610N organism_part P-MEXP-9143 1610N total_RNA P-MEXP-9144 5 ug "1610N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1722_1610N A-MEXP-86 P-MEXP-9146 R1722_1610N R1722_1610N.txt normal 1610N
+1604T organism_part 59 fresh_sample "Stage I" Adenocarcinoma 1604N "Homo sapiens" female years 1604T organism_part P-MEXP-9143 1604T total_RNA P-MEXP-9144 5 ug "1604T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1721_1604T A-MEXP-86 P-MEXP-9146 R1721_1604T R1721_1604T.txt "Stage I" Adenocarcinoma 1604N
+1604N organism_part 59 fresh_sample normal 1604N "Homo sapiens" female years 1604N organism_part P-MEXP-9143 1604N total_RNA P-MEXP-9144 5 ug "1604N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1720_1604N A-MEXP-86 P-MEXP-9146 R1720_1604N R1720_1604N.txt normal 1604N
+1570T organism_part 70 fresh_sample "Stage III or IV" Adenocarcinoma 1570N "Homo sapiens" male years 1570T organism_part P-MEXP-9143 1570T total_RNA P-MEXP-9144 5 ug "1570T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1719_1570T A-MEXP-86 P-MEXP-9146 R1719_1570T R1719_1570T.txt "Stage III or IV" Adenocarcinoma 1570N
+1570N organism_part 70 fresh_sample normal 1570N "Homo sapiens" male years 1570N organism_part P-MEXP-9143 1570N total_RNA P-MEXP-9144 5 ug "1570N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1718_1570N A-MEXP-86 P-MEXP-9146 R1718_1570N R1718_1570N.txt normal 1570N
+1545T organism_part 69 fresh_sample "Stage I" Adenocarcinoma 1545N "Homo sapiens" female years 1545T organism_part P-MEXP-9143 1545T total_RNA P-MEXP-9144 5 ug "1545T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1715_1545T A-MEXP-86 P-MEXP-9146 R1715_1545T R1715_1545T.txt "Stage I" Adenocarcinoma 1545N
+1545N organism_part 69 fresh_sample normal 1545N "Homo sapiens" female years 1545N organism_part P-MEXP-9143 1545N total_RNA P-MEXP-9144 5 ug "1545N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1714_1545N A-MEXP-86 P-MEXP-9146 R1714_1545N R1714_1545N.txt normal 1545N
+1532T organism_part 62 fresh_sample "Stage II" Adenocarcinoma 1532N "Homo sapiens" male years 1532T organism_part P-MEXP-9143 1532T total_RNA P-MEXP-9144 5 ug "1532T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1711_1532T A-MEXP-86 P-MEXP-9146 R1711_1532T R1711_1532T.txt "Stage II" Adenocarcinoma 1532N
+1532N organism_part 62 fresh_sample normal 1532N "Homo sapiens" male years 1532N organism_part P-MEXP-9143 1532N total_RNA P-MEXP-9144 5 ug "1532N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1710_1532N A-MEXP-86 P-MEXP-9146 R1710_1532N R1710_1532N.txt normal 1532N
+1498T organism_part 67 fresh_sample "Stage I" Adenocarcinoma 1498N "Homo sapiens" female years 1498T organism_part P-MEXP-9143 1498T total_RNA P-MEXP-9144 5 ug "1498T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1707_1498T A-MEXP-86 P-MEXP-9146 R1707_1498T R1707_1498T.txt "Stage I" Adenocarcinoma 1498N
+1498N organism_part 67 fresh_sample normal 1498N "Homo sapiens" female years 1498N organism_part P-MEXP-9143 1498N total_RNA P-MEXP-9144 5 ug "1498N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1600_1498N A-MEXP-86 P-MEXP-9146 R1600_1498N R1600_1498N.txt normal 1498N
+1449T organism_part 79 fresh_sample "Stage II" Adenocarcinoma 1449N "Homo sapiens" female years 1449T organism_part P-MEXP-9143 1449T total_RNA P-MEXP-9144 5 ug "1449T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1599_1449T A-MEXP-86 P-MEXP-9146 R1599_1449T R1599_1449T.txt "Stage II" Adenocarcinoma 1449N
+1449N organism_part 79 fresh_sample normal 1449N "Homo sapiens" female years 1449N organism_part P-MEXP-9143 1449N total_RNA P-MEXP-9144 5 ug "1449N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1598_1449N A-MEXP-86 P-MEXP-9146 R1598_1449N R1598_1449N.txt normal 1449N
+1249T organism_part 54 fresh_sample "Stage I" Adenocarcinoma 1249N "Homo sapiens" male years 1249T organism_part P-MEXP-9143 1249T total_RNA P-MEXP-9144 5 ug "1249T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1557_1249T A-MEXP-86 P-MEXP-9146 R1557_1249T R1557_1249T.txt "Stage I" Adenocarcinoma 1249N
+1249N organism_part 54 fresh_sample normal 1249N "Homo sapiens" male years 1249N organism_part P-MEXP-9143 1249N total_RNA P-MEXP-9144 5 ug "1249N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1550_1249N A-MEXP-86 P-MEXP-9146 R1550_1249N R1550_1249N.txt normal 1249N
+1180T organism_part 76 fresh_sample "Stage I" Adenocarcinoma 1180N "Homo sapiens" male years 1180T organism_part P-MEXP-9143 1180T total_RNA P-MEXP-9144 5 ug "1180T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R892_1180T A-MEXP-86 P-MEXP-9146 R892_1180T R892_1180T.txt "Stage I" Adenocarcinoma 1180N
+1180N organism_part 76 fresh_sample normal 1180N "Homo sapiens" male years 1180N organism_part P-MEXP-9143 1180N total_RNA P-MEXP-9144 5 ug "1180N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R891_1180N A-MEXP-86 P-MEXP-9146 R891_1180N R891_1180N.txt normal 1180N
+12979T organism_part 52 fresh_sample "Stage I" "Squamous Cell Carcinoma" 12979N "Homo sapiens" male years 12979T organism_part P-MEXP-9143 12979T total_RNA P-MEXP-9144 5 ug "12979T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1597_12979T A-MEXP-86 P-MEXP-9146 R1597_12979T R1597_12979T.txt "Stage I" "Squamous Cell Carcinoma" 12979N
+12979N organism_part 52 fresh_sample normal 12979N "Homo sapiens" male years 12979N organism_part P-MEXP-9143 12979N total_RNA P-MEXP-9144 5 ug "12979N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1596_12979N A-MEXP-86 P-MEXP-9146 R1596_12979N R1596_12979N.txt normal 12979N
+12281T organism_part 59 fresh_sample "Stage I" "Squamous Cell Carcinoma" 12281N "Homo sapiens" female years 12281T organism_part P-MEXP-9143 12281T total_RNA P-MEXP-9144 5 ug "12281T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1531_12281T A-MEXP-86 P-MEXP-9146 R1531_12281T R1531_12281T.txt "Stage I" "Squamous Cell Carcinoma" 12281N
+12281N organism_part 59 fresh_sample normal 12281N "Homo sapiens" female years 12281N organism_part P-MEXP-9143 12281N total_RNA P-MEXP-9144 5 ug "12281N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1530_12281N A-MEXP-86 P-MEXP-9146 R1530_12281N R1530_12281N.txt normal 12281N
+12187T organism_part 72 fresh_sample "Stage I" "Squamous Cell Carcinoma" 12187N "Homo sapiens" female years 12187T organism_part P-MEXP-9143 12187T total_RNA P-MEXP-9144 5 ug "12187T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1523_12187T A-MEXP-86 P-MEXP-9146 R1523_12187T R1523_12187T.txt "Stage I" "Squamous Cell Carcinoma" 12187N
+12187N organism_part 72 fresh_sample normal 12187N "Homo sapiens" female years 12187N organism_part P-MEXP-9143 12187N total_RNA P-MEXP-9144 5 ug "12187N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1522_12187N A-MEXP-86 P-MEXP-9146 R1522_12187N R1522_12187N.txt normal 12187N
+12147T organism_part 56 fresh_sample "Stage I" "Squamous Cell Carcinoma" 12147N "Homo sapiens" male years 12147T organism_part P-MEXP-9143 12147T total_RNA P-MEXP-9144 5 ug "12147T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1515_12147T A-MEXP-86 P-MEXP-9146 R1515_12147T R1515_12147T.txt "Stage I" "Squamous Cell Carcinoma" 12147N
+12147N organism_part 56 fresh_sample normal 12147N "Homo sapiens" male years 12147N organism_part P-MEXP-9143 12147N total_RNA P-MEXP-9144 5 ug "12147N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1514_12147N A-MEXP-86 P-MEXP-9146 R1514_12147N R1514_12147N.txt normal 12147N
+12104T organism_part 55 fresh_sample "Stage II" "Squamous Cell Carcinoma" 12104N "Homo sapiens" female years 12104T organism_part P-MEXP-9143 12104T total_RNA P-MEXP-9144 5 ug "12104T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1511_12104T A-MEXP-86 P-MEXP-9146 R1511_12104T R1511_12104T.txt "Stage II" "Squamous Cell Carcinoma" 12104N
+12104N organism_part 55 fresh_sample normal 12104N "Homo sapiens" female years 12104N organism_part P-MEXP-9143 12104N total_RNA P-MEXP-9144 5 ug "12104N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1510_12104N A-MEXP-86 P-MEXP-9146 R1510_12104N R1510_12104N.txt normal 12104N
+12027T organism_part 73 fresh_sample "Stage I" "Squamous Cell Carcinoma" 12027N "Homo sapiens" female years 12027T organism_part P-MEXP-9143 12027T total_RNA P-MEXP-9144 5 ug "12027T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1499_12027T A-MEXP-86 P-MEXP-9146 R1499_12027T R1499_12027T.txt "Stage I" "Squamous Cell Carcinoma" 12027N
+12027N organism_part 73 fresh_sample normal 12027N "Homo sapiens" female years 12027N organism_part P-MEXP-9143 12027N total_RNA P-MEXP-9144 5 ug "12027N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1498_12027N A-MEXP-86 P-MEXP-9146 R1498_12027N R1498_12027N.txt normal 12027N
+12025T organism_part 49 fresh_sample "Stage II" "Squamous Cell Carcinoma" 12025N "Homo sapiens" male years 12025T organism_part P-MEXP-9143 12025T total_RNA P-MEXP-9144 5 ug "12025T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1497_12025T A-MEXP-86 P-MEXP-9146 R1497_12025T R1497_12025T.txt "Stage II" "Squamous Cell Carcinoma" 12025N
+12025N organism_part 49 fresh_sample normal 12025N "Homo sapiens" male years 12025N organism_part P-MEXP-9143 12025N total_RNA P-MEXP-9144 5 ug "12025N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1100_12025N A-MEXP-86 P-MEXP-9146 R1100_12025N R1100_12025N.txt normal 12025N
+12017T organism_part 67 fresh_sample "Stage I" "Squamous Cell Carcinoma" 12017N "Homo sapiens" female years 12017T organism_part P-MEXP-9143 12017T total_RNA P-MEXP-9144 5 ug "12017T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1765_12017T A-MEXP-86 P-MEXP-9146 R1765_12017T R1765_12017T.txt "Stage I" "Squamous Cell Carcinoma" 12017N
+12017N organism_part 67 fresh_sample normal 12017N "Homo sapiens" female years 12017N organism_part P-MEXP-9143 12017N total_RNA P-MEXP-9144 5 ug "12017N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1764_12017N A-MEXP-86 P-MEXP-9146 R1764_12017N R1764_12017N.txt normal 12017N
+12003T organism_part 66 fresh_sample "Stage I" "Squamous Cell Carcinoma" 12003N "Homo sapiens" male years 12003T organism_part P-MEXP-9143 12003T total_RNA P-MEXP-9144 5 ug "12003T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1496_12003T A-MEXP-86 P-MEXP-9146 R1496_12003T R1496_12003T.txt "Stage I" "Squamous Cell Carcinoma" 12003N
+12003N organism_part 66 fresh_sample normal 12003N "Homo sapiens" male years 12003N organism_part P-MEXP-9143 12003N total_RNA P-MEXP-9144 5 ug "12003N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1495_12003N A-MEXP-86 P-MEXP-9146 R1495_12003N R1495_12003N.txt normal 12003N
+11950T organism_part 66 fresh_sample "Stage II" "Squamous Cell Carcinoma" 11950N "Homo sapiens" male years 11950T organism_part P-MEXP-9143 11950T total_RNA P-MEXP-9144 5 ug "11950T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1097_11950T A-MEXP-86 P-MEXP-9146 R1097_11950T R1097_11950T.txt "Stage II" "Squamous Cell Carcinoma" 11950N
+11950N organism_part 66 fresh_sample normal 11950N "Homo sapiens" male years 11950N organism_part P-MEXP-9143 11950N total_RNA P-MEXP-9144 5 ug "11950N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1096_11950N A-MEXP-86 P-MEXP-9146 R1096_11950N R1096_11950N.txt normal 11950N
+11923T organism_part 72 fresh_sample "Stage I" "Squamous Cell Carcinoma" 11923N "Homo sapiens" female years 11923T organism_part P-MEXP-9143 11923T total_RNA P-MEXP-9144 5 ug "11923T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1093_11923T A-MEXP-86 P-MEXP-9146 R1093_11923T R1093_11923T.txt "Stage I" "Squamous Cell Carcinoma" 11923N
+11923N organism_part 72 fresh_sample normal 11923N "Homo sapiens" female years 11923N organism_part P-MEXP-9143 11923N total_RNA P-MEXP-9144 5 ug "11923N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1092_11923N A-MEXP-86 P-MEXP-9146 R1092_11923N R1092_11923N.txt normal 11923N
+11764T organism_part 68 fresh_sample "Stage I" "Squamous Cell Carcinoma" 11764N "Homo sapiens" male years 11764T organism_part P-MEXP-9143 11764T total_RNA P-MEXP-9144 5 ug "11764T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1763_11764T A-MEXP-86 P-MEXP-9146 R1763_11764T R1763_11764T.txt "Stage I" "Squamous Cell Carcinoma" 11764N
+11764N organism_part 68 fresh_sample normal 11764N "Homo sapiens" male years 11764N organism_part P-MEXP-9143 11764N total_RNA P-MEXP-9144 5 ug "11764N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1762_11764N A-MEXP-86 P-MEXP-9146 R1762_11764N R1762_11764N.txt normal 11764N
+11759T organism_part 67 fresh_sample "Stage I" "Squamous Cell Carcinoma" 11759N "Homo sapiens" male years 11759T organism_part P-MEXP-9143 11759T total_RNA P-MEXP-9144 5 ug "11759T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R886_11759T A-MEXP-86 P-MEXP-9146 R886_11759T R886_11759T.txt "Stage I" "Squamous Cell Carcinoma" 11759N
+11759N organism_part 67 fresh_sample normal 11759N "Homo sapiens" male years 11759N organism_part P-MEXP-9143 11759N total_RNA P-MEXP-9144 5 ug "11759N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R885_11759N A-MEXP-86 P-MEXP-9146 R885_11759N R885_11759N.txt normal 11759N
+11728T organism_part 53 fresh_sample "Stage II" "Squamous Cell Carcinoma" 11728N "Homo sapiens" male years 11728T organism_part P-MEXP-9143 11728T total_RNA P-MEXP-9144 5 ug "11728T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R884_11728T A-MEXP-86 P-MEXP-9146 R884_11728T R884_11728T.txt "Stage II" "Squamous Cell Carcinoma" 11728N
+11728N organism_part 53 fresh_sample normal 11728N "Homo sapiens" male years 11728N organism_part P-MEXP-9143 11728N total_RNA P-MEXP-9144 5 ug "11728N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R883_11728N A-MEXP-86 P-MEXP-9146 R883_11728N R883_11728N.txt normal 11728N
+11588T organism_part 69 fresh_sample "Stage I" "Squamous Cell Carcinoma" 11588N "Homo sapiens" female years 11588T organism_part P-MEXP-9143 11588T total_RNA P-MEXP-9144 5 ug "11588T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1082_11588T A-MEXP-86 P-MEXP-9146 R1082_11588T R1082_11588T.txt "Stage I" "Squamous Cell Carcinoma" 11588N
+11588N organism_part 69 fresh_sample normal 11588N "Homo sapiens" female years 11588N organism_part P-MEXP-9143 11588N total_RNA P-MEXP-9144 5 ug "11588N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1081_11588N A-MEXP-86 P-MEXP-9146 R1081_11588N R1081_11588N.txt normal 11588N
+11574T organism_part 63 fresh_sample "Stage III or IV" "Squamous Cell Carcinoma" 11574N "Homo sapiens" male years 11574T organism_part P-MEXP-9143 11574T total_RNA P-MEXP-9144 5 ug "11574T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1080_11574T A-MEXP-86 P-MEXP-9146 R1080_11574T R1080_11574T.txt "Stage III or IV" "Squamous Cell Carcinoma" 11574N
+11574N organism_part 63 fresh_sample normal 11574N "Homo sapiens" male years 11574N organism_part P-MEXP-9143 11574N total_RNA P-MEXP-9144 5 ug "11574N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1079_11574N A-MEXP-86 P-MEXP-9146 R1079_11574N R1079_11574N.txt normal 11574N
+11238T organism_part 69 fresh_sample "Stage I" "Squamous Cell Carcinoma" 11238N "Homo sapiens" male years 11238T organism_part P-MEXP-9143 11238T total_RNA P-MEXP-9144 5 ug "11238T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1058_11238T A-MEXP-86 P-MEXP-9146 R1058_11238T R1058_11238T.txt "Stage I" "Squamous Cell Carcinoma" 11238N
+11238N organism_part 69 fresh_sample normal 11238N "Homo sapiens" male years 11238N organism_part P-MEXP-9143 11238N total_RNA P-MEXP-9144 5 ug "11238N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1057_11238N A-MEXP-86 P-MEXP-9146 R1057_11238N R1057_11238N.txt normal 11238N
+11047T organism_part 45 fresh_sample "Stage II" "Squamous Cell Carcinoma" 11047N "Homo sapiens" female years 11047T organism_part P-MEXP-9143 11047T total_RNA P-MEXP-9144 5 ug "11047T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1030_11047T A-MEXP-86 P-MEXP-9146 R1030_11047T R1030_11047T.txt "Stage II" "Squamous Cell Carcinoma" 11047N
+11047N organism_part 45 fresh_sample normal 11047N "Homo sapiens" female years 11047N organism_part P-MEXP-9143 11047N total_RNA P-MEXP-9144 5 ug "11047N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1029_11047N A-MEXP-86 P-MEXP-9146 R1029_11047N R1029_11047N.txt normal 11047N
+11045T organism_part 59 fresh_sample "Stage III or IV" "Squamous Cell Carcinoma" 11045N "Homo sapiens" male years 11045T organism_part P-MEXP-9143 11045T total_RNA P-MEXP-9144 5 ug "11045T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1028_11045T A-MEXP-86 P-MEXP-9146 R1028_11045T R1028_11045T.txt "Stage III or IV" "Squamous Cell Carcinoma" 11045N
+11045N organism_part 59 fresh_sample normal 11045N "Homo sapiens" male years 11045N organism_part P-MEXP-9143 11045N total_RNA P-MEXP-9144 5 ug "11045N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1027_11045N A-MEXP-86 P-MEXP-9146 R1027_11045N R1027_11045N.txt normal 11045N
+11018T organism_part 70 fresh_sample "Stage II" "Squamous Cell Carcinoma" 11018N "Homo sapiens" male years 11018T organism_part P-MEXP-9143 11018T total_RNA P-MEXP-9144 5 ug "11018T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1024_11018T A-MEXP-86 P-MEXP-9146 R1024_11018T R1024_11018T.txt "Stage II" "Squamous Cell Carcinoma" 11018N
+11018N organism_part 70 fresh_sample normal 11018N "Homo sapiens" male years 11018N organism_part P-MEXP-9143 11018N total_RNA P-MEXP-9144 5 ug "11018N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1023_11018N A-MEXP-86 P-MEXP-9146 R1023_11018N R1023_11018N.txt normal 11018N
+11008T organism_part 60 fresh_sample "Stage I" "Squamous Cell Carcinoma" 11008N "Homo sapiens" female years 11008T organism_part P-MEXP-9143 11008T total_RNA P-MEXP-9144 5 ug "11008T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1022_11008T A-MEXP-86 P-MEXP-9146 R1022_11008T R1022_11008T.txt "Stage I" "Squamous Cell Carcinoma" 11008N
+11008N organism_part 60 fresh_sample normal 11008N "Homo sapiens" female years 11008N organism_part P-MEXP-9143 11008N total_RNA P-MEXP-9144 5 ug "11008N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1021_11008N A-MEXP-86 P-MEXP-9146 R1021_11008N R1021_11008N.txt normal 11008N
+11002T organism_part 74 fresh_sample "Stage I" "Squamous Cell Carcinoma" 11002N "Homo sapiens" male years 11002T organism_part P-MEXP-9143 11002T total_RNA P-MEXP-9144 5 ug "11002T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1018_11002T A-MEXP-86 P-MEXP-9146 R1018_11002T R1018_11002T.txt "Stage I" "Squamous Cell Carcinoma" 11002N
+11002N organism_part 74 fresh_sample normal 11002N "Homo sapiens" male years 11002N organism_part P-MEXP-9143 11002N total_RNA P-MEXP-9144 5 ug "11002N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1017_11002N A-MEXP-86 P-MEXP-9146 R1017_11002N R1017_11002N.txt normal 11002N
+10941T organism_part 68 fresh_sample "Stage I" "Squamous Cell Carcinoma" 10941N "Homo sapiens" male years 10941T organism_part P-MEXP-9143 10941T total_RNA P-MEXP-9144 5 ug "10941T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1014_10941T A-MEXP-86 P-MEXP-9146 R1014_10941T R1014_10941T.txt "Stage I" "Squamous Cell Carcinoma" 10941N
+10941N organism_part 68 fresh_sample normal 10941N "Homo sapiens" male years 10941N organism_part P-MEXP-9143 10941N total_RNA P-MEXP-9144 5 ug "10941N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1013_10941N A-MEXP-86 P-MEXP-9146 R1013_10941N R1013_10941N.txt normal 10941N
+10788T organism_part 54 fresh_sample "Stage III or IV" "Squamous Cell Carcinoma" 10788N "Homo sapiens" female years 10788T organism_part P-MEXP-9143 10788T total_RNA P-MEXP-9144 5 ug "10788T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1007_10788T A-MEXP-86 P-MEXP-9146 R1007_10788T R1007_10788T.txt "Stage III or IV" "Squamous Cell Carcinoma" 10788N
+10788N organism_part 54 fresh_sample normal 10788N "Homo sapiens" female years 10788N organism_part P-MEXP-9143 10788N total_RNA P-MEXP-9144 5 ug "10788N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1000_10788N A-MEXP-86 P-MEXP-9146 R1000_10788N R1000_10788N.txt normal 10788N
+10780T organism_part 75 fresh_sample "Stage III or IV" "Squamous Cell Carcinoma" 10780N "Homo sapiens" female years 10780T organism_part P-MEXP-9143 10780T total_RNA P-MEXP-9144 5 ug "10780T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R999_10780T A-MEXP-86 P-MEXP-9146 R999_10780T R999_10780T.txt "Stage III or IV" "Squamous Cell Carcinoma" 10780N
+10780N organism_part 75 fresh_sample normal 10780N "Homo sapiens" female years 10780N organism_part P-MEXP-9143 10780N total_RNA P-MEXP-9144 5 ug "10780N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R998_10780N A-MEXP-86 P-MEXP-9146 R998_10780N R998_10780N.txt normal 10780N
+10759T organism_part 72 fresh_sample "Stage III or IV" "Squamous Cell Carcinoma" 10759N "Homo sapiens" male years 10759T organism_part P-MEXP-9143 10759T total_RNA P-MEXP-9144 5 ug "10759T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R997_10759T A-MEXP-86 P-MEXP-9146 R997_10759T R997_10759T.txt "Stage III or IV" "Squamous Cell Carcinoma" 10759N
+10759N organism_part 72 fresh_sample normal 10759N "Homo sapiens" male years 10759N organism_part P-MEXP-9143 10759N total_RNA P-MEXP-9144 5 ug "10759N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R996_10759N A-MEXP-86 P-MEXP-9146 R996_10759N R996_10759N.txt normal 10759N
+10743T organism_part 54 fresh_sample "Stage I" "Squamous Cell Carcinoma" 10743N "Homo sapiens" male years 10743T organism_part P-MEXP-9143 10743T total_RNA P-MEXP-9144 5 ug "10743T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R995_10743T A-MEXP-86 P-MEXP-9146 R995_10743T R995_10743T.txt "Stage I" "Squamous Cell Carcinoma" 10743N
+10743N organism_part 54 fresh_sample normal 10743N "Homo sapiens" male years 10743N organism_part P-MEXP-9143 10743N total_RNA P-MEXP-9144 5 ug "10743N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R994_10743N A-MEXP-86 P-MEXP-9146 R994_10743N R994_10743N.txt normal 10743N
+10417T organism_part 74 fresh_sample "Stage II" "Squamous Cell Carcinoma" 10417N "Homo sapiens" male years 10417T organism_part P-MEXP-9143 10417T total_RNA P-MEXP-9144 5 ug "10417T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R975_10417T A-MEXP-86 P-MEXP-9146 R975_10417T R975_10417T.txt "Stage II" "Squamous Cell Carcinoma" 10417N
+10417N organism_part 74 fresh_sample normal 10417N "Homo sapiens" male years 10417N organism_part P-MEXP-9143 10417N total_RNA P-MEXP-9144 5 ug "10417N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R874_10417N A-MEXP-86 P-MEXP-9146 R874_10417N R874_10417N.txt normal 10417N
+10404T organism_part 78 fresh_sample "Stage II" "Squamous Cell Carcinoma" 10404N "Homo sapiens" male years 10404T organism_part P-MEXP-9143 10404T total_RNA P-MEXP-9144 5 ug "10404T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R873_10404T A-MEXP-86 P-MEXP-9146 R873_10404T R873_10404T.txt "Stage II" "Squamous Cell Carcinoma" 10404N
+10404N organism_part 78 fresh_sample normal 10404N "Homo sapiens" male years 10404N organism_part P-MEXP-9143 10404N total_RNA P-MEXP-9144 5 ug "10404N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R871_10404N A-MEXP-86 P-MEXP-9146 R871_10404N R871_10404N.txt normal 10404N
+10296T organism_part 73 fresh_sample "Stage I" "Squamous Cell Carcinoma" 10296N "Homo sapiens" male years 10296T organism_part P-MEXP-9143 10296T total_RNA P-MEXP-9144 5 ug "10296T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R870_10296T A-MEXP-86 P-MEXP-9146 R870_10296T R870_10296T.txt "Stage I" "Squamous Cell Carcinoma" 10296N
+10296N organism_part 73 fresh_sample normal 10296N "Homo sapiens" male years 10296N organism_part P-MEXP-9143 10296N total_RNA P-MEXP-9144 5 ug "10296N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R869_10296N A-MEXP-86 P-MEXP-9146 R869_10296N R869_10296N.txt normal 10296N
+10286T organism_part 65 fresh_sample "Stage I" "Squamous Cell Carcinoma" 10286N "Homo sapiens" male years 10286T organism_part P-MEXP-9143 10286T total_RNA P-MEXP-9144 5 ug "10286T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1759_10286T A-MEXP-86 P-MEXP-9146 R1759_10286T R1759_10286T.txt "Stage I" "Squamous Cell Carcinoma" 10286N
+10286N organism_part 65 fresh_sample normal 10286N "Homo sapiens" male years 10286N organism_part P-MEXP-9143 10286N total_RNA P-MEXP-9144 5 ug "10286N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1758_10286N A-MEXP-86 P-MEXP-9146 R1758_10286N R1758_10286N.txt normal 10286N
+10070T organism_part 75 fresh_sample "Stage I" "Squamous Cell Carcinoma" 10070N "Homo sapiens" male years 10070T organism_part P-MEXP-9143 10070T total_RNA P-MEXP-9144 5 ug "10070T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1757_10070T A-MEXP-86 P-MEXP-9146 R1757_10070T R1757_10070T.txt "Stage I" "Squamous Cell Carcinoma" 10070N
+10070N organism_part 75 fresh_sample normal 10070N "Homo sapiens" male years 10070N organism_part P-MEXP-9143 10070N total_RNA P-MEXP-9144 5 ug "10070N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1750_10070N A-MEXP-86 P-MEXP-9146 R1750_10070N R1750_10070N.txt normal 10070N
+1820T organism_part 51 fresh_sample "Stage II" "Squamous Cell Carcinoma" 1820N "Homo sapiens" male years 1820T organism_part P-MEXP-9143 1820T total_RNA P-MEXP-9144 5 ug "1820T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1745_1820T A-MEXP-86 P-MEXP-9146 R1745_1820T R1745_1820T.txt "Stage II" "Squamous Cell Carcinoma" 1820N
+1820N organism_part 51 fresh_sample normal 1820N "Homo sapiens" male years 1820N organism_part P-MEXP-9143 1820N total_RNA P-MEXP-9144 5 ug "1820N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1744_1820N A-MEXP-86 P-MEXP-9146 R1744_1820N R1744_1820N.txt normal 1820N
+1800T organism_part 74 fresh_sample "Stage I" "Squamous Cell Carcinoma" 1800N "Homo sapiens" male years 1800T organism_part P-MEXP-9143 1800T total_RNA P-MEXP-9144 5 ug "1800T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1741_1800T A-MEXP-86 P-MEXP-9146 R1741_1800T R1741_1800T.txt "Stage I" "Squamous Cell Carcinoma" 1800N
+1800N organism_part 74 fresh_sample normal 1800N "Homo sapiens" male years 1800N organism_part P-MEXP-9143 1800N total_RNA P-MEXP-9144 5 ug "1800N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1740_1800N A-MEXP-86 P-MEXP-9146 R1740_1800N R1740_1800N.txt normal 1800N
+1799T organism_part 66 fresh_sample "Stage I" "Squamous Cell Carcinoma" 1799N "Homo sapiens" male years 1799T organism_part P-MEXP-9143 1799T total_RNA P-MEXP-9144 5 ug "1799T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1739_1799T A-MEXP-86 P-MEXP-9146 R1739_1799T R1739_1799T.txt "Stage I" "Squamous Cell Carcinoma" 1799N
+1799N organism_part 66 fresh_sample normal 1799N "Homo sapiens" male years 1799N organism_part P-MEXP-9143 1799N total_RNA P-MEXP-9144 5 ug "1799N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1738_1799N A-MEXP-86 P-MEXP-9146 R1738_1799N R1738_1799N.txt normal 1799N
+1797T organism_part 73 fresh_sample "Stage I" "Squamous Cell Carcinoma" 1797N "Homo sapiens" female years 1797T organism_part P-MEXP-9143 1797T total_RNA P-MEXP-9144 5 ug "1797T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1735_1797T A-MEXP-86 P-MEXP-9146 R1735_1797T R1735_1797T.txt "Stage I" "Squamous Cell Carcinoma" 1797N
+1797N organism_part 73 fresh_sample normal 1797N "Homo sapiens" female years 1797N organism_part P-MEXP-9143 1797N total_RNA P-MEXP-9144 5 ug "1797N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1734_1797N A-MEXP-86 P-MEXP-9146 R1734_1797N R1734_1797N.txt normal 1797N
+1746T organism_part 72 fresh_sample "Stage I" "Squamous Cell Carcinoma" 1746N "Homo sapiens" female years 1746T organism_part P-MEXP-9143 1746T total_RNA P-MEXP-9144 5 ug "1746T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1733_1746T A-MEXP-86 P-MEXP-9146 R1733_1746T R1733_1746T.txt "Stage I" "Squamous Cell Carcinoma" 1746N
+1746N organism_part 72 fresh_sample normal 1746N "Homo sapiens" female years 1746N organism_part P-MEXP-9143 1746N total_RNA P-MEXP-9144 5 ug "1746N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1732_1746N A-MEXP-86 P-MEXP-9146 R1732_1746N R1732_1746N.txt normal 1746N
+1614T organism_part 63 fresh_sample "Stage III or IV" "Squamous Cell Carcinoma" 1614N "Homo sapiens" male years 1614T organism_part P-MEXP-9143 1614T total_RNA P-MEXP-9144 5 ug "1614T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1725_1614T A-MEXP-86 P-MEXP-9146 R1725_1614T R1725_1614T.txt "Stage III or IV" "Squamous Cell Carcinoma" 1614N
+1614N organism_part 63 fresh_sample normal 1614N "Homo sapiens" male years 1614N organism_part P-MEXP-9143 1614N total_RNA P-MEXP-9144 5 ug "1614N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1724_1614N A-MEXP-86 P-MEXP-9146 R1724_1614N R1724_1614N.txt normal 1614N
+1535T organism_part 71 fresh_sample "Stage I" "Squamous Cell Carcinoma" 1535N "Homo sapiens" male years 1535T organism_part P-MEXP-9143 1535T total_RNA P-MEXP-9144 5 ug "1535T biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1713_1535T A-MEXP-86 P-MEXP-9146 R1713_1535T R1713_1535T.txt "Stage I" "Squamous Cell Carcinoma" 1535N
+1535N organism_part 71 fresh_sample normal 1535N "Homo sapiens" male years 1535N organism_part P-MEXP-9143 1535N total_RNA P-MEXP-9144 5 ug "1535N biotin" synthetic_DNA biotin P-MEXP-9145 25 degree_C 5 ug R1712_1535N A-MEXP-86 P-MEXP-9146 R1712_1535N R1712_1535N.txt normal 1535N
diff --git a/examples/magetab/real/E-TABM-33.idf b/examples/magetab/real/E-TABM-33.idf
new file mode 100644
index 0000000..7d5882f
--- /dev/null
+++ b/examples/magetab/real/E-TABM-33.idf
@@ -0,0 +1,27 @@
+"Investigation Title" "Micorarray analysis of gene expression in zebrafish development"
+"Experimental Design" development_or_differentiation_design time_series_design
+"Experimental Factor Name" TIME DEVELOPMENTALSTAGE
+"Experimental Factor Type" time developmental_stage
+
+"Person Last Name" Saric Konantz
+"Person First Name" Marc Martina
+"Person Mid Initial"
+"Person Affiliation" "Max-Planck-Institute for Developmental Biology, T�bingen, Germany"
+"Person Roles" submitter submitter
+
+"Quality Control Types" biological_replicate spike_quality_control real_time_PCR_quality_control
+"Public Release Date" "2005-08-18"
+
+Comment[ArrayExpressSubmissionDate] "2005-08-18"
+
+"Publication Author List" "Martina Konantz; Georg-Wilhelm Otto; Christian Weiler; Marc Saric; Robert Geisler"
+"Publication Title" "Micorarray analysis of gene expression in zebrafish development"
+"Experiment Description" "We are performing microarray experiments for expression profiling of zebrafish embryogenesis, both as a baseline for future analysis of mutant and other conditions and to validate our microarray technology. For our purpose we used the Affymetrix zebrafish array which contains approximately 15,000 genes. This represents about 50 % of the estimated number of zebrafish genes. Total RNA was collected from embryos at 16 different stages (zygote, shield stage, 75 % [...]
+
+"Protocol Name" P-TABM-55 P-TABM-56 P-AFFY-2
+"Protocol Type" "grow, sacrifice, store" "nucleic_acid_extraction, store" labeling
+"Protocol Description" "T�bingen wildtype-fish were maintained as described in N�sslein-Volhard and Dahm: Zebrafish, PAS Oxford Univ. Press ISBN 019963808 X. Staging was done by hours postfertilization (Kimmel et al) in liquid nitrogen and embryos were stored at - 80 �C. " "Briefly, total RNAs were prepared from every stages using TRIzol reagent (Invitrogen GmbH, Germany) with an added chlorophorm extraction before precipitation. Quality of embryonic RNA was assesed using the NanoDrop N [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" E-TABM-33_sdrf.txt
diff --git a/examples/magetab/real/E-TABM-33_sdrf.txt b/examples/magetab/real/E-TABM-33_sdrf.txt
new file mode 100644
index 0000000..e749a60
--- /dev/null
+++ b/examples/magetab/real/E-TABM-33_sdrf.txt
@@ -0,0 +1,31 @@
+"Source Name" "Material Type" "Characteristics [Age]" "Characteristics [BioSourceType]" "Characteristics [DevelopmentalStage]" "Characteristics [Genotype]" "Characteristics [InitialTimePoint]" "Characteristics [Organism]" "Characteristics [StrainOrLine]" "Characteristics [TimeUnit]" "Protocol REF" "Sample Name" "Material Type" "Protocol REF" "Extract Name" "Material Type" "Protocol REF" "Labeled Extract Name" "Material Type" Label "Hybridization Name" "Array Design REF" "Scan Name" "Arra [...]
+"Danio rerio Tuebingen wildtype_10h-1" whole_organism 10 frozen_sample Gastrula:Bud wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_10h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_10h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_10h-1 biotin" synthetic_DNA biotin Tue-10h-1 A-AFFY-38 Tue-10h-1 Tue-10h-1.CEL Tue-10h-1.EXP Tue-10h-1 Tue-10h-1.CHP Zebrafish.cdf Gastrula:Bud 10 h
+"Danio rerio Tuebingen wildtype_10h-2" whole_organism 10 frozen_sample Gastrula:Bud wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_10h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_10h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_10h-2 biotin" synthetic_DNA biotin Tue-10h-2 A-AFFY-38 Tue-10h-2 Tue-10h-2.CEL Tue-10h-2.EXP Tue-10h-2 Tue-10h-2.CHP Zebrafish.cdf Gastrula:Bud 10 h
+"Danio rerio Tuebingen wildtype_11.7h-1" whole_organism 11.7 frozen_sample "Segmentation:5-9 somites" wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_11.7h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_11.7h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_11.7h-1 biotin" synthetic_DNA biotin Tue-11.7h-1 A-AFFY-38 Tue-11.7h-1 Tue-11.7h-1.CEL Tue-11.7h-1.EXP Tue-11.7h-1 Tue-11.7h-1.CHP Zebrafish.cdf "Segmentation:5-9 somite [...]
+"Danio rerio Tuebingen wildtype_11.7h-2" whole_organism 11.7 frozen_sample "Segmentation:5-9 somites" wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_11.7h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_11.7h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_11.7h-2 biotin" synthetic_DNA biotin Tue-11.7h-2 A-AFFY-38 Tue-11.7h-2 Tue-11.7h-2.CEL Tue-11.7h-2.EXP Tue-11.7h-2 Tue-11.7h-2.CHP Zebrafish.cdf "Segmentation:5-9 somite [...]
+"Danio rerio Tuebingen wildtype_14d-1" whole_organism 14 frozen_sample "Larval:Days 14-20" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_14d-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_14d-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_14d-1 biotin" synthetic_DNA biotin Tue-14d-1 A-AFFY-38 Tue-14d-1 Tue-14d-1.CEL Tue-14d-1.EXP Tue-14d-1 Tue-14d-1.CHP Zebrafish.cdf "Larval:Days 14-20" 14 d
+"Danio rerio Tuebingen wildtype_14d_2" whole_organism 14 frozen_sample "Larval:Days 14-20" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_14d_2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_14d_2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_14d_2 biotin" synthetic_DNA biotin Tue-14d-2 A-AFFY-38 Tue-14d-2 Tue-14d-2.CEL Tue-14d-2.EXP Tue-14d-2 Tue-14d-2.CHP Zebrafish.cdf "Larval:Days 14-20" 14 d
+"Danio rerio Tuebingen wildtype_16h-1" whole_organism 16 frozen_sample "Segmentation:14-19 somites" wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_16h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_16h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_16h-1 biotin" synthetic_DNA biotin Tue-16h-1 A-AFFY-38 Tue-16h-1 Tue-16h-1.CEL Tue-16h-1.EXP Tue-16h-1 Tue-16h-1.CHP Zebrafish.cdf "Segmentation:14-19 somites" 16 h
+"Danio rerio Tuebingen wildtype_16h-2" whole_organism 16 frozen_sample "Segmentation:14-19 somites" wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_16h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_16h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_16h-2 biotin" synthetic_DNA biotin Tue-16h-2 A-AFFY-38 Tue-16h-2 Tue-16h-2.CEL Tue-16h-2.EXP Tue-16h-2 Tue-16h-2.CHP Zebrafish.cdf "Segmentation:14-19 somites" 16 h
+"Danio rerio Tuebingen wildtype_1c-1" whole_organism 0.25 frozen_sample Zygote:1-cell wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_1c-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_1c-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_1c-1 biotin" synthetic_DNA biotin Tue-1c-1 A-AFFY-38 Tue-1c-1 Tue-1c-1.CEL Tue-1c-1.EXP Tue-1c-1 Tue-1c-1.CHP Zebrafish.cdf Zygote:1-cell 0.25 h
+"Danio rerio Tuebingen wildtype_1c-2" whole_organism 0.25 frozen_sample Zygote:1-cell wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_1c-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_1c-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_1c-2 biotin" synthetic_DNA biotin Tue-1c-2 A-AFFY-38 Tue-1c-2 Tue-1c-2.CEL Tue-1c-2.EXP Tue-1c-2 Tue-1c-2.CHP Zebrafish.cdf Zygote:1-cell 0.25 h
+"Danio rerio Tuebingen wildtype_24h-1" whole_organism 24 frozen_sample Pharyngula:Prim-5 wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_24h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_24h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_24h-1 biotin" synthetic_DNA biotin Tue-24h-1 A-AFFY-38 Tue-24h-1 Tue-24h-1.CEL Tue-24h-1.EXP Tue-24h-1 Tue-24h-1.CHP Zebrafish.cdf Pharyngula:Prim-5 24 h
+"Danio rerio Tuebingen wildtype_24h-2" whole_organism 24 frozen_sample Pharyngula:Prim-5 wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_24h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_24h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_24h-2 biotin" synthetic_DNA biotin Tue-24h-2 A-AFFY-38 Tue-24h-2 Tue-24h-2.CEL Tue-24h-2.EXP Tue-24h-2 Tue-24h-2.CHP Zebrafish.cdf Pharyngula:Prim-5 24 h
+"Danio rerio Tuebingen wildtype_30d-1" whole_organism 30 frozen_sample "Juvenile:Days 30-44" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_30d-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_30d-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_30d-1 biotin" synthetic_DNA biotin Tue-30d-1 A-AFFY-38 Tue-30d-1 Tue-30d-1.CEL Tue-30d-1.EXP Tue-30d-1 Tue-30d-1.CHP Zebrafish.cdf "Juvenile:Days 30-44" 30 d
+"Danio rerio Tuebingen wildtype_30d-2" whole_organism 30 frozen_sample "Juvenile:Days 30-44" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_30d-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_30d-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_30d-2 biotin" synthetic_DNA biotin Tue-30d-2 A-AFFY-38 Tue-30d-2 Tue-30d-2.CEL Tue-30d-2.EXP Tue-30d-2 Tue-30d-2.CHP Zebrafish.cdf "Juvenile:Days 30-44" 30 d
+"Danio rerio Tuebingen wildtype_32h-1" whole_organism 32 frozen_sample Pharyngula:Prim-15 wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_32h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_32h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_32h-1 biotin" synthetic_DNA biotin Tue-32h-1 A-AFFY-38 Tue-32h-1 Tue-32h-1.CEL Tue-32h-1.EXP Tue-32h-1 Tue-32h-1.CHP Zebrafish.cdf Pharyngula:Prim-15 32 h
+"Danio rerio Tuebingen wildtype_32h-2" whole_organism 32 frozen_sample Pharyngula:Prim-15 wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_32h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_32h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_32h-2 biotin" synthetic_DNA biotin Tue-32h-2 A-AFFY-38 Tue-32h-2 Tue-32h-2.CEL Tue-32h-2.EXP Tue-32h-2 Tue-32h-2.CHP Zebrafish.cdf Pharyngula:Prim-15 32 h
+"Danio rerio Tuebingen wildtype_48h-1" whole_organism 48 frozen_sample Hatching:Long-pec wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_48h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_48h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_48h-1 biotin" synthetic_DNA biotin Tue-48h-1 A-AFFY-38 Tue-48h-1 Tue-48h-1.CEL Tue-48h-1.EXP Tue-48h-1 Tue-48h-1.CHP Zebrafish.cdf Hatching:Long-pec 48 h
+"Danio rerio Tuebingen wildtype_48h-2" whole_organism 48 frozen_sample Hatching:Long-pec wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_48h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_48h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_48h-2 biotin" synthetic_DNA biotin Tue-48h-2 A-AFFY-38 Tue-48h-2 Tue-48h-2.CEL Tue-48h-2.EXP Tue-48h-2 Tue-48h-2.CHP Zebrafish.cdf Hatching:Long-pec 48 h
+"Danio rerio Tuebingen wildtype_4d-1" whole_organism 4 frozen_sample "Larval:Day 4" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_4d-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_4d-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_4d-1 biotin" synthetic_DNA biotin Tue-4d-1 A-AFFY-38 Tue-4d-1 Tue-4d-1.CEL Tue-4d-1.EXP Tue-4d-1 Tue-4d-1.CHP Zebrafish.cdf "Larval:Day 4" 4 d
+"Danio rerio Tuebingen wildtype_4d-2" whole_organism 4 frozen_sample "Larval:Day 4" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_4d-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_4d-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_4d-2 biotin" synthetic_DNA biotin Tue-4d-2 A-AFFY-38 Tue-4d-2 Tue-4d-2.CEL Tue-4d-2.EXP Tue-4d-2 Tue-4d-2.CHP Zebrafish.cdf "Larval:Day 4" 4 d
+"Danio rerio Tuebingen wildtype_5d-1" whole_organism 5 frozen_sample "Larval:Day 5" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_5d-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_5d-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_5d-1 biotin" synthetic_DNA biotin Tue-5d-1 A-AFFY-38 Tue-5d-1 Tue-5d-1.CEL Tue-5d-1.EXP Tue-5d-1 Tue-5d-1.CHP Zebrafish.cdf "Larval:Day 5" 5 d
+"Danio rerio Tuebingen wildtype_5d-2" whole_organism 5 frozen_sample "Larval:Day 5" wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_5d-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_5d-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_5d-2 biotin" synthetic_DNA biotin Tue-5d-2 A-AFFY-38 Tue-5d-2 Tue-5d-2.CEL Tue-5d-2.EXP Tue-5d-2 Tue-5d-2.CHP Zebrafish.cdf "Larval:Day 5" 5 d
+"Danio rerio Tuebingen wildtype_6h-1" whole_organism 6 frozen_sample Gastrula:Shield wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_6h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_6h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_6h-1 biotin" synthetic_DNA biotin Tue-6h-1 A-AFFY-38 Tue-6h-1 Tue-6h-1.CEL Tue-6h-1.EXP Tue-6h-1 Tue-6h-1.CHP Zebrafish.cdf Gastrula:Shield 6 h
+"Danio rerio Tuebingen wildtype_6h-2" whole_organism 6 frozen_sample Gastrula:Shield wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_6h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_6h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_6h-2 biotin" synthetic_DNA biotin Tue-6h-2 A-AFFY-38 Tue-6h-2 Tue-6h-2.CEL Tue-6h-2.EXP Tue-6h-2 Tue-6h-2.CHP Zebrafish.cdf Gastrula:Shield 6 h
+"Danio rerio Tuebingen wildtype_8h-1" whole_organism 8 frozen_sample Gastrula:75%-epiboly wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_8h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_8h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_8h-1 biotin" synthetic_DNA biotin Tue-8h-1 A-AFFY-38 Tue-8h-1 Tue-8h-1.CEL Tue-8h-1.EXP Tue-8h-1 Tue-8h-1.CHP Zebrafish.cdf Gastrula:75%-epiboly 8 h
+"Danio rerio Tuebingen wildtype_8h-2" whole_organism 8 frozen_sample Gastrula:75%-epiboly wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_8h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_8h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_8h-2 biotin" synthetic_DNA biotin Tue-8h-2 A-AFFY-38 Tue-8h-2 Tue-8h-2.CEL Tue-8h-2.EXP Tue-8h-2 Tue-8h-2.CHP Zebrafish.cdf Gastrula:75%-epiboly 8 h
+"Danio rerio Tuebingen wildtype_90d-1" whole_organism 90 frozen_sample Adult wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_90d-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_90d-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_90d-1 biotin" synthetic_DNA biotin Tue-90d-1 A-AFFY-38 Tue-90d-1 Tue-90d-1.CEL Tue-90d-1.EXP Tue-90d-1 Tue-90d-1.CHP Zebrafish.cdf Adult 90 d
+"Danio rerio Tuebingen wildtype_90d-2" whole_organism 90 frozen_sample Adult wild_type fertilization "Danio rerio" Tuebingen days P-TABM-55 "Danio rerio Tuebingen wildtype_90d-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_90d-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_90d-2 biotin" synthetic_DNA biotin Tue-90d-2 A-AFFY-38 Tue-90d-2 Tue-90d-2.CEL Tue-90d-2.EXP Tue-90d-2 Tue-90d-2.CHP Zebrafish.cdf Adult 90 d
+"Danio rerio Tuebingen wildtype_9h-1" whole_organism 9 frozen_sample Gastrula:90%-epiboly wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_9h-1" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_9h-1" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_9h-1 biotin" synthetic_DNA biotin Tue-9h-1 A-AFFY-38 Tue-9h-1 Tue-9h-1.CEL Tue-9h-1.EXP Tue-9h-1 Tue-9h-1.CHP Zebrafish.cdf Gastrula:90%-epiboly 9 h
+"Danio rerio Tuebingen wildtype_9h-2" whole_organism 9 frozen_sample Gastrula:90%-epiboly wild_type fertilization "Danio rerio" Tuebingen hours P-TABM-55 "Danio rerio Tuebingen wildtype_9h-2" organism_part P-TABM-56 "Danio rerio Tuebingen wildtype_9h-2" total_RNA P-AFFY-2 "Danio rerio Tuebingen wildtype_9h-2 biotin" synthetic_DNA biotin Tue-9h-2 A-AFFY-38 Tue-9h-2 Tue-9h-2.CEL Tue-9h-2.EXP Tue-9h-2 Tue-9h-2.CHP Zebrafish.cdf Gastrula:90%-epiboly 9 h
diff --git a/examples/magetab/real/E-TABM-35.idf b/examples/magetab/real/E-TABM-35.idf
new file mode 100644
index 0000000..a3a5ba5
--- /dev/null
+++ b/examples/magetab/real/E-TABM-35.idf
@@ -0,0 +1,26 @@
+"Investigation Title" "RML C. burnetii CGH analysis"
+"Experimental Design" strain_or_line_design comparative_genome_hybridization_design
+"Experimental Factor Name" STRAINORLINE
+"Experimental Factor Type" strain_or_line
+
+"Person Last Name" Beare
+"Person First Name" Paul
+"Person Mid Initial"
+"Person Affiliation" "Rocky Mountain Laboratories, Laboratory of Intracellular Parasites, National Institute of Allergy and Infectious Diseases, National Institute of Health"
+"Person Roles" submitter
+
+"Public Release Date" "2006-06-21"
+
+Comment[ArrayExpressSubmissionDate]
+
+"Publication Author List" "P. A. Beare; J. E. Samuel; D. Howe; K. Virtaneva; S. F. Porcella; R. A. Heinzen"
+"Publication Title" "Genetic Diversity of the Q Fever agent Coxiella burnetii Assessed by Microarray-based Whole Genome Comparisons "
+"Experiment Description" "Analysis of genetic diversity of 25 Coxiella burnetii isolates with relative to the Nine Mile (RSA493) reference isolate."
+
+"Protocol Name" P-TABM-61 P-TABM-62 P-TABM-63 P-TABM-64 P-TABM-65 P-TABM-66 P-TABM-67 P-TABM-68 P-TABM-69
+"Protocol Type" grow grow nucleic_acid_extraction nucleic_acid_extraction nucleic_acid_extraction nucleic_acid_extraction nucleic_acid_extraction labeling bioassay_data_transformation
+"Protocol Description" "C. burnetii isolates were propagated in embryonated henメs eggs for 8 days." "C. burnetii isolates were propagated in African green monkey kidney (Vero) fibroblasts (CCL-81; American Type Culture Collection) grown in RPMI medium (Invitrogen) supplemented with 2% fetal bovine serum for 14 days." "Organisms were purified from infected cells by Renografin or sucrose density gradient centrifugation. Total DNA was extracted using the UltraClean microbial DNA isolation k [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" E-TABM-35_sdrf.txt
diff --git a/examples/magetab/real/E-TABM-35_sdrf.txt b/examples/magetab/real/E-TABM-35_sdrf.txt
new file mode 100644
index 0000000..7e02eb6
--- /dev/null
+++ b/examples/magetab/real/E-TABM-35_sdrf.txt
@@ -0,0 +1,62 @@
+"Source Name" "Material Type" "Characteristics [Organism]" "Characteristics [StrainOrLine]" "Protocol REF" "Sample Name" "Material Type" "Protocol REF" "Extract Name" "Material Type" "Protocol REF" "Labeled Extract Name" "Material Type" Label "Hybridization Name" "Array Design REF" "Scan Name" "Array Data File" Comment[EXP] "Protocol REF" "Normalization Name" "Derived Array Data File" Comment[CDF] "Factor Value [STRAINORLINE]"
+"Dugway 5G61-63" whole_organism "Coxiella burnetii" "Dugway 5G61-63" P-TABM-62 "Dugway 5G61-63 A" organism_part P-TABM-63 "Dugway 5G61-63 A" genomic_DNA P-TABM-68 "Dugway 5G61-63 A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Dugway 5G61-63 A" A-AFFY-48 "PAB-1-Cburnetti-Dugway 5G61-63 A" "PAB-1-Cburnetti-Dugway 5G61-63 A.CEL" "PAB-1-Cburnetti-Dugway 5G61-63 A.EXP" P-TABM-69 "PAB-1-Cburnetti-Dugway 5G61-63 A" "PAB-1-Cburnetti-Dugway 5G61-63 A.CHP" RML-chipa510998.CDF "Dugway 5G61-63"
+"Dugway 5G61-63" whole_organism "Coxiella burnetii" "Dugway 5G61-63" P-TABM-62 "Dugway 5G61-63 B" organism_part P-TABM-63 "Dugway 5G61-63 B" genomic_DNA P-TABM-68 "Dugway 5G61-63 B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Dugway 5G61-63 B" A-AFFY-48 "PAB-1-Cburnetti-Dugway 5G61-63 B" "PAB-1-Cburnetti-Dugway 5G61-63 B.CEL" "PAB-1-Cburnetti-Dugway 5G61-63 B.EXP" P-TABM-69 "PAB-1-Cburnetti-Dugway 5G61-63 B" "PAB-1-Cburnetti-Dugway 5G61-63 B.CHP" RML-chipa510998.CDF "Dugway 5G61-63"
+"Dugway 7E9-12" whole_organism "Coxiella burnetii" "Dugway 7E9-12" P-TABM-61 "Dugway 7E9-12 A" organism_part P-TABM-64 "Dugway 7E9-12 A" genomic_DNA P-TABM-68 "Dugway 7E9-12 A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Dugway 7E9-12 A" A-AFFY-48 "PAB-1-Cburnetti-Dugway 7E9-12 A" "PAB-1-Cburnetti-Dugway 7E9-12 A.CEL" "PAB-1-Cburnetti-Dugway 7E9-12 A.EXP" P-TABM-69 "PAB-1-Cburnetti-Dugway 7E9-12 A" "PAB-1-Cburnetti-Dugway 7E9-12 A.CHP" RML-chipa510998.CDF "Dugway 7E9-12"
+"Dugway 7E9-12" whole_organism "Coxiella burnetii" "Dugway 7E9-12" P-TABM-61 "Dugway 7E9-12 B" organism_part P-TABM-64 "Dugway 7E9-12 B" genomic_DNA P-TABM-68 "Dugway 7E9-12 B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Dugway 7E9-12 B" A-AFFY-48 "PAB-1-Cburnetti-Dugway 7E9-12 B" "PAB-1-Cburnetti-Dugway 7E9-12 B.CEL" "PAB-1-Cburnetti-Dugway 7E9-12 B.EXP" P-TABM-69 "PAB-1-Cburnetti-Dugway 7E9-12 B" "PAB-1-Cburnetti-Dugway 7E9-12 B.CHP" RML-chipa510998.CDF "Dugway 7E9-12"
+African whole_organism "Coxiella burnetii" African P-TABM-61 "African A" organism_part P-TABM-64 "African A" genomic_DNA P-TABM-68 "African A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-African A" A-AFFY-48 "PAB-1-Cburnetti-African A" "PAB-1-Cburnetti-African A.CEL" "PAB-1-Cburnetti-African A.EXP" P-TABM-69 "PAB-1-Cburnetti-African A" "PAB-1-Cburnetti-African A.CHP" RML-chipa510998.CDF African
+African whole_organism "Coxiella burnetii" African P-TABM-61 "African B" organism_part P-TABM-64 "African B" genomic_DNA P-TABM-68 "African B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-African B" A-AFFY-48 "PAB-1-Cburnetti-African B" "PAB-1-Cburnetti-African B.CEL" "PAB-1-Cburnetti-African B.EXP" P-TABM-69 "PAB-1-Cburnetti-African B" "PAB-1-Cburnetti-African B.CHP" RML-chipa510998.CDF African
+African whole_organism "Coxiella burnetii" African P-TABM-61 "African C" organism_part P-TABM-64 "African C" genomic_DNA P-TABM-68 "African C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-African C" A-AFFY-48 "PAB-1-Cburnetti-African C" "PAB-1-Cburnetti-African C.CEL" "PAB-1-Cburnetti-African C.EXP" P-TABM-69 "PAB-1-Cburnetti-African C" "PAB-1-Cburnetti-African C.CHP" RML-chipa510998.CDF African
+Australia whole_organism "Coxiella burnetii" Australian P-TABM-62 "Australian A" organism_part P-TABM-63 "Australian A" genomic_DNA P-TABM-68 "Australian A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Australian A" A-AFFY-48 "PAB-1-Cburnetti-Australian A" "PAB-1-Cburnetti-Australian A.CEL" "PAB-1-Cburnetti-Australian A.EXP" P-TABM-69 "PAB-1-Cburnetti-Australian A" "PAB-1-Cburnetti-Australian A.CHP" RML-chipa510998.CDF "Australian "
+Australia whole_organism "Coxiella burnetii" Australian P-TABM-62 "Australian B" organism_part P-TABM-63 "Australian B" genomic_DNA P-TABM-68 "Australian B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Australian B" A-AFFY-48 "PAB-1-Cburnetti-Australian B" "PAB-1-Cburnetti-Australian B.CEL" "PAB-1-Cburnetti-Australian B.EXP" P-TABM-69 "PAB-1-Cburnetti-Australian B" "PAB-1-Cburnetti-Australian B.CHP" RML-chipa510998.CDF "Australian "
+BDT whole_organism "Coxiella burnetii" BDT P-TABM-62 "BDT A" organism_part P-TABM-66 "BDT A" genomic_DNA P-TABM-68 "BDT A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-BDT A" A-AFFY-48 "PAB-1-Cburnetti-BDT A" "PAB-1-Cburnetti-BDT A.CEL" "PAB-1-Cburnetti-BDT A.EXP" P-TABM-69 "PAB-1-Cburnetti-BDT A" "PAB-1-Cburnetti-BDT A.CHP" RML-chipa510998.CDF BDT
+BDT whole_organism "Coxiella burnetii" BDT P-TABM-62 "BDT B" organism_part P-TABM-66 "BDT B" genomic_DNA P-TABM-68 "BDT B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-BDT B" A-AFFY-48 "PAB-1-Cburnetti-BDT B" "PAB-1-Cburnetti-BDT B.CEL" "PAB-1-Cburnetti-BDT B.EXP" P-TABM-69 "PAB-1-Cburnetti-BDT B" "PAB-1-Cburnetti-BDT B.CHP" RML-chipa510998.CDF BDT
+"California 16" whole_organism "Coxiella burnetii" "California 16" P-TABM-61 "California 16" organism_part P-TABM-64 "California 16" genomic_DNA P-TABM-68 "California 16 biotin" synthetic_DNA biotin "PAB-1-Cburnetti-California 16" A-AFFY-48 "PAB-1-Cburnetti-California 16" "PAB-1-Cburnetti-California 16.CEL" "PAB-1-Cburnetti-California 16.EXP" P-TABM-69 "PAB-1-Cburnetti-California 16" "PAB-1-Cburnetti-California 16.CHP" RML-chipa510998.CDF "California 16"
+"California 33" whole_organism "Coxiella burnetii" "California 33" P-TABM-61 "California 33" organism_part P-TABM-64 "California 33" genomic_DNA P-TABM-68 "California 33 biotin" synthetic_DNA biotin "PAB-1-Cburnetti-California 33" A-AFFY-48 "PAB-1-Cburnetti-California 33" "PAB-1-Cburnetti-California 33.CEL" "PAB-1-Cburnetti-California 33.EXP" P-TABM-69 "PAB-1-Cburnetti-California 33" "PAB-1-Cburnetti-California 33.CHP" RML-chipa510998.CDF "California 33"
+Crazy whole_organism "Coxiella burnetii" Crazy P-TABM-62 "Crazy A" organism_part P-TABM-63 "Crazy A" genomic_DNA P-TABM-68 "Crazy A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Crazy A" A-AFFY-48 "PAB-1-Cburnetti-Crazy A" "PAB-1-Cburnetti-Crazy A.CEL" "PAB-1-Cburnetti-Crazy A.EXP" P-TABM-69 "PAB-1-Cburnetti-Crazy A" "PAB-1-Cburnetti-Crazy A.CHP" RML-chipa510998.CDF Crazy
+Crazy whole_organism "Coxiella burnetii" Crazy P-TABM-62 "Crazy B" organism_part P-TABM-63 "Crazy B" genomic_DNA P-TABM-68 "Crazy B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Crazy B" A-AFFY-48 "PAB-1-Cburnetti-Crazy B" "PAB-1-Cburnetti-Crazy B.CEL" "PAB-1-Cburnetti-Crazy B.EXP" P-TABM-69 "PAB-1-Cburnetti-Crazy B" "PAB-1-Cburnetti-Crazy B.CHP" RML-chipa510998.CDF Crazy
+D whole_organism "Coxiella burnetii" D P-TABM-61 "D A" organism_part P-TABM-64 "D A" genomic_DNA P-TABM-68 "D A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-D A" A-AFFY-48 "PAB-1-Cburnetti-D A" "PAB-1-Cburnetti-D A.CEL" "PAB-1-Cburnetti-D A.EXP" P-TABM-69 "PAB-1-Cburnetti-D A" "PAB-1-Cburnetti-D A.CHP" RML-chipa510998.CDF D
+D whole_organism "Coxiella burnetii" D P-TABM-61 "D B" organism_part P-TABM-64 "D B" genomic_DNA P-TABM-68 "D B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-D B" A-AFFY-48 "PAB-1-Cburnetti-D B" "PAB-1-Cburnetti-D B.CEL" "PAB-1-Cburnetti-D B.EXP" P-TABM-69 "PAB-1-Cburnetti-D B" "PAB-1-Cburnetti-D B.CHP" RML-chipa510998.CDF D
+"El Tayeb" whole_organism "Coxiella burnetii" "El Tayeb" P-TABM-61 "El Tayeb A" organism_part P-TABM-64 "El Tayeb A" genomic_DNA P-TABM-68 "El Tayeb A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-El Tayeb A" A-AFFY-48 "PAB-1-Cburnetti-El Tayeb A" "PAB-1-Cburnetti-El Tayeb A.CEL" "PAB-1-Cburnetti-El Tayeb A.EXP" P-TABM-69 "PAB-1-Cburnetti-El Tayeb A" "PAB-1-Cburnetti-El Tayeb A.CHP" RML-chipa510998.CDF "El Tayeb"
+"El Tayeb" whole_organism "Coxiella burnetii" "El Tayeb" P-TABM-61 "El Tayeb B" organism_part P-TABM-64 "El Tayeb B" genomic_DNA P-TABM-68 "El Tayeb B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-El Tayeb B" A-AFFY-48 "PAB-1-Cburnetti-El Tayeb B" "PAB-1-Cburnetti-El Tayeb B.CEL" "PAB-1-Cburnetti-El Tayeb B.EXP" P-TABM-69 "PAB-1-Cburnetti-El Tayeb B" "PAB-1-Cburnetti-El Tayeb B.CHP" RML-chipa510998.CDF "El Tayeb"
+G whole_organism "Coxiella burnetii" G P-TABM-62 "G A" organism_part P-TABM-63 "G A" genomic_DNA P-TABM-68 "G A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-G A" A-AFFY-48 "PAB-1-Cburnetti-G A" "PAB-1-Cburnetti-G A.CEL" "PAB-1-Cburnetti-G A.EXP" P-TABM-69 "PAB-1-Cburnetti-G A" "PAB-1-Cburnetti-G A.CHP" RML-chipa510998.CDF G
+G whole_organism "Coxiella burnetii" G P-TABM-62 "G B" organism_part P-TABM-63 "G B" genomic_DNA P-TABM-68 "G B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-G B" A-AFFY-48 "PAB-1-Cburnetti-G B" "PAB-1-Cburnetti-G B.CEL" "PAB-1-Cburnetti-G B.EXP" P-TABM-69 "PAB-1-Cburnetti-G B" "PAB-1-Cburnetti-G B.CHP" RML-chipa510998.CDF G
+G whole_organism "Coxiella burnetii" G P-TABM-61 "G C" organism_part P-TABM-65 "G C" genomic_DNA P-TABM-68 "G C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-G C" A-AFFY-48 "PAB-1-Cburnetti-G C" "PAB-1-Cburnetti-G C.CEL" "PAB-1-Cburnetti-G C.EXP" P-TABM-69 "PAB-1-Cburnetti-G C" "PAB-1-Cburnetti-G C.CHP" RML-chipa510998.CDF G
+G whole_organism "Coxiella burnetii" G P-TABM-61 "G D" organism_part P-TABM-66 "G D" genomic_DNA P-TABM-68 "G D biotin" synthetic_DNA biotin "PAB-1-Cburnetti-G D" A-AFFY-48 "PAB-1-Cburnetti-G D" "PAB-1-Cburnetti-G D.CEL" "PAB-1-Cburnetti-G D.EXP" P-TABM-69 "PAB-1-Cburnetti-G D" "PAB-1-Cburnetti-G D.CHP" RML-chipa510998.CDF G
+Giroud whole_organism "Coxiella burnetii" Giroud P-TABM-61 "Giroud A" organism_part P-TABM-64 "Giroud A" genomic_DNA P-TABM-68 "Giroud A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Giroud A" A-AFFY-48 "PAB-1-Cburnetti-Giroud A" "PAB-1-Cburnetti-Giroud A.CEL" "PAB-1-Cburnetti-Giroud A.EXP" P-TABM-69 "PAB-1-Cburnetti-Giroud A" "PAB-1-Cburnetti-Giroud A.CHP" RML-chipa510998.CDF Giroud
+Giroud whole_organism "Coxiella burnetii" Giroud P-TABM-61 "Giroud B" organism_part P-TABM-64 "Giroud B" genomic_DNA P-TABM-68 "Giroud B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Giroud B" A-AFFY-48 "PAB-1-Cburnetti-Giroud B" "PAB-1-Cburnetti-Giroud B.CEL" "PAB-1-Cburnetti-Giroud B.EXP" P-TABM-69 "PAB-1-Cburnetti-Giroud B" "PAB-1-Cburnetti-Giroud B.CHP" RML-chipa510998.CDF Giroud
+He whole_organism "Coxiella burnetii" He P-TABM-61 "He A" organism_part P-TABM-64 "He A" genomic_DNA P-TABM-68 "He A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-He A" A-AFFY-48 "PAB-1-Cburnetti-He A" "PAB-1-Cburnetti-He A.CEL" "PAB-1-Cburnetti-He A.EXP" P-TABM-69 "PAB-1-Cburnetti-He A" "PAB-1-Cburnetti-He A.CHP" RML-chipa510998.CDF "He "
+He whole_organism "Coxiella burnetii" He P-TABM-61 "He B" organism_part P-TABM-64 "He B" genomic_DNA P-TABM-68 "He B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-He B" A-AFFY-48 "PAB-1-Cburnetti-He B" "PAB-1-Cburnetti-He B.CEL" "PAB-1-Cburnetti-He B.EXP" P-TABM-69 "PAB-1-Cburnetti-He B" "PAB-1-Cburnetti-He B.CHP" RML-chipa510998.CDF "He "
+Idaho whole_organism "Coxiella burnetii" Idaho P-TABM-62 "Idaho A" organism_part P-TABM-63 "Idaho A" genomic_DNA P-TABM-68 "Idaho A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Idaho A" A-AFFY-48 "PAB-1-Cburnetti-Idaho A" "PAB-1-Cburnetti-Idaho A.CEL" "PAB-1-Cburnetti-Idaho A.EXP" P-TABM-69 "PAB-1-Cburnetti-Idaho A" "PAB-1-Cburnetti-Idaho A.CHP" RML-chipa510998.CDF Idaho
+Idaho whole_organism "Coxiella burnetii" Idaho P-TABM-62 "Idaho B" organism_part P-TABM-63 "Idaho B" genomic_DNA P-TABM-68 "Idaho B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Idaho B" A-AFFY-48 "PAB-1-Cburnetti-Idaho B" "PAB-1-Cburnetti-Idaho B.CEL" "PAB-1-Cburnetti-Idaho B.EXP" P-TABM-69 "PAB-1-Cburnetti-Idaho B" "PAB-1-Cburnetti-Idaho B.CHP" RML-chipa510998.CDF Idaho
+K whole_organism "Coxiella burnetii" K P-TABM-61 "K A" organism_part P-TABM-64 "K A" genomic_DNA P-TABM-68 "K A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-K A" A-AFFY-48 "PAB-1-Cburnetti-K A" "PAB-1-Cburnetti-K A.CEL" "PAB-1-Cburnetti-K A.EXP" P-TABM-69 "PAB-1-Cburnetti-K A" "PAB-1-Cburnetti-K A.CHP" RML-chipa510998.CDF K
+K whole_organism "Coxiella burnetii" K P-TABM-61 "K B" organism_part P-TABM-64 "K B" genomic_DNA P-TABM-68 "K B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-K B" A-AFFY-48 "PAB-1-Cburnetti-K B" "PAB-1-Cburnetti-K B.CEL" "PAB-1-Cburnetti-K B.EXP" P-TABM-69 "PAB-1-Cburnetti-K B" "PAB-1-Cburnetti-K B.CHP" RML-chipa510998.CDF K
+K whole_organism "Coxiella burnetii" K P-TABM-61 "K C" organism_part P-TABM-64 "K C" genomic_DNA P-TABM-68 "K C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-K C" A-AFFY-48 "PAB-1-Cburnetti-K C" "PAB-1-Cburnetti-K C.CEL" "PAB-1-Cburnetti-K C.EXP" P-TABM-69 "PAB-1-Cburnetti-K C" "PAB-1-Cburnetti-K C.CHP" RML-chipa510998.CDF K
+KO whole_organism "Coxiella burnetii" KO P-TABM-62 "KO A" organism_part P-TABM-63 "KO A" genomic_DNA P-TABM-68 "KO A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-KO A" A-AFFY-48 "PAB-1-Cburnetti-KO A" "PAB-1-Cburnetti-KO A.CEL" "PAB-1-Cburnetti-KO A.EXP" P-TABM-69 "PAB-1-Cburnetti-KO A" "PAB-1-Cburnetti-KO A.CHP" RML-chipa510998.CDF KO
+KO whole_organism "Coxiella burnetii" KO P-TABM-62 "KO B" organism_part P-TABM-63 "KO B" genomic_DNA P-TABM-68 "KO B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-KO B" A-AFFY-48 "PAB-1-Cburnetti-KO B" "PAB-1-Cburnetti-KO B.CEL" "PAB-1-Cburnetti-KO B.EXP" P-TABM-69 "PAB-1-Cburnetti-KO B" "PAB-1-Cburnetti-KO B.CHP" RML-chipa510998.CDF KO
+KO whole_organism "Coxiella burnetii" KO P-TABM-62 "KO C" organism_part P-TABM-63 "KO C" genomic_DNA P-TABM-68 "KO C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-KO C" A-AFFY-48 "PAB-1-Cburnetti-KO C" "PAB-1-Cburnetti-KO C.CEL" "PAB-1-Cburnetti-KO C.EXP" P-TABM-69 "PAB-1-Cburnetti-KO C" "PAB-1-Cburnetti-KO C.CHP" RML-chipa510998.CDF KO
+"Le B" whole_organism "Coxiella burnetii" "Le B" P-TABM-62 "Le B A" organism_part P-TABM-63 "Le B A" genomic_DNA P-TABM-68 "Le B A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-LeB A" A-AFFY-48 "PAB-1-Cburnetti-LeB A" "PAB-1-Cburnetti-LeB A.CEL" "PAB-1-Cburnetti-LeB A.EXP" P-TABM-69 "PAB-1-Cburnetti-LeB A" "PAB-1-Cburnetti-LeB A.CHP" RML-chipa510998.CDF "Le B"
+"Le B" whole_organism "Coxiella burnetii" "Le B" P-TABM-62 "Le B B" organism_part P-TABM-63 "Le B B" genomic_DNA P-TABM-68 "Le B B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-LeB B" A-AFFY-48 "PAB-1-Cburnetti-LeB B" "PAB-1-Cburnetti-LeB B.CEL" "PAB-1-Cburnetti-LeB B.EXP" P-TABM-69 "PAB-1-Cburnetti-LeB B" "PAB-1-Cburnetti-LeB B.CHP" RML-chipa510998.CDF "Le B"
+MSU whole_organism "Coxiella burnetii" MSU P-TABM-62 "MSU A" organism_part P-TABM-63 "MSU A" genomic_DNA P-TABM-68 "MSU A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-MSU A" A-AFFY-48 "PAB-1-Cburnetti-MSU A" "PAB-1-Cburnetti-MSU A.CEL" "PAB-1-Cburnetti-MSU A.EXP" P-TABM-69 "PAB-1-Cburnetti-MSU A" "PAB-1-Cburnetti-MSU A.CHP" RML-chipa510998.CDF MSU
+MSU whole_organism "Coxiella burnetii" MSU P-TABM-62 "MSU B" organism_part P-TABM-63 "MSU B" genomic_DNA P-TABM-68 "MSU B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-MSU B" A-AFFY-48 "PAB-1-Cburnetti-MSU B" "PAB-1-Cburnetti-MSU B.CEL" "PAB-1-Cburnetti-MSU B.EXP" P-TABM-69 "PAB-1-Cburnetti-MSU B" "PAB-1-Cburnetti-MSU B.CHP" RML-chipa510998.CDF MSU
+MSU whole_organism "Coxiella burnetii" MSU P-TABM-62 "MSU C" organism_part P-TABM-63 "MSU C" genomic_DNA P-TABM-68 "MSU C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-MSU C" A-AFFY-48 "PAB-1-Cburnetti-MSU C" "PAB-1-Cburnetti-MSU C.CEL" "PAB-1-Cburnetti-MSU C.EXP" P-TABM-69 "PAB-1-Cburnetti-MSU C" "PAB-1-Cburnetti-MSU C.CHP" RML-chipa510998.CDF MSU
+NMI whole_organism "Coxiella burnetii" NMI P-TABM-62 "NMI A" organism_part P-TABM-63 "NMI A" genomic_DNA P-TABM-68 "NMI A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-NMI A" A-AFFY-48 "PAB-1-Cburnetti-NMI A" "PAB-1-Cburnetti-NMI A.CEL" "PAB-1-Cburnetti-NMI A.EXP" P-TABM-69 "PAB-1-Cburnetti-NMI A" "PAB-1-Cburnetti-NMI A.CHP" RML-chipa510998.CDF NMI
+NMI whole_organism "Coxiella burnetii" NMI P-TABM-62 "NMI B" organism_part P-TABM-63 "NMI B" genomic_DNA P-TABM-68 "NMI B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-NMI B" A-AFFY-48 "PAB-1-Cburnetti-NMI B" "PAB-1-Cburnetti-NMI B.CEL" "PAB-1-Cburnetti-NMI B.EXP" P-TABM-69 "PAB-1-Cburnetti-NMI B" "PAB-1-Cburnetti-NMI B.CHP" RML-chipa510998.CDF NMI
+NMI whole_organism "Coxiella burnetii" NMI P-TABM-62 "NMI C" organism_part P-TABM-63 "NMI C" genomic_DNA P-TABM-68 "NMI C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-NMI C" A-AFFY-48 "PAB-1-Cburnetti-NMI C" "PAB-1-Cburnetti-NMI C.CEL" "PAB-1-Cburnetti-NMI C.EXP" P-TABM-69 "PAB-1-Cburnetti-NMI C" "PAB-1-Cburnetti-NMI C.CHP" RML-chipa510998.CDF NMI
+NMI whole_organism "Coxiella burnetii" NMI P-TABM-62 "NMI D" organism_part P-TABM-63 "NMI D" genomic_DNA P-TABM-68 "NMI D biotin" synthetic_DNA biotin "PAB-1-Cburnetti-NMI D" A-AFFY-48 "PAB-1-Cburnetti-NMI D" "PAB-1-Cburnetti-NMI D.CEL" "PAB-1-Cburnetti-NMI D.EXP" P-TABM-69 "PAB-1-Cburnetti-NMI D" "PAB-1-Cburnetti-NMI D.CHP" RML-chipa510998.CDF NMI
+NMII whole_organism "Coxiella burnetii" NMII P-TABM-62 "NMII A" organism_part P-TABM-63 "NMII A" genomic_DNA P-TABM-68 "NMII A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-NMII A" A-AFFY-48 "PAB-1-Cburnetti-NMII A" "PAB-1-Cburnetti-NMII A.CEL" "PAB-1-Cburnetti-NMII A.EXP" P-TABM-69 "PAB-1-Cburnetti-NMII A" "PAB-1-Cburnetti-NMII A.CHP" RML-chipa510998.CDF NMII
+NMII whole_organism "Coxiella burnetii" NMII P-TABM-62 "NMII B" organism_part P-TABM-67 "NMII B" genomic_DNA P-TABM-68 "NMII B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-NMII B" A-AFFY-48 "PAB-1-Cburnetti-NMII B" "PAB-1-Cburnetti-NMII B.CEL" "PAB-1-Cburnetti-NMII B.EXP" P-TABM-69 "PAB-1-Cburnetti-NMII B" "PAB-1-Cburnetti-NMII B.CHP" RML-chipa510998.CDF NMII
+Ohio whole_organism "Coxiella burnetii" Ohio P-TABM-61 "Ohio A" organism_part P-TABM-64 "Ohio A" genomic_DNA P-TABM-68 "Ohio A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Ohio A" A-AFFY-48 "PAB-1-Cburnetti-Ohio A" "PAB-1-Cburnetti-Ohio A.CEL" "PAB-1-Cburnetti-Ohio A.EXP" P-TABM-69 "PAB-1-Cburnetti-Ohio A" "PAB-1-Cburnetti-Ohio A.CHP" RML-chipa510998.CDF Ohio
+Ohio whole_organism "Coxiella burnetii" Ohio P-TABM-61 "Ohio B" organism_part P-TABM-64 "Ohio B" genomic_DNA P-TABM-68 "Ohio B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Ohio B" A-AFFY-48 "PAB-1-Cburnetti-Ohio B" "PAB-1-Cburnetti-Ohio B.CEL" "PAB-1-Cburnetti-Ohio B.EXP" P-TABM-69 "PAB-1-Cburnetti-Ohio B" "PAB-1-Cburnetti-Ohio B.CHP" RML-chipa510998.CDF Ohio
+P whole_organism "Coxiella burnetii" P P-TABM-61 "P A" organism_part P-TABM-64 "P A" genomic_DNA P-TABM-68 "P A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-P A" A-AFFY-48 "PAB-1-Cburnetti-P A" "PAB-1-Cburnetti-P A.CEL" "PAB-1-Cburnetti-P A.EXP" P-TABM-69 "PAB-1-Cburnetti-P A" "PAB-1-Cburnetti-P A.CHP" RML-chipa510998.CDF P
+P whole_organism "Coxiella burnetii" P P-TABM-61 "P B" organism_part P-TABM-64 "P B" genomic_DNA P-TABM-68 "P B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-P B" A-AFFY-48 "PAB-1-Cburnetti-P B" "PAB-1-Cburnetti-P B.CEL" "PAB-1-Cburnetti-P B.EXP" P-TABM-69 "PAB-1-Cburnetti-P B" "PAB-1-Cburnetti-P B.CHP" RML-chipa510998.CDF P
+P whole_organism "Coxiella burnetii" P P-TABM-61 "P C" organism_part P-TABM-64 "P C" genomic_DNA P-TABM-68 "P C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-P C" A-AFFY-48 "PAB-1-Cburnetti-P C" "PAB-1-Cburnetti-P C.CEL" "PAB-1-Cburnetti-P C.EXP" P-TABM-69 "PAB-1-Cburnetti-P C" "PAB-1-Cburnetti-P C.CHP" RML-chipa510998.CDF P
+Panama whole_organism "Coxiella burnetii" Panama P-TABM-61 "Panama A" organism_part P-TABM-64 "Panama A" genomic_DNA P-TABM-68 "Panama A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Panama A" A-AFFY-48 "PAB-1-Cburnetti-Panama A" "PAB-1-Cburnetti-Panama A.CEL" "PAB-1-Cburnetti-Panama A.EXP" P-TABM-69 "PAB-1-Cburnetti-Panama A" "PAB-1-Cburnetti-Panama A.CHP" RML-chipa510998.CDF Panama
+Panama whole_organism "Coxiella burnetii" Panama P-TABM-61 "Panama B" organism_part P-TABM-64 "Panama B" genomic_DNA P-TABM-68 "Panama B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Panama B" A-AFFY-48 "PAB-1-Cburnetti-Panama B" "PAB-1-Cburnetti-Panama B.CEL" "PAB-1-Cburnetti-Panama B.EXP" P-TABM-69 "PAB-1-Cburnetti-Panama B" "PAB-1-Cburnetti-Panama B.CHP" RML-chipa510998.CDF Panama
+Q321 whole_organism "Coxiella burnetii" Q321 P-TABM-62 "Q321 A" organism_part P-TABM-63 "Q321 A" genomic_DNA P-TABM-68 "Q321 A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Q321 A" A-AFFY-48 "PAB-1-Cburnetti-Q321 A" "PAB-1-Cburnetti-Q321 A.CEL" "PAB-1-Cburnetti-Q321 A.EXP" P-TABM-69 "PAB-1-Cburnetti-Q321 A" "PAB-1-Cburnetti-Q321 A.CHP" RML-chipa510998.CDF Q321
+Q321 whole_organism "Coxiella burnetii" Q321 P-TABM-62 "Q321 B" organism_part P-TABM-63 "Q321 B" genomic_DNA P-TABM-68 "Q321 B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Q321 B" A-AFFY-48 "PAB-1-Cburnetti-Q321 B" "PAB-1-Cburnetti-Q321 B.CEL" "PAB-1-Cburnetti-Q321 B.EXP" P-TABM-69 "PAB-1-Cburnetti-Q321 B" "PAB-1-Cburnetti-Q321 B.CHP" RML-chipa510998.CDF Q321
+Q321 whole_organism "Coxiella burnetii" Q321 P-TABM-62 "Q321 C" organism_part P-TABM-63 "Q321 C" genomic_DNA P-TABM-68 "Q321 C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Q321 C" A-AFFY-48 "PAB-1-Cburnetti-Q321 C" "PAB-1-Cburnetti-Q321 C.CEL" "PAB-1-Cburnetti-Q321 C.EXP" P-TABM-69 "PAB-1-Cburnetti-Q321 C" "PAB-1-Cburnetti-Q321 C.CHP" RML-chipa510998.CDF Q321
+S whole_organism "Coxiella burnetii" S P-TABM-62 "S A" organism_part P-TABM-63 "S A" genomic_DNA P-TABM-68 "S A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-S A" A-AFFY-48 "PAB-1-Cburnetti-S A" "PAB-1-Cburnetti-S A.CEL" "PAB-1-Cburnetti-S A.EXP" P-TABM-69 "PAB-1-Cburnetti-S A" "PAB-1-Cburnetti-S A.CHP" RML-chipa510998.CDF S
+S whole_organism "Coxiella burnetii" S P-TABM-62 "S B" organism_part P-TABM-63 "S B" genomic_DNA P-TABM-68 "S B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-S B" A-AFFY-48 "PAB-1-Cburnetti-S B" "PAB-1-Cburnetti-S B.CEL" "PAB-1-Cburnetti-S B.EXP" P-TABM-69 "PAB-1-Cburnetti-S B" "PAB-1-Cburnetti-S B.CHP" RML-chipa510998.CDF S
+S whole_organism "Coxiella burnetii" S P-TABM-62 "S C" organism_part P-TABM-63 "S C" genomic_DNA P-TABM-68 "S C biotin" synthetic_DNA biotin "PAB-1-Cburnetti-S C" A-AFFY-48 "PAB-1-Cburnetti-S C" "PAB-1-Cburnetti-S C.CEL" "PAB-1-Cburnetti-S C.EXP" P-TABM-69 "PAB-1-Cburnetti-S C" "PAB-1-Cburnetti-S C.CHP" RML-chipa510998.CDF S
+Turkey whole_organism "Coxiella burnetii" Turkey P-TABM-61 "Turkey A" organism_part P-TABM-64 "Turkey A" genomic_DNA P-TABM-68 "Turkey A biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Turkey A" A-AFFY-48 "PAB-1-Cburnetti-Turkey A" "PAB-1-Cburnetti-Turkey A.CEL" "PAB-1-Cburnetti-Turkey A.EXP" P-TABM-69 "PAB-1-Cburnetti-Turkey A" "PAB-1-Cburnetti-Turkey A.CHP" RML-chipa510998.CDF Turkey
+Turkey whole_organism "Coxiella burnetii" Turkey P-TABM-61 "Turkey B" organism_part P-TABM-64 "Turkey B" genomic_DNA P-TABM-68 "Turkey B biotin" synthetic_DNA biotin "PAB-1-Cburnetti-Turkey B" A-AFFY-48 "PAB-1-Cburnetti-Turkey B" "PAB-1-Cburnetti-Turkey B.CEL" "PAB-1-Cburnetti-Turkey B.EXP" P-TABM-69 "PAB-1-Cburnetti-Turkey B" "PAB-1-Cburnetti-Turkey B.CHP" RML-chipa510998.CDF Turkey
diff --git a/examples/magetab/real/E-TABM-54.idf b/examples/magetab/real/E-TABM-54.idf
new file mode 100644
index 0000000..bdb664d
--- /dev/null
+++ b/examples/magetab/real/E-TABM-54.idf
@@ -0,0 +1,21 @@
+"Investigation Title" "Bordetella pertussis comparative genome hybridization"
+"Experimental Design" strain_or_line_design comparative_genome_hybridization
+"Experimental Factor Name" STRAINORLINE CLINICAL_INFORMATION
+"Experimental Factor Type" strain_or_line clinical_information
+
+"Person Last Name" Cummings
+"Person First Name" Craig
+"Person Mid Initial"
+"Person Email" cummings at pmgm2.stanford.edu
+"Person Roles" submitter
+
+"Quality Control Types" technical_replicate biological_replicate
+"Public Release Date" "2005-12-31"
+
+
+"Experiment Description" "Comparative genome hybridization (CGH) of sample of 137 Bordetella pertussis strains"
+
+"SDRF File" E-TABM-54_sdrf.txt
+
+"Term Source Name" ArrayExpress
+"Term Source File" http://www.ebi.ac.uk/arrayexpress/
diff --git a/examples/magetab/real/E-TABM-54_sdrf.txt b/examples/magetab/real/E-TABM-54_sdrf.txt
new file mode 100644
index 0000000..66e4575
--- /dev/null
+++ b/examples/magetab/real/E-TABM-54_sdrf.txt
@@ -0,0 +1,275 @@
+"Source Name" "Material Type" "Characteristics [Organism]" "Characteristics [StrainOrLine]" "Protocol REF" "Term Source REF" "Sample Name" "Material Type" "Protocol REF" "Term Source REF" "Protocol REF" "Term Source REF" "Extract Name" "Material Type" "Protocol REF" "Term Source REF" "Labeled Extract Name" "Material Type" Label "Protocol REF" "Term Source REF" "Hybridization Name" "Array Design REF" "Protocol REF" "Term Source REF" "Scan Name" "Array Data File" "Protocol REF" "Term Sourc [...]
+Bpe60 whole_organism "Bordetella pertussis" Bpe60 P-MEXP-9739 ArrayExpress Bpe60 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe60 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe60 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n061 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n061 b23n061.txt P-MEXP-9965 ArrayExpress b23n061 b23n061_norm.txt Bpe60 "Tohama I laboratory strain"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n061 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n061 b23n061.txt P-MEXP-9965 ArrayExpress b23n061 b23n061_norm.txt Bpe60 "Tohama I laborator [...]
+Bpe317 whole_organism "Bordetella pertussis" Bpe317 P-MEXP-9739 ArrayExpress Bpe317 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe317 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe317 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n119 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n119 b29n119.txt P-MEXP-9965 ArrayExpress b29n119 b29n119_norm.txt Bpe317 "GMT-1 recent lab strain"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n119 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n119 b29n119.txt P-MEXP-9965 ArrayExpress b29n119 b29n119_norm.txt Bpe317 "GMT-1 recent lab strain"
+Bpe55 whole_organism "Bordetella pertussis" Bpe55 P-MEXP-9739 ArrayExpress Bpe55 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe55 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe55 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b25n048 A-MEXP-234 P-MEXP-9747 ArrayExpress b25n048 b25n048.txt P-MEXP-9965 ArrayExpress b25n048 b25n048_norm.txt Bpe55 "Tohama II laboratory strain"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b25n048 A-MEXP-234 P-MEXP-9747 ArrayExpress b25n048 b25n048.txt P-MEXP-9965 ArrayExpress b25n048 b25n048_norm.txt Bpe55 "Tohama II laborato [...]
+Bpe243 whole_organism "Bordetella pertussis" Bpe243 P-MEXP-9739 ArrayExpress Bpe243 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe243 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe243 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n112 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n112 b29n112.txt P-MEXP-9965 ArrayExpress b29n112 b29n112_norm.txt Bpe243 "US pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n112 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n112 b29n112.txt P-MEXP-9965 ArrayExpress b29n112 b29n112_norm.txt Bpe243 "US pre-vaccine"
+Bpe244 whole_organism "Bordetella pertussis" Bpe244 P-MEXP-9739 ArrayExpress Bpe244 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe244 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe244 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n113 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n113 b29n113.txt P-MEXP-9965 ArrayExpress b29n113 b29n113_norm.txt Bpe244 "US pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n113 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n113 b29n113.txt P-MEXP-9965 ArrayExpress b29n113 b29n113_norm.txt Bpe244 "US pre-vaccine"
+Bpe245 whole_organism "Bordetella pertussis" Bpe245 P-MEXP-9739 ArrayExpress Bpe245 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe245 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe245 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n114 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n114 b29n114.txt P-MEXP-9965 ArrayExpress b29n114 b29n114_norm.txt Bpe245 "US pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n114 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n114 b29n114.txt P-MEXP-9965 ArrayExpress b29n114 b29n114_norm.txt Bpe245 "US pre-vaccine"
+Bpe247 whole_organism "Bordetella pertussis" Bpe247 P-MEXP-9739 ArrayExpress Bpe247 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe247 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe247 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n116 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n116 b29n116.txt P-MEXP-9965 ArrayExpress b29n116 b29n116_norm.txt Bpe247 "US pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n116 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n116 b29n116.txt P-MEXP-9965 ArrayExpress b29n116 b29n116_norm.txt Bpe247 "US pre-vaccine"
+Bpe41 whole_organism "Bordetella pertussis" Bpe41 P-MEXP-9739 ArrayExpress Bpe41 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe41 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe41 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n124 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n124 b23n124.txt P-MEXP-9965 ArrayExpress b23n124 b23n124_norm.txt Bpe41 "US Cincinnati outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n124 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n124 b23n124.txt P-MEXP-9965 ArrayExpress b23n124 b23n124_norm.txt Bpe41 "US Cincinnati outbreak"
+Bpe51 whole_organism "Bordetella pertussis" Bpe51 P-MEXP-9739 ArrayExpress Bpe51 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe51 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe51 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n122 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n122 b24n122.txt P-MEXP-9965 ArrayExpress b24n122 b24n122_norm.txt Bpe51 "US Cincinnati outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n122 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n122 b24n122.txt P-MEXP-9965 ArrayExpress b24n122 b24n122_norm.txt Bpe51 "US Cincinnati outbreak"
+Bpe43 whole_organism "Bordetella pertussis" Bpe43 P-MEXP-9739 ArrayExpress Bpe43 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe43 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe43 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n016 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n016 b24n016.txt P-MEXP-9965 ArrayExpress b24n016 b24n016_norm.txt Bpe43 "US Cincinnati outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n016 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n016 b24n016.txt P-MEXP-9965 ArrayExpress b24n016 b24n016_norm.txt Bpe43 "US Cincinnati outbreak"
+Bpe44 whole_organism "Bordetella pertussis" Bpe44 P-MEXP-9739 ArrayExpress Bpe44 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe44 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe44 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n017 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n017 b24n017.txt P-MEXP-9965 ArrayExpress b24n017 b24n017_norm.txt Bpe44 "US Cincinnati outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n017 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n017 b24n017.txt P-MEXP-9965 ArrayExpress b24n017 b24n017_norm.txt Bpe44 "US Cincinnati outbreak"
+Bpe48 whole_organism "Bordetella pertussis" Bpe48 P-MEXP-9739 ArrayExpress Bpe48 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe48 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe48 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n020 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n020 b24n020.txt P-MEXP-9965 ArrayExpress b24n020 b24n020_norm.txt Bpe48 "US Cincinnati outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n020 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n020 b24n020.txt P-MEXP-9965 ArrayExpress b24n020 b24n020_norm.txt Bpe48 "US Cincinnati outbreak"
+Bpe50 whole_organism "Bordetella pertussis" Bpe50 P-MEXP-9739 ArrayExpress Bpe50 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe50 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe50 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n105 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n105 b29n105.txt P-MEXP-9965 ArrayExpress b29n105 b29n105_norm.txt Bpe50 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n105 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n105 b29n105.txt P-MEXP-9965 ArrayExpress b29n105 b29n105_norm.txt Bpe50 "US Cincinnati non- [...]
+Bpe52 whole_organism "Bordetella pertussis" Bpe52 P-MEXP-9739 ArrayExpress Bpe52 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe52 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe52 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n052 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n052 b24n052.txt P-MEXP-9965 ArrayExpress b24n052 b24n052_norm.txt Bpe52 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n052 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n052 b24n052.txt P-MEXP-9965 ArrayExpress b24n052 b24n052_norm.txt Bpe52 "US Cincinnati non- [...]
+Bpe37 whole_organism "Bordetella pertussis" Bpe37 P-MEXP-9739 ArrayExpress Bpe37 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe37 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe37 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n054 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n054 b24n054.txt P-MEXP-9965 ArrayExpress b24n054 b24n054_norm.txt Bpe37 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n054 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n054 b24n054.txt P-MEXP-9965 ArrayExpress b24n054 b24n054_norm.txt Bpe37 "US Cincinnati non- [...]
+Bpe33 whole_organism "Bordetella pertussis" Bpe33 P-MEXP-9739 ArrayExpress Bpe33 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe33 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe33 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n113 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n113 b24n113.txt P-MEXP-9965 ArrayExpress b24n113 b24n113_norm.txt Bpe33 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n113 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n113 b24n113.txt P-MEXP-9965 ArrayExpress b24n113 b24n113_norm.txt Bpe33 "US Cincinnati non- [...]
+Bpe34 whole_organism "Bordetella pertussis" Bpe34 P-MEXP-9739 ArrayExpress Bpe34 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe34 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe34 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n114 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n114 b24n114.txt P-MEXP-9965 ArrayExpress b24n114 b24n114_norm.txt Bpe34 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n114 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n114 b24n114.txt P-MEXP-9965 ArrayExpress b24n114 b24n114_norm.txt Bpe34 "US Cincinnati non- [...]
+Bpe35 whole_organism "Bordetella pertussis" Bpe35 P-MEXP-9739 ArrayExpress Bpe35 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe35 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe35 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n115 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n115 b24n115.txt P-MEXP-9965 ArrayExpress b24n115 b24n115_norm.txt Bpe35 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n115 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n115 b24n115.txt P-MEXP-9965 ArrayExpress b24n115 b24n115_norm.txt Bpe35 "US Cincinnati non- [...]
+Bpe36 whole_organism "Bordetella pertussis" Bpe36 P-MEXP-9739 ArrayExpress Bpe36 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe36 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe36 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n116 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n116 b24n116.txt P-MEXP-9965 ArrayExpress b24n116 b24n116_norm.txt Bpe36 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n116 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n116 b24n116.txt P-MEXP-9965 ArrayExpress b24n116 b24n116_norm.txt Bpe36 "US Cincinnati non- [...]
+Bpe38 whole_organism "Bordetella pertussis" Bpe38 P-MEXP-9739 ArrayExpress Bpe38 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe38 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe38 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n117 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n117 b24n117.txt P-MEXP-9965 ArrayExpress b24n117 b24n117_norm.txt Bpe38 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n117 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n117 b24n117.txt P-MEXP-9965 ArrayExpress b24n117 b24n117_norm.txt Bpe38 "US Cincinnati non- [...]
+Bpe40 whole_organism "Bordetella pertussis" Bpe40 P-MEXP-9739 ArrayExpress Bpe40 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe40 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe40 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n118 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n118 b24n118.txt P-MEXP-9965 ArrayExpress b24n118 b24n118_norm.txt Bpe40 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n118 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n118 b24n118.txt P-MEXP-9965 ArrayExpress b24n118 b24n118_norm.txt Bpe40 "US Cincinnati non- [...]
+Bpe47 whole_organism "Bordetella pertussis" Bpe47 P-MEXP-9739 ArrayExpress Bpe47 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe47 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe47 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n120 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n120 b24n120.txt P-MEXP-9965 ArrayExpress b24n120 b24n120_norm.txt Bpe47 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n120 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n120 b24n120.txt P-MEXP-9965 ArrayExpress b24n120 b24n120_norm.txt Bpe47 "US Cincinnati non- [...]
+Bpe49 whole_organism "Bordetella pertussis" Bpe49 P-MEXP-9739 ArrayExpress Bpe49 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe49 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe49 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n121 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n121 b24n121.txt P-MEXP-9965 ArrayExpress b24n121 b24n121_norm.txt Bpe49 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n121 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n121 b24n121.txt P-MEXP-9965 ArrayExpress b24n121 b24n121_norm.txt Bpe49 "US Cincinnati non- [...]
+Bpe59 whole_organism "Bordetella pertussis" Bpe59 P-MEXP-9739 ArrayExpress Bpe59 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe59 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe59 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n108 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n108 b29n108.txt P-MEXP-9965 ArrayExpress b29n108 b29n108_norm.txt Bpe59 "US Cincinnati non-outbreak"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n108 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n108 b29n108.txt P-MEXP-9965 ArrayExpress b29n108 b29n108_norm.txt Bpe59 "US Cincinnati non- [...]
+Bpe58 whole_organism "Bordetella pertussis" Bpe58 P-MEXP-9739 ArrayExpress Bpe58 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe58 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe58 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n106 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n106 b29n106.txt P-MEXP-9965 ArrayExpress b29n106 b29n106_norm.txt Bpe58 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n106 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n106 b29n106.txt P-MEXP-9965 ArrayExpress b29n106 b29n106_norm.txt Bpe58 "Other US post-vaccine"
+Bpe45 whole_organism "Bordetella pertussis" Bpe45 P-MEXP-9739 ArrayExpress Bpe45 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe45 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe45 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n032 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n032 b24n032.txt P-MEXP-9965 ArrayExpress b24n032 b24n032_norm.txt Bpe45 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n032 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n032 b24n032.txt P-MEXP-9965 ArrayExpress b24n032 b24n032_norm.txt Bpe45 "Other US post-vaccine"
+Bpe329 whole_organism "Bordetella pertussis" Bpe329 P-MEXP-9739 ArrayExpress Bpe329 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe329 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe329 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n132 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n132 b29n132.txt P-MEXP-9965 ArrayExpress b29n132 b29n132_norm.txt Bpe329 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n132 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n132 b29n132.txt P-MEXP-9965 ArrayExpress b29n132 b29n132_norm.txt Bpe329 "Other US post-vaccine"
+Bpe39 whole_organism "Bordetella pertussis" Bpe39 P-MEXP-9739 ArrayExpress Bpe39 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe39 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe39 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n123 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n123 b23n123.txt P-MEXP-9965 ArrayExpress b23n123 b23n123_norm.txt Bpe39 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n123 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n123 b23n123.txt P-MEXP-9965 ArrayExpress b23n123 b23n123_norm.txt Bpe39 "Other US post-vaccine"
+Bpe42 whole_organism "Bordetella pertussis" Bpe42 P-MEXP-9739 ArrayExpress Bpe42 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe42 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe42 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n125 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n125 b23n125.txt P-MEXP-9965 ArrayExpress b23n125 b23n125_norm.txt Bpe42 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n125 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n125 b23n125.txt P-MEXP-9965 ArrayExpress b23n125 b23n125_norm.txt Bpe42 "Other US post-vaccine"
+Bpe241 whole_organism "Bordetella pertussis" Bpe241 P-MEXP-9739 ArrayExpress Bpe241 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe241 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe241 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n111 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n111 b29n111.txt P-MEXP-9965 ArrayExpress b29n111 b29n111_norm.txt Bpe241 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n111 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n111 b29n111.txt P-MEXP-9965 ArrayExpress b29n111 b29n111_norm.txt Bpe241 "Other US post-vaccine"
+Bpe46 whole_organism "Bordetella pertussis" Bpe46 P-MEXP-9739 ArrayExpress Bpe46 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe46 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe46 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n119 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n119 b24n119.txt P-MEXP-9965 ArrayExpress b24n119 b24n119_norm.txt Bpe46 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n119 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n119 b24n119.txt P-MEXP-9965 ArrayExpress b24n119 b24n119_norm.txt Bpe46 "Other US post-vaccine"
+Bpe280 whole_organism "Bordetella pertussis" Bpe280 P-MEXP-9739 ArrayExpress Bpe280 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe280 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe280 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n117 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n117 b29n117.txt P-MEXP-9965 ArrayExpress b29n117 b29n117_norm.txt Bpe280 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n117 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n117 b29n117.txt P-MEXP-9965 ArrayExpress b29n117 b29n117_norm.txt Bpe280 "Other US post-vaccine"
+Bpe281 whole_organism "Bordetella pertussis" Bpe281 P-MEXP-9739 ArrayExpress Bpe281 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe281 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe281 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n118 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n118 b29n118.txt P-MEXP-9965 ArrayExpress b29n118 b29n118_norm.txt Bpe281 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n118 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n118 b29n118.txt P-MEXP-9965 ArrayExpress b29n118 b29n118_norm.txt Bpe281 "Other US post-vaccine"
+Bpe319 whole_organism "Bordetella pertussis" Bpe319 P-MEXP-9739 ArrayExpress Bpe319 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe319 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe319 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n120 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n120 b29n120.txt P-MEXP-9965 ArrayExpress b29n120 b29n120_norm.txt Bpe319 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n120 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n120 b29n120.txt P-MEXP-9965 ArrayExpress b29n120 b29n120_norm.txt Bpe319 "Other US post-vaccine"
+Bpe320 whole_organism "Bordetella pertussis" Bpe320 P-MEXP-9739 ArrayExpress Bpe320 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe320 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe320 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n121 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n121 b29n121.txt P-MEXP-9965 ArrayExpress b29n121 b29n121_norm.txt Bpe320 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n121 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n121 b29n121.txt P-MEXP-9965 ArrayExpress b29n121 b29n121_norm.txt Bpe320 "Other US post-vaccine"
+Bpe321 whole_organism "Bordetella pertussis" Bpe321 P-MEXP-9739 ArrayExpress Bpe321 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe321 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe321 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n122 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n122 b29n122.txt P-MEXP-9965 ArrayExpress b29n122 b29n122_norm.txt Bpe321 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n122 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n122 b29n122.txt P-MEXP-9965 ArrayExpress b29n122 b29n122_norm.txt Bpe321 "Other US post-vaccine"
+Bpe322 whole_organism "Bordetella pertussis" Bpe322 P-MEXP-9739 ArrayExpress Bpe322 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe322 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe322 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n125 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n125 b29n125.txt P-MEXP-9965 ArrayExpress b29n125 b29n125_norm.txt Bpe322 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n125 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n125 b29n125.txt P-MEXP-9965 ArrayExpress b29n125 b29n125_norm.txt Bpe322 "Other US post-vaccine"
+Bpe323 whole_organism "Bordetella pertussis" Bpe323 P-MEXP-9739 ArrayExpress Bpe323 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe323 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe323 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n126 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n126 b29n126.txt P-MEXP-9965 ArrayExpress b29n126 b29n126_norm.txt Bpe323 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n126 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n126 b29n126.txt P-MEXP-9965 ArrayExpress b29n126 b29n126_norm.txt Bpe323 "Other US post-vaccine"
+Bpe324 whole_organism "Bordetella pertussis" Bpe324 P-MEXP-9739 ArrayExpress Bpe324 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe324 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe324 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n127 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n127 b29n127.txt P-MEXP-9965 ArrayExpress b29n127 b29n127_norm.txt Bpe324 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n127 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n127 b29n127.txt P-MEXP-9965 ArrayExpress b29n127 b29n127_norm.txt Bpe324 "Other US post-vaccine"
+Bpe325 whole_organism "Bordetella pertussis" Bpe325 P-MEXP-9739 ArrayExpress Bpe325 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe325 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe325 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n128 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n128 b29n128.txt P-MEXP-9965 ArrayExpress b29n128 b29n128_norm.txt Bpe325 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n128 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n128 b29n128.txt P-MEXP-9965 ArrayExpress b29n128 b29n128_norm.txt Bpe325 "Other US post-vaccine"
+Bpe326 whole_organism "Bordetella pertussis" Bpe326 P-MEXP-9739 ArrayExpress Bpe326 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe326 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe326 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n129 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n129 b29n129.txt P-MEXP-9965 ArrayExpress b29n129 b29n129_norm.txt Bpe326 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n129 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n129 b29n129.txt P-MEXP-9965 ArrayExpress b29n129 b29n129_norm.txt Bpe326 "Other US post-vaccine"
+Bpe327 whole_organism "Bordetella pertussis" Bpe327 P-MEXP-9739 ArrayExpress Bpe327 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe327 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe327 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n130 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n130 b29n130.txt P-MEXP-9965 ArrayExpress b29n130 b29n130_norm.txt Bpe327 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n130 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n130 b29n130.txt P-MEXP-9965 ArrayExpress b29n130 b29n130_norm.txt Bpe327 "Other US post-vaccine"
+Bpe328 whole_organism "Bordetella pertussis" Bpe328 P-MEXP-9739 ArrayExpress Bpe328 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe328 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe328 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n131 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n131 b29n131.txt P-MEXP-9965 ArrayExpress b29n131 b29n131_norm.txt Bpe328 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n131 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n131 b29n131.txt P-MEXP-9965 ArrayExpress b29n131 b29n131_norm.txt Bpe328 "Other US post-vaccine"
+Bpe330 whole_organism "Bordetella pertussis" Bpe330 P-MEXP-9739 ArrayExpress Bpe330 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe330 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe330 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n040 a-mexp-281! P-MEXP-9747 ArrayExpress b31n040 b31n040.txt P-MEXP-9965 ArrayExpress b31n040 b31n040_norm.txt Bpe330 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n040 a-mexp-281! P-MEXP-9747 ArrayExpress b31n040 b31n040.txt P-MEXP-9965 ArrayExpress b31n040 b31n040_norm.txt Bpe330 "Other US post-vaccine"
+Bpe331 whole_organism "Bordetella pertussis" Bpe331 P-MEXP-9739 ArrayExpress Bpe331 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe331 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe331 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n041 a-mexp-281! P-MEXP-9747 ArrayExpress b31n041 b31n041.txt P-MEXP-9965 ArrayExpress b31n041 b31n041_norm.txt Bpe331 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n041 a-mexp-281! P-MEXP-9747 ArrayExpress b31n041 b31n041.txt P-MEXP-9965 ArrayExpress b31n041 b31n041_norm.txt Bpe331 "Other US post-vaccine"
+Bpe332 whole_organism "Bordetella pertussis" Bpe332 P-MEXP-9739 ArrayExpress Bpe332 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe332 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe332 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n042 a-mexp-281! P-MEXP-9747 ArrayExpress b31n042 b31n042.txt P-MEXP-9965 ArrayExpress b31n042 b31n042_norm.txt Bpe332 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n042 a-mexp-281! P-MEXP-9747 ArrayExpress b31n042 b31n042.txt P-MEXP-9965 ArrayExpress b31n042 b31n042_norm.txt Bpe332 "Other US post-vaccine"
+Bpe333 whole_organism "Bordetella pertussis" Bpe333 P-MEXP-9739 ArrayExpress Bpe333 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe333 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe333 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n043 a-mexp-281! P-MEXP-9747 ArrayExpress b31n043 b31n043.txt P-MEXP-9965 ArrayExpress b31n043 b31n043_norm.txt Bpe333 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n043 a-mexp-281! P-MEXP-9747 ArrayExpress b31n043 b31n043.txt P-MEXP-9965 ArrayExpress b31n043 b31n043_norm.txt Bpe333 "Other US post-vaccine"
+Bpe334 whole_organism "Bordetella pertussis" Bpe334 P-MEXP-9739 ArrayExpress Bpe334 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe334 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe334 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n044 a-mexp-281! P-MEXP-9747 ArrayExpress b31n044 b31n044.txt P-MEXP-9965 ArrayExpress b31n044 b31n044_norm.txt Bpe334 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n044 a-mexp-281! P-MEXP-9747 ArrayExpress b31n044 b31n044.txt P-MEXP-9965 ArrayExpress b31n044 b31n044_norm.txt Bpe334 "Other US post-vaccine"
+Bpe335 whole_organism "Bordetella pertussis" Bpe335 P-MEXP-9739 ArrayExpress Bpe335 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe335 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe335 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n045 a-mexp-281! P-MEXP-9747 ArrayExpress b31n045 b31n045.txt P-MEXP-9965 ArrayExpress b31n045 b31n045_norm.txt Bpe335 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n045 a-mexp-281! P-MEXP-9747 ArrayExpress b31n045 b31n045.txt P-MEXP-9965 ArrayExpress b31n045 b31n045_norm.txt Bpe335 "Other US post-vaccine"
+Bpe336 whole_organism "Bordetella pertussis" Bpe336 P-MEXP-9739 ArrayExpress Bpe336 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe336 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe336 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n046 a-mexp-281! P-MEXP-9747 ArrayExpress b31n046 b31n046.txt P-MEXP-9965 ArrayExpress b31n046 b31n046_norm.txt Bpe336 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n046 a-mexp-281! P-MEXP-9747 ArrayExpress b31n046 b31n046.txt P-MEXP-9965 ArrayExpress b31n046 b31n046_norm.txt Bpe336 "Other US post-vaccine"
+Bpe337 whole_organism "Bordetella pertussis" Bpe337 P-MEXP-9739 ArrayExpress Bpe337 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe337 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe337 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n047 a-mexp-281! P-MEXP-9747 ArrayExpress b31n047 b31n047.txt P-MEXP-9965 ArrayExpress b31n047 b31n047_norm.txt Bpe337 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n047 a-mexp-281! P-MEXP-9747 ArrayExpress b31n047 b31n047.txt P-MEXP-9965 ArrayExpress b31n047 b31n047_norm.txt Bpe337 "Other US post-vaccine"
+Bpe338 whole_organism "Bordetella pertussis" Bpe338 P-MEXP-9739 ArrayExpress Bpe338 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe338 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe338 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n048 a-mexp-281! P-MEXP-9747 ArrayExpress b31n048 b31n048.txt P-MEXP-9965 ArrayExpress b31n048 b31n048_norm.txt Bpe338 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n048 a-mexp-281! P-MEXP-9747 ArrayExpress b31n048 b31n048.txt P-MEXP-9965 ArrayExpress b31n048 b31n048_norm.txt Bpe338 "Other US post-vaccine"
+Bpe339 whole_organism "Bordetella pertussis" Bpe339 P-MEXP-9739 ArrayExpress Bpe339 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe339 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe339 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n049 a-mexp-281! P-MEXP-9747 ArrayExpress b31n049 b31n049.txt P-MEXP-9965 ArrayExpress b31n049 b31n049_norm.txt Bpe339 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n049 a-mexp-281! P-MEXP-9747 ArrayExpress b31n049 b31n049.txt P-MEXP-9965 ArrayExpress b31n049 b31n049_norm.txt Bpe339 "Other US post-vaccine"
+Bpe340 whole_organism "Bordetella pertussis" Bpe340 P-MEXP-9739 ArrayExpress Bpe340 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe340 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe340 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n050 a-mexp-281! P-MEXP-9747 ArrayExpress b31n050 b31n050.txt P-MEXP-9965 ArrayExpress b31n050 b31n050_norm.txt Bpe340 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n050 a-mexp-281! P-MEXP-9747 ArrayExpress b31n050 b31n050.txt P-MEXP-9965 ArrayExpress b31n050 b31n050_norm.txt Bpe340 "Other US post-vaccine"
+Bpe341 whole_organism "Bordetella pertussis" Bpe341 P-MEXP-9739 ArrayExpress Bpe341 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe341 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe341 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n051 a-mexp-281! P-MEXP-9747 ArrayExpress b31n051 b31n051.txt P-MEXP-9965 ArrayExpress b31n051 b31n051_norm.txt Bpe341 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n051 a-mexp-281! P-MEXP-9747 ArrayExpress b31n051 b31n051.txt P-MEXP-9965 ArrayExpress b31n051 b31n051_norm.txt Bpe341 "Other US post-vaccine"
+Bpe342 whole_organism "Bordetella pertussis" Bpe342 P-MEXP-9739 ArrayExpress Bpe342 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe342 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe342 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n052 a-mexp-281! P-MEXP-9747 ArrayExpress b31n052 b31n052.txt P-MEXP-9965 ArrayExpress b31n052 b31n052_norm.txt Bpe342 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n052 a-mexp-281! P-MEXP-9747 ArrayExpress b31n052 b31n052.txt P-MEXP-9965 ArrayExpress b31n052 b31n052_norm.txt Bpe342 "Other US post-vaccine"
+Bpe343 whole_organism "Bordetella pertussis" Bpe343 P-MEXP-9739 ArrayExpress Bpe343 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe343 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe343 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n053 a-mexp-281! P-MEXP-9747 ArrayExpress b31n053 b31n053.txt P-MEXP-9965 ArrayExpress b31n053 b31n053_norm.txt Bpe343 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n053 a-mexp-281! P-MEXP-9747 ArrayExpress b31n053 b31n053.txt P-MEXP-9965 ArrayExpress b31n053 b31n053_norm.txt Bpe343 "Other US post-vaccine"
+Bpe344 whole_organism "Bordetella pertussis" Bpe344 P-MEXP-9739 ArrayExpress Bpe344 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe344 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe344 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n054 a-mexp-281! P-MEXP-9747 ArrayExpress b31n054 b31n054.txt P-MEXP-9965 ArrayExpress b31n054 b31n054_norm.txt Bpe344 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n054 a-mexp-281! P-MEXP-9747 ArrayExpress b31n054 b31n054.txt P-MEXP-9965 ArrayExpress b31n054 b31n054_norm.txt Bpe344 "Other US post-vaccine"
+Bpe345 whole_organism "Bordetella pertussis" Bpe345 P-MEXP-9739 ArrayExpress Bpe345 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe345 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe345 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n055 a-mexp-281! P-MEXP-9747 ArrayExpress b31n055 b31n055.txt P-MEXP-9965 ArrayExpress b31n055 b31n055_norm.txt Bpe345 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n055 a-mexp-281! P-MEXP-9747 ArrayExpress b31n055 b31n055.txt P-MEXP-9965 ArrayExpress b31n055 b31n055_norm.txt Bpe345 "Other US post-vaccine"
+Bpe346 whole_organism "Bordetella pertussis" Bpe346 P-MEXP-9739 ArrayExpress Bpe346 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe346 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe346 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n056 a-mexp-281! P-MEXP-9747 ArrayExpress b31n056 b31n056.txt P-MEXP-9965 ArrayExpress b31n056 b31n056_norm.txt Bpe346 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n056 a-mexp-281! P-MEXP-9747 ArrayExpress b31n056 b31n056.txt P-MEXP-9965 ArrayExpress b31n056 b31n056_norm.txt Bpe346 "Other US post-vaccine"
+Bpe347 whole_organism "Bordetella pertussis" Bpe347 P-MEXP-9739 ArrayExpress Bpe347 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe347 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe347 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n057 a-mexp-281! P-MEXP-9747 ArrayExpress b31n057 b31n057.txt P-MEXP-9965 ArrayExpress b31n057 b31n057_norm.txt Bpe347 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n057 a-mexp-281! P-MEXP-9747 ArrayExpress b31n057 b31n057.txt P-MEXP-9965 ArrayExpress b31n057 b31n057_norm.txt Bpe347 "Other US post-vaccine"
+Bpe348 whole_organism "Bordetella pertussis" Bpe348 P-MEXP-9739 ArrayExpress Bpe348 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe348 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe348 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n058 a-mexp-281! P-MEXP-9747 ArrayExpress b31n058 b31n058.txt P-MEXP-9965 ArrayExpress b31n058 b31n058_norm.txt Bpe348 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n058 a-mexp-281! P-MEXP-9747 ArrayExpress b31n058 b31n058.txt P-MEXP-9965 ArrayExpress b31n058 b31n058_norm.txt Bpe348 "Other US post-vaccine"
+Bpe349 whole_organism "Bordetella pertussis" Bpe349 P-MEXP-9739 ArrayExpress Bpe349 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe349 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe349 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n059 a-mexp-281! P-MEXP-9747 ArrayExpress b31n059 b31n059.txt P-MEXP-9965 ArrayExpress b31n059 b31n059_norm.txt Bpe349 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n059 a-mexp-281! P-MEXP-9747 ArrayExpress b31n059 b31n059.txt P-MEXP-9965 ArrayExpress b31n059 b31n059_norm.txt Bpe349 "Other US post-vaccine"
+Bpe350 whole_organism "Bordetella pertussis" Bpe350 P-MEXP-9739 ArrayExpress Bpe350 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe350 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe350 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n060 a-mexp-281! P-MEXP-9747 ArrayExpress b31n060 b31n060.txt P-MEXP-9965 ArrayExpress b31n060 b31n060_norm.txt Bpe350 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n060 a-mexp-281! P-MEXP-9747 ArrayExpress b31n060 b31n060.txt P-MEXP-9965 ArrayExpress b31n060 b31n060_norm.txt Bpe350 "Other US post-vaccine"
+Bpe351 whole_organism "Bordetella pertussis" Bpe351 P-MEXP-9739 ArrayExpress Bpe351 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe351 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe351 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n061 a-mexp-281! P-MEXP-9747 ArrayExpress b31n061 b31n061.txt P-MEXP-9965 ArrayExpress b31n061 b31n061_norm.txt Bpe351 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n061 a-mexp-281! P-MEXP-9747 ArrayExpress b31n061 b31n061.txt P-MEXP-9965 ArrayExpress b31n061 b31n061_norm.txt Bpe351 "Other US post-vaccine"
+Bpe352 whole_organism "Bordetella pertussis" Bpe352 P-MEXP-9739 ArrayExpress Bpe352 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe352 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe352 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n062 a-mexp-281! P-MEXP-9747 ArrayExpress b31n062 b31n062.txt P-MEXP-9965 ArrayExpress b31n062 b31n062_norm.txt Bpe352 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n062 a-mexp-281! P-MEXP-9747 ArrayExpress b31n062 b31n062.txt P-MEXP-9965 ArrayExpress b31n062 b31n062_norm.txt Bpe352 "Other US post-vaccine"
+Bpe353 whole_organism "Bordetella pertussis" Bpe353 P-MEXP-9739 ArrayExpress Bpe353 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe353 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe353 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b31n063 a-mexp-281! P-MEXP-9747 ArrayExpress b31n063 b31n063.txt P-MEXP-9965 ArrayExpress b31n063 b31n063_norm.txt Bpe353 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b31n063 a-mexp-281! P-MEXP-9747 ArrayExpress b31n063 b31n063.txt P-MEXP-9965 ArrayExpress b31n063 b31n063_norm.txt Bpe353 "Other US post-vaccine"
+Bpe159 whole_organism "Bordetella pertussis" Bpe159 P-MEXP-9739 ArrayExpress Bpe159 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe159 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe159 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n006 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n006 b23n006.txt P-MEXP-9965 ArrayExpress b23n006 b23n006_norm.txt Bpe159 "Other US post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n006 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n006 b23n006.txt P-MEXP-9965 ArrayExpress b23n006 b23n006_norm.txt Bpe159 "Other US post-vaccine"
+Bpe205 whole_organism "Bordetella pertussis" Bpe205 P-MEXP-9739 ArrayExpress Bpe205 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe205 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe205 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n083 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n083 b23n083.txt P-MEXP-9965 ArrayExpress b23n083 b23n083_norm.txt Bpe205 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n083 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n083 b23n083.txt P-MEXP-9965 ArrayExpress b23n083 b23n083_norm.txt Bpe205 "Dutch pre-vaccine"
+Bpe186 whole_organism "Bordetella pertussis" Bpe186 P-MEXP-9739 ArrayExpress Bpe186 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe186 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe186 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n109 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n109 b23n109.txt P-MEXP-9965 ArrayExpress b23n109 b23n109_norm.txt Bpe186 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n109 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n109 b23n109.txt P-MEXP-9965 ArrayExpress b23n109 b23n109_norm.txt Bpe186 "Dutch pre-vaccine"
+Bpe191 whole_organism "Bordetella pertussis" Bpe191 P-MEXP-9739 ArrayExpress Bpe191 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe191 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe191 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n114 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n114 b23n114.txt P-MEXP-9965 ArrayExpress b23n114 b23n114_norm.txt Bpe191 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n114 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n114 b23n114.txt P-MEXP-9965 ArrayExpress b23n114 b23n114_norm.txt Bpe191 "Dutch pre-vaccine"
+Bpe202 whole_organism "Bordetella pertussis" Bpe202 P-MEXP-9739 ArrayExpress Bpe202 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe202 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe202 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n061 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n061 b24n061.txt P-MEXP-9965 ArrayExpress b24n061 b24n061_norm.txt Bpe202 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n061 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n061 b24n061.txt P-MEXP-9965 ArrayExpress b24n061 b24n061_norm.txt Bpe202 "Dutch pre-vaccine"
+Bpe197 whole_organism "Bordetella pertussis" Bpe197 P-MEXP-9739 ArrayExpress Bpe197 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe197 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe197 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b24n056 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n056 b24n056.txt P-MEXP-9965 ArrayExpress b24n056 b24n056_norm.txt Bpe197 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n056 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n056 b24n056.txt P-MEXP-9965 ArrayExpress b24n056 b24n056_norm.txt Bpe197 "Dutch pre-vaccine"
+Bpe182 whole_organism "Bordetella pertussis" Bpe182 P-MEXP-9739 ArrayExpress Bpe182 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe182 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe182 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n105 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n105 b23n105.txt P-MEXP-9965 ArrayExpress b23n105 b23n105_norm.txt Bpe182 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n105 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n105 b23n105.txt P-MEXP-9965 ArrayExpress b23n105 b23n105_norm.txt Bpe182 "Dutch pre-vaccine"
+Bpe195 whole_organism "Bordetella pertussis" Bpe195 P-MEXP-9739 ArrayExpress Bpe195 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe195 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe195 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n118 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n118 b23n118.txt P-MEXP-9965 ArrayExpress b23n118 b23n118_norm.txt Bpe195 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n118 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n118 b23n118.txt P-MEXP-9965 ArrayExpress b23n118 b23n118_norm.txt Bpe195 "Dutch pre-vaccine"
+Bpe196 whole_organism "Bordetella pertussis" Bpe196 P-MEXP-9739 ArrayExpress Bpe196 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe196 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe196 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n119 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n119 b23n119.txt P-MEXP-9965 ArrayExpress b23n119 b23n119_norm.txt Bpe196 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n119 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n119 b23n119.txt P-MEXP-9965 ArrayExpress b23n119 b23n119_norm.txt Bpe196 "Dutch pre-vaccine"
+Bpe179 whole_organism "Bordetella pertussis" Bpe179 P-MEXP-9739 ArrayExpress Bpe179 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe179 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe179 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n101 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n101 b23n101.txt P-MEXP-9965 ArrayExpress b23n101 b23n101_norm.txt Bpe179 "Dutch pre-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n101 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n101 b23n101.txt P-MEXP-9965 ArrayExpress b23n101 b23n101_norm.txt Bpe179 "Dutch pre-vaccine"
+Bpe1 whole_organism "Bordetella pertussis" Bpe1 P-MEXP-9739 ArrayExpress Bpe1 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe1 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe1 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n101 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n101 b29n101.txt P-MEXP-9965 ArrayExpress b29n101 b29n101_norm.txt Bpe1 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n101 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n101 b29n101.txt P-MEXP-9965 ArrayExpress b29n101 b29n101_norm.txt Bpe1 "Dutch post-vaccine"
+Bpe11 whole_organism "Bordetella pertussis" Bpe11 P-MEXP-9739 ArrayExpress Bpe11 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe11 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe11 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n102 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n102 b29n102.txt P-MEXP-9965 ArrayExpress b29n102 b29n102_norm.txt Bpe11 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n102 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n102 b29n102.txt P-MEXP-9965 ArrayExpress b29n102 b29n102_norm.txt Bpe11 "Dutch post-vaccine"
+Bpe14 whole_organism "Bordetella pertussis" Bpe14 P-MEXP-9739 ArrayExpress Bpe14 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe14 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe14 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n103 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n103 b29n103.txt P-MEXP-9965 ArrayExpress b29n103 b29n103_norm.txt Bpe14 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n103 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n103 b29n103.txt P-MEXP-9965 ArrayExpress b29n103 b29n103_norm.txt Bpe14 "Dutch post-vaccine"
+Bpe15 whole_organism "Bordetella pertussis" Bpe15 P-MEXP-9739 ArrayExpress Bpe15 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe15 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe15 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b29n104 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n104 b29n104.txt P-MEXP-9965 ArrayExpress b29n104 b29n104_norm.txt Bpe15 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n104 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n104 b29n104.txt P-MEXP-9965 ArrayExpress b29n104 b29n104_norm.txt Bpe15 "Dutch post-vaccine"
+Bpe206 whole_organism "Bordetella pertussis" Bpe206 P-MEXP-9739 ArrayExpress Bpe206 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe206 genomic_DNA P-MEXP-9967 ArrayExpress "Bpe206 Cy5" synthetic_DNA Cy5 P-MEXP-9746 ArrayExpress b23n085 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n085 b23n085.txt P-MEXP-9965 ArrayExpress b23n085 b23n085_norm.txt Bpe206 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n085 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n085 b23n085.txt P-MEXP-9965 ArrayExpress b23n085 b23n085_norm.txt Bpe206 "Dutch post-vaccine"
+Bpe180 whole_organism "Bordetella pertussis" Bpe180 P-MEXP-9739 ArrayExpress Bpe180 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe180 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe180 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n103 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n103 b23n103.txt P-MEXP-9965 ArrayExpress b23n103 b23n103_norm.txt Bpe180 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n103 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n103 b23n103.txt P-MEXP-9965 ArrayExpress b23n103 b23n103_norm.txt Bpe180 "Dutch post-vaccine"
+Bpe181 whole_organism "Bordetella pertussis" Bpe181 P-MEXP-9739 ArrayExpress Bpe181 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe181 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe181 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n104 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n104 b23n104.txt P-MEXP-9965 ArrayExpress b23n104 b23n104_norm.txt Bpe181 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n104 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n104 b23n104.txt P-MEXP-9965 ArrayExpress b23n104 b23n104_norm.txt Bpe181 "Dutch post-vaccine"
+Bpe183 whole_organism "Bordetella pertussis" Bpe183 P-MEXP-9739 ArrayExpress Bpe183 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe183 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe183 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n106 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n106 b23n106.txt P-MEXP-9965 ArrayExpress b23n106 b23n106_norm.txt Bpe183 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n106 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n106 b23n106.txt P-MEXP-9965 ArrayExpress b23n106 b23n106_norm.txt Bpe183 "Dutch post-vaccine"
+Bpe184 whole_organism "Bordetella pertussis" Bpe184 P-MEXP-9739 ArrayExpress Bpe184 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe184 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe184 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n107 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n107 b23n107.txt P-MEXP-9965 ArrayExpress b23n107 b23n107_norm.txt Bpe184 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n107 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n107 b23n107.txt P-MEXP-9965 ArrayExpress b23n107 b23n107_norm.txt Bpe184 "Dutch post-vaccine"
+Bpe185 whole_organism "Bordetella pertussis" Bpe185 P-MEXP-9739 ArrayExpress Bpe185 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe185 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe185 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n108 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n108 b23n108.txt P-MEXP-9965 ArrayExpress b23n108 b23n108_norm.txt Bpe185 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n108 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n108 b23n108.txt P-MEXP-9965 ArrayExpress b23n108 b23n108_norm.txt Bpe185 "Dutch post-vaccine"
+Bpe187 whole_organism "Bordetella pertussis" Bpe187 P-MEXP-9739 ArrayExpress Bpe187 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe187 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe187 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n110 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n110 b23n110.txt P-MEXP-9965 ArrayExpress b23n110 b23n110_norm.txt Bpe187 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n110 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n110 b23n110.txt P-MEXP-9965 ArrayExpress b23n110 b23n110_norm.txt Bpe187 "Dutch post-vaccine"
+Bpe188 whole_organism "Bordetella pertussis" Bpe188 P-MEXP-9739 ArrayExpress Bpe188 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe188 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe188 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n111 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n111 b23n111.txt P-MEXP-9965 ArrayExpress b23n111 b23n111_norm.txt Bpe188 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n111 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n111 b23n111.txt P-MEXP-9965 ArrayExpress b23n111 b23n111_norm.txt Bpe188 "Dutch post-vaccine"
+Bpe189 whole_organism "Bordetella pertussis" Bpe189 P-MEXP-9739 ArrayExpress Bpe189 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe189 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe189 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n112 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n112 b23n112.txt P-MEXP-9965 ArrayExpress b23n112 b23n112_norm.txt Bpe189 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n112 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n112 b23n112.txt P-MEXP-9965 ArrayExpress b23n112 b23n112_norm.txt Bpe189 "Dutch post-vaccine"
+Bpe190 whole_organism "Bordetella pertussis" Bpe190 P-MEXP-9739 ArrayExpress Bpe190 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe190 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe190 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n113 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n113 b23n113.txt P-MEXP-9965 ArrayExpress b23n113 b23n113_norm.txt Bpe190 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n113 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n113 b23n113.txt P-MEXP-9965 ArrayExpress b23n113 b23n113_norm.txt Bpe190 "Dutch post-vaccine"
+Bpe192 whole_organism "Bordetella pertussis" Bpe192 P-MEXP-9739 ArrayExpress Bpe192 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe192 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe192 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n115 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n115 b23n115.txt P-MEXP-9965 ArrayExpress b23n115 b23n115_norm.txt Bpe192 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n115 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n115 b23n115.txt P-MEXP-9965 ArrayExpress b23n115 b23n115_norm.txt Bpe192 "Dutch post-vaccine"
+Bpe193 whole_organism "Bordetella pertussis" Bpe193 P-MEXP-9739 ArrayExpress Bpe193 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe193 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe193 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n116 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n116 b23n116.txt P-MEXP-9965 ArrayExpress b23n116 b23n116_norm.txt Bpe193 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n116 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n116 b23n116.txt P-MEXP-9965 ArrayExpress b23n116 b23n116_norm.txt Bpe193 "Dutch post-vaccine"
+Bpe194 whole_organism "Bordetella pertussis" Bpe194 P-MEXP-9739 ArrayExpress Bpe194 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe194 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe194 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n117 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n117 b23n117.txt P-MEXP-9965 ArrayExpress b23n117 b23n117_norm.txt Bpe194 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n117 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n117 b23n117.txt P-MEXP-9965 ArrayExpress b23n117 b23n117_norm.txt Bpe194 "Dutch post-vaccine"
+Bpe204 whole_organism "Bordetella pertussis" Bpe204 P-MEXP-9739 ArrayExpress Bpe204 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe204 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe204 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n009 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n009 b24n009.txt P-MEXP-9965 ArrayExpress b24n009 b24n009_norm.txt Bpe204 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n009 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n009 b24n009.txt P-MEXP-9965 ArrayExpress b24n009 b24n009_norm.txt Bpe204 "Dutch post-vaccine"
+Bpe198 whole_organism "Bordetella pertussis" Bpe198 P-MEXP-9739 ArrayExpress Bpe198 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe198 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe198 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n057 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n057 b24n057.txt P-MEXP-9965 ArrayExpress b24n057 b24n057_norm.txt Bpe198 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n057 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n057 b24n057.txt P-MEXP-9965 ArrayExpress b24n057 b24n057_norm.txt Bpe198 "Dutch post-vaccine"
+Bpe203 whole_organism "Bordetella pertussis" Bpe203 P-MEXP-9739 ArrayExpress Bpe203 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe203 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe203 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n110 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n110 b29n110.txt P-MEXP-9965 ArrayExpress b29n110 b29n110_norm.txt Bpe203 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b29n110 A-MEXP-236 P-MEXP-9747 ArrayExpress b29n110 b29n110.txt P-MEXP-9965 ArrayExpress b29n110 b29n110_norm.txt Bpe203 "Dutch post-vaccine"
+Bpe199 whole_organism "Bordetella pertussis" Bpe199 P-MEXP-9739 ArrayExpress Bpe199 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe199 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe199 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n058 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n058 b24n058.txt P-MEXP-9965 ArrayExpress b24n058 b24n058_norm.txt Bpe199 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n058 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n058 b24n058.txt P-MEXP-9965 ArrayExpress b24n058 b24n058_norm.txt Bpe199 "Dutch post-vaccine"
+Bpe200 whole_organism "Bordetella pertussis" Bpe200 P-MEXP-9739 ArrayExpress Bpe200 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe200 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe200 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n059 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n059 b24n059.txt P-MEXP-9965v ArrayExpress b24n059 b24n059_norm.txt Bpe200 "Dutch post-vaccine"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n059 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n059 b24n059.txt P-MEXP-9965 ArrayExpress b24n059 b24n059_norm.txt Bpe200 "Dutch post-vaccine"
+WCH01 whole_organism "Bordetella pertussis" WCH01 P-MEXP-9739 ArrayExpress WCH01 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH01 genomic_DNA P-MEXP-9745 ArrayExpress "WCH01 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n086 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n086 b23n086.txt P-MEXP-9965 ArrayExpress b23n086 b23n086_norm.txt WCH01 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n086 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n086 b23n086.txt P-MEXP-9965 ArrayExpress b23n086 b23n086_norm.txt WCH01 Australian
+WCH02 whole_organism "Bordetella pertussis" WCH02 P-MEXP-9739 ArrayExpress WCH02 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH02 genomic_DNA P-MEXP-9745 ArrayExpress "WCH02 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n087 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n087 b23n087.txt P-MEXP-9965 ArrayExpress b23n087 b23n087_norm.txt WCH02 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n087 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n087 b23n087.txt P-MEXP-9965 ArrayExpress b23n087 b23n087_norm.txt WCH02 Australian
+WCH03 whole_organism "Bordetella pertussis" WCH03 P-MEXP-9739 ArrayExpress WCH03 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH03 genomic_DNA P-MEXP-9745 ArrayExpress "WCH03 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n088 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n088 b23n088.txt P-MEXP-9965 ArrayExpress b23n088 b23n088_norm.txt WCH03 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n088 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n088 b23n088.txt P-MEXP-9965 ArrayExpress b23n088 b23n088_norm.txt WCH03 Australian
+WCH04 whole_organism "Bordetella pertussis" WCH04 P-MEXP-9739 ArrayExpress WCH04 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH04 genomic_DNA P-MEXP-9745 ArrayExpress "WCH04 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n089 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n089 b23n089.txt P-MEXP-9965 ArrayExpress b23n089 b23n089_norm.txt WCH04 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n089 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n089 b23n089.txt P-MEXP-9965 ArrayExpress b23n089 b23n089_norm.txt WCH04 Australian
+WCH07 whole_organism "Bordetella pertussis" WCH07 P-MEXP-9739 ArrayExpress WCH07 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH07 genomic_DNA P-MEXP-9745 ArrayExpress "WCH07 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n090 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n090 b23n090.txt P-MEXP-9965 ArrayExpress b23n090 b23n090_norm.txt WCH07 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n090 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n090 b23n090.txt P-MEXP-9965 ArrayExpress b23n090 b23n090_norm.txt WCH07 Australian
+WCH08 whole_organism "Bordetella pertussis" WCH08 P-MEXP-9739 ArrayExpress WCH08 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH08 genomic_DNA P-MEXP-9745 ArrayExpress "WCH08 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n091 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n091 b23n091.txt P-MEXP-9965 ArrayExpress b23n091 b23n091_norm.txt WCH08 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n091 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n091 b23n091.txt P-MEXP-9965 ArrayExpress b23n091 b23n091_norm.txt WCH08 Australian
+WCH23 whole_organism "Bordetella pertussis" WCH23 P-MEXP-9739 ArrayExpress WCH23 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH23 genomic_DNA P-MEXP-9745 ArrayExpress "WCH23 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n092 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n092 b23n092.txt P-MEXP-9965 ArrayExpress b23n092 b23n092_norm.txt WCH23 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n092 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n092 b23n092.txt P-MEXP-9965 ArrayExpress b23n092 b23n092_norm.txt WCH23 Australian
+WCH05 whole_organism "Bordetella pertussis" WCH05 P-MEXP-9739 ArrayExpress WCH05 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH05 genomic_DNA P-MEXP-9745 ArrayExpress "WCH05 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n085 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n085 b24n085.txt P-MEXP-9965 ArrayExpress b24n085 b24n085_norm.txt WCH05 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n085 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n085 b24n085.txt P-MEXP-9965 ArrayExpress b24n085 b24n085_norm.txt WCH05 Australian
+WCH06 whole_organism "Bordetella pertussis" WCH06 P-MEXP-9739 ArrayExpress WCH06 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH06 genomic_DNA P-MEXP-9745 ArrayExpress "WCH06 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n086 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n086 b24n086.txt P-MEXP-9965 ArrayExpress b24n086 b24n086_norm.txt WCH06 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n086 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n086 b24n086.txt P-MEXP-9965 ArrayExpress b24n086 b24n086_norm.txt WCH06 Australian
+WCH11 whole_organism "Bordetella pertussis" WCH11 P-MEXP-9739 ArrayExpress WCH11 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH11 genomic_DNA P-MEXP-9745 ArrayExpress "WCH11 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n087 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n087 b24n087.txt P-MEXP-9965 ArrayExpress b24n087 b24n087_norm.txt WCH11 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n087 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n087 b24n087.txt P-MEXP-9965 ArrayExpress b24n087 b24n087_norm.txt WCH11 Australian
+WCH12 whole_organism "Bordetella pertussis" WCH12 P-MEXP-9739 ArrayExpress WCH12 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH12 genomic_DNA P-MEXP-9745 ArrayExpress "WCH12 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n088 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n088 b24n088.txt P-MEXP-9965 ArrayExpress b24n088 b24n088_norm.txt WCH12 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n088 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n088 b24n088.txt P-MEXP-9965 ArrayExpress b24n088 b24n088_norm.txt WCH12 Australian
+WCH19 whole_organism "Bordetella pertussis" WCH19 P-MEXP-9739 ArrayExpress WCH19 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH19 genomic_DNA P-MEXP-9745 ArrayExpress "WCH19 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n089 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n089 b24n089.txt P-MEXP-9965 ArrayExpress b24n089 b24n089_norm.txt WCH19 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n089 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n089 b24n089.txt P-MEXP-9965 ArrayExpress b24n089 b24n089_norm.txt WCH19 Australian
+WCH20 whole_organism "Bordetella pertussis" WCH20 P-MEXP-9739 ArrayExpress WCH20 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH20 genomic_DNA P-MEXP-9745 ArrayExpress "WCH20 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n090 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n090 b24n090.txt P-MEXP-9965 ArrayExpress b24n090 b24n090_norm.txt WCH20 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n090 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n090 b24n090.txt P-MEXP-9965 ArrayExpress b24n090 b24n090_norm.txt WCH20 Australian
+WCH21 whole_organism "Bordetella pertussis" WCH21 P-MEXP-9739 ArrayExpress WCH21 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH21 genomic_DNA P-MEXP-9745 ArrayExpress "WCH21 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n091 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n091 b24n091.txt P-MEXP-9965 ArrayExpress b24n091 b24n091_norm.txt WCH21 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n091 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n091 b24n091.txt P-MEXP-9965 ArrayExpress b24n091 b24n091_norm.txt WCH21 Australian
+WCH22 whole_organism "Bordetella pertussis" WCH22 P-MEXP-9739 ArrayExpress WCH22 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH22 genomic_DNA P-MEXP-9745 ArrayExpress "WCH22 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n092 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n092 b24n092.txt P-MEXP-9965 ArrayExpress b24n092 b24n092_norm.txt WCH22 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n092 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n092 b24n092.txt P-MEXP-9965 ArrayExpress b24n092 b24n092_norm.txt WCH22 Australian
+WCH24 whole_organism "Bordetella pertussis" WCH24 P-MEXP-9739 ArrayExpress WCH24 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress WCH24 genomic_DNA P-MEXP-9745 ArrayExpress "WCH24 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n093 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n093 b24n093.txt P-MEXP-9965 ArrayExpress b24n093 b24n093_norm.txt WCH24 Australian
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n093 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n093 b24n093.txt P-MEXP-9965 ArrayExpress b24n093 b24n093_norm.txt WCH24 Australian
+3588 whole_organism "Bordetella pertussis" 3588 P-MEXP-9739 ArrayExpress 3588 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 3588 genomic_DNA P-MEXP-9745 ArrayExpress "3588 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n043 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n043 b23n043.txt P-MEXP-9965 ArrayExpress b23n043 b23n043_norm.txt 3588 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n043 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n043 b23n043.txt P-MEXP-9965 ArrayExpress b23n043 b23n043_norm.txt 3588 "Italian (unvaccinat [...]
+10641 whole_organism "Bordetella pertussis" 10641 P-MEXP-9739 ArrayExpress 10641 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 10641 genomic_DNA P-MEXP-9745 ArrayExpress "10641 Cy3" synthetic_DvNA Cy3 P-MEXP-9746 ArrayExpress b23n044 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n044 b23n044.txt P-MEXP-9965 ArrayExpress b23n044 b23n044_norm.txt 10641 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n044 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n044 b23n044.txt P-MEXP-9965 ArrayExpress b23n044 b23n044_norm.txt 10641 "Italian (unvaccina [...]
+15550 whole_organism "Bordetella pertussis" 15550 P-MEXP-9739 ArrayExpress 15550 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 15550 genomic_DNA P-MEXP-9745 ArrayExpress "15550 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n047 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n047 b23n047.txt P-MEXP-9965 ArrayExpress b23n047 b23n047_norm.txt 15550 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n047 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n047 b23n047.txt P-MEXP-9965 ArrayExpress b23n047 b23n047_norm.txt 15550 "Italian (unvaccina [...]
+9859 whole_organism "Bordetella pertussis" 9859 P-MEXP-9739 ArrayExpress 9859 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 9859 genomic_DNA P-MEXP-9745 ArrayExpress "9859 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n051 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n051 b23n051.txt P-MEXP-9965 ArrayExpress b23n051 b23n051_norm.txt 9859 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n051 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n051 b23n051.txt P-MEXP-9965 ArrayExpress b23n051 b23n051_norm.txt 9859 "Italian (unvaccinat [...]
+4558 whole_organism "Bordetella pertussis" 4558 P-MEXP-9739 ArrayExpress 4558 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 4558 genomic_DNA P-MEXP-9745 ArrayExpress "4558 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n058 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n058 b23n058.txt P-MEXP-9965 ArrayExpress b23n058 b23n058_norm.txt 4558 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n058 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n058 b23n058.txt P-MEXP-9965 ArrayExpress b23n058 b23n058_norm.txt 4558 "Italian (unvaccinat [...]
+Bp01568 whole_organism "Bordetella pertussis" Bp01568 P-MEXP-9739 ArrayExpress Bp01568 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp01568 genomic_DNA P-MEXP-9745 ArrayExpress "Bp01568 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n011 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n011 b24n011.txt P-MEXP-9965 ArrayExpress b24n011 b24n011_norm.txt Bp01568 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n011 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n011 b24n011.txt P-MEXP-9965 ArrayExpress b24n011 b24n011_norm.txt Bp01568 "Italian (unvacci [...]
+Bp12892 whole_organism "Bordetella pertussis" Bp12892 P-MEXP-9739 ArrayExpress Bp12892 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp12892 genomic_DNA P-MEXP-9745 ArrayExpress "Bp12892 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n012 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n012 b24n012.txt P-MEXP-9965 ArrayExpress b24n012 b24n012_norm.txt Bp12892 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n012 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n012 b24n012.txt P-MEXP-9965 ArrayExpress b24n012 b24n012_norm.txt Bp12892 "Italian (unvacci [...]
+Bp05275 whole_organism "Bordetella pertussis" Bp05275 P-MEXP-9739 ArrayExpress Bp05275 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp05275 genomic_DNA P-MEXP-9745 ArrayExpress "Bp05275 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n013 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n013 b24n013.txt P-MEXP-9965 ArrayExpress b24n013 b24n013_norm.txt Bp05275 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n013 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n013 b24n013.txt P-MEXP-9965 ArrayExpress b24n013 b24n013_norm.txt Bp05275 "Italian (unvacci [...]
+Bp13732 whole_organism "Bordetella pertussis" Bp13732 P-MEXP-9739 ArrayExpress Bp13732 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp13732 genomic_DNA P-MEXP-9745 ArrayExpress "Bp13732 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n080 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n080 b24n080.txt P-MEXP-9965 ArrayExpress b24n080 b24n080_norm.txt Bp13732 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n080 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n080 b24n080.txt P-MEXP-9965 ArrayExpress b24n080 b24n080_norm.txt Bp13732 "Italian (unvacci [...]
+Bp12262 whole_organism "Bordetella pertussis" Bp12262 P-MEXP-9739 ArrayExpress Bp12262 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp12262 genomic_DNA P-MEXP-9745 ArrayExpress "Bp12262 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n082 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n082 b24n082.txt P-MEXP-9965 ArrayExpress b24n082 b24n082_norm.txt Bp12262 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n082 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n082 b24n082.txt P-MEXP-9965 ArrayExpress b24n082 b24n082_norm.txt Bp12262 "Italian (unvacci [...]
+Bp15622 whole_organism "Bordetella pertussis" Bp15622 P-MEXP-9739 ArrayExpress Bp15622 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp15622 genomic_DNA P-MEXP-9745 ArrayExpress "Bp15622 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n083 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n083 b24n083.txt P-MEXP-9965 ArrayExpress b24n083 b24n083_norm.txt Bp15622 "Italian (unvaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n083 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n083 b24n083.txt P-MEXP-9965 ArrayExpress b24n083 b24n083_norm.txt Bp15622 "Italian (unvacci [...]
+3520 whole_organism "Bordetella pertussis" 3520 P-MEXP-9739 ArrayExpress 3520 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 3520 genomic_DNA P-MEXP-9745 ArrayExpress "3520 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n041 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n041 b23n041.txt P-MEXP-9965 ArrayExpress b23n041 b23n041_norm.txt 3520 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n041 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n041 b23n041.txt P-MEXP-9965 ArrayExpress b23n041 b23n041_norm.txt 3520 "Italian (vaccinated [...]
+9878 whole_organism "Bordetella pertussis" 9878 P-MEXP-9739 ArrayExpress 9878 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 9878 genomic_DNA P-MEXP-9745 ArrayExpress "9878 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n042 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n042 b23n042.txt P-MEXP-9965 ArrayExpress b23n042 b23n042_norm.txt 9878 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n042 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n042 b23n042.txt P-MEXP-9965 ArrayExpress b23n042 b23n042_norm.txt 9878 "Italian (vaccinated [...]
+15637 whole_organism "Bordetella pertussis" 15637 P-MEXP-9739 ArrayExpress 15637 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 15637 genomic_DNA P-MEXP-9745 ArrayExpress "15637 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n050 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n050 b23n050.txt P-MEXP-9965 ArrayExpress b23n050 b23n050_norm.txt 15637 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n050 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n050 b23n050.txt P-MEXP-9965 ArrayExpress b23n050 b23n050_norm.txt 15637 "Italian (vaccinate [...]
+6453 whole_organism "Bordetella pertussis" 6453 P-MEXP-9739 ArrayExpress 6453 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 6453 genomic_DNA P-MEXP-9745 ArrayExpress "6453 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n052 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n052 b23n052.txt P-MEXP-9965 ArrayExpress b23n052 b23n052_norm.txt 6453 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n052 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n052 b23n052.txt P-MEXP-9965 ArrayExpress b23n052 b23n052_norm.txt 6453 "Italian (vaccinated [...]
+9585 whole_organism "Bordetella pertussis" 9585 P-MEXP-9739 ArrayExpress 9585 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 9585 genomic_DNA P-MEXP-9745 ArrayExpress "9585 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n056 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n056 b23n056.txt P-MEXP-9965 ArrayExpress b23n056 b23n056_norm.txt 9585 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n056 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n056 b23n056.txt P-MEXP-9965 ArrayExpress b23n056 b23n056_norm.txt 9585 "Italian (vaccinated [...]
+14782 whole_organism "Bordetella pertussis" 14782 P-MEXP-9739 ArrayExpress 14782 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 14782 genomic_DNA P-MEXP-9745 ArrayExpress "14782 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n057 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n057 b23n057.txt P-MEXP-9965 ArrayExpress b23n057 b23n057_norm.txt 14782 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n057 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n057 b23n057.txt P-MEXP-9965 ArrayExpress b23n057 b23n057_norm.txt 14782 "Italian (vaccinate [...]
+12464 whole_organism "Bordetella pertussis" 12464 P-MEXP-9739 ArrayExpress 12464 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress 12464 genomic_DNA P-MEXP-9745 ArrayExpress "12464 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n059 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n059 b23n059.txt P-MEXP-9965 ArrayExpress b23n059 b23n059_norm.txt 12464 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n059 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n059 b23n059.txt P-MEXP-9965 ArrayExpress b23n059 b23n059_norm.txt 12464 "Italian (vaccinate [...]
+Bp01994 whole_organism "Bordetella pertussis" Bp01994 P-MEXP-9739 ArrayExpress Bp01994 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp01994 genomic_DNA P-MEXP-9745 ArrayExpress "Bp01994 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n076 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n076 b24n076.txt P-MEXP-9965 ArrayExpress b24n076 b24n076_norm.txt Bp01994 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n076 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n076 b24n076.txt P-MEXP-9965 ArrayExpress b24n076 b24n076_norm.txt Bp01994 "Italian (vaccina [...]
+Bp03975 whole_organism "Bordetella pertussis" Bp03975 P-MEXP-9739 ArrayExpress Bp03975 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp03975 genomic_DNA P-MEXP-9745 ArrayExpress "Bp03975 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n081 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n081 b24n081.txt P-MEXP-9965 ArrayExpress b24n081 b24n081_norm.txt Bp03975 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n081 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n081 b24n081.txt P-MEXP-9965 ArrayExpress b24n081 b24n081_norm.txt Bp03975 "Italian (vaccina [...]
+Bp04844 whole_organism "Bordetella pertussis" Bp04844 P-MEXP-9739 ArrayExpress Bp04844 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bp04844 genomic_DNA P-MEXP-9745 ArrayExpress "Bp04844 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n084 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n084 b24n084.txt P-MEXP-9965 ArrayExpress b24n084 b24n084_norm.txt Bp04844 "Italian (vaccinated children)"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b24n084 A-MEXP-234 P-MEXP-9747 ArrayExpress b24n084 b24n084.txt P-MEXP-9965 ArrayExpress b24n084 b24n084_norm.txt Bp04844 "Italian (vaccina [...]
+Bpe53 whole_organism "Bordetella pertussis" Bpe53 P-MEXP-9739 ArrayExpress Bpe53 whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress Bpe53 genomic_DNA P-MEXP-9745 ArrayExpress "Bpe53 Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n093 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n093 b23n093.txt P-MEXP-9965 ArrayExpress b23n093 b23n093_norm.txt Bpe53 "18323 laboratory strain"
+"Bordetella reference strains" whole_organism "B. bronchiseptica + B. parapertussis + B. pertussis" "Bordetella reference strains" P-MEXP-9739 ArrayExpress "DNA reference" whole_organism P-MEXP-9744 ArrayExpress P-MEXP-9958 ArrayExpress "DNA reference" genomic_DNA P-MEXP-9745 ArrayExpress "DNA reference Cy3" synthetic_DNA Cy3 P-MEXP-9746 ArrayExpress b23n093 A-MEXP-234 P-MEXP-9747 ArrayExpress b23n093 b23n093.txt P-MEXP-9965 ArrayExpress b23n093 b23n093_norm.txt Bpe53 "18323 laboratory strain"
diff --git a/examples/magetab/real/E-TABM-66.idf b/examples/magetab/real/E-TABM-66.idf
new file mode 100644
index 0000000..1fd8320
--- /dev/null
+++ b/examples/magetab/real/E-TABM-66.idf
@@ -0,0 +1,29 @@
+"Investigation Title" "Comparison of normal and malignant human breast epithelial cells"
+"Experimental Design" cell_type_comparison_design
+"Experimental Factor Name" DISEASESTATE CELLTYPE
+"Experimental Factor Type" disease_state cell_type
+
+"Person Last Name" Grigoriadis
+"Person First Name" Anita
+"Person Mid Initial"
+"Person Affiliation"
+"Person Roles" submitter
+
+"Public Release Date"
+
+Comment[ArrayExpressSubmissionDate]
+
+"Publication Author List"
+"Publication Title"
+"Experiment Description" "Multiple genome-wide microarrays have been used to analyse the transcriptomes of immunomagnetically separated normal human luminal epithelial and myoepithelial cells, as well as primary malignant breast epithelial cells."
+
+"Protocol Name" P-MEXP-10843 P-MEXP-10844 P-MEXP-10845 P-TABM-AnitaG-10115 P-MEXP-14985 P-MEXP-14987 P-MEXP-14988
+"Protocol Type" pool pool pool labeling labeling hybridization image_acquisition
+"Protocol Description" "100 ug total RNA of 352,453,463,621,688,697,723,729,794,1098 were combined." "100 ug total RNA of 723,729,697,822,1016,1031,1040,1053,1123,1130 were combined." "50ug total RNA of 489,552,593,695,725,842,845,896,983,1000,1033,1045,1052,1062,1063 were combined" "1. One round of amplification through to dye coupling reaction performed according to Amino Allyl MessageAmp aRNA Kit protocol (Ambion, cat #1752 http://www.ambion.com/techlib/prot/fm_1752.pdf ). Cy3 monor [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" E-TABM-66_sdrf.txt
+
+"Term Source Name" ArrayExpress
+"Term Source File" http://www.ebi.ac.uk/arrayexpress/
diff --git a/examples/magetab/real/E-TABM-66_sdrf.txt b/examples/magetab/real/E-TABM-66_sdrf.txt
new file mode 100644
index 0000000..025e89b
--- /dev/null
+++ b/examples/magetab/real/E-TABM-66_sdrf.txt
@@ -0,0 +1,154 @@
+"Source Name" "Material Type" "Characteristics [Age]" "Characteristics [BioSourceProvider]" "Characteristics [CellType]" "Characteristics [ClinicalHistory]" "Characteristics [DevelopmentalStage]" "Characteristics [DiseaseState]" "Characteristics [Individual]" "Characteristics [OrganismPart]" "Characteristics [Organism]" "Characteristics [Sex]" "Characteristics [TimeUnit]" "Protocol REF" "Term Source REF" "Sample Name" "Material Type" "Protocol REF" "Term Source REF" "Protocol REF" "Extra [...]
+SAMPLE_human_1016 organism_part 30 "St. Anthony's Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_1016 "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_1016 organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy3_agil2" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress L_pool_cy3_agil2 A-MEXP-218 P-MEXP-10145 ArrayExpress L_pool_cy3_agil2 251239125464.gpr "luminal epith [...]
+SAMPLE_human_1031 organism_part 33 "St. Elisabeth Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_1031 "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_1031 organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy3_agil3" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress L_pool_cy3_agil3 A-MEXP-218 P-MEXP-10145 ArrayExpress L_pool_cy3_agil3 251239125951.gpr "luminal epith [...]
+SAMPLE_human_1040 organism_part 33 "Middlesex Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_1040 "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_1040 organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy5_agil1" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress L_pool_cy5_agil1 A-MEXP-218 P-MEXP-10145 ArrayExpress L_pool_cy5_agil1 251239119797.gpr "luminal epithelia [...]
+SAMPLE_human_1053 organism_part 22 "Ashstead Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_1053 "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_1053 organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy5_agil2" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress L_pool_cy5_agil2 A-MEXP-218 P-MEXP-10145 ArrayExpress L_pool_cy5_agil2 251239125237.gpr "luminal epithelial [...]
+SAMPLE_human_1123 organism_part 32 "University College London Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_1123 "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_1123 organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy5_agil3" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress L_pool_cy5_agil3 A-MEXP-218 P-MEXP-10145 ArrayExpress L_pool_cy5_agil3 251239125466.gpr "l [...]
+SAMPLE_human_1130 organism_part 32 "University College London Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_1130 "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_1130 organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress L_pool_cy3_breakthrough1 A-MEXP-259 P-MEXP-10145 ArrayExpress L_pool_cy3_breakthroug [...]
+SAMPLE_human_697L organism_part 29 "St. George's Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_697L "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_697L organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-AFFY-2 ArrayExpress "L_pool_affy3 biotin" synthetic_DNA biotin L_pool_affy3 A-AFFY-44 L_pool_affy3 L3.CEL L3.EXP "luminal epithelial" normal
+SAMPLE_human_723L organism_part 32 "St. George's Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_723L "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_723L organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-AFFY-2 ArrayExpress "L_pool_affy1 biotin" synthetic_DNA biotin L_pool_affy1 A-AFFY-44 L_pool_affy1 L1.CEL L1.EXP "luminal epithelial" normal
+SAMPLE_human_729L organism_part 31 "St. George's Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_729L "mammary gland" "Homo sapiens" female years P-MEXP-975 ArrayExpress SAMPLE_human_729L organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-AFFY-2 ArrayExpress "L_pool_affy2 biotin" synthetic_DNA biotin L_pool_affy2 A-AFFY-44 L_pool_affy2 L2.CEL L2.EXP "luminal epithelial" normal
+SAMPLE_human_822 organism_part "Royal Free Hospital, London,UK" "luminal epithelial" "Reduction mammoplasties" adult normal Human_822 "mammary gland" "Homo sapiens" female P-MEXP-975 ArrayExpress SAMPLE_human_822 organism_part P-MEXP-972 ArrayExpress P-MEXP-10844 E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy3_agil1" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress L_pool_cy3_agil1 A-MEXP-218 P-MEXP-10145 ArrayExpress L_pool_cy3_agil1 251239114659.gpr "luminal epithelial" normal
+ E_luminal_pool total_RNA P-MEXP-14985 "L_pool_amersham1" synthetic_DNA Cy5 P-MEXP-14987 L_pool_amersham1 A-GEHB-1 P-MEXP-14988 L_pool_amersham1 T00297797 T00297797.txt "luminal epithelial" normal
+ E_luminal_pool total_RNA P-MEXP-14985 "L_pool_amersham2" synthetic_DNA Cy5 P-MEXP-14987 L_pool_amersham2 A-GEHB-1 P-MEXP-14988 L_pool_amersham2 T00297799 T00297799.txt "luminal epithelial" normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress L_pool_cy5_breakthrough2 A-MEXP-259 P-MEXP-10145 ArrayExpress L_pool_cy5_breakthrough2 90000340.gpr "luminal epithelial" normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress L_pool_cy5_breakthrough3 A-MEXP-259 P-MEXP-10145 ArrayExpress L_pool_cy5_breakthrough3 90000418.gpr "luminal epithelial" normal
+ E_luminal_pool total_RNA P-MEXP-14985 "L_pool_amersham3" synthetic_DNA Cy5 P-MEXP-14987 L_pool_amersham3 A-GEHB-1 P-MEXP-14988 L_pool_amersham3 T00297800 T00297800.txt "luminal epithelial" normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress L_pool_cy3_breakthrough3 A-MEXP-259 P-MEXP-10145 ArrayExpress L_pool_cy3_breakthrough3 90000421.gpr "luminal epithelial" normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress L_pool_cy3_breakthrough2 A-MEXP-259 P-MEXP-10145 ArrayExpress L_pool_cy3_breakthrough2 90000343.gpr "luminal epithelial" normal
+ E_luminal_pool total_RNA P-MEXP-14985 "L_pool_amersham4" synthetic_DNA Cy5 P-MEXP-14987 L_pool_amersham4 A-GEHB-1 P-MEXP-14988 L_pool_amersham4 T00297801 T00297801.txt "luminal epithelial" normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "L_pool_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress L_pool_cy5_breakthrough1 A-MEXP-259 P-MEXP-10145 ArrayExpress L_pool_cy5_breakthrough1 90000310.gpr "luminal epithelial" normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough1 95000067.gpr luminal normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy3_breakthrough4" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough4 95000080.gpr luminal normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough3 95000079.gpr luminal normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy5_breakthrough4" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough4 95000074.gpr luminal normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough3 95000073.gpr luminal normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough2 95000068.gpr luminal normal
+ E_luminal_pool total_RNA P-MEXP-14985 "2nd_L_pool_amersham1" synthetic_DNA Cy5 P-MEXP-14987 2nd_L_pool_amersham1 A-GEHB-1 P-MEXP-14988 2nd_L_pool_amersham1 T00253433 T00253433.txt luminal normal
+ E_luminal_pool total_RNA P-MEXP-14985 "2nd_L_pool_amersham3" synthetic_DNA Cy5 P-MEXP-14987 2nd_L_pool_amersham3 A-GEHB-1 P-MEXP-14988 2nd_L_pool_amersham3 T00250228 T00250228.txt luminal normal
+ E_luminal_pool total_RNA P-MEXP-14985 "2nd_L_pool_amersham2" synthetic_DNA Cy5 P-MEXP-14987 2nd_L_pool_amersham2 A-GEHB-1 P-MEXP-14988 2nd_L_pool_amersham2 T00253434 T00253434.txt luminal normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough2 95000062.gpr luminal normal
+ E_luminal_pool total_RNA P-TABM-AnitaG-10115 "2nd_L_pool_L_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough1 95000061.gpr luminal normal
+ E_luminal_pool total_RNA P-MEXP-14985 "2nd_L_pool_amersham4" synthetic_DNA Cy5 P-MEXP-14987 2nd_L_pool_amersham4 A-GEHB-1 P-MEXP-14988 2nd_L_pool_amersham4 T00250229 T00250229.txt luminal normal
+SAMPLE_human_1098 organism_part 26 "Queen Victoria Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_1098 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_1098 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy5_agil3" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress M_pool_cy5_agil3 A-MEXP-218 P-MEXP-10145 ArrayExpress M_pool_cy5_agil3 251239125240.gpr myoepithelial normal
+SAMPLE_human_352 organism_part 30 "Queen Mary's University Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_352 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_352 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-AFFY-2 ArrayExpress "M_pool_affy1 biotin" synthetic_DNA biotin M_pool_affy1 A-AFFY-44 M_pool_affy1 M1.CEL M1.EXP myoepithelial normal
+SAMPLE_human_453 organism_part 22 "Queen Mary's University Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_453 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_453 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-AFFY-2 ArrayExpress "M_pool_affy2 biotin" synthetic_DNA biotin M_pool_affy2 A-AFFY-44 M_pool_affy2 M2.CEL M2.EXP myoepithelial normal
+SAMPLE_human_463 organism_part 31 "Queen Mary's University Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_463 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_463 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-AFFY-2 ArrayExpress "M_pool_affy3 biotin" synthetic_DNA biotin M_pool_affy3 A-AFFY-44 M_pool_affy3 M3.CEL M3.EXP myoepithelial normal
+SAMPLE_human_621 organism_part 38 "Middlesex University Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_621 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_621 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy3_agil1" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress M_pool_cy3_agil1 A-MEXP-218 P-MEXP-10145 ArrayExpress M_pool_cy3_agil1 251239114660.gpr myoepitheli [...]
+SAMPLE_human_688 organism_part 26 "St. George's Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_688 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_688 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy3_agil2" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress M_pool_cy3_agil2 A-MEXP-218 P-MEXP-10145 ArrayExpress M_pool_cy3_agil2 251239125465.gpr myoepithelial normal
+SAMPLE_human_697 organism_part 29 "St. George's University Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_697 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_697 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy3_agil3" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress M_pool_cy3_agil3 A-MEXP-218 P-MEXP-10145 ArrayExpress M_pool_cy3_agil3 251239125953.gpr myoepith [...]
+SAMPLE_human_723 organism_part 32 "St. George's University Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_723 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_723 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy5_agil1" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress M_pool_cy5_agil1 A-MEXP-218 P-MEXP-10145 ArrayExpress M_pool_cy5_agil1 251239119798.gpr myoepith [...]
+SAMPLE_human_729 organism_part 31 "St. George's Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_729 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_729 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy5_agil2" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress M_pool_cy5_agil2 A-MEXP-218 P-MEXP-10145 ArrayExpress M_pool_cy5_agil2 251239125238.gpr myoepithelial normal
+SAMPLE_human_794 organism_part 39 "Ashstead Hospital, London,UK" myoepithelial "Reduction mammoplasties" adult normal Human_794 "mammary gland" "Homo sapiens" female years P-MEXP-976 ArrayExpress SAMPLE_human_794 organism_part P-MEXP-972 ArrayExpress P-MEXP-10843 E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress M_pool_cy3_breakthrough1 A-MEXP-259 P-MEXP-10145 ArrayExpress M_pool_cy3_breakthrough1 90000338.gpr my [...]
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "2nd_M_pool_amersham1" synthetic_DNA Cy5 P-MEXP-14987 2nd_M_pool_amersham1 A-GEHB-1 P-MEXP-14988 2nd_M_pool_amersham1 T00253435 T00253435.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough3 95000081.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "M_pool_amersham4" synthetic_DNA Cy5 P-MEXP-14987 M_pool_amersham4 A-GEHB-1 P-MEXP-14988 M_pool_amersham4 T00297805 T00297805.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress M_pool_cy3_breakthrough3 A-MEXP-259 P-MEXP-10145 ArrayExpress M_pool_cy3_breakthrough3 90000422.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "2nd_M_pool_amersham2" synthetic_DNA Cy5 P-MEXP-14987 2nd_M_pool_amersham2 A-GEHB-1 P-MEXP-14988 2nd_M_pool_amersham2 T00253436 T00253436.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress M_pool_cy3_breakthrough2 A-MEXP-259 P-MEXP-10145 ArrayExpress M_pool_cy3_breakthrough2 90000344.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "M_pool_amersham3" synthetic_DNA Cy5 P-MEXP-14987 M_pool_amersham3 A-GEHB-1 P-MEXP-14988 M_pool_amersham3 T00297804 T00297804.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough1 95000063.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "M_pool_amersham2" synthetic_DNA Cy5 P-MEXP-14987 M_pool_amersham2 A-GEHB-1 P-MEXP-14988 M_pool_amersham2 T00297803 T00297803.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "2nd_M_pool_amersham3" synthetic_DNA Cy5 P-MEXP-14987 2nd_M_pool_amersham3 A-GEHB-1 P-MEXP-14988 2nd_M_pool_amersham3 T00250230 T00250230.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "2nd_M_pool_amersham4" synthetic_DNA Cy5 P-MEXP-14987 2nd_M_pool_amersham4 A-GEHB-1 P-MEXP-14988 2nd_M_pool_amersham4 T00250231 T00250231.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough2 95000064.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-MEXP-14985 "M_pool_amersham1" synthetic_DNA Cy5 P-MEXP-14987 M_pool_amersham1 A-GEHB-1 P-MEXP-14988 M_pool_amersham1 T00297802 T00297802.txt myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress M_pool_cy5_breakthrough2 A-MEXP-259 P-MEXP-10145 ArrayExpress M_pool_cy5_breakthrough2 90000341.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough2 95000070.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress M_pool_cy5_breakthrough1 A-MEXP-259 P-MEXP-10145 ArrayExpress M_pool_cy5_breakthrough1 90000311.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy5_breakthrough4" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough4 95000076.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough3 95000075.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "M_pool_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress M_pool_cy5_breakthrough3 A-MEXP-259 P-MEXP-10145 ArrayExpress M_pool_cy5_breakthrough3 90000419.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy3_breakthrough4" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough4 95000082.gpr myoepithelial normal
+ E_myoepithelial_pool total_RNA P-TABM-AnitaG-10115 "2nd_M_pool_L_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough1 95000069.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough2" synthetic_DNA Cy3 T_pool_cy5_breakthrough2 T_pool_cy5_breakthrough2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil3" synthetic_DNA Cy3 M_pool_cy5_agil3 M_pool_cy5_agil3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough1" synthetic_DNA Cy5 T_pool_cy3_breakthrough1 T_pool_cy3_breakthrough1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil2" synthetic_DNA Cy5 T_pool_cy3_agil2 T_pool_cy3_agil2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil3" synthetic_DNA Cy5 T_pool_cy3_agil3 T_pool_cy3_agil3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil1" synthetic_DNA Cy3 T_pool_cy5_agil1 T_pool_cy5_agil1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough2" synthetic_DNA Cy3 L_pool_cy5_breakthrough2 L_pool_cy5_breakthrough2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough3" synthetic_DNA Cy3 T_pool_cy5_breakthrough3 T_pool_cy5_breakthrough3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough2" synthetic_DNA Cy5 T_pool_cy3_breakthrough2 T_pool_cy3_breakthrough2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough3" synthetic_DNA Cy5 T_pool_cy3_breakthrough3 T_pool_cy3_breakthrough3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough3" synthetic_DNA Cy3 L_pool_cy5_breakthrough3 L_pool_cy5_breakthrough3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough1" synthetic_DNA Cy5 M_pool_cy3_breakthrough1 M_pool_cy3_breakthrough1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil2" synthetic_DNA Cy3 T_pool_cy5_agil2 T_pool_cy5_agil2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough1" synthetic_DNA Cy3 M_pool_cy5_breakthrough1 M_pool_cy5_breakthrough1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil1" synthetic_DNA Cy3 M_pool_cy5_agil1 M_pool_cy5_agil1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough3" synthetic_DNA Cy5 M_pool_cy3_breakthrough3 M_pool_cy3_breakthrough3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil2" synthetic_DNA Cy3 M_pool_cy5_agil2 M_pool_cy5_agil2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough2" synthetic_DNA Cy5 M_pool_cy3_breakthrough2 M_pool_cy3_breakthrough2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough2" synthetic_DNA Cy3 M_pool_cy5_breakthrough2 M_pool_cy5_breakthrough2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil1" synthetic_DNA Cy5 M_pool_cy3_agil1 M_pool_cy3_agil1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough3" synthetic_DNA Cy3 M_pool_cy5_breakthrough3 M_pool_cy5_breakthrough3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil1" synthetic_DNA Cy5 T_pool_cy3_agil1 T_pool_cy3_agil1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil3" synthetic_DNA Cy5 M_pool_cy3_agil3 M_pool_cy3_agil3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil3" synthetic_DNA Cy3 T_pool_cy5_agil3 T_pool_cy5_agil3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough1" synthetic_DNA Cy3 T_pool_cy5_breakthrough1 T_pool_cy5_breakthrough1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil2" synthetic_DNA Cy5 M_pool_cy3_agil2 M_pool_cy3_agil2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough4" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough4 95000076.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough95000083" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000083 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000083 95000083.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough95000084" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000084 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000084 95000084.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough95000065" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000065 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000065 95000065.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough95000066" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000066 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000066 95000066.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough3 95000077.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough4" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough4 95000078.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough1 95000071.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough4" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough4 95000074.gpr luminal normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough3 95000081.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough4" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough4 95000082.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough1 95000063.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough2 95000070.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough3 95000075.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy3_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy3_breakthrough2 95000064.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough2 95000072.gpr "breast epithelial" "invasive ductual carcinoma"
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_M_pool_cy5_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_M_pool_cy5_breakthrough1 95000069.gpr myoepithelial normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough2" synthetic_DNA Cy5 L_pool_cy3_breakthrough2 L_pool_cy3_breakthrough2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough2 95000062.gpr luminal normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil2" synthetic_DNA Cy3 L_pool_cy5_agil2 L_pool_cy5_agil2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough3 95000079.gpr luminal normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil3" synthetic_DNA Cy5 L_pool_cy3_agil3 L_pool_cy3_agil3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough1" synthetic_DNA Cy5 L_pool_cy3_breakthrough1 L_pool_cy3_breakthrough1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil3" synthetic_DNA Cy3 L_pool_cy5_agil3 L_pool_cy5_agil3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_breakthrough1" synthetic_DNA Cy3 L_pool_cy5_breakthrough1 L_pool_cy5_breakthrough1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_breakthrough3" synthetic_DNA Cy5 L_pool_cy3_breakthrough3 L_pool_cy3_breakthrough3
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough1 95000061.gpr luminal normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy3_agil1" synthetic_DNA Cy3 L_pool_cy5_agil1 L_pool_cy5_agil1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil1" synthetic_DNA Cy5 L_pool_cy3_agil1 L_pool_cy3_agil1
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough3 95000073.gpr luminal normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough1 95000067.gpr luminal normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy5_breakthrough4" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy3_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy3_breakthrough4 95000080.gpr luminal normal
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "REF_pool_cy5_agil2" synthetic_DNA Cy5 L_pool_cy3_agil2 L_pool_cy3_agil2
+"REF sources" whole_organism "REF sources" organism_part E_REF total_RNA P-TABM-AnitaG-10115 "2nd_REF_pool_L_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_L_pool_cy5_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_L_pool_cy5_breakthrough2 95000068.gpr luminal normal
+SAMPLE_human_1000 organism_part "University College London Hospital, London,UK" "breast epithelial" adult "invasive ductual carcinoma" Human_1000 "mammary gland" "Homo sapiens" female SAMPLE_human_1000 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy3_breakthrough1" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress T_pool_cy3_breakthrough1 A-MEXP-259 P-MEXP-10145 ArrayExpress T_pool_cy3_breakthrough1 90000339.gpr "breast epithel [...]
+SAMPLE_human_1033 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size58, 1/15 lymph node, ESR1 negative, HER2 negative, progesteron receptor negative" adult "invasive ductual carcinoma" Human_1033 "mammary gland" "Homo sapiens" female SAMPLE_human_1033 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy3_breakthrough2" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress T_pool_cy3_breakthrough2 A [...]
+SAMPLE_human_1045 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad2,size50, ESR1 negative, HER2 negative" adult "invasive ductual carcinoma" Human_1045 "mammary gland" "Homo sapiens" female SAMPLE_human_1045 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy3_breakthrough3" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress T_pool_cy3_breakthrough3 A-MEXP-259 P-MEXP-10145 ArrayExpress T_pool_cy3_b [...]
+SAMPLE_human_1052 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size35, ESR1 negative, progesteron receptor negative" adult "invasive ductual carcinoma" Human_1052 "mammary gland" "Homo sapiens" female SAMPLE_human_1052 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress T_pool_cy5_breakthrough1 A-MEXP-259 P-MEXP-10145 ArrayExpr [...]
+SAMPLE_human_1062 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad2,size20,vascular invasion, 1/25 lymph node, ESR1 positive, HER2 negative, progesteron receptor negative" adult "invasive ductual carcinoma" Human_1062 "mammary gland" "Homo sapiens" female SAMPLE_human_1062 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress T_pool_c [...]
+SAMPLE_human_1063 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size45,vascular invasion, ESR1 positive, HER2 positive, progesteron receptor positive" adult "invasive ductual carcinoma" Human_1063 "mammary gland" "Homo sapiens" female SAMPLE_human_1063 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress T_pool_cy5_breakthrough3 [...]
+SAMPLE_human_489 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size25,vascular invasion, ESR1 positive, HER2 negative" adult "invasive ductual carcinoma" Human_489 "mammary gland" "Homo sapiens" female SAMPLE_human_489 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-AFFY-2 ArrayExpress "T_pool_affy1 biotin" synthetic_DNA biotin T_pool_affy1 A-AFFY-44 T_pool_affy1 T1.CEL T1.EXP "breast epithelial" "invasive [...]
+SAMPLE_human_552 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size40,vascular invasion, 8/9 lymph node, ESR1 negative, HER2 negative" adult "invasive ductual carcinoma" Human_552 "mammary gland" "Homo sapiens" female SAMPLE_human_552 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-AFFY-2 ArrayExpress "T_pool_affy2 biotin" synthetic_DNA biotin T_pool_affy2 A-AFFY-44 T_pool_affy2 T2.CEL T2.EXP "breast epithe [...]
+SAMPLE_human_593 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad2,size35, 1/9 lymph node, ESR1 positive" adult "invasive lobular carcinoma" Human_593 "mammary gland" "Homo sapiens" female SAMPLE_human_593 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-AFFY-2 ArrayExpress "T_pool_affy3 biotin" synthetic_DNA biotin T_pool_affy3 A-AFFY-44 T_pool_affy3 T3.CEL T3.EXP "breast epithelial" "invasive lobular carcinoma"
+SAMPLE_human_695 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad1,size42, 9/15 lymph node, ESR1 positive, progesteron receptor negative" adult "invasive lobular carcinoma" Human_695 "mammary gland" "Homo sapiens" female SAMPLE_human_695 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy3_agil1" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress T_pool_cy3_agil1 A-MEXP-218 P-MEXP-10145 ArrayExpress [...]
+SAMPLE_human_725 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size35,vascular invasion, 1/15 lymph node, ESR1 positive" adult "invasive ductual carcinoma" Human_725 "mammary gland" "Homo sapiens" female SAMPLE_human_725 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy3_agil2" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress T_pool_cy3_agil2 A-MEXP-218 P-MEXP-10145 ArrayExpress T_pool_cy3_a [...]
+SAMPLE_human_842 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,vascular invasion, 1/10 lymph node, ESR1 positive, progesteron receptor positive" adult "invasive ductual carcinoma" Human_842 "mammary gland" "Homo sapiens" female SAMPLE_human_842 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy3_agil3" synthetic_DNA Cy3 P-MEXP-6162 ArrayExpress T_pool_cy3_agil3 A-MEXP-218 P-MEXP-10145 A [...]
+SAMPLE_human_845 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size50,vascular invasion, 2/9 lymph node,ESR1 positive, progesteron receptor negative" adult "invasive ductual carcinoma" Human_845 "mammary gland" "Homo sapiens" female SAMPLE_human_845 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy5_agil1" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress T_pool_cy5_agil1 A-MEXP-218 P-MEXP-10 [...]
+SAMPLE_human_896 organism_part "University College London Hospital, London,UK" "breast epithelial" "size45, 0/2 lymph node" adult "invasive ductual carcinoma" Human_896 "mammary gland" "Homo sapiens" female SAMPLE_human_896 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy5_agil2" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress T_pool_cy5_agil2 A-MEXP-218 P-MEXP-10145 ArrayExpress T_pool_cy5_agil2 251239125239.gpr "breast epithel [...]
+SAMPLE_human_983 organism_part "University College London Hospital, London,UK" "breast epithelial" "grad3,size15,vascular invasion, ESR1 negative, HER2 positive, progesteron receptor negative" adult "invasive ductual carcinoma" Human_983 "mammary gland" "Homo sapiens" female SAMPLE_human_983 organism_part P-MEXP-972 ArrayExpress P-MEXP-10845 E_tumour_pool total_RNA P-TABM-AnitaG-10115 "T_pool_cy5_agil3" synthetic_DNA Cy5 P-MEXP-6162 ArrayExpress T_pool_cy5_agil3 A-MEXP-218 P-MEXP-10 [...]
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy5_breakthrough4" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough4 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough4 95000078.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy5_breakthrough2" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough2 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough2 95000072.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy5_breakthrough3" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough3 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough3 95000077.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy3_breakthrough95000083" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000083 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000083 95000083.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "T_pool_amersham1" synthetic_DNA Cy5 P-MEXP-14987 T_pool_amersham1 A-GEHB-1 P-MEXP-14988 T_pool_amersham1 T00297808 T00297808.txt "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "T_pool_amersham2" synthetic_DNA Cy5 P-MEXP-14987 T_pool_amersham2 A-GEHB-1 P-MEXP-14988 T_pool_amersham2 T00297810 T00297810.txt "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "T_pool_amersham3" synthetic_DNA Cy5 P-MEXP-14987 T_pool_amersham3 A-GEHB-1 P-MEXP-14988 T_pool_amersham3 T00297811 T00297811.txt "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy3_breakthrough95000066" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000066 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000066 95000066.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy3_breakthrough95000065" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000065 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000065 95000065.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "2nd_T_pool_amersham4" synthetic_DNA Cy5 P-MEXP-14987 2nd_T_pool_amersham4 A-GEHB-1 P-MEXP-14988 2nd_T_pool_amersham4 T00250233 T00250233.txt "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "2nd_T_pool_amersham1" synthetic_DNA Cy5 P-MEXP-14987 2nd_T_pool_amersham1 A-GEHB-1 P-MEXP-14988 2nd_T_pool_amersham1 T00253437 T00253437.txt "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy3_breakthrough95000084" synthetic_DNA Cy3 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy3_breakthrough95000084 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy3_breakthrough95000084 95000084.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-TABM-AnitaG-10115 "2nd_T_pool_L_cy5_breakthrough1" synthetic_DNA Cy5 P-MEXP-10140 ArrayExpress 2nd_T_pool_cy5_breakthrough1 A-MEXP-339 P-MEXP-10145 ArrayExpress 2nd_T_pool_cy5_breakthrough1 95000071.gpr "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "T_pool_amersham4" synthetic_DNA Cy5 P-MEXP-14987 T_pool_amersham4 A-GEHB-1 P-MEXP-14988 T_pool_amersham4 T00297812 T00297812.txt "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "2nd_T_pool_amersham3" synthetic_DNA Cy5 P-MEXP-14987 2nd_T_pool_amersham3 A-GEHB-1 P-MEXP-14988 2nd_T_pool_amersham3 T00250232 T00250232.txt "breast epithelial" "invasive ductual carcinoma"
+ E_tumour_pool total_RNA P-MEXP-14985 "2nd_T_pool_amersham2" synthetic_DNA Cy5 P-MEXP-14987 2nd_T_pool_amersham2 A-GEHB-1 P-MEXP-14988 2nd_T_pool_amersham2 T00253438 T00253438.txt "breast epithelial" "invasive ductual carcinoma"
diff --git a/examples/magetab/real/E-TABM-70.idf b/examples/magetab/real/E-TABM-70.idf
new file mode 100644
index 0000000..bc27d11
--- /dev/null
+++ b/examples/magetab/real/E-TABM-70.idf
@@ -0,0 +1,28 @@
+"Investigation Title" IGR_PLOIDY_STUDY_GK
+"Experimental Design" compound_treatment_design
+"Experimental Factor Name" CELLLINE PLOIDY
+"Experimental Factor Type" cell_line ploidy
+
+"Person Last Name" Dessen
+"Person First Name" Philippe
+"Person Mid Initial"
+"Person Email" dessen at igr.fr
+"Person Address" "Centre Natiol de la Recherche Scientifique, UMR8125,�; Unit� de G�nomique Fonctionnelle, Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; INSERM U-517, Faculty of Medicine and Pharmacy, 7 Boulevard Jeanne d;Arc, 21033 Dijon, France�; INSERM U362,Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; Unit� d�Immunologie, ERM0208 INSERM, IFR54, Institut Gustave Roussy, Villejuif, France"
+"Person Roles" submitter
+
+"Quality Control Types" dye_swap_quality_control
+"Public Release Date" "2006-01-23"
+
+Comment[ArrayExpressSubmissionDate] "2005-12-19"
+
+"Publication Author List" "Maria Castedo; Arud Coquelle; Sonia Vivet; Audrey Kauffmann; Marie O. Pequignot; Noelia Casares; Alexander Valent; Shahul Mouhamad; Elise Schmitt; Nazanine Modjtahedi; William Vainchenker; Laurence Zitvogel; Vladimir Lazar; Carmen Garrido; Guido Kroemer"
+"Publication Title" "Apoptosis regulation in tetraploid cells"
+"Experiment Description" "During the oncogenic process, tetraploidy is a candidate intermediate stage leading from diploidy to aneuploidy. The aim of our experiment was to establish tetraploid clones and to characterize their transcriptome."
+
+"Protocol Name" P_IGR_SAMPLE_04_TETRA_1 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04
+"Protocol Type" compound_based_treatment compound_based_treatment nucleic_acid_extraction pool pool labeling labeling hybridization image_acquisition "Transformation protocol"
+"Protocol Description" "RKO cells were grown in McCoy' 5A medium supplemented with 10%FCS. In order to generate tetraploid and diploid clones, the cell line RKO containing ~5% tetraploid cells was subcloned by limiting dilution into diploid and tetraploid clones. One diploid clone was transfected with a cDNA encoding H2B-GFP, selected in blasticidine (20 ᄉg/ml), FACS-separated into subsets of cells enriched in a diploid or tetraploid DNA content to generate diploユd and tetraploid H2B-GFP [...]
+"Protocol Parameters"
+"Protocol Software"
+
+"SDRF File" E-TABM-70_sdrf.txt
diff --git a/examples/magetab/real/E-TABM-70_sdrf.txt b/examples/magetab/real/E-TABM-70_sdrf.txt
new file mode 100644
index 0000000..1b0b711
--- /dev/null
+++ b/examples/magetab/real/E-TABM-70_sdrf.txt
@@ -0,0 +1,65 @@
+"Source Name" "Material Type" "Characteristics [CellLine]" "Characteristics [Organism]" "Characteristics [Ploidy]" "Characteristics [Provider]" "Characteristics [Treatment]" "Protocol REF" "Sample Name" "Material Type" "Protocol REF" "Protocol REF" "Extract Name" "Material Type" "Protocol REF" "Labeled Extract Name" "Material Type" Label "Protocol REF" "Hybridization Name" "Array Design REF" "Protocol REF" "Scan Name" "Array Data File" "Protocol REF" "Normalization Name" "Comment[Normali [...]
+DW "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DW sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DW extract" total_RNA P_IGR_LABEL_CY5_04 "DW Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DW Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119523_S01_A01 US14702370_251239119523_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DW Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119523_S01_A01 US14702370_251239119523_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+DW "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DW sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DW extract" total_RNA P_IGR_LABEL_CY3_04 "DW Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DW Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119541_S01_A01 US14702370_251239119541_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DW Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119541_S01_A01 US14702370_251239119541_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+DX "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DX sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DX extract" total_RNA P_IGR_LABEL_CY5_04 "DX Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DX Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119609_S01_A01 US14702370_251239119609_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DX Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119609_S01_A01 US14702370_251239119609_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+DX "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DX sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DX extract" total_RNA P_IGR_LABEL_CY3_04 "DX Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DX Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119524_S01_A01 US14702370_251239119524_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DX Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119524_S01_A01 US14702370_251239119524_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+DY "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DY sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DY extract" total_RNA P_IGR_LABEL_CY5_04 "DY Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DY Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119530_S01_A01 US14702370_251239119530_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DY Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119530_S01_A01 US14702370_251239119530_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+DY "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DY sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DY extract" total_RNA P_IGR_LABEL_CY3_04 "DY Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DY Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119525_S01_A01 US14702370_251239119525_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DY Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119525_S01_A01 US14702370_251239119525_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+DZ "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DZ sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DZ extract" total_RNA P_IGR_LABEL_CY5_04 "DZ Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DZ Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119531_S01_A01 US14702370_251239119531_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DZ Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119531_S01_A01 US14702370_251239119531_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+DZ "cell line" "colon RKO" "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as diploid" P_IGR_SAMPLE_04_TETRA_1 "DZ sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "DZ extract" total_RNA P_IGR_LABEL_CY3_04 "DZ Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "DZ Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119614_S01_A01 US14702370_251239119614_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.tx [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "DZ Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119614_S01_A01 US14702370_251239119614_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TA "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TA sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TA extract" total_RNA P_IGR_LABEL_CY5_04 "TA Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TA Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119519_S01_A01 US14702370_251239119519_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TA Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119519_S01_A01 US14702370_251239119519_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TA "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TA sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TA extract" total_RNA P_IGR_LABEL_CY3_04 "TA Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TA Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119487_S01_A01 US14702370_251239119487_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TA Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119487_S01_A01 US14702370_251239119487_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TB "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TB sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TB extract" total_RNA P_IGR_LABEL_CY5_04 "TB Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TB Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119520_S01_A01 US14702370_251239119520_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TB Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119520_S01_A01 US14702370_251239119520_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TB "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TB sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TB extract" total_RNA P_IGR_LABEL_CY3_04 "TB Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TB Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119489_S01_A01 US14702370_251239119489_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TB Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119489_S01_A01 US14702370_251239119489_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TC "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TC sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TC extract" total_RNA P_IGR_LABEL_CY5_04 "TC Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TC Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119608_S01_A01 US14702370_251239119608_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TC Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119608_S01_A01 US14702370_251239119608_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TC "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TC sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TC extract" total_RNA P_IGR_LABEL_CY3_04 "TC Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TC Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119538_S01_A01 US14702370_251239119538_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TC Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119538_S01_A01 US14702370_251239119538_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TD "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TD sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TD extract" total_RNA P_IGR_LABEL_CY5_04 "TD Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TD Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119522_S01_A01 US14702370_251239119522_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TD Cy5_Pool1 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119522_S01_A01 US14702370_251239119522_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+TD "cell line" "colon RKO" "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "clone selection as tetraploid" P_IGR_SAMPLE_04_TETRA_1 "TD sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "TD extract" total_RNA P_IGR_LABEL_CY3_04 "TD Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "TD Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119540_S01_A01 US14702370_251239119540_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps [...]
+Pool1 "cell line" "colon RKO" "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_1 "Pool1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04A "Pool1 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "TD Cy3_Pool1 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239119540_S01_A01 US14702370_251239119540_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_ [...]
+Co3 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co3 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co3 extract" total_RNA P_IGR_LABEL_CY5_04 "Co3 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co3 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124865_S01_A01 US14702370_251239124865_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co3 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124865_S01_A01 US14702370_251239124865_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+Co3 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co3 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co3 extract" total_RNA P_IGR_LABEL_CY3_04 "Co3 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co3 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124928_S01_A01 US14702370_251239124928_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co3 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124928_S01_A01 US14702370_251239124928_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+Co4 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co4 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co4 extract" total_RNA P_IGR_LABEL_CY5_04 "Co4 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co4 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124866_S01_A01 US14702370_251239124866_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co4 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124866_S01_A01 US14702370_251239124866_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+Co4 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co4 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co4 extract" total_RNA P_IGR_LABEL_CY3_04 "Co4 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co4 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124929_S01_A01 US14702370_251239124929_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co4 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124929_S01_A01 US14702370_251239124929_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+Co1 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co1 extract" total_RNA P_IGR_LABEL_CY5_04 "Co1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co1 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124869_S01_A01 US14702370_251239124869_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co1 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124869_S01_A01 US14702370_251239124869_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+Co1 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co1 extract" total_RNA P_IGR_LABEL_CY3_04 "Co1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co1 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124932_S01_A01 US14702370_251239124932_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co1 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124932_S01_A01 US14702370_251239124932_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+Co2 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co2 extract" total_RNA P_IGR_LABEL_CY5_04 "Co2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co2 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124925_S01_A01 US14702370_251239124925_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "Co2 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124925_S01_A01 US14702370_251239124925_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+Co2 "cell line" HCT116 "Homo sapiens" diploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Co2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Co2 extract" total_RNA P_IGR_LABEL_CY3_04 "Co2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co2 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124933_S01_A01 US14702370_251239124933_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "Co2 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124933_S01_A01 US14702370_251239124933_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_ [...]
+C1 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "cytochalasin D " P_IGR_SAMPLE_04_TETRA_2 "C1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "C1 extract" total_RNA P_IGR_LABEL_CY5_04 "C1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "C1 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124926_S01_A01 US14702370_251239124926_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "C1 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124926_S01_A01 US14702370_251239124926_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+C1 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "cytochalasin D " P_IGR_SAMPLE_04_TETRA_2 "C1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "C1 extract" total_RNA P_IGR_LABEL_CY3_04 "C1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "C1 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124934_S01_A01 US14702370_251239124934_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "C1 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124934_S01_A01 US14702370_251239124934_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+C2 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "cytochalasin D " P_IGR_SAMPLE_04_TETRA_2 "C2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "C2 extract" total_RNA P_IGR_LABEL_CY5_04 "C2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "C2 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124927_S01_A01 US14702370_251239124927_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "C2 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124927_S01_A01 US14702370_251239124927_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+C2 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" "cytochalasin D " P_IGR_SAMPLE_04_TETRA_2 "C2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "C2 extract" total_RNA P_IGR_LABEL_CY3_04 "C2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "C2 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124940_S01_A01 US14702370_251239124940_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "C2 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124940_S01_A01 US14702370_251239124940_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+N1 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" nocodazole P_IGR_SAMPLE_04_TETRA_2 "N1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "N1 extract" total_RNA P_IGR_LABEL_CY5_04 "N1 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "N1 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124867_S01_A01 US14702370_251239124867_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "N1 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124867_S01_A01 US14702370_251239124867_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+N1 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" nocodazole P_IGR_SAMPLE_04_TETRA_2 "N1 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "N1 extract" total_RNA P_IGR_LABEL_CY3_04 "N1 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "N1 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124930_S01_A01 US14702370_251239124930_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "N1 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124930_S01_A01 US14702370_251239124930_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+N2 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" nocodazole P_IGR_SAMPLE_04_TETRA_2 "N2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "N2 extract" total_RNA P_IGR_LABEL_CY5_04 "N2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "N2 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124868_S01_A01 US14702370_251239124868_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY5_04 "Pool2 Cy5 Cy5" synthetic_RNA Cy5 P_IGR_HYBRID_04 "N2 Cy5-Pool2 Cy5 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124868_S01_A01 US14702370_251239124868_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
+N2 "cell line" HCT116 "Homo sapiens" tetraploid "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" nocodazole P_IGR_SAMPLE_04_TETRA_2 "N2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "N2 extract" total_RNA P_IGR_LABEL_CY3_04 "N2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "N2 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124931_S01_A01 US14702370_251239124931_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_ [...]
+Pool2 "cell line" HCT116 "Homo sapiens" "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" P_IGR_SAMPLE_04_TETRA_2 "Pool2 sample" organism_part P_IGR_EXTRACT_04 P_IGR_POOL_04B "Pool2 extract" total_RNA P_IGR_LABEL_CY3_04 "Pool2 Cy3 Cy3" synthetic_RNA Cy3 P_IGR_HYBRID_04 "N2 Cy3-Pool2 Cy3 hyb" A-MEXP-304 P_IGR_SCANNING_04 US14702370_251239124931_S01_A01 US14702370_251239124931_S01_A01.txt P_IGR_TRANSFORM_04 igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess igr_ploidy_gk_all_s [...]
diff --git a/examples/magetab/real/solexa_example.idf b/examples/magetab/real/solexa_example.idf
new file mode 100644
index 0000000..53e419b
--- /dev/null
+++ b/examples/magetab/real/solexa_example.idf
@@ -0,0 +1,36 @@
+"Investigation Title" "Solexa analysis of Galapagos finches"
+"Experiment Description" "Solexa analysis of Galapagos finches"
+"Experimental Design" "growth_condition_design" "replicate_design" "co-expression_design"
+
+"Experimental Factor Name" "STRAINORLINE"
+"Experimental Factor Type" "strain_or_line"
+"Experimental Factor Term Source REF" "MGED Ontology"
+
+"Quality Control Type" "biological_replicate"
+
+"Public Release Date" "2008-01-01"
+
+"Person Last Name" "Darwin"
+"Person First Name" "Charles"
+"Person Mid Initials"
+"Person Email" "Charles at darwin.com"
+"Person Phone"
+"Person Address" "Beagle Institute of Theoretical Experiments, Down House, Downe, Kent, UK"
+"Person Affiliation" "Beagle Institute of Theoretical Experiments"
+"Person Roles" "submitter"
+
+"PubMed ID"
+"Publication Author List" "Darwin, C"
+"Publication Title" "Solexa analysis of Galapagos finches"
+"Publication Status" "Submitted"
+
+"Protocol Name" "EXTRACTION" "NORMALIZATION" "SOLEXA SEQ" "SOLEXA DATA ACQUISITION"
+"Protocol Type" "nucleic_acid_extraction" "bioassay_data_transformation"
+"Protocol Description" "All cDNA samples were prepared by treating ~1 mg of total RNA for 30 min with amplification grade RNase-free DNase (Invitrogen), according to the manufacturer's protocols. PolyA enriched RNA was then prepared using an oligo(dT) selection kit (Oligotex Direct mRNA MiniKit, Qiagen). The resulting poly(A)-enriched RNA was then converted to double-stranded cDNA using a cDNA synthesis kit (Superscript choice system for cDNA synthesis, Invitrogen)." "FASTQ files of sequ [...]
+"Protocol Term Source REF" "MGED Ontology"
+
+"Term Source Name" "MGED Ontology"
+"Term Source File" "http://mged.sourceforge.net/ontologies/MGEDontology.php"
+"Term Source Version" "1.3.0.1"
+
+"SDRF File" "solexa_example.sdrf.txt"
diff --git a/examples/magetab/real/solexa_example.sdrf.txt b/examples/magetab/real/solexa_example.sdrf.txt
new file mode 100644
index 0000000..3fb8bf5
--- /dev/null
+++ b/examples/magetab/real/solexa_example.sdrf.txt
@@ -0,0 +1,11 @@
+"Source Name" "Material Type" "Term Source REF" "Characteristics[Organism]" "Characteristics[Sex]" "Characteristics[StrainOrLine]" "Protocol REF" "Extract Name" "Protocol REF" "Assay Name" "Technology Type" "Protocol REF" "Array Data File" "Protocol REF" "Derived Array Data File" "Factor Value[STRAINORLINE]"
+"finch 1" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Pinta" "EXTRACTION" "extract pinta 1" "SOLEXA SEQ" "pinta 1" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run1.fastq" "NORMALIZATION" "run1_norm_log2score.txt.gz" "Pinta"
+"finch 2" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Pinta" "EXTRACTION" "extract pinta 2" "SOLEXA SEQ" "pinta 2" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run2.fastq" "NORMALIZATION" "run2_norm_log2score.txt.gz" "Pinta"
+"finch 3" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Marchesa" "EXTRACTION" "extract marchesa 1" "SOLEXA SEQ" "marchesa 1" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run3.fastq" "NORMALIZATION" "run3_norm_log2score.txt.gz" "Marchesa"
+"finch 4" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Marchesa" "EXTRACTION" "extract marchesa 2" "SOLEXA SEQ" "marchesa 2" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run4.fastq" "NORMALIZATION" "run4_norm_log2score.txt.gz" "Marchesa"
+"finch 5" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Santiago" "EXTRACTION" "extract santiago 1" "SOLEXA SEQ" "santiago 1" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run5.fastq" "NORMALIZATION" "run5_norm_log2score.txt.gz" "Santiago"
+"finch 6" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Santiago" "EXTRACTION" "extract santiago 2" "SOLEXA SEQ" "santiago 2" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run6.fastq" "NORMALIZATION" "run6_norm_log2score.txt.gz" "Santiago"
+"finch 7" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Floreana" "EXTRACTION" "extract floreana 1" "SOLEXA SEQ" "floreana 1" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run7.fastq" "NORMALIZATION" "run7_norm_log2score.txt.gz" "Floreana"
+"finch 8" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Floreana" "EXTRACTION" "extract floreana 2" "SOLEXA SEQ" "floreana 2" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run8.fastq" "NORMALIZATION" "run8_norm_log2score.txt.gz" "Floreana"
+"finch 9" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Pinzon" "EXTRACTION" "extract pinzon 1" "SOLEXA SEQ" "pinzon 1" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run9.fastq" "NORMALIZATION" "run9_norm_log2score.txt.gz" "Pinzon"
+"finch 10" "organism_part" "MGED Ontology" "Geospiza fortis" "male" "Pinzon" "EXTRACTION" "extract pinzon 2" "SOLEXA SEQ" "pinzon 2" "high_throughput_sequencing" "SOLEXA DATA ACQUISITION" "run10.fastq" "NORMALIZATION" "run10_norm_log2score.txt.gz" "Pinzon"
diff --git a/examples/magetab/two_color/Data1.txt b/examples/magetab/two_color/Data1.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/magetab/two_color/Data1.txt
@@ -0,0 +1,37 @@
+ATF 1
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 52 376 621 616 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 120 778 1979 1904 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 120 810 1379 1491 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 120 796 1696 1663 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 120 930 1334 1251 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 120 914 426 438 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 80 552 126 134 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 120 816 1089 1052 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 120 796 408 418 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 120 810 1757 1648 825 932 786 862 0
diff --git a/examples/magetab/two_color/Data2.txt b/examples/magetab/two_color/Data2.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/magetab/two_color/Data2.txt
@@ -0,0 +1,40 @@
+ATF 1
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 80 656 204 221 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 120 820 1042 1177 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 120 820 611 851 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 120 820 385 608 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 120 820 935 984 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 120 810 244 241 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 52 340 78 79 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 120 820 834 845 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 80 616 302 328 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 120 930 507 708 187 320 237 471 0
diff --git a/examples/magetab/two_color/Data3.txt b/examples/magetab/two_color/Data3.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/magetab/two_color/Data3.txt
@@ -0,0 +1,37 @@
+ATF 1
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 52 376 621 616 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 120 778 1979 1904 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 120 810 1379 1491 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 120 796 1696 1663 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 120 930 1334 1251 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 120 914 426 438 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 80 552 126 134 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 120 816 1089 1052 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 120 796 408 418 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 120 810 1757 1648 825 932 786 862 0
diff --git a/examples/magetab/two_color/Data4.txt b/examples/magetab/two_color/Data4.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/magetab/two_color/Data4.txt
@@ -0,0 +1,40 @@
+ATF 1
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 80 656 204 221 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 120 820 1042 1177 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 120 820 611 851 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 120 820 385 608 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 120 820 935 984 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 120 810 244 241 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 52 340 78 79 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 120 820 834 845 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 80 616 302 328 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 120 930 507 708 187 320 237 471 0
diff --git a/examples/magetab/two_color/NormData1.txt b/examples/magetab/two_color/NormData1.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/magetab/two_color/NormData1.txt
@@ -0,0 +1,11 @@
+MetaColumn MetaRow Column Row Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+1 1 1 1 A102340 0.475 0.481 0.494 0.491 0.129 0.413 0.731 -1.074
+1 1 2 1 A102341 0.494 0.496 0.5 0.599 0.409 0.433 0.777 -1.019
+1 1 3 1 A102342 0.501 0.49 0.493 0.641 1.28 0.46 0.837 -0.998
+1 1 4 1 A102343 0.78 0.782 0.819 1.372 3.252 0.675 0.722 -0.359
+1 1 5 1 A102344 0.455 0.46 0.45 0.6 0.632 0.382 0.751 -1.137
+1 1 6 1 A102345 0.42 0.431 0.473 0.696 2.121 0.362 0.589 -1.252
+1 1 7 1 A102346 0.223 0.229 0.226 0.313 0.289 0.16 0.283 -2.163
+1 1 8 1 A102347 0.525 0.503 0.522 0.741 1.71 0.445 0.754 -0.929
+1 1 9 1 A102348 0.388 0.384 0.368 0.443 0.369 0.351 0.721 -1.367
+1 1 10 1 A102349 0.885 0.912 0.923 1.477 3.031 0.751 0.698 -0.176
diff --git a/examples/magetab/two_color/NormData2.txt b/examples/magetab/two_color/NormData2.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/magetab/two_color/NormData2.txt
@@ -0,0 +1,11 @@
+Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+A102340 0.447 0.473 0.502 0.428 2.467 0.472 0.332 -1.162
+A102341 0.53 0.484 0.477 0.503 3.009 0.418 0.611 -0.916
+A102342 0.669 0.588 0.592 0.604 3.17 0.511 0.592 -0.579
+A102343 0.742 0.684 0.755 0.675 3.312 0.576 0.412 -0.43
+A102344 0.479 0.514 0.506 0.502 2.251 0.507 0.593 -1.061
+A102345 0.506 0.525 0.546 0.488 3.047 0.532 0.398 -0.982
+A102346 0.592 0.612 1.025 0.705 2.93 1.819 0.071 -0.757
+A102347 0.497 0.506 0.543 0.522 3.195 0.482 0.645 -1.008
+A102348 0.606 0.624 0.669 0.614 2.841 0.577 0.414 -0.722
+A102349 0.584 0.503 0.457 0.508 2.361 0.481 0.581 -0.775
diff --git a/examples/magetab/two_color/NormData3.txt b/examples/magetab/two_color/NormData3.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/magetab/two_color/NormData3.txt
@@ -0,0 +1,11 @@
+MetaColumn MetaRow Column Row Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+1 1 1 1 A102340 0.475 0.481 0.494 0.491 0.129 0.413 0.731 -1.074
+1 1 2 1 A102341 0.494 0.496 0.5 0.599 0.409 0.433 0.777 -1.019
+1 1 3 1 A102342 0.501 0.49 0.493 0.641 1.28 0.46 0.837 -0.998
+1 1 4 1 A102343 0.78 0.782 0.819 1.372 3.252 0.675 0.722 -0.359
+1 1 5 1 A102344 0.455 0.46 0.45 0.6 0.632 0.382 0.751 -1.137
+1 1 6 1 A102345 0.42 0.431 0.473 0.696 2.121 0.362 0.589 -1.252
+1 1 7 1 A102346 0.223 0.229 0.226 0.313 0.289 0.16 0.283 -2.163
+1 1 8 1 A102347 0.525 0.503 0.522 0.741 1.71 0.445 0.754 -0.929
+1 1 9 1 A102348 0.388 0.384 0.368 0.443 0.369 0.351 0.721 -1.367
+1 1 10 1 A102349 0.885 0.912 0.923 1.477 3.031 0.751 0.698 -0.176
diff --git a/examples/magetab/two_color/NormData4.txt b/examples/magetab/two_color/NormData4.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/magetab/two_color/NormData4.txt
@@ -0,0 +1,11 @@
+Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+A102340 0.447 0.473 0.502 0.428 2.467 0.472 0.332 -1.162
+A102341 0.53 0.484 0.477 0.503 3.009 0.418 0.611 -0.916
+A102342 0.669 0.588 0.592 0.604 3.17 0.511 0.592 -0.579
+A102343 0.742 0.684 0.755 0.675 3.312 0.576 0.412 -0.43
+A102344 0.479 0.514 0.506 0.502 2.251 0.507 0.593 -1.061
+A102345 0.506 0.525 0.546 0.488 3.047 0.532 0.398 -0.982
+A102346 0.592 0.612 1.025 0.705 2.93 1.819 0.071 -0.757
+A102347 0.497 0.506 0.543 0.522 3.195 0.482 0.645 -1.008
+A102348 0.606 0.624 0.669 0.614 2.841 0.577 0.414 -0.722
+A102349 0.584 0.503 0.457 0.508 2.361 0.481 0.581 -0.775
diff --git a/examples/magetab/two_color/test.idf b/examples/magetab/two_color/test.idf
new file mode 100644
index 0000000..77f5431
--- /dev/null
+++ b/examples/magetab/two_color/test.idf
@@ -0,0 +1,49 @@
+"Investigation Title" "University of Heidelberg H sapiens TK6"
+"Experimental Design" "genetic_modification_design"
+"Experimental Factor Name" "EF1" "EF2"
+"Experimental Factor Type" "genetic_modification" "compound"
+"Experimental Factor Term Source REF" "MO"
+
+"Person Last Name" "Maier" "Fleckenstein" "Li" "Laufs" "Zeller" "Freuhauf" "Herskind" "Wenz"
+"Person First Name" "Patrick" "Katharina" "Li" "Stephanie" "Jens" "Stephan" "Carsten" "Frederik"
+"Person Mid Initials" "W"
+"Person Email" "patrick.maier at radonk.ma.uni-heidelberg.de"
+"Person Phone" 491334787822
+"Person Fax" 123
+"Person Address" "Theodor-Kutzer-Ufer 1-3"
+"Person Affiliation" "Department of Radiation Oncology, University of Heidelberg" "Department of Radiation Oncology, University of Heidelberg" "University of Heidelberg, ZMF" "DKFZ, Heidelberg" "DKFZ, Heidelberg" "University of Heidelberg, Department of Int Med V" "Department of Radiation Oncology, University of Heidelberg" "Department of Radiation Oncology, University of Heidelberg"
+"Person Roles" "submitter;investigator" "investigator" "investigator" "investigator" "investigator" "investigator" "investigator" "investigator"
+"Person Roles Term Source REF" "MO"
+
+"Quality Control Type" "biological_replicate"
+"Quality Control Term Source REF" "MO"
+"Replicate Type" "biological_replicate"
+"Replicate Term Source REF" "MO"
+"Normalization Type" "whatever"
+"Normalization Term Source REF" "MO"
+"Date of Experiment" "2005-01-04"
+"Public Release Date" "2005-04-01"
+
+"PubMed ID" 16953664
+"Publication DOI" "doi: some number here"
+"Publication Author List" "Me, him and her"
+"Publication Title" "What I did on my holidays"
+"Publication Status" "Universally panned"
+"Publication Status Term Source REF" "MO"
+
+"Experiment Description" "Gene expression of TK6 cells transduced with an oncoretrovirus expressing MDR1 (TK6MDR1) was compared to untransduced TK6 cells and to TK6 cell transduced with an oncoretrovirus expressing the Neomycin resistance gene (TK6neo). Two biological replicates of each were generated and the expression profiles were determined using Affymetrix Human Genome U133 Plus2.0 GeneChip microarrays. Comparisons between the sample groups allow the identification of genes with exp [...]
+
+"Protocol Name" "GROWTHPRTCL10653" "EXTPRTCL10654" "TRANPRTCL10656"
+"Protocol Type" "grow" "nucleic_acid_extraction" "bioassay_data_transformation"
+"Protocol Description" "TK6 cells were grown in suspension cultures in RPMI 1640 medium supplemented with 10% horse serum (Invitrogen, Karlsruhe, Germany). The cells were routinely maintained at 37 C and 5% CO2." "Approximately 10^6 cells were lysed in RLT buffer (Qiagen).Total RNA was extracted from the cell lysate using an RNeasy kit (Qiagen)." "Mixed Model Normalization with SAS Micro Array Solutions (version 1.3)."
+"Protocol Parameters" "media" "Extracted Product; Amplification"
+"Protocol Term Source REF" "MO"
+"Protocol Hardware" "MyHardware"
+"Protocol Software" "MySoftware"
+"Protocol Contact" "MyContact"
+
+"SDRF File" "test_sdrf.txt"
+
+"Term Source Name" "CTO" "MO" "ncbitax" "ArrayExpress" "NCI META"
+"Term Source File" "http://obo.sourceforge.net/cgi-bin/detail.cgi?cell" "http://mged.sourceforge.net/ontologies/MGEDontology.php" "http://www.ncbi.nlm.nih.gov/Taxonomy/taxonomyhome.html/" "http://www.ebi.ac.uk/arrayexpress/" "http://ncimeta.nci.nih.gov/indexMetaphrase.html"
+"Term Source Version" "1.3.0.1"
diff --git a/examples/magetab/two_color/test_sdrf.txt b/examples/magetab/two_color/test_sdrf.txt
new file mode 100644
index 0000000..7a3939e
--- /dev/null
+++ b/examples/magetab/two_color/test_sdrf.txt
@@ -0,0 +1,6 @@
+Source Name Provider Characteristics[ OrganismPart ] Characteristics[DiseaseState] Term Source REF:test namespace Term Accession Number Material Type Description Comment[MyNVT] Sample Name Characteristics[Age] Unit[TimeUnit] Term Source REF Material Type Comment[sample comment] Protocol REF Performer Parameter Value[Extracted Product] Date Comment[P_COMM] Extract Name Material Type LabeledExtract Name MaterialType Term Source REF Label Term Source REF Protocol REF Term Source REF Hybridi [...]
+my source the guy in the next room root hemophilia NCI META CL:111111 organism_part description_text mycomment my sample 6 hours MO cell sample comment value EXTPRTCL10654 the guy in the next room total RNA 21/02/07 This did not happen. I was not here. my extract not_a_MO_term my LE1 total_RNA MO Cy3 MO P-XMPL-7 ArrayExpress my hybridization hyb conditions were suboptimal A-TEST-1 My favourite array design scanning protocol my scan imagefile1.TIFF this was a great picture Data1.txt TRAN [...]
+my source the guy in the next room root hemophilia NCI META CL:111111 organism_part description_text mycomment my sample 6 hours MO cell sample comment value EXTPRTCL10654 the guy in the next room total RNA 21/02/07 This did not happen. I was not here. my extract not_a_MO_term my LE2 total_RNA MO Cy5 MO P-XMPL-7 ArrayExpress my hybridization hyb conditions were suboptimal A-TEST-1 My favourite array design scanning protocol my scan imagefile1.TIFF this was a great picture Data2.txt not a [...]
+sparse source 1 normal blah blah ignore me EXTPRTCL10654 polyA RNA sparse LE Cy5 Cy5 P-XMPL-11 ArrayExpress sparse hyb A-TEST-1 sparse scan1 testing.jpg Data3.txt TRANPRTCL10656 norm 3 NormData3.txt pained expression
+sparse source 2 normal EXTPRTCL10654 polyA RNA sparse LE Cy3 Cy3 P-XMPL-11 ArrayExpress sparse hyb A-TEST-1 sparse scan2 Data4.txt TRANPRTCL10656 norm 3 NormData3.txt pregnant pause
+sparse source 3 normal EXTPRTCL10654 polyA RNA sparse LE biotin biotin P-XMPL-11 ArrayExpress sparse hyb b A-TEST-1 scanning protocol sparse scan3 imagefile2.TIFF a bit blurry TRANPRTCL10656 norm 4 NormData4.txt preternatural calm
diff --git a/examples/normalized_data/Data1.txt b/examples/normalized_data/Data1.txt
new file mode 100644
index 0000000..6d8b389
--- /dev/null
+++ b/examples/normalized_data/Data1.txt
@@ -0,0 +1,37 @@
+ATF 1
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 52 376 621 616 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 120 778 1979 1904 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 120 810 1379 1491 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 120 796 1696 1663 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 120 930 1334 1251 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 120 914 426 438 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 80 552 126 134 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 120 816 1089 1052 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 120 796 408 418 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 120 810 1757 1648 825 932 786 862 0
diff --git a/examples/normalized_data/Data2.txt b/examples/normalized_data/Data2.txt
new file mode 100644
index 0000000..6975cfa
--- /dev/null
+++ b/examples/normalized_data/Data2.txt
@@ -0,0 +1,40 @@
+ATF 1
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "F635 Median - B635" "F532 Median - B532" "F635 Mean - B635" "F532 Mean - B532" "Flags"
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 80 656 204 221 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 120 820 1042 1177 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 120 820 611 851 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 120 820 385 608 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 120 820 935 984 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 120 810 244 241 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 52 340 78 79 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 120 820 834 845 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 80 616 302 328 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 120 930 507 708 187 320 237 471 0
diff --git a/examples/normalized_data/NormData1.txt b/examples/normalized_data/NormData1.txt
new file mode 100644
index 0000000..ade2275
--- /dev/null
+++ b/examples/normalized_data/NormData1.txt
@@ -0,0 +1,11 @@
+MetaColumn MetaRow Column Row Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+1 1 1 1 A102340 0.475 0.481 0.494 0.491 0.129 0.413 0.731 -1.074
+1 1 2 1 A102341 0.494 0.496 0.5 0.599 0.409 0.433 0.777 -1.019
+1 1 3 1 A102342 0.501 0.49 0.493 0.641 1.28 0.46 0.837 -0.998
+1 1 4 1 A102343 0.78 0.782 0.819 1.372 3.252 0.675 0.722 -0.359
+1 1 5 1 A102344 0.455 0.46 0.45 0.6 0.632 0.382 0.751 -1.137
+1 1 6 1 A102345 0.42 0.431 0.473 0.696 2.121 0.362 0.589 -1.252
+1 1 7 1 A102346 0.223 0.229 0.226 0.313 0.289 0.16 0.283 -2.163
+1 1 8 1 A102347 0.525 0.503 0.522 0.741 1.71 0.445 0.754 -0.929
+1 1 9 1 A102348 0.388 0.384 0.368 0.443 0.369 0.351 0.721 -1.367
+1 1 10 1 A102349 0.885 0.912 0.923 1.477 3.031 0.751 0.698 -0.176
diff --git a/examples/normalized_data/NormData2.txt b/examples/normalized_data/NormData2.txt
new file mode 100644
index 0000000..52c7687
--- /dev/null
+++ b/examples/normalized_data/NormData2.txt
@@ -0,0 +1,11 @@
+Reporter Identifier "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "Log Ratio"
+A102340 0.447 0.473 0.502 0.428 2.467 0.472 0.332 -1.162
+A102341 0.53 0.484 0.477 0.503 3.009 0.418 0.611 -0.916
+A102342 0.669 0.588 0.592 0.604 3.17 0.511 0.592 -0.579
+A102343 0.742 0.684 0.755 0.675 3.312 0.576 0.412 -0.43
+A102344 0.479 0.514 0.506 0.502 2.251 0.507 0.593 -1.061
+A102345 0.506 0.525 0.546 0.488 3.047 0.532 0.398 -0.982
+A102346 0.592 0.612 1.025 0.705 2.93 1.819 0.071 -0.757
+A102347 0.497 0.506 0.543 0.522 3.195 0.482 0.645 -1.008
+A102348 0.606 0.624 0.669 0.614 2.841 0.577 0.414 -0.722
+A102349 0.584 0.503 0.457 0.508 2.361 0.481 0.581 -0.775
diff --git a/examples/normalized_data/normalized_data.png b/examples/normalized_data/normalized_data.png
new file mode 100644
index 0000000..bf1b7e1
Binary files /dev/null and b/examples/normalized_data/normalized_data.png differ
diff --git a/examples/normalized_data/normalized_data.txt b/examples/normalized_data/normalized_data.txt
new file mode 100644
index 0000000..bc7b765
--- /dev/null
+++ b/examples/normalized_data/normalized_data.txt
@@ -0,0 +1,40 @@
+# Simple example summary spreadsheet for tab2mage.pl script.
+# This file illustrates how to incorporate normalized data into the spreadsheet.
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-4
+quality_control dye_swap_quality_control
+experiment_design_type strain_or_line_design
+name Transcriptome analysis of invasive verses non-invasive strains of budding yeast
+description An experiment was performed to...
+release_date 2004-08-30
+submission_date 2004-07-28
+submitter John Falstaff
+# The publication details below are fictitious
+publication_title Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast
+authors John Falstaff; Robin Goodfellow
+journal Nature Genetics
+volume 12
+issue 4
+pages 123-456
+year 2004
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-EXML-1 Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8 Yeast growth
+P-EXML-2 Your protocol text here Yeast cell harvesting
+P-EXML-3 Your protocol text here Cell lysis and RNA prep
+P-EXML-4 Your protocol text here cDNA labeling
+P-EXML-5 Your protocol text here Hybridization
+P-EXML-6 Your protocol text here Scanning
+P-EXML-7 Your protocol text here Log Ratio Normalization
+
+Hybridization section
+File[raw] File[normalized] NormalizationType Protocol[normalization] Array[accession] Array[serial] Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[labeling] Protocol[hybridization] Protocol[scanning] BioSource BioSourceMaterial SampleMaterial ExtractMaterial LabeledExtractMaterial Dye FactorValue[StrainOrLine] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Sex]
+Data1.txt NormData1.txt log_ratio P-EXML-7 A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 S288C whole_organism whole_organism total_RNA synthetic_DNA Cy5 S288C Saccharomyces cerevisiae S288C mating_type_a
+Data1.txt NormData1.txt log_ratio P-EXML-7 A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 Sigma1278b whole_organism whole_organism total_RNA synthetic_DNA Cy3 Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
+Data2.txt NormData2.txt log_ratio P-EXML-7 A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 S288C whole_organism whole_organism total_RNA synthetic_DNA Cy3 S288C Saccharomyces cerevisiae S288C mating_type_a
+Data2.txt NormData2.txt log_ratio P-EXML-7 A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 Sigma1278b whole_organism whole_organism total_RNA synthetic_DNA Cy5 Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
diff --git a/examples/other_QTs.txt b/examples/other_QTs.txt
new file mode 100644
index 0000000..8f5ec43
--- /dev/null
+++ b/examples/other_QTs.txt
@@ -0,0 +1,27 @@
+# Example custom QT list for tab2mage and expt_check
+#
+# $Id: other_QTs.txt 619 2006-01-05 13:03:12Z tfrayner $
+#
+# Note: the information below is not fully curated, and is provided solely
+# as an example of the QT file format. Use it at your own risk!
+#
+# Name DataType Scale MAGE Subclass isBackground ConfidenceIndicator target QT Channel Description
+>>>ArrayVision[Imaging Research]
+Ch2 Dens - Levels float linear_scale SpecializedQuantitationType 0 Cy3
+Ch2 SD - Levels float linear_scale Error 0 Ch2 Dens - Levels Cy3
+Ch2 Area - mm2 float linear_scale SpecializedQuantitationType 1 Cy3
+Ch2 Bkgd float linear_scale MeasuredSignal 1 Cy3
+Ch2 sDens float linear_scale MeasuredSignal 0 Cy3
+Ch2 S/N float linear_scale Ratio 0 Cy3
+Ch2 Flag boolean unscaled PresentAbsent 0 Cy3
+Ch1 Dens - Levels float linear_scale SpecializedQuantitationType 0 Cy5
+Ch1 SD - Levels float linear_scale Error 0 Ch1 Dens - Levels Cy5
+Ch1 Area - mm2 float linear_scale SpecializedQuantitationType 1 Cy5
+Ch1 Bkgd float linear_scale MeasuredSignal 1 Cy5
+Ch1 sDens float linear_scale MeasuredSignal 0 Cy5
+Ch1 S/N float linear_scale Ratio 0 Cy5
+Ch1 Flag boolean unscaled PresentAbsent 0 Cy5
+>>>Miscellaneous[ebi.ac.uk]
+RMA float linear_scale DerivedSignal 0
+Normalized Intensity float linear_scale DerivedSignal 0
+Normalized Log Ratio float log_base_2 Ratio 0
diff --git a/examples/parameters/Data1.txt b/examples/parameters/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/parameters/Data1.txt
@@ -0,0 +1,37 @@
+ATF 1.0
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 0.475 0.481 0.494 0.491 0.129 0.413 0.731 52 376 621 616 -1.074 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 0.494 0.496 0.500 0.599 0.409 0.433 0.777 120 778 1979 1904 -1.019 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 0.501 0.490 0.493 0.641 1.280 0.460 0.837 120 810 1379 1491 -0.998 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 0.780 0.782 0.819 1.372 3.252 0.675 0.722 120 796 1696 1663 -0.359 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 0.455 0.460 0.450 0.600 0.632 0.382 0.751 120 930 1334 1251 -1.137 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 0.420 0.431 0.473 0.696 2.121 0.362 0.589 120 914 426 438 -1.252 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 0.223 0.229 0.226 0.313 0.289 0.160 0.283 80 552 126 134 -2.163 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 0.525 0.503 0.522 0.741 1.710 0.445 0.754 120 816 1089 1052 -0.929 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 0.388 0.384 0.368 0.443 0.369 0.351 0.721 120 796 408 418 -1.367 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 0.885 0.912 0.923 1.477 3.031 0.751 0.698 120 810 1757 1648 -0.176 825 932 786 862 0
diff --git a/examples/parameters/Data2.txt b/examples/parameters/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/parameters/Data2.txt
@@ -0,0 +1,40 @@
+ATF 1.0
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 0.447 0.473 0.502 0.428 2.467 0.472 0.332 80 656 204 221 -1.162 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 0.530 0.484 0.477 0.503 3.009 0.418 0.611 120 820 1042 1177 -0.916 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 0.669 0.588 0.592 0.604 3.170 0.511 0.592 120 820 611 851 -0.579 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 0.742 0.684 0.755 0.675 3.312 0.576 0.412 120 820 385 608 -0.430 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 0.479 0.514 0.506 0.502 2.251 0.507 0.593 120 820 935 984 -1.061 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 0.506 0.525 0.546 0.488 3.047 0.532 0.398 120 810 244 241 -0.982 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 0.592 0.612 1.025 0.705 2.930 1.819 0.071 52 340 78 79 -0.757 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 0.497 0.506 0.543 0.522 3.195 0.482 0.645 120 820 834 845 -1.008 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 0.606 0.624 0.669 0.614 2.841 0.577 0.414 80 616 302 328 -0.722 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 0.584 0.503 0.457 0.508 2.361 0.481 0.581 120 930 507 708 -0.775 187 320 237 471 0
diff --git a/examples/parameters/parameters.png b/examples/parameters/parameters.png
new file mode 100644
index 0000000..dc7c4c4
Binary files /dev/null and b/examples/parameters/parameters.png differ
diff --git a/examples/parameters/parameters.txt b/examples/parameters/parameters.txt
new file mode 100644
index 0000000..5810839
--- /dev/null
+++ b/examples/parameters/parameters.txt
@@ -0,0 +1,39 @@
+# Simple example summary spreadsheet for tab2mage.pl script.
+# This file illustrates how channels should be combined when describing a two-color hybridization (e.g. Cy3/Cy5).
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-6
+quality_control dye_swap_quality_control
+experiment_design_type strain_or_line_design
+name Transcriptome analysis of invasive verses non-invasive strains of budding yeast
+description An experiment was performed to...
+release_date 2004-08-30
+submission_date 2004-07-28
+submitter John Falstaff
+# The publication details below are fictitious
+publication_title Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast
+authors John Falstaff; Robin Goodfellow
+journal Nature Genetics
+volume 12
+issue 4
+pages 123-456
+year 2004
+
+# Protocol text may be formatted using standard HTML tags # Note that parameter units should match those in the MAGE model.
+Protocol section
+accession text name parameters
+P-EXML-1 Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8 Yeast growth growth temperature (degree_C); pH
+P-EXML-2 Your protocol text here Yeast cell harvesting pellet weight (mg)
+P-EXML-3 Your protocol text here Cell lysis and RNA prep
+P-EXML-4 Your protocol text here cDNA labeling
+P-EXML-5 Your protocol text here Hybridization hyb temp (degree_C); hyb volume (uL)
+P-EXML-6 Your protocol text here Scanning
+
+Hybridization section
+File[raw] Array[accession] Array[serial] Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[labeling] Protocol[hybridization] Protocol[scanning] Parameter[growth temperature] Parameter[pH] Parameter[pellet weight] Parameter[hyb temp] Parameter[hyb volume] BioSource BioSourceMaterial SampleMaterial ExtractMaterial LabeledExtractMaterial Dye FactorValue[StrainOrLine] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Sex]
+Data1.txt A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 30 6 10 56 50 S288C whole_organism whole_organism total_RNA synthetic_DNA Cy5 S288C Saccharomyces cerevisiae S288C mating_type_a
+Data1.txt A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 31 6 12 56 50 Sigma1278b whole_organism whole_organism total_RNA synthetic_DNA Cy3 Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
+Data2.txt A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 30 6 10 55 50 S288C whole_organism whole_organism total_RNA synthetic_DNA Cy3 S288C Saccharomyces cerevisiae S288C mating_type_a
+Data2.txt A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 31 6 12 55 50 Sigma1278b whole_organism whole_organism total_RNA synthetic_DNA Cy5 Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
diff --git a/examples/real/E-MEXP-880.png b/examples/real/E-MEXP-880.png
new file mode 100644
index 0000000..13cb790
Binary files /dev/null and b/examples/real/E-MEXP-880.png differ
diff --git a/examples/real/E-MEXP-880.txt b/examples/real/E-MEXP-880.txt
new file mode 100644
index 0000000..78ebaba
--- /dev/null
+++ b/examples/real/E-MEXP-880.txt
@@ -0,0 +1,68 @@
+"Experiment section"
+"domain" "ebi.ac.uk"
+"accession" "E-MEXP-880"
+"name" "BF-H.sapiens-HOXAmethylation"
+"description" "To evaluate DNA methylation profile associated with breast cancer in 125 kB region of HOXA cluster we analyzed DNA from normal cancerous breast specimens and cell lines. Analysis was performed on the tiling array covering entire HOXA region with 500bp resolution. Immunoprecipitation with anti-methylcytosine antibody was for target preparation."
+"experiment_design_type" "disease_state_design"
+"submitter" "Petr Novak"
+"submitter_email" "futscherlab at azcc.arizona.edu"
+"organization" "University of Arizona"
+"address" "1515 N Campbell Ave, Tucson, AZ, 85722, USA"
+"release_date" "2006-09-30"
+"submission_date" "2006-10-04"
+"publication_title" "Epigenetic Inactivation of the HOXA Gene Cluster in Breast Cancer"
+
+"Protocol section"
+"accession" "text" "name"
+"BF_CGI_hyb" "Cy3 and Cy5 labeled extracts were mixed and purified using Qiagen PCR purification kit according manufacturer's instruction.Hybridization Protocol:Slide Preparation Protocol:1. Remove all slides that you need from N2 storage and label accordingly. On the back side of the slide, scratch an outline of the corners of the array using a diamond-tipped scribe. The front is defined as the side the array is printed on and should have a label on it.Note: Only label with a pencil. Do [...]
+"HMEC" "According manufacturer’s instructions (Clonetics, San Diego,CA) (Parameters: start time = 2, stop time = 8, time unit = weeks, min temperature = 37, max temperature = C, temperature unit = MEGM)" "HMEC"
+"CCL-231-BT549" "According Manufacturer's instruction (American Type Culture Collection, Rockville, MD) (Parameters: time unit = seconds, min temperature = 37, max temperature = C, temperature unit = RPMI)" "CCL-231-BT549"
+"BREAST_TISSUE" "Flash frozen specimens derived from normal or cancerous breast tissue were obtained from patients who underwent surgery for breast cancer, either lumpectomy or mastectomy, at the University Medical Center in Tucson, AZ., from 2003-2004. All patients signed surgical and clinical research consents for tissue collection in accordance with the University of Arizona Institutional Review Board and HIPAA regulations (Parameters: time unit = seconds, temperature unit = C)" "BRE [...]
+"MeCIP_Cy3" "Random priming reactions in the presence of 3ul of Cy3-dUTP (Amersham Pharmacia) with the use of a BioPrime DNA labeling kit (Invitrogen) according manufacturer's protocol with one modification. Reaction time was prolonget to 16hrs (instead of 2) (Parameters: Amount of nucleic acid labeled = 200, Label used = Cy3, Amplification = none, Mass unit = Nano gram)" "MeCIP_Cy3"
+"BF_CGI_Scan" "Parameters of scanning and image analysis stored in raw data files.PMT values were set to obtain approximately same range of intensities in both channelsEach grinding was visually examined and anomalous spots were flagged for exclussion (Parameters: Scanning hardware = GenePix 4000B [Axon Instruments], Scanning software = GenePix Pro [Axon Instruments])" "BF_CGI_Scan"
+"BF-MeCIP" "DNA was isolated according tissue Protocol (QIAamp DNA Mini Kit-www.qiagen.com). Using anti-MethylCytosine antibody(Aviva System Biology, cat.no. AMM99021), methylated fraction of DNA was enriched by immunoprecipitation. Input- sonicated DNA serves as reference:Day 1First, 5ug of genomic DNA was resuspended in 50ul of SDS lysis buffer(1% SDS, 10mM EDTA, 50mM Tris-HCl, pH 8.1) + 450ul ChIP dilution buffer (0.01% SDS, 1.1% Triton X-100, 1.2mM EDTA, 16.7mM Tris-HCl, pH 8.1, 167m [...]
+"MeCIP_Cy5" "Random priming reactions in the presence of 3ul of Cy3-dUTP (Amersham Pharmacia) with the use of a BioPrime DNA labeling kit (Invitrogen) according manufacturer's protocol with one modification. Reaction time was prolonget to 16hrs (instead of 2hrs) (Parameters: Amount of nucleic acid labeled = 200, Mass unit = Nano gram, Label used = Cy5, Amplification = none)" "MeCIP_Cy5"
+"BF-HOXA_transf" "The data from scanned microarray images were extracted using GenePix software. Median signal intensity of each spot was used for further analysis. Obtained data were were normalized in Excel. Only features corresponding HOXA tiling array were used in data processing but not features of CGI array. To exclude low quality spot data, the only spots with median of pixel intensity 1.5 higher than median of its local backgrounds where used for further analysis. Two step normal [...]
+
+"Hybridization section"
+"BioSource" "BioMaterialCharacteristics[Organism]" "BioMaterialCharacteristics[BioSourceProvider]" "BioMaterialCharacteristics[BioSourceType]" "BioMaterialCharacteristics[DevelopmentalStage]" "BioMaterialCharacteristics[OrganismPart]" "BioMaterialCharacteristics[Sex]" "BioMaterialCharacteristics[Individual]" "BioMaterialCharacteristics[DiseaseState]" "BioMaterialCharacteristics[TargetedCellType]" "BioMaterialCharacteristics[CellLine]" "BioMaterialCharacteristics[ClinicalHistory]" "Protoc [...]
+"1139T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 1139 "Lymph node metastasis" "Epithelial" 1139 "Lymph node metastasis" "BREAST_TISSUE" "BF-MeCIP" "E_1139T" "genomic_DNA" "L_1139T IP" "molecular_mixture" "MeCIP_Cy3" "L_1139T" "Cy3" "BF_CGI_hyb" "H_1139T_1139T_1" "A-MEXP-568" "BF_CGI_Scan" "1139T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Lymph node metastasis" 1139 "molecular_mixture"
+"1139T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 1139 "Lymph node metastasis" "Epithelial" 1139 "Lymph node metastasis" "BREAST_TISSUE" "BF-MeCIP" "E_1139T" "genomic_DNA" "L_1139T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_1139T_1" "Cy5" "BF_CGI_hyb" "H_1139T_1139T_1" "A-MEXP-568" "BF_CGI_Scan" "1139T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Lymph node metastasis" 1139 "genomic_DNA"
+"120T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 120 "Infiltrating ductal carcinoma" "Epithelial" 120 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_120T" "genomic_DNA" "L_120T IP" "molecular_mixture" "MeCIP_Cy3" "L_120T" "Cy3" "BF_CGI_hyb" "H_120T_120T_1" "A-MEXP-568" "BF_CGI_Scan" "120T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 120 "molecular_mixture"
+"120T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 120 "Infiltrating ductal carcinoma" "Epithelial" 120 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_120T" "genomic_DNA" "L_120 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_120T_1" "Cy5" "BF_CGI_hyb" "H_120T_120T_1" "A-MEXP-568" "BF_CGI_Scan" "120T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 120 "genomic_DNA"
+"2845T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 2845 "Infiltrating ductal carcinoma" "Epithelial" 2845 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_2845T" "genomic_DNA" "L_2845T IP" "molecular_mixture" "MeCIP_Cy3" "L_2845T" "Cy3" "BF_CGI_hyb" "H_2845T_2845T_1" "A-MEXP-568" "BF_CGI_Scan" "2845T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 2845 "molecular_mixture"
+"2845T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 2845 "Infiltrating ductal carcinoma" "Epithelial" 2845 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_2845T" "genomic_DNA" "L_2845T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_2845T_1" "Cy5" "BF_CGI_hyb" "H_2845T_2845T_1" "A-MEXP-568" "BF_CGI_Scan" "2845T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 2845 "genomic_DNA"
+"5256T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 5256 "Infiltrating ductal carcinoma" "Epithelial" 5256 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_5256" "genomic_DNA" "L_5256T IP" "molecular_mixture" "MeCIP_Cy3" "L_5256T" "Cy3" "BF_CGI_hyb" "H_5256T_5256T_1" "A-MEXP-568" "BF_CGI_Scan" "5256T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 5256 "molecular_mixture"
+"5256T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 5256 "Infiltrating ductal carcinoma" "Epithelial" 5256 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_5256" "genomic_DNA" "L_5256T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_5256T_1" "Cy5" "BF_CGI_hyb" "H_5256T_5256T_1" "A-MEXP-568" "BF_CGI_Scan" "5256T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 5256 "genomic_DNA"
+"5343N" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 5343 "Normal" "Epithelial" 5343 "BREAST_TISSUE" "BF-MeCIP" "E_5343N" "genomic_DNA" "L_5343N IP" "molecular_mixture" "MeCIP_Cy3" "L_5343N" "Cy3" "BF_CGI_hyb" "H_5343N_5343N_1" "A-MEXP-568" "BF_CGI_Scan" "5343N.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" 5343 "molecular_mixture"
+"5343N" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 5343 "Normal" "Epithelial" 5343 "BREAST_TISSUE" "BF-MeCIP" "E_5343N" "genomic_DNA" "L_5343N sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_5343N_1" "Cy5" "BF_CGI_hyb" "H_5343N_5343N_1" "A-MEXP-568" "BF_CGI_Scan" "5343N.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" 5343 "genomic_DNA"
+"5799T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 5799 "Infiltrating ductal carcinoma" "Epithelial" 5799 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_5799T" "genomic_DNA" "L_5799T IP" "molecular_mixture" "MeCIP_Cy3" "L_5799T" "Cy3" "BF_CGI_hyb" "H_5799T_5799T_1" "A-MEXP-568" "BF_CGI_Scan" "5799T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 5799 "molecular_mixture"
+"5799T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 5799 "Infiltrating ductal carcinoma" "Epithelial" 5799 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_5799T" "genomic_DNA" "L_5799T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_5799T_1" "Cy5" "BF_CGI_hyb" "H_5799T_5799T_1" "A-MEXP-568" "BF_CGI_Scan" "5799T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 5799 "genomic_DNA"
+"6245T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 6245 "Infiltrating ductal carcinoma" "Epithelial" 6245 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_6245T" "genomic_DNA" "L_6245T IP" "molecular_mixture" "MeCIP_Cy3" "L_6245T" "Cy3" "BF_CGI_hyb" "H_6245T_6245T_1" "A-MEXP-568" "BF_CGI_Scan" "6245T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 6245 "molecular_mixture"
+"6245T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 6245 "Infiltrating ductal carcinoma" "Epithelial" 6245 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_6245T" "genomic_DNA" "L_6245T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_6245T_1" "Cy5" "BF_CGI_hyb" "H_6245T_6245T_1" "A-MEXP-568" "BF_CGI_Scan" "6245T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 6245 "genomic_DNA"
+"6333N" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 6333 "Normal" "Epithelial" 6333 "BREAST_TISSUE" "BF-MeCIP" "E_6333N" "genomic_DNA" "L_6333N IP" "molecular_mixture" "MeCIP_Cy3" "L_6333N" "Cy3" "BF_CGI_hyb" "H_6333N_6333N_1" "A-MEXP-568" "BF_CGI_Scan" "6333N.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" 6333 "molecular_mixture"
+"6333N" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 6333 "Normal" "Epithelial" 6333 "BREAST_TISSUE" "BF-MeCIP" "E_6333N" "genomic_DNA" "L_6333N sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_6333N_1" "Cy5" "BF_CGI_hyb" "H_6333N_6333N_1" "A-MEXP-568" "BF_CGI_Scan" "6333N.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" 6333 "genomic_DNA"
+"7732N" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7732 "Normal" "Epithelial" 7732 "BREAST_TISSUE" "BF-MeCIP" "E_7732N" "genomic_DNA" "L_7732N IP" "molecular_mixture" "MeCIP_Cy3" "L_7732N" "Cy3" "BF_CGI_hyb" "H_7732N_7732N_1" "A-MEXP-568" "BF_CGI_Scan" "7732N.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" 7732 "molecular_mixture"
+"7732N" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7732 "Normal" "Epithelial" 7732 "BREAST_TISSUE" "BF-MeCIP" "E_7732N" "genomic_DNA" "L_7732N sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_7732N_1" "Cy5" "BF_CGI_hyb" "H_7732N_7732N_1" "A-MEXP-568" "BF_CGI_Scan" "7732N.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" 7732 "genomic_DNA"
+"7732T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7732 "Infiltrating ductal carcinoma" "Epithelial" 7732 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_7732T" "genomic_DNA" "L_7732T IP" "molecular_mixture" "MeCIP_Cy3" "L_7732T" "Cy3" "BF_CGI_hyb" "H_7732T_7732T_1" "A-MEXP-568" "BF_CGI_Scan" "7732T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 7732 "molecular_mixture"
+"7732T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7732 "Infiltrating ductal carcinoma" "Epithelial" 7732 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_7732T" "genomic_DNA" "L_7732T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_7732T_1" "Cy5" "BF_CGI_hyb" "H_7732T_7732T_1" "A-MEXP-568" "BF_CGI_Scan" "7732T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 7732 "genomic_DNA"
+"7768T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7768 "Infiltrating ductal carcinoma" "Epithelial" 7768 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_7768T" "genomic_DNA" "L_7768T IP" "molecular_mixture" "MeCIP_Cy3" "L_7768T" "Cy3" "BF_CGI_hyb" "H_7768T_7768T_1" "A-MEXP-568" "BF_CGI_Scan" "7768T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 7768 "molecular_mixture"
+"7768T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7768 "Infiltrating ductal carcinoma" "Epithelial" 7768 "Infiltrating ductal carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_7768T" "genomic_DNA" "L_7768T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_7768T_1" "Cy5" "BF_CGI_hyb" "H_7768T_7768T_1" "A-MEXP-568" "BF_CGI_Scan" "7768T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating ductal carcinoma" 7768 "genomic_DNA"
+"7788T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7788 "Infiltrating lobular carcinoma" "Epithelial" 7788 "Infiltrating lobular carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_7788T" "genomic_DNA" "L_7788T IP" "molecular_mixture" "MeCIP_Cy3" "L_7788T" "Cy3" "BF_CGI_hyb" "H_7788T_7788T_1" "A-MEXP-568" "BF_CGI_Scan" "7788T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating lobular carcinoma" 7788 "molecular_mixture"
+"7788T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 7788 "Infiltrating lobular carcinoma" "Epithelial" 7788 "Infiltrating lobular carcinoma" "BREAST_TISSUE" "BF-MeCIP" "E_7788T" "genomic_DNA" "L_7788T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_7788T_1" "Cy5" "BF_CGI_hyb" "H_7788T_7788T_1" "A-MEXP-568" "BF_CGI_Scan" "7788T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Infiltrating lobular carcinoma" 7788 "genomic_DNA"
+"9663T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 9663 "Lymph node metastasis" "Epithelial" 9663 "Lymph node metastasis" "BREAST_TISSUE" "BF-MeCIP" "E_9663T" "genomic_DNA" "L_9663T IP" "molecular_mixture" "MeCIP_Cy3" "L_9663T" "Cy3" "BF_CGI_hyb" "H_9663T_9663T_1" "A-MEXP-568" "BF_CGI_Scan" "9663T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Lymph node metastasis" 9663 "molecular_mixture"
+"9663T" "Homo sapiens" "University Medical Center, Tucson,AZ" "frozen_sample" "adult" "mammary gland" "female" 9663 "Lymph node metastasis" "Epithelial" 9663 "Lymph node metastasis" "BREAST_TISSUE" "BF-MeCIP" "E_9663T" "genomic_DNA" "L_9663T sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_9663T_1" "Cy5" "BF_CGI_hyb" "H_9663T_9663T_1" "A-MEXP-568" "BF_CGI_Scan" "9663T.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Lymph node metastasis" 9663 "genomic_DNA"
+"BT 549-1" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "BT 549" "Breast Cancer Cell line" "Epithelial" "BT 549" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_BT 549-1" "genomic_DNA" "L_BT 549-1 IP" "molecular_mixture" "MeCIP_Cy3" "L_BT 549-1" "Cy3" "BF_CGI_hyb" "H_BT 549-1_BT 549-1_1" "A-MEXP-568" "BF_CGI_Scan" "BT549-1.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast Cancer Cell line" "BT 549" "molecular_mixture"
+"BT 549-1" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "BT 549" "Breast Cancer Cell line" "Epithelial" "BT 549" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_BT 549-1" "genomic_DNA" "L_BT 549-1 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_BT 549-1_1" "Cy5" "BF_CGI_hyb" "H_BT 549-1_BT 549-1_1" "A-MEXP-568" "BF_CGI_Scan" "BT549-1.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast Cancer Cell line" "BT 549" "genomic_DNA"
+"BT 549-2" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "BT 549" "Breast Cancer Cell line" "Epithelial" "BT 549" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_BT 549-2" "genomic_DNA" "L_BT 549-2 IP" "molecular_mixture" "MeCIP_Cy3" "L_BT 549-2" "Cy3" "BF_CGI_hyb" "H_BT 549-2_BT 549-2_1" "A-MEXP-568" "BF_CGI_Scan" "BT549-2.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast Cancer Cell line" "BT 549" "molecular_mixture"
+"BT 549-2" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "BT 549" "Breast Cancer Cell line" "Epithelial" "BT 549" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_BT 549-2" "genomic_DNA" "L_BT 549-2 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_BT 549-2_1" "Cy5" "BF_CGI_hyb" "H_BT 549-2_BT 549-2_1" "A-MEXP-568" "BF_CGI_Scan" "BT549-2.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast Cancer Cell line" "BT 549" "genomic_DNA"
+"HMEC1" "Homo sapiens" "Clonetics, San Diego, CA" "adult" "mammary gland" "female" "HMEC" "Normal" "Epithelial" "HMEC" "Human Mammary Epithelial Cell" "HMEC" "BF-MeCIP" "E_HMEC1" "genomic_DNA" "L_HMEC1 IP" "molecular_mixture" "MeCIP_Cy3" "L_HMEC1" "Cy3" "BF_CGI_hyb" "H_HMEC1_HMEC1_1" "A-MEXP-568" "BF_CGI_Scan" "HMEC1.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" "HMEC" "molecular_mixture"
+"HMEC1" "Homo sapiens" "Clonetics, San Diego, CA" "adult" "mammary gland" "female" "HMEC" "Normal" "Epithelial" "HMEC" "Human Mammary Epithelial Cell" "HMEC" "BF-MeCIP" "E_HMEC1" "genomic_DNA" "L_HMEC1 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_HMEC1_1" "Cy5" "BF_CGI_hyb" "H_HMEC1_HMEC1_1" "A-MEXP-568" "BF_CGI_Scan" "HMEC1.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" "HMEC" "genomic_DNA"
+"HMEC2" "Homo sapiens" "Clonetics, San Diego, CA" "adult" "mammary gland" "female" "HMEC" "Normal" "Epithelial" "HMEC" "Human Mammary Epithelial Cell" "HMEC" "BF-MeCIP" "E_HMEC2" "genomic_DNA" "L_HMEC2 IP" "molecular_mixture" "MeCIP_Cy3" "L_HMEC2" "Cy3" "BF_CGI_hyb" "H_HMEC2_HMEC2_1" "A-MEXP-568" "BF_CGI_Scan" "HMEC2.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" "HMEC" "molecular_mixture"
+"HMEC2" "Homo sapiens" "Clonetics, San Diego, CA" "adult" "mammary gland" "female" "HMEC" "Normal" "Epithelial" "HMEC" "Human Mammary Epithelial Cell" "HMEC" "BF-MeCIP" "E_HMEC2" "genomic_DNA" "L_HMEC2 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_HMEC2_1" "Cy5" "BF_CGI_hyb" "H_HMEC2_HMEC2_1" "A-MEXP-568" "BF_CGI_Scan" "HMEC2.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" "HMEC" "genomic_DNA"
+"HMEC3" "Homo sapiens" "Clonetics, San Diego, CA" "adult" "mammary gland" "female" "HMEC" "Normal" "Epithelial" "HMEC" "Human Mammary Epithelial Cell" "HMEC" "BF-MeCIP" "E_HMEC3" "genomic_DNA" "L_HMEC3 IP" "molecular_mixture" "MeCIP_Cy3" "L_HMEC3" "Cy3" "BF_CGI_hyb" "H_HMEC3_HMEC3_1" "A-MEXP-568" "BF_CGI_Scan" "HMEC3.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" "HMEC" "molecular_mixture"
+"HMEC3" "Homo sapiens" "Clonetics, San Diego, CA" "adult" "mammary gland" "female" "HMEC" "Normal" "Epithelial" "HMEC" "Human Mammary Epithelial Cell" "HMEC" "BF-MeCIP" "E_HMEC3" "genomic_DNA" "L_HMEC3 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_HMEC3_1" "Cy5" "BF_CGI_hyb" "H_HMEC3_HMEC3_1" "A-MEXP-568" "BF_CGI_Scan" "HMEC3.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "normal" "HMEC" "genomic_DNA"
+"MB-MDA-231-1" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "MB-MDA-231" "Breast Cancer Cell line" "Epithelial" "MB-MDA-231" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_MB-MDA-231-1" "genomic_DNA" "L_MB-MDA-231-1 IP" "molecular_mixture" "MeCIP_Cy3" "L_MB-MDA-231-1" "Cy3" "BF_CGI_hyb" "H_MB-MDA-231-1_MB-MDA-231-1_1" "A-MEXP-568" "BF_CGI_Scan" "MDA-MB-231-1.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast Cancer Cell [...]
+"MB-MDA-231-1" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "MB-MDA-231" "Breast Cancer Cell line" "Epithelial" "MB-MDA-231" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_MB-MDA-231-1" "genomic_DNA" "L_MB-MDA-231-1 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_MB-MDA-231-1_1" "Cy5" "BF_CGI_hyb" "H_MB-MDA-231-1_MB-MDA-231-1_1" "A-MEXP-568" "BF_CGI_Scan" "MDA-MB-231-1.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast [...]
+"MB-MDA-231-2" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "MB-MDA-231" "Breast Cancer Cell line" "Epithelial" "MB-MDA-231" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_MB-MDA-231-2" "genomic_DNA" "L_MB-MDA-231-2 IP" "molecular_mixture" "MeCIP_Cy3" "L_MB-MDA-231-2" "Cy3" "BF_CGI_hyb" "H_MB-MDA-231-2_MB-MDA-231-2_1" "A-MEXP-568" "BF_CGI_Scan" "MDA-MB-231-2.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast Cancer Cell [...]
+"MB-MDA-231-2" "Homo sapiens" "American Type Culture Collection (Rockville, MD)" "adult" "mammary gland" "female" "MB-MDA-231" "Breast Cancer Cell line" "Epithelial" "MB-MDA-231" "Breast Cancer Cell line" "CCL-231-BT549" "BF-MeCIP" "E_MB-MDA-231-2" "genomic_DNA" "L_MB-MDA-231-2 sonicated input DNA" "genomic_DNA" "MeCIP_Cy5" "L_MB-MDA-231-2_1" "Cy5" "BF_CGI_hyb" "H_MB-MDA-231-2_MB-MDA-231-2_1" "A-MEXP-568" "BF_CGI_Scan" "MDA-MB-231-2.gpr" "BF-HOXA_transf" "combined_data_HOXA.txt" "Breast [...]
diff --git a/examples/real/E-TABM-102.png b/examples/real/E-TABM-102.png
new file mode 100644
index 0000000..20479a1
Binary files /dev/null and b/examples/real/E-TABM-102.png differ
diff --git a/examples/real/E-TABM-102.txt b/examples/real/E-TABM-102.txt
new file mode 100755
index 0000000..b9df9fd
--- /dev/null
+++ b/examples/real/E-TABM-102.txt
@@ -0,0 +1,56 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-102
+quality_control biological_replicate
+experiment_design_type "compound_treatment_design,genetic_modification_design,time_series_design"
+name "Transcription profiling of wild-type and ATF3 -/-mouse bone marrow macrophages stimulated with lipopolysaccharide after 1, 2, 4, 8 and 24 hours"
+description Time course data from lipopolysaccharide-stimulated wild-type and ATF3 -/- mouse bone marrow macrophages were collected to investigate the transcriptional network in Toll-like recepter 4-activated macrophages.
+release_date 2006-05-30
+submission_date 2006-05-30
+submitter Bruz Marzolf
+organization Institute for Systems Biology
+publication_title Systems biology approaches identify ATF3 as a negative regulator of Toll-like receptor 4
+authors Mark Gilchrist; Vesteinn Thorsson; Bin Li; Alistair G. Rust; Martin Korb; Kathleen Kennedy; Tsonwin Hai; Hamid Bolouri and Alan Aderem
+journal Nature
+volume 441
+issue 2006 May 11
+pages 173-178
+year 2006
+
+Protocol section
+accession text name type
+P-TABM-gilchrist1 "Flush femurs from C57BL/6 mice (Jackson Laboratories) with complete RPMI (RPMI 1640 supplemented with 10% FCS, 2mM L-glutamine, 100 IU/mL penicillin and 100ug/mL streptomycin, all from Cellgro, Mediatech, except the FCS which is from from Hyclone). Plate bone marrow cells on non-tissue culture treated plastic in complete RPMI supplemented with recombinant human M-CSF (rhM-CSF) at 50 ng/mL (Chiron). On day 3, wash the cells two times with complete RPMI and then grow for [...]
+P-TABM-gilchrist2 "On day 7, stimulate the cells with 10 ng/mL lipopolysaccharide (S. Minnesota, List) for 1, 2, 4, 8 and 24 hours without changing the media." Bone marrow macrophage stimulation treatment
+
+Hybridization section
+File[raw] File[normalized] File[exp] File[cdf] Array[accession] Array[serial] Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[labeling] BioSource BioSourceMaterial BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BiomaterialCharacteristics[Genotype] BioMaterialCharacteristics[Sex] BioMaterialCharacteristics[CellType] BioMaterialCharacteristics[GeneticModification] FactorValue[Compound] FactorValue[Dose(ng/mL)] FactorValue[Time(h)] FactorValue [...]
+20040603_03_LPS1-0.CEL 20040603_03_LPS1-0.CHP 20040603_03_LPS1-0.EXP Mouse430_2.cdf A-AFFY-45 4001319 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 0 hr Mouse 1 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage none 0 0 wild_type
+20040621_01_LPS2-0.CEL 20040621_01_LPS2-0.CHP 20040621_01_LPS2-0.EXP Mouse430_2.cdf A-AFFY-45 4001319 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 0 hr Mouse 2 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage none 0 0 wild_type
+20040622_01_LPS3-0.CEL 20040622_01_LPS3-0.CHP 20040622_01_LPS3-0.EXP Mouse430_2.cdf A-AFFY-45 4001319 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 0 hr Mouse 3 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage none 0 0 wild_type
+20040609_03_LPS1-60.CEL 20040609_03_LPS1-60.CHP 20040609_03_LPS1-60.EXP Mouse430_2.cdf A-AFFY-45 4001319 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 1 hr Mouse 1 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 1 wild_type
+20040621_04_LPS2-60.CEL 20040621_04_LPS2-60.CHP 20040621_04_LPS2-60.EXP Mouse430_2.cdf A-AFFY-45 4001319 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 1 hr Mouse 2 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 1 wild_type
+20040622_04_LPS3-60.CEL 20040622_04_LPS3-60.CHP 20040622_04_LPS3-60.EXP Mouse430_2.cdf A-AFFY-45 4001789 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 1 hr Mouse 3 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 1 wild_type
+20040603_04_LPS1-120.CEL 20040603_04_LPS1-120.CHP 20040603_04_LPS1-120.EXP Mouse430_2.cdf A-AFFY-45 4001319 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 2 hr Mouse 1 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 2 wild_type
+20040621_06_LPS2-120.CEL 20040621_06_LPS2-120.CHP 20040621_06_LPS2-120.EXP Mouse430_2.cdf A-AFFY-45 4001319 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 2 hr Mouse 2 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 2 wild_type
+20040622_06_LPS3-120.CEL 20040622_06_LPS3-120.CHP 20040622_06_LPS3-120.EXP Mouse430_2.cdf A-AFFY-45 4001789 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 2 hr Mouse 3 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 2 wild_type
+20050310_03_C57_A-240.CEL 20050310_03_C57_A-240.CHP 20050310_03_C57_A-240.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 4 hr Mouse 1 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 4 wild_type
+20050505_09_C57_LPS_B_240.CEL 20050505_09_C57_LPS_B_240.CHP 20050505_09_C57_LPS_B_240.EXP Mouse430_2.cdf A-AFFY-45 4006289 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 4 hr Mouse 2 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 4 wild_type
+20050623_03_C57_LPS_C_240min.CEL 20050623_03_C57_LPS_C_240min.CHP 20050623_03_C57_LPS_C_240min.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 4 hr Mouse 3 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 4 wild_type
+20050622_09_LPS_A1_8_hr.CEL 20050622_09_LPS_A1_8_hr.CHP 20050622_09_LPS_A1_8_hr.EXP Mouse430_2.cdf A-AFFY-45 4009043 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 8 hr Mouse 1 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 8 wild_type
+20050622_11_LPS_B1_8_hr.CEL 20050622_11_LPS_B1_8_hr.CHP 20050622_11_LPS_B1_8_hr.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 8 hr Mouse 2 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 8 wild_type
+20050623_01_LPS_C1_8hr.CEL 20050623_01_LPS_C1_8hr.CHP 20050623_01_LPS_C1_8hr.EXP Mouse430_2.cdf A-AFFY-45 4006263 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 8 hr Mouse 3 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 8 wild_type
+20050622_10_LPS_A1_24_hr.CEL 20050622_10_LPS_A1_24_hr.CHP 20050622_10_LPS_A1_24_hr.EXP Mouse430_2.cdf A-AFFY-45 4009043 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 24 hr Mouse 1 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 24 wild_type
+20050622_12_LPS_B1_24_hr.CEL 20050622_12_LPS_B1_24_hr.CHP 20050622_12_LPS_B1_24_hr.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 24 hr Mouse 2 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 24 wild_type
+20050623_04_LPS_C2_24hr.CEL 20050623_04_LPS_C2_24hr.CHP 20050623_04_LPS_C2_24hr.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 LPS 24 hr Mouse 3 cell Mus musculus C57BL/6 wild_type mixed_sex macrophage lipopolysaccharide 10 24 wild_type
+20050310_04_ATF3--_A-0.CEL 20050310_04_ATF3--_A-0.CHP 20050310_04_ATF3--_A-0.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 0 hr Mouse 1 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out none 0 0 ATF3-/-
+20050331_08_ATF3--_B-0.CEL 20050331_08_ATF3--_B-0.CHP 20050331_08_ATF3--_B-0.EXP Mouse430_2.cdf A-AFFY-45 4006290 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 0 hr Mouse 2 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out none 0 0 ATF3-/-
+20050504_09_ATF3_LPS_E-0.CEL 20050504_09_ATF3_LPS_E-0.CHP 20050504_09_ATF3_LPS_E-0.EXP Mouse430_2.cdf A-AFFY-45 4006289 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 0 hr Mouse 3 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out none 0 0 ATF3-/-
+20050310_05_ATF3--_A-60.CEL 20050310_05_ATF3--_A-60.CHP 20050310_05_ATF3--_A-60.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 1 hr Mouse 1 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 1 ATF3-/-
+20050411_04_ATF3--_B-60.CEL 20050411_04_ATF3--_B-60.CHP 20050411_04_ATF3--_B-60.EXP Mouse430_2.cdf A-AFFY-45 4006290 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 1 hr Mouse 2 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 1 ATF3-/-
+20050504_10_ATF3_LPS_E-60.CEL 20050504_10_ATF3_LPS_E-60.CHP 20050504_10_ATF3_LPS_E-60.EXP Mouse430_2.cdf A-AFFY-45 4006289 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 1 hr Mouse 3 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 1 ATF3-/-
+20050310_06_ATF3--_A-120.CEL 20050310_06_ATF3--_A-120.CHP 20050310_06_ATF3--_A-120.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 2 hr Mouse 1 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 2 ATF3-/-
+20050411_05_ATF3--_B-120.CEL 20050411_05_ATF3--_B-120.CHP 20050411_05_ATF3--_B-120.EXP Mouse430_2.cdf A-AFFY-45 4006290 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 2 hr Mouse 2 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 2 ATF3-/-
+20050504_11_ATF3_LPS_E-120.CEL 20050504_11_ATF3_LPS_E-120.CHP 20050504_11_ATF3_LPS_E-120.EXP Mouse430_2.cdf A-AFFY-45 4006289 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 2 hr Mouse 3 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 2 ATF3-/-
+20050310_07_ATF3--_A-240.CEL 20050310_07_ATF3--_A-240.CHP 20050310_07_ATF3--_A-240.EXP Mouse430_2.cdf A-AFFY-45 4005732 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 4 hr Mouse 1 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 4 ATF3-/-
+20050623_02_ATF3_B_240min.CEL 20050623_02_ATF3_B_240min.CHP 20050623_02_ATF3_B_240min.EXP Mouse430_2.cdf A-AFFY-45 4006263 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 4 hr Mouse 2 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 4 ATF3-/-
+20050504_12_ATF3_LPS_E-240.CEL 20050504_12_ATF3_LPS_E-240.CHP 20050504_12_ATF3_LPS_E-240.EXP Mouse430_2.cdf A-AFFY-45 4006289 P-TABM-gilchrist1 P-TABM-gilchrist2 P-AFFY-1 P-AFFY-2 ATF3 -/- LPS 4 hr Mouse 3 cell Mus musculus ATF3 -/- C57BL/6 ATF3 -/- mixed_sex macrophage gene_knock_out lipopolysaccharide 10 4 ATF3-/-
diff --git a/examples/real/E-TABM-134.png b/examples/real/E-TABM-134.png
new file mode 100644
index 0000000..afd833c
Binary files /dev/null and b/examples/real/E-TABM-134.png differ
diff --git a/examples/real/E-TABM-134.txt b/examples/real/E-TABM-134.txt
new file mode 100755
index 0000000..0e1f15d
--- /dev/null
+++ b/examples/real/E-TABM-134.txt
@@ -0,0 +1,57 @@
+Experiment Section
+domain ebi.ac.uk
+accession E-TABM-134
+quality_control
+experiment_design_type "genotyping_design, disease_state_design, optimization_design"
+name WGA-LCM and Genomewide Survey of Lung Cancer
+description A series of experiments were performed to validate whole genome amplification of genomic DNA from laser captured microdissectates and the technique was then applied to serial samples of developing lung cancers to identify early events. 10K Affymetrix SNP arrays were used.
+release_date 2006-10-01
+submission_date 2006-08-11
+submitter Helen Bird
+submitter_email H.E.Bird at warwick.ac.uk
+organization Warwick University
+publication_title Validation of a method for processing laser captures microdissected samples for analysis using high density single nucleotide polymorphism arrays & Genomewide loss of heterozygosity in small cell and non-small cell lung cancer and precursor bronchial epithelium
+authors Bird H; Snead DRJ; Hey Y; Brown PE; Davis SA; James S; Knowles MA; Hurst CD; Blanks A; Pepper SD & Snead DRJ; Brown PE; James S; Davis SA; Suortamo S; Pandey S; Blanks A; Grammatopoulos D; Vohra H; Bird H
+journal in submission to Journal of Pathology
+volume
+issue
+pages
+year
+
+Protocol Section
+accession text name parameters
+P-EXML-2 "Resected tumour, tissue of bronchial origin and lymph node were snap frozen in liquid nitrogen. Frozen sections were cut and stained with haematoxylin and eosin." Patient Sample Processing
+P-EXML-3 "LCM was performed using the Nikon Sl� cut instrument (Nikon UK Ltd.) on frozen sections which had been stored at -80�C. Target lesions were visualised directly, excised and captured on to the adhesive cap of the LCM tubes (Nikon UK Ltd.). Captured cells were stored at -80�C prior to WGA and microarray analysis." Laser Capture Microdissection
+P-EXML-4 "For cell lines: DNA was extracted from the bladder tumour-derived cell line, J82, and from the patient-matched normal cell line (J82EBV, immortalised normal lymphoblasts). Aliquots of J82 tumour cell line DNA and of J82EBV normal cell line DNA were adjusted to either 250ng/5�L (for non-WGA controls), 3ng/3�L or 1ng/3�L (for WGA) to start the next step of the protocol. <br><br>For patient tissue: For non-WGA controls, complete sections were taken and DNA extracted using the DNea [...]
+P-EXML-6 "Protocol was followed exactly according to manufacturer's instructions (Affymetrix, High Wycombe, UK), starting with 250ng DNA in all cases." "Digestion, Ligation, PCR, Purification, Labeling, Fragmentation"
+P-EXML-8 "80�L hybridisation cocktail was hybridisaed overnight at 48�C according to manufacturer's instructions (Affymetrix, High Wycombe, UK). Mapping10K_Xba131 arrays were processed with DNAARRAY_WS2 on Fluidics 400" Washing & Staining_131
+P-EXML-9 "80�L hybridisation cocktail was hybridisaed overnight at 48�C according to manufacturer's instructions (Affymetrix, High Wycombe, UK). Mapping10K_Xba142 arrays were processed with Mini_Mapping10Kv1_450 on Fluidics 450" Washing & Staining_142
+P-EXML-10 "CEL files were analysed in GDAS using the same default settings for Xba131 and Xba142 arrays: HCRBound = 0.070000, CallZoneBound=0.800000, DSBound = 0.080000, GenderAttributeName = Gender, MaleAttributeValue = M, FemaleAttributeValue = F" Data Processing
+
+Hybridization Section
+File[raw] File[normalized] File[cdf] Array[accession] Array[serial] BioSource BioMaterialCharacteristics[DiseaseStaging] FactorValue[DiseaseStaging] BioMaterialCharacteristics[Individual] FactorValue[Individual] BioMaterialCharacteristics[Organism] BioSourceMaterial BioMaterialCharacteristics[OrganismPart] BioMaterialCharacteristics[DiseaseState] FactorValue[DiseaseState] BioMaterialCharacteristics[TumourGrading] BioMaterialCharacteristics[Sex] BioMaterialCharacteristics[Age] BioMaterial [...]
+nJ82_P.CEL nJ82_P.CHP Mapping10K_Xba131.CDF A-AFFY-64 3003935 nJ82_P not applicable patient J82 patient J82 Homo sapiens cell_line tumour cell line tumour cell line P-EXML-4 250ng DNA none none PICR P-EXML-6 P-EXML-8 P-EXML-10
+nJ82ebv_P.CEL nJ82ebv_P.CHP Mapping10K_Xba131.CDF A-AFFY-64 3003935 nJ82ebv_P not applicable patient J82 patient J82 Homo sapiens cell_line immortalised cell line immortalised cell line P-EXML-4 250ng DNA none none PICR P-EXML-6 P-EXML-8 P-EXML-10
+aJ82_1_W.CEL aJ82_1_W.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 aJ82_1_W not applicable patient J82 patient J82 Homo sapiens cell_line tumour cell line tumour cell line P-EXML-4 1ng DNA WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+aJ82ebv_1_W.CEL aJ82ebv_1_W.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 aJ82ebv_1_W not applicable patient J82 patient J82 Homo sapiens cell_line immortalised cell line immortalised cell line P-EXML-4 1ng DNA WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+aJ82_3_W.CEL aJ82_3_W.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 aJ82_3_W not applicable patient J82 patient J82 Homo sapiens cell_line tumour cell line tumour cell line P-EXML-4 3ng DNA WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+nJ82_W.CEL nJ82_W.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 nJ82_W not applicable patient J82 patient J82 Homo sapiens cell_line tumour cell line tumour cell line P-EXML-4 250ng DNA none none Warwick P-EXML-6 P-EXML-8 P-EXML-10
+0904_MK18_10K_90J82.CEL 0904_MK18_10K_90J82.CHP Mapping10K_Xba131.CDF A-AFFY-64 3003935 0904_MK18_10K_90J82 not applicable patient J82 patient J82 Homo sapiens cell_line mixed source cell line mixed source cell line P-EXML-4 250ng DNA none none PICR P-EXML-6 P-EXML-8 P-EXML-10
+0904_MK19_10K_80J82.CEL 0904_MK19_10K_80J82.CHP Mapping10K_Xba131.CDF A-AFFY-64 3003935 0904_MK19_10K_80J82 not applicable patient J82 patient J82 Homo sapiens cell_line mixed source cell line mixed source cell line P-EXML-4 250ng DNA none none PICR P-EXML-6 P-EXML-8 P-EXML-10
+0904_MK20_10K_70J82.CEL 0904_MK20_10K_70J82.CHP Mapping10K_Xba131.CDF A-AFFY-64 3003935 0904_MK20_10K_70J82 not applicable patient J82 patient J82 Homo sapiens cell_line mixed source cell line mixed source cell line P-EXML-4 250ng DNA none none PICR P-EXML-6 P-EXML-8 P-EXML-10
+Case1_aLN.CEL Case1_aLN.CHP Mapping10K_Xba131.CDF A-AFFY-64 3003935 Case1_aLN aLN aLN case 1 case 1 Homo sapiens organism_part lymph node squamous cell carcinoma squamous cell carcinoma "T2, N0, stage 1b" male 65 years smoking history: 50 pack years P-EXML-2 P-EXML-3 P-EXML-4 1035 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case1_aHYP.CEL Case1_aHYP.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case1_aHYP aHYP aHYP case 1 case 1 Homo sapiens organism_part lung squamous cell carcinoma squamous cell carcinoma "T2, N0, stage 1b" male 65 years smoking history: 50 pack years P-EXML-2 P-EXML-3 P-EXML-4 816 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case1_aTUM.CEL Case1_aTUM.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case1_aTUM aTUM aTUM case 1 case 1 Homo sapiens organism_part lung squamous cell carcinoma squamous cell carcinoma "T2, N0, stage 1b" male 65 years smoking history: 50 pack years P-EXML-2 P-EXML-3 P-EXML-4 1136 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case2_aAVM.CEL Case2_aAVM.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case2_aAVM aAVM aAVM case 2 case 2 Homo sapiens organism_part macrophage large cell neuroendocrine carcinoma large cell neuroendocrine carcinoma "T2, N0, M0, stage 1b" female 70 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 558 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case2_aBE.CEL Case2_aBE.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case2_aBE aBE aBE case 2 case 2 Homo sapiens organism_part lung large cell neuroendocrine carcinoma large cell neuroendocrine carcinoma "T2, N0, M0, stage 1b" female 70 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 nk WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case2_aOE.CEL Case2_aOE.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case2_aOE aOE aOE case 2 case 2 Homo sapiens organism_part lung large cell neuroendocrine carcinoma large cell neuroendocrine carcinoma "T2, N0, M0, stage 1b" female 70 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 419 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case2_aTUM1.CEL Case2_aTUM1.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case2_aTUM1 aTUM1 aTUM1 case 2 case 2 Homo sapiens organism_part lung large cell neuroendocrine carcinoma large cell neuroendocrine carcinoma "T2, N0, M0, stage 1b" female 70 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 446 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case2_aTUM2.CEL Case2_aTUM2.CHP Mapping10K_Xba142.CDF A-AFFY-65 4008473 Case2_aTUM2 aTUM2 aTUM2 case 2 case 2 Homo sapiens organism_part lung large cell neuroendocrine carcinoma large cell neuroendocrine carcinoma "T2, N0, M0, stage 1b" female 70 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 498 cells WGA WGA Warwick P-EXML-6 P-EXML-9 P-EXML-10
+Case3_nLN.CEL Case3_nLN.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case3_nLN nLN nLN case 3 case 3 Homo sapiens organism_part lymph node small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-4 250ng DNA none none Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case3_aLN.CEL Case3_aLN.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case3_aLN aLN aLN case 3 case 3 Homo sapiens organism_part lymph node small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 545 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case3_aBE.CEL Case3_aBE.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case3_aBE aBE aBE case 3 case 3 Homo sapiens organism_part lung small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 843 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case3_aOE.CEL Case3_aOE.CHP Mapping10K_Xba142.CDF A-AFFY-65 4008473 Case3_aOE aOE aOE case 3 case 3 Homo sapiens organism_part lung small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 200 cells WGA WGA Warwick P-EXML-6 P-EXML-9 P-EXML-10
+Case3_nTB.CEL Case3_nTB.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case3_nTB nTB nTB case 3 case 3 Homo sapiens organism_part lung small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-4 250ng DNA none none Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case3_aTUM1.CEL Case3_aTUM1.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case3_aTUM1 aTUM1 aTUM1 case 3 case 3 Homo sapiens organism_part lung small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 1300 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case3_aTUM2.CEL Case3_aTUM2.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case3_aTUM2 aTUM2 aTUM2 case 3 case 3 Homo sapiens organism_part lung small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 997 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
+Case3_aTUM3.CEL Case3_aTUM3.CHP Mapping10K_Xba131.CDF A-AFFY-64 3002019 Case3_aTUM3 aTUM3 aTUM3 case 3 case 3 Homo sapiens organism_part lung small cell lung cancer small cell lung cancer "T1, N2, M0, stage 3" female 47 years smoking history: 30 pack years P-EXML-2 P-EXML-3 P-EXML-4 350 cells WGA WGA Warwick P-EXML-6 P-EXML-8 P-EXML-10
diff --git a/examples/real/E-TABM-136.png b/examples/real/E-TABM-136.png
new file mode 100644
index 0000000..f26cd2f
Binary files /dev/null and b/examples/real/E-TABM-136.png differ
diff --git a/examples/real/E-TABM-136.txt b/examples/real/E-TABM-136.txt
new file mode 100755
index 0000000..bdbb9be
--- /dev/null
+++ b/examples/real/E-TABM-136.txt
@@ -0,0 +1,106 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-136
+quality_control biological_replicate_design
+experiment_design_type organism_part_comparison_design;species_comparison_design;
+name Functionality of Intergenic Transcription: An Evolutionary Comparison
+description "Although a large proportion of human transcription occurs outside the boundaries of known genes, the functional significance of this transcription remains unknown. We have compared the expression patterns of known genes as well as intergenic transcripts within the ENCODE regions between humans and chimpanzees in brain, heart, testis and lymphoblastoid cell lines. We find that intergenic transcripts show patterns of tissue-specific conservation of their expression which are c [...]
+ArrayExpressDisplayName "Transcription profiling of human and chimpanzee heart, brain, testis and lymphblastoid cell lines to study functionality of intergenic transcription."
+release_date 2006-09-30
+submission_date 2006-08-18
+submitter Philipp Khaitovich
+submitter_email khaitovich at eva.mpg.de
+publication_title Functionality of Intergenic Transcription: An Evolutionary Comparison
+authors Philipp Khaitovich; Janet Kelso; Henriette Franz; Johann Visagie; Thomas Giger; Sabrina Joerchel; Ekkehard Petzold; Richard E. Green; Michael Lachmann; Svante P��bo
+journal PLoS Genetics
+volume
+issue
+pages
+year 2006
+
+Protocol section
+accession type text name
+P-TABM-47 labeling Following Affymetrix Whole Transcript (WT) Sense Target Labeling Assay protocol. Starting amount of material: 2 ug of total RNA per sample Whole Transcript (WT) Sense Target Labeling Assay
+
+Hybridization section
+File[raw] File[exp] Array[serial] Array[accession] Protocol[extraction] Protocol[labeling] Protocol[hybridization] Protocol[scanning] BioSource BioSourceMaterial BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[Sex] BioMaterialCharacteristics[Age(years)] BioMaterialCharacteristics[OrganismPart] BioMaterialCharacteristics[OrganismStatus] FactorValue[Organism] FactorValue[OrganismPart] FactorValue[lymphoblastoid cellLine]
+CB1F.CEL CB1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c01 organism_part Pan troglodytes male 7 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB2F.CEL CB2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c02 organism_part Pan troglodytes male 12 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB3F.CEL CB3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c03 organism_part Pan troglodytes male 12 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB4F.CEL CB4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c04 organism_part Pan troglodytes male 12 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB5F.CEL CB5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c05 organism_part Pan troglodytes male >40 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CC1F.CEL CC1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c06 organism_part Pan troglodytes female 19 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC2F.CEL CC2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c07 organism_part Pan troglodytes female 20 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC3F.CEL CC3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c08 organism_part Pan troglodytes female 32 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC4F.CEL CC4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c09 organism_part Pan troglodytes female 12 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC5F.CEL CC5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c10 organism_part Pan troglodytes female 37 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CH1F.CEL CH1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c01 organism_part Pan troglodytes male 7 heart muscle postmortem Pan troglodytes heart
+CH2F.CEL CH2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c11 organism_part Pan troglodytes male 34 heart muscle postmortem Pan troglodytes heart
+CH3F.CEL CH3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c03 organism_part Pan troglodytes male 12 heart muscle postmortem Pan troglodytes heart
+CH4F.CEL CH4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c12 organism_part Pan troglodytes male 26 heart muscle postmortem Pan troglodytes heart
+CH5F.CEL CH5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c02 organism_part Pan troglodytes male 12 heart muscle postmortem Pan troglodytes heart
+CT1F.CEL CT1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c13 organism_part Pan troglodytes male 20 testis postmortem Pan troglodytes testis
+CT2F.CEL CT2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c02 organism_part Pan troglodytes male 12 testis postmortem Pan troglodytes testis
+CT3F.CEL CT3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c03 organism_part Pan troglodytes male 12 testis postmortem Pan troglodytes testis
+CT4F.CEL CT4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c04 organism_part Pan troglodytes male 12 testis postmortem Pan troglodytes testis
+CT5F.CEL CT5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c05 organism_part Pan troglodytes male >40 testis postmortem Pan troglodytes testis
+HB1F.CEL HB1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h01 organism_part Homo sapiens male 45 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB2F.CEL HB2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h02 organism_part Homo sapiens male 45 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB3F.CEL HB3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h03 organism_part Homo sapiens male 48 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB4F.CEL HB4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h04 organism_part Homo sapiens male 56 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB5F.CEL HB5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h05 organism_part Homo sapiens male 67 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HC1F.CEL HC1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h06 organism_part Homo sapiens female 13 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC2F.CEL HC2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h07 organism_part Homo sapiens female 12 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC3F.CEL HC3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h08 organism_part Homo sapiens female 20 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC4F.CEL HC4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h09 organism_part Homo sapiens female 22 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC5F.CEL HC5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h10 organism_part Homo sapiens female 32 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HH1F.CEL HH1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h11 organism_part Homo sapiens male 71 heart muscle postmortem Homo sapiens heart
+HH2F.CEL HH2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h12 organism_part Homo sapiens male 74 heart muscle postmortem Homo sapiens heart
+HH3F.CEL HH3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h13 organism_part Homo sapiens male 57 heart muscle postmortem Homo sapiens heart
+HH4F.CEL HH4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h14 organism_part Homo sapiens male 77 heart muscle postmortem Homo sapiens heart
+HH5F.CEL HH5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h15 organism_part Homo sapiens male 51 heart muscle postmortem Homo sapiens lymphoblastoid cell line
+HT1F.CEL HT1F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h16 organism_part Homo sapiens male 27 testis postmortem Homo sapiens testis
+HT2F.CEL HT2F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h17 organism_part Homo sapiens male 32 testis postmortem Homo sapiens testis
+HT3F.CEL HT3F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h18 organism_part Homo sapiens male 25 testis postmortem Homo sapiens testis
+HT4F.CEL HT4F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h19 organism_part Homo sapiens male 41 testis postmortem Homo sapiens testis
+HT5F.CEL HT5F.EXP 4011140 A-AFFY-61 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h20 organism_part Homo sapiens male 27 testis postmortem Homo sapiens testis
+CB1R.CEL CB1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c01 organism_part Pan troglodytes male 7 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB2R.CEL CB2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c02 organism_part Pan troglodytes male 12 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB3R.CEL CB3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c03 organism_part Pan troglodytes male 12 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB4R.CEL CB4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c04 organism_part Pan troglodytes male 12 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CB5R.CEL CB5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c05 organism_part Pan troglodytes male >40 "brain, prefrontal cortex" postmortem Pan troglodytes brain
+CC1R.CEL CC1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c06 organism_part Pan troglodytes female 19 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC2R.CEL CC2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c07 organism_part Pan troglodytes female 20 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC3R.CEL CC3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c08 organism_part Pan troglodytes female 32 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC4R.CEL CC4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c09 organism_part Pan troglodytes female 12 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CC5R.CEL CC5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c10 organism_part Pan troglodytes female 37 lymphoblastoid cell line postmortem Pan troglodytes lymphoblastoid cell line
+CH1R.CEL CH1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c01 organism_part Pan troglodytes male 7 heart muscle postmortem Pan troglodytes heart
+CH2R.CEL CH2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c11 organism_part Pan troglodytes male 34 heart muscle postmortem Pan troglodytes heart
+CH3R.CEL CH3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c03 organism_part Pan troglodytes male 12 heart muscle postmortem Pan troglodytes heart
+CH4R.CEL CH4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c12 organism_part Pan troglodytes male 26 heart muscle postmortem Pan troglodytes heart
+CH5R.CEL CH5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c02 organism_part Pan troglodytes male 12 heart muscle postmortem Pan troglodytes heart
+CT1R.CEL CT1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c13 organism_part Pan troglodytes male 20 testis postmortem Pan troglodytes testis
+CT2R.CEL CT2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c02 organism_part Pan troglodytes male 12 testis postmortem Pan troglodytes testis
+CT3R.CEL CT3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c03 organism_part Pan troglodytes male 12 testis postmortem Pan troglodytes testis
+CT4R.CEL CT4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c04 organism_part Pan troglodytes male 12 testis postmortem Pan troglodytes testis
+CT5R.CEL CT5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 c05 organism_part Pan troglodytes male >40 testis postmortem Pan troglodytes testis
+HB1R.CEL HB1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h01 organism_part Homo sapiens male 45 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB2R.CEL HB2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h02 organism_part Homo sapiens male 45 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB3R.CEL HB3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h03 organism_part Homo sapiens male 48 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB4R.CEL HB4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h04 organism_part Homo sapiens male 56 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HB5R.CEL HB5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h05 organism_part Homo sapiens male 67 "brain, prefrontal cortex" postmortem Homo sapiens brain
+HC1R.CEL HC1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h06 organism_part Homo sapiens female 13 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC2R.CEL HC2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h07 organism_part Homo sapiens female 12 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC3R.CEL HC3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h08 organism_part Homo sapiens female 20 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC4R.CEL HC4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h09 organism_part Homo sapiens female 22 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HC5R.CEL HC5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h10 organism_part Homo sapiens female 32 lymphoblastoid cell line postmortem Homo sapiens lymphoblastoid cell line
+HH1R.CEL HH1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h11 organism_part Homo sapiens male 71 heart muscle postmortem Homo sapiens heart
+HH2R.CEL HH2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h12 organism_part Homo sapiens male 74 heart muscle postmortem Homo sapiens heart
+HH3R.CEL HH3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h13 organism_part Homo sapiens male 57 heart muscle postmortem Homo sapiens heart
+HH4R.CEL HH4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h14 organism_part Homo sapiens male 77 heart muscle postmortem Homo sapiens heart
+HH5R.CEL HH5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h15 organism_part Homo sapiens male 51 heart muscle postmortem Homo sapiens heart
+HT1R.CEL HT1R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h16 organism_part Homo sapiens male 27 testis postmortem Homo sapiens testis
+HT2R.CEL HT2R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h17 organism_part Homo sapiens male 32 testis postmortem Homo sapiens testis
+HT3R.CEL HT3R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h18 organism_part Homo sapiens male 25 testis postmortem Homo sapiens testis
+HT4R.CEL HT4R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h19 organism_part Homo sapiens male 41 testis postmortem Homo sapiens testis
+HT5R.CEL HT5R.EXP 4011140 A-AFFY-67 P-TABM-44 P-TABM-47 Affymetrix:Protocol:Hybridization-MES_EukGE-WS2v5_450-DEV P-TABM-46 h20 organism_part Homo sapiens male 27 testis postmortem Homo sapiens testis
diff --git a/examples/real/E-TABM-140.png b/examples/real/E-TABM-140.png
new file mode 100644
index 0000000..620d18c
Binary files /dev/null and b/examples/real/E-TABM-140.png differ
diff --git a/examples/real/E-TABM-140.txt b/examples/real/E-TABM-140.txt
new file mode 100755
index 0000000..a94d554
--- /dev/null
+++ b/examples/real/E-TABM-140.txt
@@ -0,0 +1,108 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-140
+quality_control biological_replicate
+experiment_design_type binding_site_identification_design
+name Chromatin immunoprecipitation microarray (ChIP-chip) study using human erythroleukemia cell line K-562 and H3K4me2 (ab7766); H3K4me3 (ab8580); H3ac (06-599); H4ac (06-866): Histone H2B(ab1790) and Histone H3 (ab1791) antibodies (ENCODE).
+description "ENCODE ChIP-chip study using human myelogenous leukemia cell line K-562 and anti histone H3K4me2 (Abcam; ab7766); H3K4me3 (Abcam; ab8580); H3ac (Upstate; 06-599); H4ac (Upstate; 06-866); Histone H2B (Abcam: ab1790) and Histone H3 (Abcam: ab1791) antibodies. Each antibody experiment was conducted in three biological replicates, with two technical replicates performed for each biological replicate"
+ae_display_name Chromatin immunoprecipitation of Histone H3K4me2; H3K4me3; H3K9/14ac; H4K5/8/12/16ac; H2B and H3
+release_date 2006-09-01
+submission_date 2006-08-18
+submitter Shane Dillon
+submitter_email scd at sanger.ac.uk
+investigator David Vetrie
+investigator_email vt1 at sanger.ac.uk
+organization The Wellcome Trust Sanger Institute
+address Wellcome Trust Genome Campus;Hinxton; Cambridge; CB10 1SA; UK
+publication_title The Landscape of Activating Histone Modifications across 1% of the Human Genome.
+authors Christoph M. Koch; Robert M. Andrews; Paul Flicek; Shane C. Dillon; Ula Kara�z; Gayle K. Clelland; Sarah Wilcox; David M. Beare; Joanna C. Fowler; Phillippe Couttet; Keith D. James; Gregory C. Lefebvre; Alexander W. Bruce; Oliver M. Dovey; Peter D. Ellis; Pawandeep Dhami; Cordelia F. Langford; Zhiping Weng; Ewan Birney; Nigel P. Carter; David Vetrie; Ian Dunham
+journal Genome Res
+volume
+issue
+pages
+year
+
+Protocol section
+accession name text
+GROW_K-562 K-562 CULTURE CONDITIONS Cells were grown in DMEM supplemented with 10% FBS and 1% Penicillin/Streptomycin to a total number of ~ 3x10^8 cells. Dilute cell density to 3 x10^5/ml every two to three days. Split and feed 16 hours before cross-linking the cells.
+CROSSLINK_K-562_1 K-562_CHROMATIN PREPARATION_1 "Day 1 Cross linking cells and chromatin extraction/preparation<br>1. Grow or harvest 1x10^8 cells. Collect the cells by centrifuging at 1200 rpm for 5-8 minutes.<br>2. Resuspend the cells in pre-warmed 50 ml serum free media in a glass flask. Add 0.5 ml of formaldehyde solution in fume hood (37%; final concentration 0.37%) to cross link DNA-protein interactions. Incubate at room temperature with gentle agitation for 10 minutes.<br>3. Add 3 [...]
+CROSSLINK_K-562_2 K-562_CHROMATIN PREPARATION_2 "Same as K-562_CHROMATIN PREPARATION_1, except cells were crosslinked with 1.35 ml formaldehyde(final concentration =1%) and 3.425 ml of glycine added to quench crosslinking. "
+CHROMATIN_IMMUNOPRECIPITATION CHROMATINIMMUNOPRECIPITATION and DNA RECOVERY "Immunoprecipitation Day One<br>Control conditions<br><br>Normal rabbit IgG: 675 ul chromatin + 675 ul 1:4 NLB:IPDB buffer + 10 ul Rabbit IgG (Upstate)<br>Input 270 ul of chromatin store at -20�C until step 5 next day.<br><br>Immunoprecipitation conditions<br><br>Histone H3 tri methyl K4 (Abcam, ab8895): 675 ul chromatin + 675 ul 1:4 NLB:IPDB buffer + 10 ug Histone H3 trimethyl K4 antibody<br>Histone H3 di methy [...]
+HYBRIDISATION_TECAN Preparation of Hybridisation mixture "Tecan Encode array hybridisation (gridsize: 2x6 cm)<br><br><br>Equipment and reagents<br><br>Hybridisation buffer: (50 % formamide; 5 % dextran sulphate; 0.1 % Tween 20; 2x SSC; 10 mM Tris pH 7.4)<br>* 3 M NaAc pH 5.2<br>* human Cot1 DNA (Invitrogen, order. No. 15279011, or Roche, order. No. 1581074, test the quality prior to use)<br>* Herring sperm DNA (Sigma, order. No. D7290)<br>* 100 % Ethanol, 80 % Ethanol<br>* Yeast tRNA (In [...]
+SCANARRAYEXPRESS-2 Scanning of Slides and Analysis Microarrays were scanned using a ScanArray 4000 confocal laser-based scanner (Perkin Elmer). Mean spot intensities from images were quantified using ScanArray Express (Perkin Elmer) with background subtraction. Spots affected by dust were manually flagged as �not found� and subsequently excluded from the analysis. Wiggle tracks were generated combining data of all replicates to the median using an R script (see also ftp://ftp.sanger.ac.u [...]
+TRANSFORMATION transformation "The Data of all replicates of a ChIP experiment were combined to the median using an R script (see also ftp://ftp.sanger.ac.uk/pub/encode/). It takes the ""Ch2 N Ratio of Means"" values over all technical replicates, where ""Ch2 N Ratio of Means"" equals the ratio of the enriched and non-enriched fractions (both background corrected and global normalised) and combines the values to the median. Tracks of this median data can be visualised using the UCSC geno [...]
+
+Hybridization section
+File[raw] File[transformed] Array[accession] Array[serial] Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[labeling] Protocol[hybridization] Protocol[scanning] Protocol[TRANSFORMATION] BioSource Sample Extract LabeledExtract Hybridization BioSourceMaterial SampleMaterial ExtractMaterial LabeledExtractMaterial Dye BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[BioSourceType] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[CellType] Bio [...]
+1106-16_raw.txt FGEDM_complete.txt A-MEXP-215 1106-16 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 "IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766)" K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-16_raw.txt FGEDM_complete.txt A-MEXP-215 1106-16 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep1 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1106-17_raw.txt FGEDM_complete.txt A-MEXP-215 1106-17 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 "IP of K-562_BIOREP1 with anti H3K4me2 (Abcam, ab7766)" K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-17_raw.txt FGEDM_complete.txt A-MEXP-215 1106-17 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep2 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1140-6_raw.txt FGEDM_complete.txt A-MEXP-215 1140-6 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 "IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766)" K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1140-6_raw.txt FGEDM_complete.txt A-MEXP-215 1140-6 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep1 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1140-7_raw.txt FGEDM_complete.txt A-MEXP-215 1140-7 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 "IP of K-562_BIOREP2 with anti H3K4me2 (Abcam, ab7766)" K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1140-7_raw.txt FGEDM_complete.txt A-MEXP-215 1140-7 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep2 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1140-12_raw.txt FGEDM_complete.txt A-MEXP-215 1140-12 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 "IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766)" K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1140-12_raw.txt FGEDM_complete.txt A-MEXP-215 1140-12 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep1 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1140-13_raw.txt FGEDM_complete.txt A-MEXP-215 1140-13 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 "IP of K-562_BIOREP3 with anti H3K4me2 (Abcam, ab7766)" K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1140-13_raw.txt FGEDM_complete.txt A-MEXP-215 1140-13 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep2 ChIP H3K4me2 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1023-16_raw.txt FGEDM_complete.txt A-MEXP-156 1023-16 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 "IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895)" K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1023-16_raw.txt FGEDM_complete.txt A-MEXP-156 1023-16 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep1 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1023-17_raw.txt FGEDM_complete.txt A-MEXP-156 1023-17 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 "IP of K-562_BIOREP1 with anti H3K4me3 (Abcam, ab8895)" K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1023-17_raw.txt FGEDM_complete.txt A-MEXP-156 1023-17 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep2 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1106-10_raw.txt FGEDM_complete.txt A-MEXP-215 1106-10 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 "IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895)" K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-10_raw.txt FGEDM_complete.txt A-MEXP-215 1106-10 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep1 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1106-11_raw.txt FGEDM_complete.txt A-MEXP-215 1106-11 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 "IP of K-562_BIOREP2 with anti H3K4me3 (Abcam, ab8895)" K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisi [...]
+1106-11_raw.txt FGEDM_complete.txt A-MEXP-215 1106-11 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep2 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1163-8_raw.txt FGEDM_complete.txt A-MEXP-215 1163-8 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 "IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895)" K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1163-8_raw.txt FGEDM_complete.txt A-MEXP-215 1163-8 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep1 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1163-9_raw.txt FGEDM_complete.txt A-MEXP-215 1163-9 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 "IP of K-562_BIOREP3 with anti H3K4me3 (Abcam, ab8895)" K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1163-9_raw.txt FGEDM_complete.txt A-MEXP-215 1163-9 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep2 ChIP H3K4me3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1023-13_raw.txt FGEDM_complete.txt A-MEXP-156 1023-13 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 "IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599)" K-562 BIOREP1_techrep1 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fe [...]
+1023-13_raw.txt FGEDM_complete.txt A-MEXP-156 1023-13 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep1 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1023-18_raw.txt FGEDM_complete.txt A-MEXP-156 1023-18 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 "IP of K-562_BIOREP1 with anti H3ac (Upstate, 06-599)" K-562 BIOREP1_techrep2 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fe [...]
+1023-18_raw.txt FGEDM_complete.txt A-MEXP-156 1023-18 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep2 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1106-2_raw.txt FGEDM_complete.txt A-MEXP-215 1106-2 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 "IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599)" K-562 BIOREP2_techrep1 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1106-2_raw.txt FGEDM_complete.txt A-MEXP-215 1106-2 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep1 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1106-3_raw.txt FGEDM_complete.txt A-MEXP-215 1106-3 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 "IP of K-562_BIOREP2 with anti H3ac (Upstate, 06-599)" K-562 BIOREP2_techrep2 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1106-3_raw.txt FGEDM_complete.txt A-MEXP-215 1106-3 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep2 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1140-2_raw.txt FGEDM_complete.txt A-MEXP-215 1140-2 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 "IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599)" K-562 BIOREP3_techrep1 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1140-2_raw.txt FGEDM_complete.txt A-MEXP-215 1140-2 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep1 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1140-3_raw.txt FGEDM_complete.txt A-MEXP-215 1140-3 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 "IP of K-562_BIOREP3 with anti H3ac (Upstate, 06-599)" K-562 BIOREP3_techrep2 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1140-3_raw.txt FGEDM_complete.txt A-MEXP-215 1140-3 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep2 ChIP H3ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1043-6_raw.txt FGEDM_complete.txt A-MEXP-156 1043-6 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 "IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866)" K-562 BIOREP1_techrep1 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1043-6_raw.txt FGEDM_complete.txt A-MEXP-156 1043-6 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep1 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep1 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1043-7_raw.txt FGEDM_complete.txt A-MEXP-156 1043-7 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 "IP of K-562_BIOREP1 with anti H4ac (Upstate, 06-866)" K-562 BIOREP1_techrep2 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1043-7_raw.txt FGEDM_complete.txt A-MEXP-156 1043-7 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1 K-562_BIOREP1_techrep2 Input control DNA of K-562_BIOREP1 K-562 BIOREP1_techrep2 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1106-4_raw.txt FGEDM_complete.txt A-MEXP-215 1106-4 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 "IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866)" K-562 BIOREP2_techrep1 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1106-4_raw.txt FGEDM_complete.txt A-MEXP-215 1106-4 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep1 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep1 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1106-5_raw.txt FGEDM_complete.txt A-MEXP-215 1106-5 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 "IP of K-562_BIOREP2 with anti H4ac (Upstate, 06-866)" K-562 BIOREP2_techrep2 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1106-5_raw.txt FGEDM_complete.txt A-MEXP-215 1106-5 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2 K-562_BIOREP2_techrep2 Input control DNA of K-562_BIOREP2 K-562 BIOREP2_techrep2 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1140-20_raw.txt FGEDM_complete.txt A-MEXP-215 1140-20 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 "IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866)" K-562 BIOREP3_techrep1 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fe [...]
+1140-20_raw.txt FGEDM_complete.txt A-MEXP-215 1140-20 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep1 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep1 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1163-2_raw.txt FGEDM_complete.txt A-MEXP-215 1163-2 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 "IP of K-562_BIOREP3 with anti H4ac (Upstate, 06-866)" K-562 BIOREP3_techrep2 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis fema [...]
+1163-2_raw.txt FGEDM_complete.txt A-MEXP-215 1163-2 GROW_K-562 CROSSLINK_K-562_1 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3 K-562_BIOREP3_techrep2 Input control DNA of K-562_BIOREP3 K-562 BIOREP3_techrep2 ChIP H4ac hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-84_raw.txt FGEDM_complete.txt A-MEXP-215 1604-84 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep1a "IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790)" K-562 BIOREP1a_techrep1a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-84_raw.txt FGEDM_complete.txt A-MEXP-215 1604-84 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep1a Input control DNA of K-562_BIOREP1a K-562 BIOREP1a_techrep1a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-89_raw.txt FGEDM_complete.txt A-MEXP-215 1604-89 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep2a "IP of K-562_BIOREP1a with anti H2B (Abcam, ab1790)" K-562 BIOREP1a_techrep2a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-89_raw.txt FGEDM_complete.txt A-MEXP-215 1604-89 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep2a Input control DNA of K-562_BIOREP1a K-562 BIOREP1a_techrep2a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-97_raw.txt FGEDM_complete.txt A-MEXP-215 1604-97 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep1a "IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790)" K-562 BIOREP2a_techrep1a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-97_raw.txt FGEDM_complete.txt A-MEXP-215 1604-97 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep1a Input control DNA of K-562_BIOREP2a K-562 BIOREP2a_techrep1a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-98_raw.txt FGEDM_complete.txt A-MEXP-215 1604-98 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep2a "IP of K-562_BIOREP2a with anti H2B (Abcam, ab1790)" K-562 BIOREP2a_techrep2a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-98_raw.txt FGEDM_complete.txt A-MEXP-215 1604-98 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep2a Input control DNA of K-562_BIOREP2a K-562 BIOREP2a_techrep2a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-88_raw.txt FGEDM_complete.txt A-MEXP-215 1604-88 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep1a "IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790)" K-562 BIOREP3a_techrep1a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-88_raw.txt FGEDM_complete.txt A-MEXP-215 1604-88 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep1a Input control DNA of K-562_BIOREP3a K-562 BIOREP3a_techrep1a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-99_raw.txt FGEDM_complete.txt A-MEXP-215 1604-99 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep2a "IP of K-562_BIOREP3a with anti H2B (Abcam, ab1790)" K-562 BIOREP3a_techrep2a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-99_raw.txt FGEDM_complete.txt A-MEXP-215 1604-99 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep2a Input control DNA of K-562_BIOREP3a K-562 BIOREP3a_techrep2a ChIP H2B hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-85_raw.txt FGEDM_complete.txt A-MEXP-215 1604-85 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep1a "IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791)" K-562 BIOREP1a_techrep1a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-85_raw.txt FGEDM_complete.txt A-MEXP-215 1604-85 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep1a Input control DNA of K-562_BIOREP1a K-562 BIOREP1a_techrep1a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-91_raw.txt FGEDM_complete.txt A-MEXP-215 1604-91 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep2a "IP of K-562_BIOREP1a with anti H3 (Upstate, ab1791)" K-562 BIOREP1a_techrep2a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-91_raw.txt FGEDM_complete.txt A-MEXP-215 1604-91 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP1a K-562_BIOREP1a_techrep2a Input control DNA of K-562_BIOREP1a K-562 BIOREP1a_techrep2a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-95_raw.txt FGEDM_complete.txt A-MEXP-215 1604-95 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep1a "IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791)" K-562 BIOREP2a_techrep1a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-95_raw.txt FGEDM_complete.txt A-MEXP-215 1604-95 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep1a Input control DNA of K-562_BIOREP2a K-562 BIOREP2a_techrep1a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-96_raw.txt FGEDM_complete.txt A-MEXP-215 1604-96 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep2a "IP of K-562_BIOREP2a with anti H3 (Upstate, ab1791)" K-562 BIOREP2a_techrep2a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-96_raw.txt FGEDM_complete.txt A-MEXP-215 1604-96 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP2a K-562_BIOREP2a_techrep2a Input control DNA of K-562_BIOREP2a K-562 BIOREP2a_techrep2a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-90_raw.txt FGEDM_complete.txt A-MEXP-215 1604-90 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep1a "IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791)" K-562 BIOREP3a_techrep1a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-90_raw.txt FGEDM_complete.txt A-MEXP-215 1604-90 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep1a Input control DNA of K-562_BIOREP3a K-562 BIOREP3a_techrep1a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
+1604-94_raw.txt FGEDM_complete.txt A-MEXP-215 1604-94 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7423 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep2a "IP of K-562_BIOREP3a with anti H3 (Upstate, ab1791)" K-562 BIOREP3a_techrep2a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy3 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis [...]
+1604-94_raw.txt FGEDM_complete.txt A-MEXP-215 1604-94 GROW_K-562 CROSSLINK_K-562_2 CHROMATIN_IMMUNOPRECIPITATION P-MEXP-7422 HYBRIDISATION_TECAN SCANARRAYEXPRESS-2 TRANSFORMATION K-562 K-562_BIOREP3a K-562_BIOREP3a_techrep2a Input control DNA of K-562_BIOREP3a K-562 BIOREP3a_techrep2a ChIP H3 hyb cell genomic_DNA genomic_DNA synthetic_DNA Cy5 Homo sapiens frozen_sample K-562 human erythroleukemia cell line derived from a chronic myeloid leukemia patient in blast crisis female K-562
diff --git a/examples/real/E-TABM-16.png b/examples/real/E-TABM-16.png
new file mode 100644
index 0000000..059001b
Binary files /dev/null and b/examples/real/E-TABM-16.png differ
diff --git a/examples/real/E-TABM-16.txt b/examples/real/E-TABM-16.txt
new file mode 100755
index 0000000..03d2b2c
--- /dev/null
+++ b/examples/real/E-TABM-16.txt
@@ -0,0 +1,166 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-16
+experiment_design_type quality_control_testing_design
+name FDA-CDER MTS RNA reagent cross platform test
+description "This experiment is a test of a reagent that can provide a basis for performance assessments on Affymetrix RAE230A, CodeLink UniSet Rat I, and Agilent G4130A rat oligonucleotide arrays. The reagent is a set of two mixed tissue RNA samples formulated to contain different ratios of RNA for each of four rat tissues (brain, liver, kidney, and testes). The analytes in this assay bind to a subset of gene probes that were matched across platforms to the same UniGene cluster or [...]
+release_date 2005-11-23
+submission_date 2005-06-01
+submitter Scott Pine
+organization FDA-CDER
+publication_title
+authors
+journal unknown
+volume
+issue
+pages
+year
+
+Protocol section
+accession type text name parameters
+6377 grow Rodents received certified rodent diet #5002C ad lib and drinking water purified by reverse osmosis. The animals were acclimated for 6 days prior to euthanasia. The animals were on a 12 hr light/dark cycle and euthanasia was performed consistently within 4-6 hr after the start of the light cycle. Each batch was made up of 8 animals from the same shipment. MTS Batches1-3
+6380 nucleic_acid_extraction "After euthanasia in a slow charged CO2 chamber, the rats were immediately decapitated to allow for rapid access to the brain. Four organs were quickly removed in the following order: brain, liver, kidneys, and testes. The whole brain, including brain stem but excluding pituitary gland, was collected. Tissues were quickly dissected into 0.5 cm sections while submersed in RNAlater (Ambion, Austin, TX) in sterile petri plates and placed into 50 mL tubes con [...]
+6396 pool "Total RNA was quantitated by ultraviolet/visible wavelength spectrophotometry. For each tissue, equal amounts of RNA were pooled from each of the 8 animals in the same shipment to create tissue shipment specific pools. 2 mg each of two mixtures (Mix1 and Mix2) were prepared for each shipment from the pooled same-tissue RNAs. " MTS Tissue pools
+6396-mix1 pool "Mix1 consisted of 200 ug testis RNA, 600 ug liver RNA, 800 ug brain RNA, and 400 ug kidney RNA. 50 ug aliquots of each mixture were frozen at -70C." MTS Mix1
+6396-mix2 pool "Mix2 consisted of 800 ug testis RNA, 400 ug liver RNA, 400 ug brain RNA, and 400 ug kidney RNA. 50 ug aliquots of each mixture were frozen at -70C." MTS Mix2
+6407 image_aquisition GeneChips were scanned on a GeneArray 2500 scanner using MicroArray Suite v. 5.0.0.032 and default scanner settings. MTS Affymetrix - GeneArray Scanner
+6409 image_aquisition GeneChips were scanned on a GeneChip 3000 scanner using default scanner settings. MTS Affymetrix - GeneChip Scanner
+6486 labeling "1st strand cDNA synthesis: <br><br>For each sample, add<br><br>2 ul T7-(dT)24 primer @ 50 uM (Affymetrix PN 900375) to <br><br>5 ug (up to 8 ul) total RNA. <br><br>Incubate @ 70C, 10 minutes, quick spin, quench on ice.<br><br><br><br>Make the following cocktail for 5 reactions:<br><br>20 ul 5X 1st Strand cDNA Buffer (Invitrogen PN 18064014), <br><br>10 ul 0.1 M DTT, and<br><br> 5 ul 10 mM dNTP mix (Invitrogen PN 18427013). [...]
+6489 labeling "1st strand cDNA synthesis: <br><br><br><br>For each sample, add 2 ul T7-(dT)24 primer @ 50 uM (Affymetrix PN 900375) to 5 ug (up to 8 ul) total RNA. Incubate @ 70C, 10 minutes, quick spin, quench on ice.<br><br><br><br>Make the following cocktail for 5 reactions using Invitrogen SuperScript Double-Stranded cDNA Synthesis Kit (PN 11917-010): 20 ul 5X 1st Strand cDNA Buffer, 10 ul 0.1 M DTT, and 5 ul 10 mM dNTP mix. Add 7 ul to each reaction. Incubate @ 45 C for 2 minute [...]
+6491 hybridization "Hybridization cocktail for single array: 15 ug fragmented labeled cRNA, 5 uL Control Oligonucleotide B2 (component in kit; Affymetrix P/N 900299), 15 uL 20x Eukaryotic Hybridization Controls (component in kit; Affymetrix P/N 900299), 3 uL Herring Sperm DNA (10 mg/mL) (Promega P/N D1811), 3 uL Acetylated BSA (50 mg/mL) (Invitrogen P/N 15561-020), 2x Hybridization Buffer (200 mM MES, 2 M [Na+], 40 mM EDTA, 0.02 % Tween 20), and H2O to final volume of 300 uL. <br><br>< [...]
+6552 bioassay_data_transformation "Microarray Suite v. 5.0.0.032 software (Affymetrix, Inc.) was used to calculate signal values using default settings (Parameters: Gamma 1L, 0.0045; Gamma 1H, 0.0045; Gamma 2L, 0.006; Gamma 2H, 0.006; Perturbation, 1.1; alpha1, 0.05; alpha2, 0.065; tau, 0.015). All data was global scaled to a target intensity of 500. " MTS Affymetrix MAS5
+6553 bioassay_data_transformation "Probe set signal values were calculated from CEL files with GeneChip Operating Software v. 1.2.0.003 using the Probe Logarithmic Intensity Error Estimation (PLIER, Affymetrix, Inc.) algorithm using default settings (Probe selection, PM-MM; Background method, none; Optimization, full; Combine replicates, no; Quantile normalization, yes; GM control, 0.15, Attenuation, 0.005, Probe penalty, 0.001, Concentration penalty, 1e-006; Background percent, 0.05; al [...]
+6564 bioassay_data_transformation "MAS5: Microarray Suite v. 5.0.0.032 software (Affymetrix, Inc.) was used to calculate signal values using default settings (Parameters: Gamma 1L, 0.0045; Gamma 1H, 0.0045; Gamma 2L, 0.006; Gamma 2H, 0.006; Perturbation, 1.1; alpha1, 0.05; alpha2, 0.065; tau, 0.015). All data was global scaled to a target intensity of 500.<br><br>PLIER: Probe set signal values were calculated from CEL files with GeneChip Operating Software v. 1.2.0.003 using the Probe L [...]
+6672 nucleic_acid_extraction "Total RNAs isolated from rat brain (Cat.# 7912), kidney (Cat.# 7926), liver (Cat.# 7910), and testis (Cat.# 7934) were obtained from Ambion (Austin, TX). Total RNAs isolated from rat brain (Cat.# 737001), kidney (Cat.# 737007), liver (Cat.# 737009), and testis (Cat.# 737023) were obtained from Statagene (La Jolla, CA)." MTS Commercial RNA
+6673 grow Commercial source. MTS Batches4-5
+6841 pool MTS were prepared from commercial RNAs in proportions based upon the RNA concentrations provided by the supplier. MTS Mix1 Mix2 (commercial source)
+6841-mix1 pool "Mix1 consisted of 5 ug testis RNA, 15 ug liver RNA, 20 ug brain RNA, and 10 ug kidney RNA. 50 ug aliquots of each mixture were frozen at -70C." MTS Mix1 (commercial source)
+6841-mix2 pool "Mix2 consisted of 20 ug testis RNA, 10 ug liver RNA, 10 ug brain RNA, and 10 ug kidney RNA. 50 ug aliquots of each mixture were frozen at -70C." MTS Mix2 (commercial source)
+7671 labeling "Target labeling of 2 ug total RNA per reaction was performed essentially as described in the CodeLink Expression Bioarrays Automated Target Preparation addendum using the Qiagen BioRobot 9604. CDNA synthesis, cRNA preparation, and cRNA purification were processed in a 96-well format using automated Qiagen Biorobot 9604 procedures. CDNA synthesis were performed using reagents in the CodeLink cDNA Synthesis Kit (PN 320003) supplemented with 10 mM biotin-11-UTP and 10 mM bi [...]
+7672 labeling Target labeling of 2 ug total RNA per reaction was performed as specified in the manufacturers� protocol in the manual �Codelink Gene Expression System: Manual Labelled cRNA Target Preparation.� CDNA synthesis and in vitro transcription reactions were performed using reagents in the CodeLink Expression Assay Reagent Kit (PN 320012) and 10 mM biotin-11-UTP (PerkinElmer NEL543 or 999-543). Purification of double-stranded cDNA was performed using a QIAquick PCR purificatio [...]
+8188 hybridization "Hybridization and detection was performed as specified in the instruction manual ""Codelink Gene Expression System: Single-Assay Bioarray Hybridization and Detection."" Reagents for fragmentation and hybridization were from the CodeLink Expression Assay Reagent Kit (PN 320012). After fragmentation, 10 ug cRNA was hybridized in a total volume of 260 ul to each CodeLink array for 20 hr at 37C on an orbital shaker. <br><br>Post-hybridization, the arrays were washed i [...]
+8189 hybridization "Hybridization and detection was performed as specified in the instruction manual ""Codelink Gene Expression System: Single-Assay Bioarray Hybridization and Detection"" and the user guide CodeLink Expression Bioarrays Alexa Fluor 647-Streptavidin Detection Addendum."" Reagents for fragmentation and hybridization were from the CodeLink Expression Assay Reagent Kit (PN 320012). After fragmentation, 10 ug cRNA was hybridized in a total volume of 260 ul to each CodeLink [...]
+8190 image_aquisition "Scanning was performed using an Axon 4000B scanner at 10 micron scan resolution, using settings defined in the user manual. Feature extraction was performed using CodeLink Expression Analysis software v2.2.25. Spot intensity was calculated as the average of the pixel signals in the area of interest minus the scaled 2-pixel median background. The intensities were then median normalized by calculating the median of all spots, minus the control probes and blanks, [...]
+8309 image_aquisition "Scanning was performed using an Axon 4000B scanner at 10 micron scan resolution, using settings defined in the user manual. Feature extraction was performed using CodeLink Expression Analysis software v2.3.2. Spot intensity was calculated as the average of the pixel signals in the area of interest minus the scaled 2-pixel median background. The intensities were then median normalized by calculating the median of all spots, minus the control probes and blanks, a [...]
+8532 labeling 5 ug of total RNA was labeled using the protocol described in Hughes et al. Nature Biotechnol 19: 342-347. MTS Agilent 1 (Cy3)
+8534 labeling 5 ug of total RNA was labeled using the protocol described in Hughes et al. Nature Biotechnol 19: 342-347. MTS Agilent 1 (Cy5)
+8535 labeling 100 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523). MTS Agilent 2 (Cy3)
+8536 labeling 100 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523). MTS Agilent 2 (Cy5)
+8537 labeling 500 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523). MTS Agilent 3 (Cy3)
+8538 labeling 500 ng of total RNA was labeled using the Agilent Low Input RNA Fluorescent Linear Amplification Kit (P/N 5184-3523). MTS Agilent 3 (Cy5)
+8573 hybridization 5 ug of cRNA was hybridized to Agilent G4130A arrays using the protocol described in Hughes et al. Nature Biotechnol 19: 342-347. MTS Agilent 1
+8588 hybridization 0.75 ug labeled cRNA was hybridized per dye channel to Agilent G4130A arrays using Agilent microarray hybridization chambers (G2534A). The arrays were hybridized for 17 hr at 60C at rotor setting 4 in Agilent recommended oven (G2505-80081). Microarrays were washed and dried as specified in the user manual (Agilent 60-mer Oligo Microarray Processing Protocol). MTS Agilent 2
+8589 hybridization 0.75 ug labeled cRNA was hybridized per dye channel to Agilent G4130A arrays using Agilent microarray hybridization chambers (G2534A). The arrays were hybridized for 17 hr at 60C at rotor setting 5 in Agilent recommended oven (G2505-80081). Microarrays were washed and dried as specified in the user manual (Agilent 60-mer Oligo Microarray Processing Protocol). MTS Agilent 3
+8590 image_aquisition "Agilent arrays were scanned using the Agilent DNA microarray scanner (G2565BA) using all default settings. TIFF images were processed with Agilent Feature Extraction software version A.7.4.47 (a prerelease version which is algorithmically identical to v 7.5.1) using default settings. Adjustment for local variations in background signal was performed using a spatial detrending algorithm. To normalize and correct for dye bias, a combined method of linear scaling i [...]
+8749 bioassay_data_transformation Mix1 signals were normalized to Mix2 using the 10% trimmed mean signal of the kidney-selective probe subset. The ratio of Mix1 to Mix2 was calculated using the kidney normalized data. <br><br> MTS Kidney-Selective Probe Normalization
+8764 bioassay_data_transformation "To normalize and correct for dye bias, a combined method of linear scaling in each channel followed by LOWESS curve fitting (�Linear&LOWESS� option in Agilent Feature Extraction Software version A.7.5) was used. Mix1 and Mix2 signals were calculated from the average of dye-swap replicates. This step was followed by normalization of Mix1 to Mix2 using the 10% trimmed mean signal of the kidney-selective probe subset. Features flagged as outliers by the [...]
+8946 bioassay_data_transformation "Gene summary values were calculated from CEL files using the Probe Logarithmic Intensity Error Estimation (PLIER) algorithm (http://www.affymetrix.com/support/developer/adn_news/and_news_1004.affx). An affinity model for the PLIER analysis was constructed from single-tissue hybridizations. RNA from each of 4 different tissues was pooled across 8 animals from a single shipment (n=3) and hybridized to Affymetrix RAE230A arrays (n=12). PLIER signal calc [...]
+8947 bioassay_data_transformation (null) TRANPRTCL8947
+
+
+Hybridization section
+TSAMPLE_SYSUID BioSource BioSourceMaterial BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[BioSourceProvider] BioMaterialCharacteristics[Age] BioMaterialCharacteristics[Sex] BioMaterialCharacteristics[StrainOrLine] Protocol[grow] Protocol[treatment] FactorValue[Platform] Sample Extract Protocol[?] Protocol[extraction] LabeledExtract dye Protocol[labeling] Hybridization FactorValue[Individual] FactorValue[Mix] FactorValue[Operator] Protocol[hybridization] Protocol[scanning [...]
+173435 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site1 Batch1 Mix1 biotin 6486 MTS Site1 Batch1 Mix1 Batch1 Mix1 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch1_Mix1.CEL Site1_Batch1_Mix1.EXP Site1_Batch1_Mix1_PLIER Site1_Batch1_Mix1_PLIER.CHP
+173436 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site1 Batch1 Mix2 biotin 6486 MTS Site1 Batch1 Mix2 Batch1 Mix2 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch1_Mix2.CEL Site1_Batch1_Mix2.EXP Site1_Batch1_Mix2_PLIER Site1_Batch1_Mix2_PLIER.CHP
+173437 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site1 Batch2 Mix1 biotin 6486 MTS Site1 Batch2 Mix1 Batch2 Mix1 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch2_Mix1.CEL Site1_Batch2_Mix1.EXP Site1_Batch2_Mix1_PLIER Site1_Batch2_Mix1_PLIER.CHP
+173438 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site1 Batch2 Mix2 biotin 6486 MTS Site1 Batch2 Mix2 Batch2 Mix2 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch2_Mix2.CEL Site1_Batch2_Mix2.EXP Site1_Batch2_Mix2_PLIER Site1_Batch2_Mix2_PLIER.CHP
+173439 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site1 Batch3 Mix1 biotin 6486 MTS Site1 Batch3 Mix1 Batch3 Mix1 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch3_Mix1.CEL Site1_Batch3_Mix1.EXP Site1_Batch3_Mix1_PLIER Site1_Batch3_Mix1_PLIER.CHP
+173440 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site1 Batch3 Mix2 biotin 6486 MTS Site1 Batch3 Mix2 Batch3 Mix2 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch3_Mix2.CEL Site1_Batch3_Mix2.EXP Site1_Batch3_Mix2_PLIER Site1_Batch3_Mix2_PLIER.CHP
+173435 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site2 Batch1 Mix1 biotin 6489 MTS Site2 Batch1 Mix1 Batch1 Mix1 Site2 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch1_Mix1.CEL Site2_Batch1_Mix1.EXP Site2_Batch1_Mix1_PLIER Site2_Batch1_Mix1_PLIER.CHP
+173436 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site2 Batch1 Mix2 biotin 6489 MTS Site2 Batch1 Mix2 Batch1 Mix2 Site2 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch1_Mix2.CEL Site2_Batch1_Mix2.EXP Site2_Batch1_Mix2_PLIER Site2_Batch1_Mix2_PLIER.CHP
+173437 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site2 Batch2 Mix1 biotin 6489 MTS Site2 Batch2 Mix1 Batch2 Mix1 Site2 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch2_Mix1.CEL Site2_Batch2_Mix1.EXP Site2_Batch2_Mix1_PLIER Site2_Batch2_Mix1_PLIER.CHP
+173438 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site2 Batch2 Mix2 biotin 6489 MTS Site2 Batch2 Mix2 Batch2 Mix2 Site2 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch2_Mix2.CEL Site2_Batch2_Mix2.EXP Site2_Batch2_Mix2_PLIER Site2_Batch2_Mix2_PLIER.CHP
+173439 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site2 Batch3 Mix1 biotin 6489 MTS Site2 Batch3 Mix1 Batch3 Mix1 Site2 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch3_Mix1.CEL Site2_Batch3_Mix1.EXP Site2_Batch3_Mix1_PLIER Site2_Batch3_Mix1_PLIER.CHP
+173440 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site2 Batch3 Mix2 biotin 6489 MTS Site2 Batch3 Mix2 Batch3 Mix2 Site2 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch3_Mix2.CEL Site2_Batch3_Mix2.EXP Site2_Batch3_Mix2_PLIER Site2_Batch3_Mix2_PLIER.CHP
+173435 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site3 Batch1 Mix1 biotin 6486 MTS Site3 Batch1 Mix1 Batch1 Mix1 Site3 6491 6409 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch1_Mix1.CEL Site3_Batch1_Mix1.EXP Site3_Batch1_Mix1_PLIER Site3_Batch1_Mix1_PLIER.CHP
+173436 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site3 Batch1 Mix2 biotin 6486 MTS Site3 Batch1 Mix2 Batch1 Mix2 Site3 6491 6409 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch1_Mix2.CEL Site3_Batch1_Mix2.EXP Site3_Batch1_Mix2_PLIER Site3_Batch1_Mix2_PLIER.CHP
+173437 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site3 Batch2 Mix1 biotin 6486 MTS Site3 Batch2 Mix1 Batch2 Mix1 Site3 6491 6409 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch2_Mix1.CEL Site3_Batch2_Mix1.EXP Site3_Batch2_Mix1_PLIER Site3_Batch2_Mix1_PLIER.CHP
+173438 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site3 Batch2 Mix2 biotin 6486 MTS Site3 Batch2 Mix2 Batch2 Mix2 Site3 6491 6409 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch2_Mix2.CEL Site3_Batch2_Mix2.EXP Site3_Batch2_Mix2_PLIER Site3_Batch2_Mix2_PLIER.CHP
+173439 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site3 Batch3 Mix1 biotin 6486 MTS Site3 Batch3 Mix1 Batch3 Mix1 Site3 6491 6409 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch3_Mix1.CEL Site3_Batch3_Mix1.EXP Site3_Batch3_Mix1_PLIER Site3_Batch3_Mix1_PLIER.CHP
+173440 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site3 Batch3 Mix2 biotin 6486 MTS Site3 Batch3 Mix2 Batch3 Mix2 Site3 6491 6409 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch3_Mix2.CEL Site3_Batch3_Mix2.EXP Site3_Batch3_Mix2_PLIER Site3_Batch3_Mix2_PLIER.CHP
+173435 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site1 Batch1 Mix1 1YR biotin 6486 MTS Site1 Batch1 Mix1 1YR Batch1 Mix1 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002256 Site1_Batch1_Mix1_1YR.CEL Site1_Batch1_Mix1_1YR.EXP Site1_Batch1_Mix1_1YR_PLIER Site1_Batch1_Mix1_1YR_PLIER.CHP
+173436 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site1 Batch1 Mix2 1YR biotin 6486 MTS Site1 Batch1 Mix2 1YR Batch1 Mix2 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002256 Site1_Batch1_Mix2_1YR.CEL Site1_Batch1_Mix2_1YR.EXP Site1_Batch1_Mix2_1YR_PLIER Site1_Batch1_Mix2_1YR_PLIER.CHP
+173795 MTS Batch4 total_RNA Rattus norvegicus "Ambion (Austin, TX)" unknown_sex Sprague-Dawley 6673 6672 Affymetrix MTS Batch4 MTS Batch4 Mix1 6841 6841-mix1 MTS Site1 Batch4 Mix1 biotin 6486 MTS Site1 Batch4 Mix1 Batch4 Mix1 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002254 Site1_Batch4_Mix1.CEL Site1_Batch4_Mix1.EXP Site1_Batch4_Mix1_PLIER Site1_Batch4_Mix1_PLIER.CHP
+173796 MTS Batch4 total_RNA Rattus norvegicus "Ambion (Austin, TX)" unknown_sex Sprague-Dawley 6673 6672 Affymetrix MTS Batch4 MTS Batch4 Mix2 6841 6841-mix2 MTS Site1 Batch4 Mix2 biotin 6486 MTS Site1 Batch4 Mix2 Batch4 Mix2 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002254 Site1_Batch4_Mix2.CEL Site1_Batch4_Mix2.EXP Site1_Batch4_Mix2_PLIER Site1_Batch4_Mix2_PLIER.CHP
+173797 MTS Batch5 total_RNA Rattus norvegicus "Stratagene (La Jolla, CA)" unknown_sex Sprague-Dawley 6673 6672 Affymetrix MTS Batch5 MTS Batch5 Mix1 6841 6841-mix1 MTS Site1 Batch5 Mix1 biotin 6486 MTS Site1 Batch5 Mix1 Batch5 Mix1 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002256 Site1_Batch5_Mix1.CEL Site1_Batch5_Mix1.EXP Site1_Batch5_Mix1_PLIER Site1_Batch5_Mix1_PLIER.CHP
+173798 MTS Batch5 total_RNA Rattus norvegicus "Stratagene (La Jolla, CA)" unknown_sex Sprague-Dawley 6673 6672 Affymetrix MTS Batch5 MTS Batch5 Mix2 6841 6841-mix2 MTS Site1 Batch5 Mix2 biotin 6486 MTS Site1 Batch5 Mix2 Batch5 Mix2 Site1 6491 6407 6553 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002256 Site1_Batch5_Mix2.CEL Site1_Batch5_Mix2.EXP Site1_Batch5_Mix2_PLIER Site1_Batch5_Mix2_PLIER.CHP
+175308 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site4 Batch1 Mix1 biotin 7672 MTS Site4 Batch1 Mix1 Batch1 Mix1 Site4 8188 8309 A-MEXP-124 T00231505 Site4_Batch1_Mix1_Data.txt
+175309 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site4 Batch1 Mix2 biotin 7672 MTS Site4 Batch1 Mix2 Batch1 Mix2 Site4 8188 8309 A-MEXP-124 T00231509 Site4_Batch1_Mix2_Data.txt
+175310 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site4 Batch2 Mix1 biotin 7672 MTS Site4 Batch2 Mix1 Batch2 Mix1 Site4 8188 8309 A-MEXP-124 T00231506 Site4_Batch2_Mix1_Data.txt
+175311 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site4 Batch2 Mix2 biotin 7672 MTS Site4 Batch2 Mix2 Batch2 Mix2 Site4 8188 8309 A-MEXP-124 T00231504 Site4_Batch2_Mix2_Data.txt
+175312 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site4 Batch3 Mix1 biotin 7672 MTS Site4 Batch3 Mix1 Batch3 Mix1 Site4 8188 8309 A-MEXP-124 T00231511 Site4_Batch3_Mix1_Data.txt
+175313 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site4 Batch3 Mix2 biotin 7672 MTS Site4 Batch3 Mix2 Batch3 Mix2 Site4 8188 8309 A-MEXP-124 T00231510 Site4_Batch3_Mix2_Data.txt
+175308 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site5 Batch1 Mix1 biotin 7671 MTS Site5 Batch1 Mix1 Batch1 Mix1 Site5 8189 8190 A-MEXP-124 T00221468 Site5_Batch1_Mix1_Data.txt
+175309 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site5 Batch1 Mix2 biotin 7671 MTS Site5 Batch1 Mix2 Batch1 Mix2 Site5 8189 8190 A-MEXP-124 T00221469 Site5_Batch1_Mix2_Data.txt
+175310 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site5 Batch2 Mix1 biotin 7671 MTS Site5 Batch2 Mix1 Batch2 Mix1 Site5 8189 8190 A-MEXP-124 T00221470 Site5_Batch2_Mix1_Data.txt
+175311 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site5 Batch2 Mix2 biotin 7671 MTS Site5 Batch2 Mix2 Batch2 Mix2 Site5 8189 8190 A-MEXP-124 T00221471 Site5_Batch2_Mix2_Data.txt
+175312 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site5 Batch3 Mix1 biotin 7671 MTS Site5 Batch3 Mix1 Batch3 Mix1 Site5 8189 8190 A-MEXP-124 T00221472 Site5_Batch3_Mix1_Data.txt
+175313 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Codelink MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site5 Batch3 Mix2 biotin 7671 MTS Site5 Batch3 Mix2 Batch3 Mix2 Site5 8189 8190 A-MEXP-124 T00221473 Site5_Batch3_Mix2_Data.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site7 Batch1 Mix2 (Cy3) Cy3 8532 MTS Site7 Batch1 rMix1 gMix2 Batch1 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020448_A01 Site7_Batch1_rMix1_gMix2.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site7 Batch1 Mix1 (Cy5) Cy5 8534 MTS Site7 Batch1 rMix1 gMix2 Batch1 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020448_A01 Site7_Batch1_rMix1_gMix2.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site7 Batch1 Mix2 (Cy5) Cy5 8534 MTS Site7 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020457_A01 Site7_Batch1_rMix2_gMix1.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site7 Batch1 Mix1 (Cy3) Cy3 8532 MTS Site7 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020457_A01 Site7_Batch1_rMix2_gMix1.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site7 Batch2 Mix1 (Cy5) Cy5 8534 MTS Site7 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020452_A01 Site7_Batch2_rMix1_gMix2.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site7 Batch2 Mix2 (Cy3) Cy3 8532 MTS Site7 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020452_A01 Site7_Batch2_rMix1_gMix2.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site7 Batch2 Mix2 (Cy5) Cy5 8534 MTS Site7 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020451_A01 Site7_Batch2_rMix2_gMix1.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site7 Batch2 Mix1 (Cy3) Cy3 8532 MTS Site7 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020451_A01 Site7_Batch2_rMix2_gMix1.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site7 Batch3 Mix2 (Cy3) Cy3 8532 MTS Site7 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020450_A01 Site7_Batch3_rMix1_gMix2.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site7 Batch3 Mix1 (Cy5) Cy5 8534 MTS Site7 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020450_A01 Site7_Batch3_rMix1_gMix2.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site7 Batch3 Mix2 (Cy5) Cy5 8534 MTS Site7 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020449_A01 Site7_Batch3_rMix2_gMix1.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site7 Batch3 Mix1 (Cy3) Cy3 8532 MTS Site7 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site7 8573 8590 A-AGIL-6 16011868020449_A01 Site7_Batch3_rMix2_gMix1.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site8 Batch1 Mix2 (Cy3) Cy3 8535 MTS Site8 Batch1 rMix1 g Mix2 Batch1 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016369_A01 Site8_Batch1_rMix1_gMix2.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site8 Batch1 Mix1 (Cy5) Cy5 8536 MTS Site8 Batch1 rMix1 g Mix2 Batch1 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016369_A01 Site8_Batch1_rMix1_gMix2.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site8 Batch1 Mix2 (Cy5) Cy5 8536 MTS Site8 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016362_A01 Site8_Batch1_rMix2_gMix1.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site8 Batch1 Mix1 (Cy3) Cy3 8535 MTS Site8 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016362_A01 Site8_Batch1_rMix2_gMix1.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site8 Batch2 Mix2 (Cy3) Cy3 8535 MTS Site8 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016360_A01 Site8_Batch2_rMix1_gMix2.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site8 Batch2 Mix1 (Cy5) Cy5 8536 MTS Site8 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016360_A01 Site8_Batch2_rMix1_gMix2.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site8 Batch2 Mix2 (Cy5) Cy5 8536 MTS Site8 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016352_A01 Site8_Batch2_rMix2_gMix1.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site8 Batch2 Mix1 (Cy3) Cy3 8535 MTS Site8 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016352_A01 Site8_Batch2_rMix2_gMix1.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site8 Batch3 Mix1 (Cy5) Cy5 8536 MTS Site8 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016683_A01 Site8_Batch3_rMix1_gMix2.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site8 Batch3 Mix2 (Cy3) Cy3 8535 MTS Site8 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016683_A01 Site8_Batch3_rMix1_gMix2.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site8 Batch3 Mix2 (Cy5) Cy5 8536 MTS Site8 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016334_A01 Site8_Batch3_rMix2_gMix1.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site8 Batch3 Mix1 (Cy3) Cy3 8535 MTS Site8 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site8 8588 8590 A-AGIL-6 16011868016334_A01 Site8_Batch3_rMix2_gMix1.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site9 Batch1 Mix2 (Cy3) Cy3 8537 MTS Site9 Batch1 rMix1 gMix2 Batch1 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021269_A01 Site9_Batch1_rMix1_gMix2.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site9 Batch1 Mix1 (Cy5) Cy5 8538 MTS Site9 Batch1 rMix1 gMix2 Batch1 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021269_A01 Site9_Batch1_rMix1_gMix2.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site9 Batch1 Mix2 (Cy5) Cy5 8538 MTS Site9 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021268_A01 Site9_Batch1_rMix2_gMix1.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site9 Batch1 Mix1 (Cy3) Cy3 8537 MTS Site9 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021268_A01 Site9_Batch1_rMix2_gMix1.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site9 Batch2 Mix2 (Cy3) Cy3 8537 MTS Site9 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021050_A01 Site9_Batch2_rMix1_gMix2.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site9 Batch2 Mix1 (Cy5) Cy5 8538 MTS Site9 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021050_A01 Site9_Batch2_rMix1_gMix2.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site9 Batch2 Mix1 (Cy3) Cy3 8537 MTS Site9 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021270_A01 Site9_Batch2_rMix2_gMix1.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site9 Batch2 Mix2 (Cy5) Cy5 8538 MTS Site9 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021270_A01 Site9_Batch2_rMix2_gMix1.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site9 Batch3 Mix2 (Cy3) Cy3 8537 MTS Site9 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021048_A01 Site9_Batch3_rMix1_gMix2.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site9 Batch3 Mix1 (Cy5) Cy5 8538 MTS Site9 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021048_A01 Site9_Batch3_rMix1_gMix2.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site9 Batch3 Mix2 (Cy5) Cy5 8538 MTS Site9 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021049_A01 Site9_Batch3_rMix2_gMix1.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site9 Batch3 Mix1 (Cy3) Cy3 8537 MTS Site9 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site9 8589 8590 A-AGIL-6 16011868021049_A01 Site9_Batch3_rMix2_gMix1.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site3 Batch1 Mix1 (Cy5) Cy5 8538 MTS Site3 Batch1 rMix1 gMix2 Batch1 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020210_A01 Site3_Batch1_rMix1_gMix2.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site3 Batch1 Mix2 (Cy3) Cy3 8537 MTS Site3 Batch1 rMix1 gMix2 Batch1 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020210_A01 Site3_Batch1_rMix1_gMix2.txt
+177825 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix2 6396 6396-mix2 MTS Site3 Batch1 Mix2 (Cy5) Cy5 8538 MTS Site3 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020211_A01 Site3_Batch1_rMix2_gMix1.txt
+177824 MTS Batch1 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch1 MTS Batch1 Mix1 6396 6396-mix1 MTS Site3 Batch1 Mix1 (Cy3) Cy3 8537 MTS Site3 Batch1 rMix2 gMix1 Batch1 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020211_A01 Site3_Batch1_rMix2_gMix1.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site3 Batch2 Mix2 (Cy3) Cy3 8537 MTS Site3 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020208_A01 Site3_Batch2_rMix1_gMix2.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site3 Batch2 Mix1 (Cy5) Cy5 8538 MTS Site3 Batch2 rMix1 gMix2 Batch2 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020208_A01 Site3_Batch2_rMix1_gMix2.txt
+177827 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Spraque-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix2 6396 6396-mix2 MTS Site3 Batch2 Mix2 (Cy5) Cy5 8538 MTS Site3 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020209_A01 Site3_Batch2_rMix2_gMix1.txt
+177826 MTS Batch2 whole_organism Rattus norvegicus "Charles River, Raleigh, NC" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch2 MTS Batch2 Mix1 6396 6396-mix1 MTS Site3 Batch2 Mix1 (Cy3) Cy3 8537 MTS Site3 Batch2 rMix2 gMix1 Batch2 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020209_A01 Site3_Batch2_rMix2_gMix1.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site3 Batch3 Mix2 (Cy3) Cy3 8537 MTS Site3 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020251_A01 Site3_Batch3_rMix1_gMix2.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site3 Batch3 Mix1 (Cy5) Cy5 8538 MTS Site3 Batch3 rMix1 gMix2 Batch3 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020251_A01 Site3_Batch3_rMix1_gMix2.txt
+177828 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix1 6396 6396-mix1 MTS Site3 Batch3 Mix1 (Cy3) Cy3 8537 MTS Site3 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020252_A01 Site3_Batch3_rMix2_gMix1.txt
+177829 MTS Batch3 whole_organism Rattus norvegicus "Harlan, Frederick, MD" 6-7 weeks male Sprague-Dawley 6377 6380 Agilent MTS Batch3 MTS Batch3 Mix2 6396 6396-mix2 MTS Site3 Batch3 Mix2 (Cy5) Cy5 8538 MTS Site3 Batch3 rMix2 gMix1 Batch3 Mix1;Mix2 Site3 8589 8590 A-AGIL-6 16011868020252_A01 Site3_Batch3_rMix2_gMix1.txt
+178297 MTS Batch6 whole_organism Rattus norvegicus Site3 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch6 MTS Batch6 Mix1 6396 6396-mix1 MTS Site3 Batch6 Mix1 biotin 6486 MTS Site1 Batch6 Mix1 Batch6 Mix1 Site1 6491 6407 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002254 Site1_Batch6_Mix1.CEL Site1_Batch6_Mix1.EXP MTS Site1 Batch6 Mix1
+178298 MTS Batch6 whole_organism Rattus norvegicus Site3 6-7 weeks male Sprague-Dawley 6377 6380 Affymetrix MTS Batch6 MTS Batch6 Mix2 6396 6396-mix2 MTS Site1 Batch6 Mix2 biotin 6486 MTS Site1 Batch6 Mix2 Batch6 Mix2 Site1 6491 6407 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002254 Site1_Batch6_Mix2.CEL Site1_Batch6_Mix2.EXP MTS Site1 Batch6 Mix2
+ Affymetrix MTS Site1 Batch1 Mix1 Batch1 Mix1 Site1 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch1_Mix1.CEL Site1_Batch1_Mix1.EXP Site1_Batch1_Mix1_MAS5 Site1_Batch1_Mix1_MAS5.CHP
+ Affymetrix MTS Site1 Batch1 Mix2 Batch1 Mix2 Site1 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch1_Mix2.CEL Site1_Batch1_Mix2.EXP Site1_Batch1_Mix2_MAS5 Site1_Batch1_Mix2_MAS5.CHP
+ Affymetrix MTS Site1 Batch2 Mix1 Batch2 Mix1 Site1 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch2_Mix1.CEL Site1_Batch2_Mix1.EXP Site1_Batch2_Mix1_MAS5 Site1_Batch2_Mix1_MAS5.CHP
+ Affymetrix MTS Site1 Batch2 Mix2 Batch2 Mix2 Site1 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch2_Mix2.CEL Site1_Batch2_Mix2.EXP Site1_Batch2_Mix2_MAS5 Site1_Batch2_Mix2_MAS5.CHP
+ Affymetrix MTS Site1 Batch3 Mix1 Batch3 Mix1 Site1 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch3_Mix1.CEL Site1_Batch3_Mix1.EXP Site1_Batch3_Mix1_MAS5 Site1_Batch3_Mix1_MAS5.CHP
+ Affymetrix MTS Site1 Batch3 Mix2 Batch3 Mix2 Site1 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3002245 Site1_Batch3_Mix2.CEL Site1_Batch3_Mix2.EXP Site1_Batch3_Mix2_MAS5 Site1_Batch3_Mix2_MAS5.CHP
+ Affymetrix MTS Site2 Batch1 Mix1 Batch1 Mix1 Site2 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch1_Mix1.CEL Site2_Batch1_Mix1.EXP Site2_Batch1_Mix1_MAS5 Site2_Batch1_Mix1_MAS5.CHP
+ Affymetrix MTS Site2 Batch1 Mix2 Batch1 Mix2 Site2 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch1_Mix2.CEL Site2_Batch1_Mix2.EXP Site2_Batch1_Mix2_MAS5 Site2_Batch1_Mix2_MAS5.CHP
+ Affymetrix MTS Site2 Batch2 Mix1 Batch2 Mix1 Site2 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch2_Mix1.CEL Site2_Batch2_Mix1.EXP Site2_Batch2_Mix1_MAS5 Site2_Batch2_Mix1_MAS5.CHP
+ Affymetrix MTS Site2 Batch2 Mix2 Batch2 Mix2 Site2 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch2_Mix2.CEL Site2_Batch2_Mix2.EXP Site2_Batch2_Mix2_MAS5 Site2_Batch2_Mix2_MAS5.CHP
+ Affymetrix MTS Site2 Batch3 Mix1 Batch3 Mix1 Site2 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch3_Mix1.CEL Site2_Batch3_Mix1.EXP Site2_Batch3_Mix1_MAS5 Site2_Batch3_Mix1_MAS5.CHP
+ Affymetrix MTS Site2 Batch3 Mix2 Batch3 Mix2 Site2 6491 6407 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005334 Site2_Batch3_Mix2.CEL Site2_Batch3_Mix2.EXP Site2_Batch3_Mix2_MAS5 Site2_Batch3_Mix2_MAS5.CHP
+ Affymetrix MTS Site3 Batch1 Mix1 Batch1 Mix1 Site3 6491 6409 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch1_Mix1.CEL Site3_Batch1_Mix1.EXP Site3_Batch1_Mix1_MAS5 Site3_Batch1_Mix1_MAS5.CHP
+ Affymetrix MTS Site3 Batch1 Mix2 Batch1 Mix2 Site3 6491 6409 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch1_Mix2.CEL Site3_Batch1_Mix2.EXP Site3_Batch1_Mix2_MAS5 Site3_Batch1_Mix2_MAS5.CHP
+ Affymetrix MTS Site3 Batch2 Mix1 Batch2 Mix1 Site3 6491 6409 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch2_Mix1.CEL Site3_Batch2_Mix1.EXP Site3_Batch2_Mix1_MAS5 Site3_Batch2_Mix1_MAS5.CHP
+ Affymetrix MTS Site3 Batch2 Mix2 Batch2 Mix2 Site3 6491 6409 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch2_Mix2.CEL Site3_Batch2_Mix2.EXP Site3_Batch2_Mix2_MAS5 Site3_Batch2_Mix2_MAS5.CHP
+ Affymetrix MTS Site3 Batch3 Mix1 Batch3 Mix1 Site3 6491 6409 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch3_Mix1.CEL Site3_Batch3_Mix1.EXP Site3_Batch3_Mix1_MAS5 Site3_Batch3_Mix1_MAS5.CHP
+ Affymetrix MTS Site3 Batch3 Mix2 Batch3 Mix2 Site3 6491 6409 6552 A-AFFY-25 /net/nfs6/vol4/ma-db/shoja/Affy_Data/RAE230A.CDF 3005331 Site3_Batch3_Mix2.CEL Site3_Batch3_Mix2.EXP Site3_Batch3_Mix2_MAS5 Site3_Batch3_Mix2_MAS5.CHP
diff --git a/examples/real/E-TABM-163.png b/examples/real/E-TABM-163.png
new file mode 100644
index 0000000..db71e97
Binary files /dev/null and b/examples/real/E-TABM-163.png differ
diff --git a/examples/real/E-TABM-163.txt b/examples/real/E-TABM-163.txt
new file mode 100755
index 0000000..a89f805
--- /dev/null
+++ b/examples/real/E-TABM-163.txt
@@ -0,0 +1,55 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-163
+quality_control biological_replicate
+experiment_design_type "time_series_design, development_or_differentiation_design,co_expression_design"
+name Identification of cyclic genes of the mouse segmentation clock
+description "A microarray time series was generated to identify cyclic genes of the segmentation clock in the mouse. The right posterior half presomitic mesoderms (PSM) from 17 mouse embryos were dissected while the contralateral side of the embryo containing the left PSM was immediately fixed to be analyzed by in situ hybridization using a Lfng probe to order the samples along the segmentation clock oscillation cycle. Probes were produced from RNA extracted from the 17 dissected posteri [...]
+release_date 2006-10-30
+submission_date 2006-10-30
+submitter Mary-Lee Dequ�ant
+organization Stowers Institute For Medical Research
+publication_title A complex oscillating network of signaling genes underlies the mouse segmentation clock
+authors "Mary-Lee Dequ�ant, Earl Glynn, Karin Gaudenz, Matthias Wahl, Jie Chen, Arcady Mushegian, Olivier Pourqui�"
+journal Science
+volume
+issue
+pages
+year
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-MLD6-1 "CD-1 mouse embryos (9.0 days post-coitus [dpc]) were dissected in cold Tyrode's solution prepared in RNAse-free conditions. Mouse embryos from each litter were dissected one by one, while the rest of the embryos were kept on ice. No more than three embryos per litter were dissected in order to limit the time between removal of the uterus and embryo dissection to reduce the risk of RNA degradation. The time spent for dissection per embryo was limited to 15-20 min. After tail bud [...]
+P-MLD6-2 "Total RNA was isolated from each dissected posterior half PSM (containing around 5,000 cells) using Trizol." RNA extraction
+P-MLD6-3 "Amplification of the samples was performed as described in GeneChip Eukaryotic small sample target labeling Assay version II. Total RNA from each sample was used to generate first-strand cDNA by using a T7-linked oligo (dT) primer. After second-strand synthesis, in vitro transcription was performed with unlabelled ribonucleotides yielding 1.2 to 2.4 ug of cRNA. Using 400ng of cRNA from the first round of amplification, a second round of cDNA synthesis was then performed followe [...]
+P-MLD6-4 Arrays were hybridized according to the manufacturer's instructions. Each chip was hybridized to 13 ug fragmented cRNA. Hybridization
+P-MLD6-5 Arrays were scanned with a GeneArray scanner (Agilent G2500A) at the Microarray Core Facility of the Stowers Institute for Medical Research Scanning
+P-MLD6-6 The array readout was processed using Affymetrix Microarray Suite (MAS) 5.0 Image_analysis
+P-MLD6-7 Scaling normalisation (target signal 150) using Affymetrix MAS 5.0 was done for the .CHP files submitted to this database Normalization
+
+Hybridization section
+File[raw] File[normalized] File[exp] File[cdf] Array[accession] Array[serial] BioSource SampleMaterial ExtractMaterial LabeledExtractMaterial BioSourceMaterial BioMaterialCharacteristics[Individual] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Age(days)] BioMaterialCharacteristics[DevelopmentalStage] BioMaterialCharacteristics[OrganismPart] FactorValue[OrganismPart] FactorValue[Individual] FactorValue[time(m)] Protocol[treatment [...]
+Emb1a-5D2_MGU74Av2.CEL Emb1a-5D2_MGU74Av2.CHP Emb1a-5D2_MGU74Av2.EXP MG_U74Av2.CDF A-AFFY-6 3000681 Charles River # 022 organism_part total_RNA synthetic_RNA organism_part Embryo 1 Mus musculus CD-1 9.5 embryo right or left presomitic mesoderm (posterior half)(a) right or left presomitic mesoderm (posterior half)(a) Embryo 1 n/a P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb1b-5G1_MGU74Av2.CEL Emb1b-5G1_MGU74Av2.CHP Emb1b-5G1_MGU74Av2.EXP MG_U74Av2.CDF A-AFFY-6 3000681 Charles River # 023 organism_part total_RNA synthetic_RNA organism_part Embryo 1 Mus musculus CD-1 9.5 embryo right or left presomitic mesoderm (posterior half)(b) right or left presomitic mesoderm (posterior half)(b) Embryo 1 n/a P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb2a-3_MGU74Av2.CEL Emb2a-3_MGU74Av2.CHP Emb2a-3_MGU74Av2.EXP MG_U74Av2.CDF A-AFFY-6 3000681 Charles River # 024 organism_part total_RNA synthetic_RNA organism_part Embryo 2 Mus musculus CD-1 9.5 embryo right or left presomitic mesoderm (posterior half)(a) right or left presomitic mesoderm (posterior half)(a) Embryo 2 n/a P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb2b-4_MGU74Av2.CEL Emb2b-4_MGU74Av2.CHP Emb2b-4_MGU74Av2.EXP MG_U74Av2.CDF A-AFFY-6 3000681 Charles River # 025 organism_part total_RNA synthetic_RNA organism_part Embryo 2 Mus musculus CD-1 9.5 embryo right or left presomitic mesoderm (posterior half)(b) right or left presomitic mesoderm (posterior half)(b) Embryo 2 n/a P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb3a-D_MGU74Av2.CEL Emb3a-D_MGU74Av2.CHP Emb3a-D_MGU74Av2.EXP MG_U74Av2.CDF A-AFFY-6 3000681 Charles River # 026 organism_part total_RNA synthetic_RNA organism_part Embryo 3 Mus musculus CD-1 9.5 embryo right or left presomitic mesoderm (posterior half)(a) right or left presomitic mesoderm (posterior half)(a) Embryo 3 n/a P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb3b-E_MGU74Av2.CEL Emb3b-E_MGU74Av2.CHP Emb3b-E_MGU74Av2.EXP MG_U74Av2.CDF A-AFFY-6 3000681 Charles River # 027 organism_part total_RNA synthetic_RNA organism_part Embryo 3 Mus musculus CD-1 9.5 embryo right or left presomitic mesoderm (posterior half)(b) right or left presomitic mesoderm (posterior half)(b) Embryo 3 n/a P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb01-6.3_MOE430A.CEL Emb01-6.3_MOE430A.CHP Emb01-6.3_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 028 organism_part total_RNA synthetic_RNA organism_part Embryo 01 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 01 0 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb02-10.3_MOE430A.CEL Emb02-10.3_MOE430A.CHP Emb02-10.3_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 029 organism_part total_RNA synthetic_RNA organism_part Embryo 02 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 02 7 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb03-7.3_MOE430A.CEL Emb03-7.3_MOE430A.CHP Emb03-7.3_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 030 organism_part total_RNA synthetic_RNA organism_part Embryo 03 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 03 14 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb04-11.3_MOE430A.CEL Emb04-11.3_MOE430A.CHP Emb04-11.3_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 031 organism_part total_RNA synthetic_RNA organism_part Embryo 04 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 04 21 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb05-12.1_MOE430A.CEL Emb05-12.1_MOE430A.CHP Emb05-12.1_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 032 organism_part total_RNA synthetic_RNA organism_part Embryo 05 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 05 28 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb06-19.1_MOE430A.CEL Emb06-19.1_MOE430A.CHP Emb06-19.1_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 033 organism_part total_RNA synthetic_RNA organism_part Embryo 06 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 06 35 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb07-20.1_MOE430A.CEL Emb07-20.1_MOE430A.CHP Emb07-20.1_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 034 organism_part total_RNA synthetic_RNA organism_part Embryo 07 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 07 42 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb08-4.1_MOE430A.CEL Emb08-4.1_MOE430A.CHP Emb08-4.1_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 035 organism_part total_RNA synthetic_RNA organism_part Embryo 08 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 08 49 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb09-22.1_MOE430A.CEL Emb09-22.1_MOE430A.CHP Emb09-22.1_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 036 organism_part total_RNA synthetic_RNA organism_part Embryo 09 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 09 56 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb10-9.1_MOE430A.CEL Emb10-9.1_MOE430A.CHP Emb10-9.1_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 037 organism_part total_RNA synthetic_RNA organism_part Embryo 10 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 10 63 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb11-23.2_MOE430A.CEL Emb11-23.2_MOE430A.CHP Emb11-23.2_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 038 organism_part total_RNA synthetic_RNA organism_part Embryo 11 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 11 70 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb12-13.2_MOE430A.CEL Emb12-13.2_MOE430A.CHP Emb12-13.2_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 039 organism_part total_RNA synthetic_RNA organism_part Embryo 12 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 12 77 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb13-14.2_MOE430A.CEL Emb13-14.2_MOE430A.CHP Emb13-14.2_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 040 organism_part total_RNA synthetic_RNA organism_part Embryo 13 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 13 84 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb14-17.2_MOE430A.CEL Emb14-17.2_MOE430A.CHP Emb14-17.2_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 041 organism_part total_RNA synthetic_RNA organism_part Embryo 14 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 14 91 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb15-16.2_MOE430A.CEL Emb15-16.2_MOE430A.CHP Emb15-16.2_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 042 organism_part total_RNA synthetic_RNA organism_part Embryo 15 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 15 98 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb16-15.2_MOE430A.CEL Emb16-15.2_MOE430A.CHP Emb16-15.2_MOE430A.EXP MOE430A.CDF A-AFFY-23 3004217 Charles River # 043 organism_part total_RNA synthetic_RNA organism_part Embryo 16 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 16 105 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
+Emb17-18.2_MOE430A.CEL Emb17-18.2_MOE430A.CHP Emb17-18.2_MOE430A.EXP MOE430A.CDF A-AFFY-23 3005274 Charles River # 044 organism_part total_RNA synthetic_RNA organism_part Embryo 17 Mus musculus CD-1 9.5 embryo right presomitic mesoderm (posterior half) right presomitic mesoderm (posterior half) Embryo 17 112 P-MLD6-1 P-MLD6-2 P-MLD6-3 P-MLD6-4 P-MLD6-5 P-MLD6-6 P-MLD6-7
diff --git a/examples/real/E-TABM-18.png b/examples/real/E-TABM-18.png
new file mode 100644
index 0000000..dea1b84
Binary files /dev/null and b/examples/real/E-TABM-18.png differ
diff --git a/examples/real/E-TABM-18.txt b/examples/real/E-TABM-18.txt
new file mode 100755
index 0000000..126131f
--- /dev/null
+++ b/examples/real/E-TABM-18.txt
@@ -0,0 +1,82 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-18
+experiment_design_type strain_or_line_design
+name Ecotype
+description "Seedlings of 35 different Arabidopsis thaliana ecotypes were compared. Triplicates were performed of 10 ecotpyes, single arrays of 25 ecotypes. "
+release_date 2005-06-15
+submission_date
+submitter Janne Lempe
+organization "Max Planck Institute for Developmental Biology, Department of Molecular Biology, Detlef Weigel"
+publication_title Diversity of flowering responses in wild Arabidopsis thaliana strains
+authors Janne Lempe;Sureshkumar Balasubramanian;Sridevi Sureshkumar;Anandita Singh;Markus Schmid;Detlef Weigel
+journal PLOS Genetics
+volume 1
+issue
+pages
+year 2005
+
+Protocol section
+accession type text name parameters
+P-TABM-Janne5 growth "Plants were grown on soil for 4 days under continuous light at 23�C and 65% relative humidity. Most replicates were grown at the same time, exceptions to this are indicated by the 'replicate timepoint' parameter." strain comparison replicate timepoint
+P-TABM-Janne2 extraction pdf of the protocol (Probe preparation Development) can be downloaded at http://www.weigelworld.org/resources/microarray/AtGenExpress/; also at TAIR http://www.arabidopsis.org Protocol:501712557
+P-TABM-Janne6 pooling above soil plant material of 15 seedlings was pooled per replicate
+P-TABM-Janne4 labeling pdf of the protocol (Probe preparation Development) can be downloaded at http://www.weigelworld.org/resources/microarray/AtGenExpress/; also at TAIR http://www.arabidopsis.org Protocol:501712557
+
+Hybridization section
+File[raw] File[exp] File[normalized] Array[accession] Extract BioSource BioSourceMaterial BioMaterialCharacteristics[Ecotype] FactorValue[Ecotype] BioMaterialCharacteristics[nasc] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[OrganismPart] Protocol[grow] Parameter[replicate timepoint] Protocol[extraction] Protocol[pool] Protocol[labeling] Dye File[cdf]
+ATGE_111_A.CEL ATGE_111_A.EXP ATGE_111_A.CHP A-AFFY-2 AtGE_111_A AtGE_111_A organism_part Bay-0 Bay-0 CS954 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_111_B.CEL ATGE_111_B.EXP ATGE_111_B.CHP A-AFFY-2 AtGE_111_B AtGE_111_B organism_part Bay-0 Bay-0 CS954 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_111_C.CEL ATGE_111_C.EXP ATGE_111_C.CHP A-AFFY-2 AtGE_111_C AtGE_111_C organism_part Bay-0 Bay-0 CS954 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_112_A.CEL ATGE_112_A.EXP ATGE_112_A.CHP A-AFFY-2 AtGE_112_A AtGE_112_A organism_part C24 C24 CS906 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_112_C.CEL ATGE_112_C.EXP ATGE_112_C.CHP A-AFFY-2 AtGE_112_C AtGE_112_C organism_part C24 C24 CS906 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_112_D.CEL ATGE_112_D.EXP ATGE_112_D.CHP A-AFFY-2 AtGE_112_D AtGE_112_D organism_part C24 C24 CS906 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_113_A.CEL ATGE_113_A.EXP ATGE_113_A.CHP A-AFFY-2 AtGE_113_A AtGE_113_A organism_part Col-0 Col-0 N1093 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_113_C.CEL ATGE_113_C.EXP ATGE_113_C.CHP A-AFFY-2 AtGE_113_C AtGE_113_C organism_part Col-0 Col-0 N1093 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_113_D.CEL ATGE_113_D.EXP ATGE_113_D.CHP A-AFFY-2 AtGE_113_D AtGE_113_D organism_part Col-0 Col-0 N1093 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 B P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_114_A.CEL ATGE_114_A.EXP ATGE_114_A.CHP A-AFFY-2 AtGE_114_A AtGE_114_A organism_part Cvi Cvi CS8580 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_114_B.CEL ATGE_114_B.EXP ATGE_114_B.CHP A-AFFY-2 AtGE_114_B AtGE_114_B organism_part Cvi Cvi CS8580 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_114_C.CEL ATGE_114_C.EXP ATGE_114_C.CHP A-AFFY-2 AtGE_114_C AtGE_114_C organism_part Cvi Cvi CS8580 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_115_A.CEL ATGE_115_A.EXP ATGE_115_A.CHP A-AFFY-2 AtGE_115_A AtGE_115_A organism_part Est Est CS6173 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_115_B.CEL ATGE_115_B.EXP ATGE_115_B.CHP A-AFFY-2 AtGE_115_B AtGE_115_B organism_part Est Est CS6173 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_115_D.CEL ATGE_115_D.EXP ATGE_115_D.CHP A-AFFY-2 AtGE_115_D AtGE_115_D organism_part Est Est CS6173 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 B P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_116_A.CEL ATGE_116_A.EXP ATGE_116_A.CHP A-AFFY-2 AtGE_116_A AtGE_116_A organism_part Kin-0 Kin-0 CS6755 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_116_B.CEL ATGE_116_B.EXP ATGE_116_B.CHP A-AFFY-2 AtGE_116_B AtGE_116_B organism_part Kin-0 Kin-0 CS6755 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_116_C.CEL ATGE_116_C.EXP ATGE_116_C.CHP A-AFFY-2 AtGE_116_C AtGE_116_C organism_part Kin-0 Kin-0 CS6755 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_117_B.CEL ATGE_117_B.EXP ATGE_117_B.CHP A-AFFY-2 AtGE_117_B AtGE_117_B organism_part Ler Ler CS8581 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_117_C.CEL ATGE_117_C.EXP ATGE_117_C.CHP A-AFFY-2 AtGE_117_C AtGE_117_C organism_part Ler Ler CS8581 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_117_D.CEL ATGE_117_D.EXP ATGE_117_D.CHP A-AFFY-2 AtGE_117_D AtGE_117_D organism_part Ler Ler CS8581 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 B P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_118_A.CEL ATGE_118_A.EXP ATGE_118_A.CHP A-AFFY-2 AtGE_118_A AtGE_118_A organism_part Nd-1 Nd-1 CS1636 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_118_B.CEL ATGE_118_B.EXP ATGE_118_B.CHP A-AFFY-2 AtGE_118_B AtGE_118_B organism_part Nd-1 Nd-1 CS1636 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_118_C.CEL ATGE_118_C.EXP ATGE_118_C.CHP A-AFFY-2 AtGE_118_C AtGE_118_C organism_part Nd-1 Nd-1 CS1636 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_119_A.CEL ATGE_119_A.EXP ATGE_119_A.CHP A-AFFY-2 AtGE_119_A AtGE_119_A organism_part Shahdara Shahdara CS929 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_119_C.CEL ATGE_119_C.EXP ATGE_119_C.CHP A-AFFY-2 AtGE_119_C AtGE_119_C organism_part Shahdara Shahdara CS929 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_119_D.CEL ATGE_119_D.EXP ATGE_119_D.CHP A-AFFY-2 AtGE_119_D AtGE_119_D organism_part Shahdara Shahdara CS929 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_120_A.CEL ATGE_120_A.EXP ATGE_120_A.CHP A-AFFY-2 AtGE_120_A AtGE_120_A organism_part Van-0 Van-0 CS6884 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_120_B.CEL ATGE_120_B.EXP ATGE_120_B.CHP A-AFFY-2 AtGE_120_B AtGE_120_B organism_part Van-0 Van-0 CS6884 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_120_C.CEL ATGE_120_C.EXP ATGE_120_C.CHP A-AFFY-2 AtGE_120_C AtGE_120_C organism_part Van-0 Van-0 CS6884 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 A P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_121_A.CEL ATGE_121_A.EXP ATGE_121_A.CHP A-AFFY-2 AtGE_121_A AtGE_121_A organism_part Ak-1 Ak-1 N939 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_124_A.CEL ATGE_124_A.EXP ATGE_124_A.CHP A-AFFY-2 AtGE_124_A AtGE_124_A organism_part Bla-5 Bla-5 N6620 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_125_A.CEL ATGE_125_A.EXP ATGE_125_A.CHP A-AFFY-2 AtGE_125_A AtGE_125_A organism_part Can-0 Can-0 N1065 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_126_A.CEL ATGE_126_A.EXP ATGE_126_A.CHP A-AFFY-2 AtGE_126_A AtGE_126_A organism_part Cen-0 Cen-0 N1067 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_127_A.CEL ATGE_127_A.EXP ATGE_127_A.CHP A-AFFY-2 AtGE_127_A AtGE_127_A organism_part CIBC10 CIBC10 N22229 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_128_A.CEL ATGE_128_A.EXP ATGE_128_A.CHP A-AFFY-2 AtGE_128_A AtGE_128_A organism_part Dra-1 Dra-1 N1119 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_129_A.CEL ATGE_129_A.EXP ATGE_129_A.CHP A-AFFY-2 AtGE_129_A AtGE_129_A organism_part Enkheim-T Enkheim-T N6176 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_130_A.CEL ATGE_130_A.EXP ATGE_130_A.CHP A-AFFY-2 AtGE_130_A AtGE_130_A organism_part Er-0 Er-0 N1143 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_131_A.CEL ATGE_131_A.EXP ATGE_131_A.CHP A-AFFY-2 AtGE_131_A AtGE_131_A organism_part Fr-2 Fr-2 N1169 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_132_A.CEL ATGE_132_A.EXP ATGE_132_A.CHP A-AFFY-2 AtGE_132_A AtGE_132_A organism_part GOT1 GOT1 N22277 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_133_A.CEL ATGE_133_A.EXP ATGE_133_A.CHP A-AFFY-2 AtGE_133_A AtGE_133_A organism_part HR-5 HR-5 N22205 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_134_A.CEL ATGE_134_A.EXP ATGE_134_A.CHP A-AFFY-2 AtGE_134_A AtGE_134_A organism_part Is-0 Is-0 N1241 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_136_A.CEL ATGE_136_A.EXP ATGE_136_A.CHP A-AFFY-2 AtGE_136_A AtGE_136_A organism_part Li-2:1 Li-2:1 N1315 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_137_A.CEL ATGE_137_A.EXP ATGE_137_A.CHP A-AFFY-2 AtGE_137_A AtGE_137_A organism_part M7323S M7323S N6184 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_138_A.CEL ATGE_138_A.EXP ATGE_138_A.CHP A-AFFY-2 AtGE_138_A AtGE_138_A organism_part Ms-0 Ms-0 N1377 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_139_A.CEL ATGE_139_A.EXP ATGE_139_A.CHP A-AFFY-2 AtGE_139_A AtGE_139_A organism_part Nc-1 Nc-1 N1389 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_140_A.CEL ATGE_140_A.EXP ATGE_140_A.CHP A-AFFY-2 AtGE_140_A AtGE_140_A organism_part NFE1 NFE1 N22163 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_141_A.CEL ATGE_141_A.EXP ATGE_141_A.CHP A-AFFY-2 AtGE_141_A AtGE_141_A organism_part Nok-1 Nok-1 N1401 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_142_A.CEL ATGE_142_A.EXP ATGE_142_A.CHP A-AFFY-2 AtGE_142_A AtGE_142_A organism_part Nw-1 Nw-1 N1411 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_144_A.CEL ATGE_144_A.EXP ATGE_144_A.CHP A-AFFY-2 AtGE_144_A AtGE_144_A organism_part Old-2 Old-2 N1429 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_145_A.CEL ATGE_145_A.EXP ATGE_145_A.CHP A-AFFY-2 AtGE_145_A AtGE_145_A organism_part Ove-0 Ove-0 N1435 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_146_A.CEL ATGE_146_A.EXP ATGE_146_A.CHP A-AFFY-2 AtGE_146_A AtGE_146_A organism_part Se-0 Se-0 N1503 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_147_A.CEL ATGE_147_A.EXP ATGE_147_A.CHP A-AFFY-2 AtGE_147_A AtGE_147_A organism_part Sf-2e Sf-2e N1675 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_148_A.CEL ATGE_148_A.EXP ATGE_148_A.CHP A-AFFY-2 AtGE_148_A AtGE_148_A organism_part Ta-0 Ta-0 N1549 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
+ATGE_149_A.CEL ATGE_149_A.EXP ATGE_149_A.CHP A-AFFY-2 AtGE_149_A AtGE_149_A organism_part Uk-3 Uk-3 N1577 Arabidopsis thaliana "seedling, aerial parts" P-TABM-Janne5 P-TABM-Janne2 P-TABM-Janne6 P-TABM-Janne4 biotin /net/nfs6/vol4/ma-db/shoja/Affy_Data/ATH1-121501.CDF
diff --git a/examples/real/E-TABM-22.png b/examples/real/E-TABM-22.png
new file mode 100644
index 0000000..5e3837f
Binary files /dev/null and b/examples/real/E-TABM-22.png differ
diff --git a/examples/real/E-TABM-22.txt b/examples/real/E-TABM-22.txt
new file mode 100644
index 0000000..11e925f
--- /dev/null
+++ b/examples/real/E-TABM-22.txt
@@ -0,0 +1,274 @@
+"Experiment section"
+"domain" "ebi.ac.uk"
+"accession" "E-TABM-22"
+"experiment_design_type" "cell_type_comparison_design, stimulus_or_stress_design, compound_treatment_design, disease_state_design"
+"name" "Lung cancers"
+"description" "Distinct miRNA expression significance in human lung cancers and lung cancer cell lines."
+"release_date" "2006-07-01"
+"submission_date" "2005-06-20"
+"submitter" "Nozomu Yanaihara"
+"organization" "Laboratory of Human Carcinogenesis, NCI, NIH"
+
+"Protocol section"
+"accession" "type" "text" "name" "parameters"
+"P-MEXP-9141" "grow" "Human NSCLC cell lines were cultured with 10% FCS containing RPMI 1640 at 37C with 5% CO2." "lung cancer cell line" "medium"
+"P-MEXP-9142" "compound_based_treatment" "For the first 48h, cells were incubated with medium containing 1.0 uM 5-aza-dC, and then for another 24h with the addition of 1.0 uM TSA." "Aza and TSA" "compound"
+"P-MEXP-9143" "nucleic_acid_extraction" "Total RNA was isolated with TRIzol according to the manufacturer's protocol." "RNA"
+"P-MEXP-9144" "labeling" "Five micrograms of total RNA were separately added to reaction mix in a final volume of 12 ul, containing 1 ug of [3'-(N)8-(A)12-biotin-(A)12-biotin-5_] oligonucleotide primer. The mixture was incubated for 10 min at 70C and chilled on ice. With the mixture remaining on ice, 4 ul of 5x first-strand buffer, 2 ul of 0.1 M DTT, 1 ul of 10 mM dNTP mix, and 1 ul of SuperScript II RNaseH reverse transcriptase (200 units/ul) was added to a final volume of 20 ul, and th [...]
+"P-MEXP-9145" "hybridization" "The microarrays were hybridized in 6xSSPE (0.9M sodium chloride/60mM sodium phosphate/8 mM EDTA, pH 7.4)/30% formamide at 25C for 18 h, washed in 0.75x TNT (Tris HCl/sodium chloride/Tween 20) at 37C for 40 min, and processed by using direct detection of the biotin-containing transcripts by Streptavidin-Alexa647 conjugate." "miRNA array hybridization" "RNA hybridized(ug); Hyb temperature(degree_C)"
+"P-MEXP-9146" "image_acquisition" "Processed slides were scanned by using a PerkinElmer ScanArray XL5K Scanner with the laser set to 635 nm, at power 80 and PMT 70 settings, and a scan resolution of 10 microm.Images were quantified by QUANTARRAY software (PerkinElmer). Signal intensities for each spot were calculated by subtracting local background (based on the median intensity of the area surrounding each spot) from total intensities." "miRNA array scanning"
+"P-MEXP-9147" "bioassay_data_transformation" "An average value of the three spot replicates of each miRNA were normalized and analyzed in BRB-ArrayTools version 3.2.3. After excluding negative values that is hybridization intensity below background, normalization was performed by using a per-chip on median normalization method and normalization to median array as reference." "miRNA array normalization"
+
+"Hybridization section"
+"File[raw]" "Array[accession]" "BioSource" "Protocol[grow]" "Protocol[treatment]" "Parameter[compound]" "Parameter[medium]" "Protocol[extraction]" "Protocol[labeling]" "Parameter[RNA labelled]" "Dye" "Protocol[hybridization]" "Parameter[RNA hybridized]" "Parameter[Hyb temperature]" "Protocol[scanning]" "xProtocol[normalization]" "BioSourceMaterial" "BioMaterialCharacteristics[BioSourceType]" "BioMaterialCharacteristics[Organism]" "BioMaterialCharacteristics[DiseaseState]" "BioMaterialCha [...]
+"R1791_A549.txt" "A-MEXP-86" "A549" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1790_A427.txt" "A-MEXP-86" "A-427" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A-427" "Non-Small Cell Lung Carcinoma" "A-427" "cell"
+"R1792_A2182.txt" "A-MEXP-86" "A2182" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A2182" "Non-Small Cell Lung Carcinoma" "A2182" "cell"
+"R1793_Calu1.txt" "A-MEXP-86" "Calu-1" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "Calu-1" "Non-Small Cell Lung Carcinoma" "Calu-1" "cell"
+"R1794_Calu6.txt" "A-MEXP-86" "Calu-6" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "Calu-6" "Non-Small Cell Lung Carcinoma" "Calu-6" "cell"
+"R1795_DMS92.txt" "A-MEXP-86" "DMS92" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Small Cell Lung Carcinoma" "DMS92" "Small Cell Lung Carcinoma" "DMS92" "cell"
+"R1796_NCIH82.txt" "A-MEXP-86" "NCI-H82" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Small Cell Lung Carcinoma" "NCI-H82" "Small Cell Lung Carcinoma" "NCI-H82" "cell"
+"R1797_NCIH146.txt" "A-MEXP-86" "NCI-H146" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Small Cell Lung Carcinoma" "NCI-H146" "Small Cell Lung Carcinoma" "NCI-H146" "cell"
+"R1798_NCIH157.txt" "A-MEXP-86" "NCI-H157" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1805_NCIH446.txt" "A-MEXP-86" "NCI-H446" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Small Cell Lung Carcinoma" "NCI-H446" "Small Cell Lung Carcinoma" "NCI-H446" "cell"
+"R1807_NCIH1155.txt" "A-MEXP-86" "NCI-H1155" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H1155" "Non-Small Cell Lung Carcinoma" "NCI-H1155" "cell"
+"R1808_NCIN417.txt" "A-MEXP-86" "NCI-N417" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Small Cell Lung Carcinoma" "NCI-N417" "Small Cell Lung Carcinoma" "NCI-N417" "cell"
+"R1806_NCIH596.txt" "A-MEXP-86" "NCI-H596" "P-MEXP-9141" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H596" "Non-Small Cell Lung Carcinoma" "NCI-H596" "cell"
+"R1811_A5491-1.txt" "A-MEXP-86" "A549_ex1_nontreat" "P-MEXP-9141" "P-MEXP-9142" "none" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "null" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1812_A5491-2.txt" "A-MEXP-86" "A549_ex1_aza" "P-MEXP-9141" "P-MEXP-9142" "Aza" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "Aza" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1813_A5491-3.txt" "A-MEXP-86" "A549_ex1_tsa" "P-MEXP-9141" "P-MEXP-9142" "TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "TSA" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1814_A5491-4.txt" "A-MEXP-86" "A549_ex1_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "Aza" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1814_A5491-4.txt" "A-MEXP-86" "A549_ex1_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "TSA" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1815_A5492-1.txt" "A-MEXP-86" "A549_ex2_nontreat" "P-MEXP-9141" "P-MEXP-9142" "none" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "null" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1816_A5492-2.txt" "A-MEXP-86" "A549_ex2_aza" "P-MEXP-9141" "P-MEXP-9142" "Aza" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "Aza" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1817_A5492-3.txt" "A-MEXP-86" "A549_ex2_tsa" "P-MEXP-9141" "P-MEXP-9142" "TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "TSA" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1818_A5492-4.txt" "A-MEXP-86" "A549_ex2_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "Aza" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1818_A5492-4.txt" "A-MEXP-86" "A549_ex2_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "TSA" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1819_A5493-1.txt" "A-MEXP-86" "A549_ex3_nontreat" "P-MEXP-9141" "P-MEXP-9142" "none" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "null" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1820_A5493-2.txt" "A-MEXP-86" "A549_ex3_aza" "P-MEXP-9141" "P-MEXP-9142" "Aza" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "Aza" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1821_A5493-3.txt" "A-MEXP-86" "A549_ex3_tsa" "P-MEXP-9141" "P-MEXP-9142" "TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "TSA" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1822_A5493-4.txt" "A-MEXP-86" "A549_ex3_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "Aza" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1822_A5493-4.txt" "A-MEXP-86" "A549_ex3_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "A549" "TSA" "Non-Small Cell Lung Carcinoma" "A549" "cell"
+"R1835_NCIH1571-1.txt" "A-MEXP-86" "NCI-H157_ex1_nontreat" "P-MEXP-9141" "P-MEXP-9142" "none" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "null" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1836_NCIH1571-2.txt" "A-MEXP-86" "NCI-H157_ex1_aza" "P-MEXP-9141" "P-MEXP-9142" "Aza" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "Aza" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1837_NCIH1571-3.txt" "A-MEXP-86" "NCI-H157_ex1_tsa" "P-MEXP-9141" "P-MEXP-9142" "TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "TSA" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1838_NCIH1571-4.txt" "A-MEXP-86" "NCI-H157_ex1_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "Aza" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1838_NCIH1571-4.txt" "A-MEXP-86" "NCI-H157_ex1_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "TSA" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1839_NCIH1572-1.txt" "A-MEXP-86" "NCI-H157_ex2_nontreat" "P-MEXP-9141" "P-MEXP-9142" "none" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "null" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1840_NCIH1572-2.txt" "A-MEXP-86" "NCI-H157_ex2_aza" "P-MEXP-9141" "P-MEXP-9142" "Aza" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "Aza" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1841_NCIH1572-3.txt" "A-MEXP-86" "NCI-H157_ex2_tsa" "P-MEXP-9141" "P-MEXP-9142" "TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "TSA" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1842_NCIH1572-4.txt" "A-MEXP-86" "NCI-H157_ex2_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "Aza" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1842_NCIH1572-4.txt" "A-MEXP-86" "NCI-H157_ex2_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "TSA" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1843_NCIH1573-1.txt" "A-MEXP-86" "NCI-H157_ex3_nontreat" "P-MEXP-9141" "P-MEXP-9142" "none" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "null" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1844_NCIH1573-2.txt" "A-MEXP-86" "NCI-H157_ex3_aza" "P-MEXP-9141" "P-MEXP-9142" "Aza" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "Aza" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1845_NCIH1573-3.txt" "A-MEXP-86" "NCI-H157_ex3_tsa" "P-MEXP-9141" "P-MEXP-9142" "TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "TSA" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1846_NCIH1573-4.txt" "A-MEXP-86" "NCI-H157_ex3_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "Aza" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1846_NCIH1573-4.txt" "A-MEXP-86" "NCI-H157_ex3_both" "P-MEXP-9141" "P-MEXP-9142" "Aza+TSA" "RPMI 1640" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "cell" "fresh_sample" "Homo sapiens" "Non-Small Cell Lung Carcinoma" "NCI-H157" "TSA" "Non-Small Cell Lung Carcinoma" "NCI-H157" "cell"
+"R1595_12902T.txt" "A-MEXP-86" "12902T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 74 "years" "male" "Stage II" "12902N" "Adenocarcinoma" "Stage II" "12902N"
+"R1594_12902N.txt" "A-MEXP-86" "12902N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 74 "years" "male" "12902N" "normal" "12902N"
+"R1591_12822T.txt" "A-MEXP-86" "12822T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 66 "years" "male" "Stage I" "12822N" "Adenocarcinoma" "Stage I" "12822N"
+"R1590_12822N.txt" "A-MEXP-86" "12822N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 66 "years" "male" "12822N" "normal" "12822N"
+"R1583_12821T.txt" "A-MEXP-86" "12821T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 68 "years" "male" "Stage II" "12821N" "Adenocarcinoma" "Stage II" "12821N"
+"R1582_12821N.txt" "A-MEXP-86" "12821N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 68 "years" "male" "12821N" "normal" "12821N"
+"R1581_12745T.txt" "A-MEXP-86" "12745T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 71 "years" "male" "Stage I" "12745N" "Adenocarcinoma" "Stage I" "12745N"
+"R1580_12745N.txt" "A-MEXP-86" "12745N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 71 "years" "male" "12745N" "normal" "12745N"
+"R1579_12681T.txt" "A-MEXP-86" "12681T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 75 "years" "male" "Stage I" "12681N" "Adenocarcinoma" "Stage I" "12681N"
+"R1578_12681N.txt" "A-MEXP-86" "12681N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 75 "years" "male" "12681N" "normal" "12681N"
+"R1569_12606T.txt" "A-MEXP-86" "12606T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 66 "years" "male" "Stage I" "12606N" "Adenocarcinoma" "Stage I" "12606N"
+"R1568_12606N.txt" "A-MEXP-86" "12606N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 66 "years" "male" "12606N" "normal" "12606N"
+"R1567_12577T.txt" "A-MEXP-86" "12577T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 71 "years" "male" "Stage I" "12577N" "Adenocarcinoma" "Stage I" "12577N"
+"R1566_12577N.txt" "A-MEXP-86" "12577N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 71 "years" "male" "12577N" "normal" "12577N"
+"R1563_12520T.txt" "A-MEXP-86" "12520T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 49 "years" "male" "Stage I" "12520N" "Adenocarcinoma" "Stage I" "12520N"
+"R1562_12520N.txt" "A-MEXP-86" "12520N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 49 "years" "male" "12520N" "normal" "12520N"
+"R1559_12503T.txt" "A-MEXP-86" "12503T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 75 "years" "male" "Stage I" "12503N" "Adenocarcinoma" "Stage I" "12503N"
+"R1558_12503N.txt" "A-MEXP-86" "12503N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 75 "years" "male" "12503N" "normal" "12503N"
+"R1547_12490T.txt" "A-MEXP-86" "12490T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 57 "years" "male" "Stage III or IV" "12490N" "Adenocarcinoma" "Stage III or IV" "12490N"
+"R1546_12490N.txt" "A-MEXP-86" "12490N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 57 "years" "male" "12490N" "normal" "12490N"
+"R1539_12394T.txt" "A-MEXP-86" "12394T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 51 "years" "female" "Stage I" "12394N" "Adenocarcinoma" "Stage I" "12394N"
+"R1538_12394N.txt" "A-MEXP-86" "12394N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 51 "years" "female" "12394N" "normal" "12394N"
+"R1537_12374T.txt" "A-MEXP-86" "12374T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 68 "years" "male" "Stage I" "12374N" "Adenocarcinoma" "Stage I" "12374N"
+"R1536_12374N.txt" "A-MEXP-86" "12374N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 68 "years" "male" "12374N" "normal" "12374N"
+"R1527_12251T.txt" "A-MEXP-86" "12251T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 63 "years" "female" "Stage I" "12251N" "Adenocarcinoma" "Stage I" "12251N"
+"R1526_12251N.txt" "A-MEXP-86" "12251N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 63 "years" "female" "12251N" "normal" "12251N"
+"R1525_12189T.txt" "A-MEXP-86" "12189T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 61 "years" "male" "Stage I" "12189N" "Adenocarcinoma" "Stage I" "12189N"
+"R1524_12189N.txt" "A-MEXP-86" "12189N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 61 "years" "male" "12189N" "normal" "12189N"
+"R1521_12178T.txt" "A-MEXP-86" "12178T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 63 "years" "male" "Stage II" "12178N" "Adenocarcinoma" "Stage II" "12178N"
+"R1520_12178N.txt" "A-MEXP-86" "12178N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 63 "years" "male" "12178N" "normal" "12178N"
+"R1519_12170T.txt" "A-MEXP-86" "12170T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 50 "years" "female" "Stage I" "12170N" "Adenocarcinoma" "Stage I" "12170N"
+"R1518_12170N.txt" "A-MEXP-86" "12170N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 50 "years" "female" "12170N" "normal" "12170N"
+"R1517_12167T.txt" "A-MEXP-86" "12167T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 71 "years" "male" "Stage I" "12167N" "Adenocarcinoma" "Stage I" "12167N"
+"R1516_12167N.txt" "A-MEXP-86" "12167N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 71 "years" "male" "12167N" "normal" "12167N"
+"R1778_12165T.txt" "A-MEXP-86" "12165T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 38 "years" "female" "Stage III or IV" "12165N" "Adenocarcinoma" "Stage III or IV" "12165N"
+"R1777_12165N.txt" "A-MEXP-86" "12165N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 38 "years" "female" "12165N" "normal" "12165N"
+"R1513_12118T.txt" "A-MEXP-86" "12118T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 57 "years" "male" "Stage I" "12118N" "Adenocarcinoma" "Stage I" "12118N"
+"R1512_12118N.txt" "A-MEXP-86" "12118N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 57 "years" "male" "12118N" "normal" "12118N"
+"R1507_12084T.txt" "A-MEXP-86" "12084T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 75 "years" "male" "Stage I" "12084N" "Adenocarcinoma" "Stage I" "12084N"
+"R1500_12084N.txt" "A-MEXP-86" "12084N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 75 "years" "male" "12084N" "normal" "12084N"
+"R1099_12012T.txt" "A-MEXP-86" "12012T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 61 "years" "male" "Stage I" "12012N" "Adenocarcinoma" "Stage I" "12012N"
+"R1098_12012N.txt" "A-MEXP-86" "12012N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 61 "years" "male" "12012N" "normal" "12012N"
+"R1091_11922T.txt" "A-MEXP-86" "11922T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 80 "years" "male" "Stage III or IV" "11922N" "Adenocarcinoma" "Stage III or IV" "11922N"
+"R1090_11922N.txt" "A-MEXP-86" "11922N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 80 "years" "male" "11922N" "normal" "11922N"
+"R1776_11908T.txt" "A-MEXP-86" "11908T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 57 "years" "female" "Stage I" "11908N" "Adenocarcinoma" "Stage I" "11908N"
+"R1775_11908N.txt" "A-MEXP-86" "11908N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 57 "years" "female" "11908N" "normal" "11908N"
+"R1089_11898T.txt" "A-MEXP-86" "11898T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 84 "years" "female" "Stage III or IV" "11898N" "Adenocarcinoma" "Stage III or IV" "11898N"
+"R1088_11898N.txt" "A-MEXP-86" "11898N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 84 "years" "female" "11898N" "normal" "11898N"
+"R1087_11891T.txt" "A-MEXP-86" "11891T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 69 "years" "female" "Stage III or IV" "11891N" "Adenocarcinoma" "Stage III or IV" "11891N"
+"R1085_11891N.txt" "A-MEXP-86" "11891N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 69 "years" "female" "11891N" "normal" "11891N"
+"R1084_11885T.txt" "A-MEXP-86" "11885T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 65 "years" "male" "Stage I" "11885N" "Adenocarcinoma" "Stage I" "11885N"
+"R1083_11885N.txt" "A-MEXP-86" "11885N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 65 "years" "male" "11885N" "normal" "11885N"
+"R896_11823T.txt" "A-MEXP-86" "11823T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 67 "years" "male" "Stage I" "11823N" "Adenocarcinoma" "Stage I" "11823N"
+"R895_11823N.txt" "A-MEXP-86" "11823N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 67 "years" "male" "11823N" "normal" "11823N"
+"R894_11812T.txt" "A-MEXP-86" "11812T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 67 "years" "male" "Stage III or IV" "11812N" "Adenocarcinoma" "Stage III or IV" "11812N"
+"R893_11812N.txt" "A-MEXP-86" "11812N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 67 "years" "male" "11812N" "normal" "11812N"
+"R890_11807T.txt" "A-MEXP-86" "11807T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 73 "years" "female" "Stage I" "11807N" "Adenocarcinoma" "Stage I" "11807N"
+"R889_11807N.txt" "A-MEXP-86" "11807N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 73 "years" "female" "11807N" "normal" "11807N"
+"R888_11777T.txt" "A-MEXP-86" "11777T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 72 "years" "female" "Stage I" "11777N" "Adenocarcinoma" "Stage I" "11777N"
+"R887_11777N.txt" "A-MEXP-86" "11777N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 72 "years" "female" "11777N" "normal" "11777N"
+"R882_11671T.txt" "A-MEXP-86" "11671T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 53 "years" "male" "Stage I" "11671N" "Adenocarcinoma" "Stage I" "11671N"
+"R881_11671N.txt" "A-MEXP-86" "11671N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 53 "years" "male" "11671N" "normal" "11671N"
+"R880_11669T.txt" "A-MEXP-86" "11669T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 67 "years" "female" "Stage I" "11669N" "Adenocarcinoma" "Stage I" "11669N"
+"R879_11669N.txt" "A-MEXP-86" "11669N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 67 "years" "female" "11669N" "normal" "11669N"
+"R876_11616T.txt" "A-MEXP-86" "11616T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 65 "years" "male" "Stage III or IV" "11616N" "Adenocarcinoma" "Stage III or IV" "11616N"
+"R875_11616N.txt" "A-MEXP-86" "11616N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 65 "years" "male" "11616N" "normal" "11616N"
+"R1074_11547T.txt" "A-MEXP-86" "11547T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 74 "years" "female" "Stage III or IV" "11547N" "Adenocarcinoma" "Stage III or IV" "11547N"
+"R1073_11547N.txt" "A-MEXP-86" "11547N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 74 "years" "female" "11547N" "normal" "11547N"
+"R1072_11541T.txt" "A-MEXP-86" "11541T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 70 "years" "male" "Stage I" "11541N" "Adenocarcinoma" "Stage I" "11541N"
+"R1071_11541N.txt" "A-MEXP-86" "11541N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 70 "years" "male" "11541N" "normal" "11541N"
+"R1070_11502T.txt" "A-MEXP-86" "11502T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 61 "years" "female" "Stage III or IV" "11502N" "Adenocarcinoma" "Stage III or IV" "11502N"
+"R1069_11502N.txt" "A-MEXP-86" "11502N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 61 "years" "female" "11502N" "normal" "11502N"
+"R1068_11447T.txt" "A-MEXP-86" "11447T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 52 "years" "female" "Stage I" "11447N" "Adenocarcinoma" "Stage I" "11447N"
+"R1067_11447N.txt" "A-MEXP-86" "11447N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 52 "years" "female" "11447N" "normal" "11447N"
+"R900_11404T.txt" "A-MEXP-86" "11404T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 38 "years" "female" "Stage I" "11404N" "Adenocarcinoma" "Stage I" "11404N"
+"R899_11404N.txt" "A-MEXP-86" "11404N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 38 "years" "female" "11404N" "normal" "11404N"
+"R1050_11343T.txt" "A-MEXP-86" "11343T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 68 "years" "male" "Stage II" "11343N" "Adenocarcinoma" "Stage II" "11343N"
+"R1049_11343N.txt" "A-MEXP-86" "11343N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 68 "years" "male" "11343N" "normal" "11343N"
+"R1048_11231T.txt" "A-MEXP-86" "11231T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 56 "years" "male" "Stage III or IV" "11231N" "Adenocarcinoma" "Stage III or IV" "11231N"
+"R1047_11231N.txt" "A-MEXP-86" "11231N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 56 "years" "male" "11231N" "normal" "11231N"
+"R1042_11191T.txt" "A-MEXP-86" "11191T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 47 "years" "male" "Stage I" "11191N" "Adenocarcinoma" "Stage I" "11191N"
+"R1041_11191N.txt" "A-MEXP-86" "11191N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 47 "years" "male" "11191N" "normal" "11191N"
+"R1040_11184T.txt" "A-MEXP-86" "11184T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 53 "years" "female" "Stage I" "11184N" "Adenocarcinoma" "Stage I" "11184N"
+"R1039_11184N.txt" "A-MEXP-86" "11184N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 53 "years" "female" "11184N" "normal" "11184N"
+"R1036_11139T.txt" "A-MEXP-86" "11139T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 49 "years" "male" "Stage I" "11139N" "Adenocarcinoma" "Stage I" "11139N"
+"R1035_11139N.txt" "A-MEXP-86" "11139N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 49 "years" "male" "11139N" "normal" "11139N"
+"R1026_11019T.txt" "A-MEXP-86" "11019T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 59 "years" "male" "Stage III or IV" "11019N" "Adenocarcinoma" "Stage III or IV" "11019N"
+"R1025_11019N.txt" "A-MEXP-86" "11019N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 59 "years" "male" "11019N" "normal" "11019N"
+"R1020_11005T.txt" "A-MEXP-86" "11005T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 76 "years" "female" "Stage I" "11005N" "Adenocarcinoma" "Stage I" "11005N"
+"R1019_11005N.txt" "A-MEXP-86" "11005N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 76 "years" "female" "11005N" "normal" "11005N"
+"R991_10622T.txt" "A-MEXP-86" "10622T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 57 "years" "male" "Stage I" "10622N" "Adenocarcinoma" "Stage I" "10622N"
+"R990_10622N.txt" "A-MEXP-86" "10622N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 57 "years" "male" "10622N" "normal" "10622N"
+"R989_10605T.txt" "A-MEXP-86" "10605T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 62 "years" "male" "Stage I" "10605N" "Adenocarcinoma" "Stage I" "10605N"
+"R988_10605N.txt" "A-MEXP-86" "10605N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 62 "years" "male" "10605N" "normal" "10605N"
+"R983_10455T.txt" "A-MEXP-86" "10455T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 66 "years" "female" "Stage III or IV" "10455N" "Adenocarcinoma" "Stage III or IV" "10455N"
+"R982_10455N.txt" "A-MEXP-86" "10455N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 66 "years" "female" "10455N" "normal" "10455N"
+"R977_10419T.txt" "A-MEXP-86" "10419T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 72 "years" "female" "Stage I" "10419N" "Adenocarcinoma" "Stage I" "10419N"
+"R976_10419N.txt" "A-MEXP-86" "10419N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 72 "years" "female" "10419N" "normal" "10419N"
+"R868_10213T.txt" "A-MEXP-86" "10213T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 72 "years" "male" "Stage III or IV" "10213N" "Adenocarcinoma" "Stage III or IV" "10213N"
+"R867_10213N.txt" "A-MEXP-86" "10213N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 72 "years" "male" "10213N" "normal" "10213N"
+"R866_10210T.txt" "A-MEXP-86" "10210T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 79 "years" "male" "Stage I" "10210N" "Adenocarcinoma" "Stage I" "10210N"
+"R865_10210N.txt" "A-MEXP-86" "10210N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 79 "years" "male" "10210N" "normal" "10210N"
+"R1749_10018T.txt" "A-MEXP-86" "10018T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 63 "years" "female" "Stage III or IV" "10018N" "Adenocarcinoma" "Stage III or IV" "10018N"
+"R1748_10018N.txt" "A-MEXP-86" "10018N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 63 "years" "female" "10018N" "normal" "10018N"
+"R1747_1822T.txt" "A-MEXP-86" "1822T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 61 "years" "male" "Stage III or IV" "1822N" "Adenocarcinoma" "Stage III or IV" "1822N"
+"R1746_1822N.txt" "A-MEXP-86" "1822N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 61 "years" "male" "1822N" "normal" "1822N"
+"R1743_1818T.txt" "A-MEXP-86" "1818T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 55 "years" "male" "Stage I" "1818N" "Adenocarcinoma" "Stage I" "1818N"
+"R1742_1818N.txt" "A-MEXP-86" "1818N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 55 "years" "male" "1818N" "normal" "1818N"
+"R1731_1729T.txt" "A-MEXP-86" "1729T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 73 "years" "female" "Stage I" "1729N" "Adenocarcinoma" "Stage I" "1729N"
+"R1730_1729N.txt" "A-MEXP-86" "1729N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 73 "years" "female" "1729N" "normal" "1729N"
+"R1729_1718T.txt" "A-MEXP-86" "1718T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 58 "years" "female" "Stage II" "1718N" "Adenocarcinoma" "Stage II" "1718N"
+"R1728_1718N.txt" "A-MEXP-86" "1718N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 58 "years" "female" "1718N" "normal" "1718N"
+"R1723_1610T.txt" "A-MEXP-86" "1610T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 69 "years" "female" "Stage II" "1610N" "Adenocarcinoma" "Stage II" "1610N"
+"R1722_1610N.txt" "A-MEXP-86" "1610N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 69 "years" "female" "1610N" "normal" "1610N"
+"R1721_1604T.txt" "A-MEXP-86" "1604T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 59 "years" "female" "Stage I" "1604N" "Adenocarcinoma" "Stage I" "1604N"
+"R1720_1604N.txt" "A-MEXP-86" "1604N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 59 "years" "female" "1604N" "normal" "1604N"
+"R1719_1570T.txt" "A-MEXP-86" "1570T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 70 "years" "male" "Stage III or IV" "1570N" "Adenocarcinoma" "Stage III or IV" "1570N"
+"R1718_1570N.txt" "A-MEXP-86" "1570N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 70 "years" "male" "1570N" "normal" "1570N"
+"R1715_1545T.txt" "A-MEXP-86" "1545T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 69 "years" "female" "Stage I" "1545N" "Adenocarcinoma" "Stage I" "1545N"
+"R1714_1545N.txt" "A-MEXP-86" "1545N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 69 "years" "female" "1545N" "normal" "1545N"
+"R1711_1532T.txt" "A-MEXP-86" "1532T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 62 "years" "male" "Stage II" "1532N" "Adenocarcinoma" "Stage II" "1532N"
+"R1710_1532N.txt" "A-MEXP-86" "1532N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 62 "years" "male" "1532N" "normal" "1532N"
+"R1707_1498T.txt" "A-MEXP-86" "1498T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 67 "years" "female" "Stage I" "1498N" "Adenocarcinoma" "Stage I" "1498N"
+"R1600_1498N.txt" "A-MEXP-86" "1498N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 67 "years" "female" "1498N" "normal" "1498N"
+"R1599_1449T.txt" "A-MEXP-86" "1449T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 79 "years" "female" "Stage II" "1449N" "Adenocarcinoma" "Stage II" "1449N"
+"R1598_1449N.txt" "A-MEXP-86" "1449N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 79 "years" "female" "1449N" "normal" "1449N"
+"R1557_1249T.txt" "A-MEXP-86" "1249T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 54 "years" "male" "Stage I" "1249N" "Adenocarcinoma" "Stage I" "1249N"
+"R1550_1249N.txt" "A-MEXP-86" "1249N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 54 "years" "male" "1249N" "normal" "1249N"
+"R892_1180T.txt" "A-MEXP-86" "1180T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Adenocarcinoma" 76 "years" "male" "Stage I" "1180N" "Adenocarcinoma" "Stage I" "1180N"
+"R891_1180N.txt" "A-MEXP-86" "1180N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 76 "years" "male" "1180N" "normal" "1180N"
+"R1597_12979T.txt" "A-MEXP-86" "12979T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 52 "years" "male" "Stage I" "12979N" "Squamous Cell Carcinoma" "Stage I" "12979N"
+"R1596_12979N.txt" "A-MEXP-86" "12979N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 52 "years" "male" "12979N" "normal" "12979N"
+"R1531_12281T.txt" "A-MEXP-86" "12281T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 59 "years" "female" "Stage I" "12281N" "Squamous Cell Carcinoma" "Stage I" "12281N"
+"R1530_12281N.txt" "A-MEXP-86" "12281N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 59 "years" "female" "12281N" "normal" "12281N"
+"R1523_12187T.txt" "A-MEXP-86" "12187T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 72 "years" "female" "Stage I" "12187N" "Squamous Cell Carcinoma" "Stage I" "12187N"
+"R1522_12187N.txt" "A-MEXP-86" "12187N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 72 "years" "female" "12187N" "normal" "12187N"
+"R1515_12147T.txt" "A-MEXP-86" "12147T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 56 "years" "male" "Stage I" "12147N" "Squamous Cell Carcinoma" "Stage I" "12147N"
+"R1514_12147N.txt" "A-MEXP-86" "12147N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 56 "years" "male" "12147N" "normal" "12147N"
+"R1511_12104T.txt" "A-MEXP-86" "12104T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 55 "years" "female" "Stage II" "12104N" "Squamous Cell Carcinoma" "Stage II" "12104N"
+"R1510_12104N.txt" "A-MEXP-86" "12104N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 55 "years" "female" "12104N" "normal" "12104N"
+"R1499_12027T.txt" "A-MEXP-86" "12027T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 73 "years" "female" "Stage I" "12027N" "Squamous Cell Carcinoma" "Stage I" "12027N"
+"R1498_12027N.txt" "A-MEXP-86" "12027N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 73 "years" "female" "12027N" "normal" "12027N"
+"R1497_12025T.txt" "A-MEXP-86" "12025T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 49 "years" "male" "Stage II" "12025N" "Squamous Cell Carcinoma" "Stage II" "12025N"
+"R1100_12025N.txt" "A-MEXP-86" "12025N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 49 "years" "male" "12025N" "normal" "12025N"
+"R1765_12017T.txt" "A-MEXP-86" "12017T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 67 "years" "female" "Stage I" "12017N" "Squamous Cell Carcinoma" "Stage I" "12017N"
+"R1764_12017N.txt" "A-MEXP-86" "12017N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 67 "years" "female" "12017N" "normal" "12017N"
+"R1496_12003T.txt" "A-MEXP-86" "12003T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 66 "years" "male" "Stage I" "12003N" "Squamous Cell Carcinoma" "Stage I" "12003N"
+"R1495_12003N.txt" "A-MEXP-86" "12003N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 66 "years" "male" "12003N" "normal" "12003N"
+"R1097_11950T.txt" "A-MEXP-86" "11950T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 66 "years" "male" "Stage II" "11950N" "Squamous Cell Carcinoma" "Stage II" "11950N"
+"R1096_11950N.txt" "A-MEXP-86" "11950N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 66 "years" "male" "11950N" "normal" "11950N"
+"R1093_11923T.txt" "A-MEXP-86" "11923T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 72 "years" "female" "Stage I" "11923N" "Squamous Cell Carcinoma" "Stage I" "11923N"
+"R1092_11923N.txt" "A-MEXP-86" "11923N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 72 "years" "female" "11923N" "normal" "11923N"
+"R1763_11764T.txt" "A-MEXP-86" "11764T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 68 "years" "male" "Stage I" "11764N" "Squamous Cell Carcinoma" "Stage I" "11764N"
+"R1762_11764N.txt" "A-MEXP-86" "11764N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 68 "years" "male" "11764N" "normal" "11764N"
+"R886_11759T.txt" "A-MEXP-86" "11759T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 67 "years" "male" "Stage I" "11759N" "Squamous Cell Carcinoma" "Stage I" "11759N"
+"R885_11759N.txt" "A-MEXP-86" "11759N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 67 "years" "male" "11759N" "normal" "11759N"
+"R884_11728T.txt" "A-MEXP-86" "11728T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 53 "years" "male" "Stage II" "11728N" "Squamous Cell Carcinoma" "Stage II" "11728N"
+"R883_11728N.txt" "A-MEXP-86" "11728N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 53 "years" "male" "11728N" "normal" "11728N"
+"R1082_11588T.txt" "A-MEXP-86" "11588T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 69 "years" "female" "Stage I" "11588N" "Squamous Cell Carcinoma" "Stage I" "11588N"
+"R1081_11588N.txt" "A-MEXP-86" "11588N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 69 "years" "female" "11588N" "normal" "11588N"
+"R1080_11574T.txt" "A-MEXP-86" "11574T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 63 "years" "male" "Stage III or IV" "11574N" "Squamous Cell Carcinoma" "Stage III or IV" "11574N"
+"R1079_11574N.txt" "A-MEXP-86" "11574N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 63 "years" "male" "11574N" "normal" "11574N"
+"R1058_11238T.txt" "A-MEXP-86" "11238T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 69 "years" "male" "Stage I" "11238N" "Squamous Cell Carcinoma" "Stage I" "11238N"
+"R1057_11238N.txt" "A-MEXP-86" "11238N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 69 "years" "male" "11238N" "normal" "11238N"
+"R1030_11047T.txt" "A-MEXP-86" "11047T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 45 "years" "female" "Stage II" "11047N" "Squamous Cell Carcinoma" "Stage II" "11047N"
+"R1029_11047N.txt" "A-MEXP-86" "11047N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 45 "years" "female" "11047N" "normal" "11047N"
+"R1028_11045T.txt" "A-MEXP-86" "11045T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 59 "years" "male" "Stage III or IV" "11045N" "Squamous Cell Carcinoma" "Stage III or IV" "11045N"
+"R1027_11045N.txt" "A-MEXP-86" "11045N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 59 "years" "male" "11045N" "normal" "11045N"
+"R1024_11018T.txt" "A-MEXP-86" "11018T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 70 "years" "male" "Stage II" "11018N" "Squamous Cell Carcinoma" "Stage II" "11018N"
+"R1023_11018N.txt" "A-MEXP-86" "11018N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 70 "years" "male" "11018N" "normal" "11018N"
+"R1022_11008T.txt" "A-MEXP-86" "11008T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 60 "years" "female" "Stage I" "11008N" "Squamous Cell Carcinoma" "Stage I" "11008N"
+"R1021_11008N.txt" "A-MEXP-86" "11008N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 60 "years" "female" "11008N" "normal" "11008N"
+"R1018_11002T.txt" "A-MEXP-86" "11002T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 74 "years" "male" "Stage I" "11002N" "Squamous Cell Carcinoma" "Stage I" "11002N"
+"R1017_11002N.txt" "A-MEXP-86" "11002N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 74 "years" "male" "11002N" "normal" "11002N"
+"R1014_10941T.txt" "A-MEXP-86" "10941T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 68 "years" "male" "Stage I" "10941N" "Squamous Cell Carcinoma" "Stage I" "10941N"
+"R1013_10941N.txt" "A-MEXP-86" "10941N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 68 "years" "male" "10941N" "normal" "10941N"
+"R1007_10788T.txt" "A-MEXP-86" "10788T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 54 "years" "female" "Stage III or IV" "10788N" "Squamous Cell Carcinoma" "Stage III or IV" "10788N"
+"R1000_10788N.txt" "A-MEXP-86" "10788N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 54 "years" "female" "10788N" "normal" "10788N"
+"R999_10780T.txt" "A-MEXP-86" "10780T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 75 "years" "female" "Stage III or IV" "10780N" "Squamous Cell Carcinoma" "Stage III or IV" "10780N"
+"R998_10780N.txt" "A-MEXP-86" "10780N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 75 "years" "female" "10780N" "normal" "10780N"
+"R997_10759T.txt" "A-MEXP-86" "10759T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 72 "years" "male" "Stage III or IV" "10759N" "Squamous Cell Carcinoma" "Stage III or IV" "10759N"
+"R996_10759N.txt" "A-MEXP-86" "10759N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 72 "years" "male" "10759N" "normal" "10759N"
+"R995_10743T.txt" "A-MEXP-86" "10743T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 54 "years" "male" "Stage I" "10743N" "Squamous Cell Carcinoma" "Stage I" "10743N"
+"R994_10743N.txt" "A-MEXP-86" "10743N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 54 "years" "male" "10743N" "normal" "10743N"
+"R975_10417T.txt" "A-MEXP-86" "10417T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 74 "years" "male" "Stage II" "10417N" "Squamous Cell Carcinoma" "Stage II" "10417N"
+"R874_10417N.txt" "A-MEXP-86" "10417N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 74 "years" "male" "10417N" "normal" "10417N"
+"R873_10404T.txt" "A-MEXP-86" "10404T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 78 "years" "male" "Stage II" "10404N" "Squamous Cell Carcinoma" "Stage II" "10404N"
+"R871_10404N.txt" "A-MEXP-86" "10404N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 78 "years" "male" "10404N" "normal" "10404N"
+"R870_10296T.txt" "A-MEXP-86" "10296T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 73 "years" "male" "Stage I" "10296N" "Squamous Cell Carcinoma" "Stage I" "10296N"
+"R869_10296N.txt" "A-MEXP-86" "10296N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 73 "years" "male" "10296N" "normal" "10296N"
+"R1759_10286T.txt" "A-MEXP-86" "10286T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 65 "years" "male" "Stage I" "10286N" "Squamous Cell Carcinoma" "Stage I" "10286N"
+"R1758_10286N.txt" "A-MEXP-86" "10286N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 65 "years" "male" "10286N" "normal" "10286N"
+"R1757_10070T.txt" "A-MEXP-86" "10070T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 75 "years" "male" "Stage I" "10070N" "Squamous Cell Carcinoma" "Stage I" "10070N"
+"R1750_10070N.txt" "A-MEXP-86" "10070N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 75 "years" "male" "10070N" "normal" "10070N"
+"R1745_1820T.txt" "A-MEXP-86" "1820T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 51 "years" "male" "Stage II" "1820N" "Squamous Cell Carcinoma" "Stage II" "1820N"
+"R1744_1820N.txt" "A-MEXP-86" "1820N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 51 "years" "male" "1820N" "normal" "1820N"
+"R1741_1800T.txt" "A-MEXP-86" "1800T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 74 "years" "male" "Stage I" "1800N" "Squamous Cell Carcinoma" "Stage I" "1800N"
+"R1740_1800N.txt" "A-MEXP-86" "1800N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 74 "years" "male" "1800N" "normal" "1800N"
+"R1739_1799T.txt" "A-MEXP-86" "1799T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 66 "years" "male" "Stage I" "1799N" "Squamous Cell Carcinoma" "Stage I" "1799N"
+"R1738_1799N.txt" "A-MEXP-86" "1799N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 66 "years" "male" "1799N" "normal" "1799N"
+"R1735_1797T.txt" "A-MEXP-86" "1797T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 73 "years" "female" "Stage I" "1797N" "Squamous Cell Carcinoma" "Stage I" "1797N"
+"R1734_1797N.txt" "A-MEXP-86" "1797N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 73 "years" "female" "1797N" "normal" "1797N"
+"R1733_1746T.txt" "A-MEXP-86" "1746T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 72 "years" "female" "Stage I" "1746N" "Squamous Cell Carcinoma" "Stage I" "1746N"
+"R1732_1746N.txt" "A-MEXP-86" "1746N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 72 "years" "female" "1746N" "normal" "1746N"
+"R1725_1614T.txt" "A-MEXP-86" "1614T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 63 "years" "male" "Stage III or IV" "1614N" "Squamous Cell Carcinoma" "Stage III or IV" "1614N"
+"R1724_1614N.txt" "A-MEXP-86" "1614N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 63 "years" "male" "1614N" "normal" "1614N"
+"R1713_1535T.txt" "A-MEXP-86" "1535T" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "Squamous Cell Carcinoma" 71 "years" "male" "Stage I" "1535N" "Squamous Cell Carcinoma" "Stage I" "1535N"
+"R1712_1535N.txt" "A-MEXP-86" "1535N" "P-MEXP-9143" "P-MEXP-9144" 5 "biotin" "P-MEXP-9145" 5 25 "P-MEXP-9146" "P-MEXP-9147" "organism_part" "fresh_sample" "Homo sapiens" "normal" 71 "years" "male" "1535N" "normal" "1535N"
diff --git a/examples/real/E-TABM-33.png b/examples/real/E-TABM-33.png
new file mode 100644
index 0000000..a56dfa4
Binary files /dev/null and b/examples/real/E-TABM-33.png differ
diff --git a/examples/real/E-TABM-33.txt b/examples/real/E-TABM-33.txt
new file mode 100644
index 0000000..313941b
--- /dev/null
+++ b/examples/real/E-TABM-33.txt
@@ -0,0 +1,55 @@
+"# Data for immediate release, although no paper has been submitted yet."
+"Experiment section"
+"domain" "ebi.ac.uk"
+"accession" "E-TABM-33"
+"quality_control" "biological_replicate, spike_quality_control, real_time_PCR_quality_control"
+"experiment_design_type" "development_or_differentiation_design, time_series_design"
+"name" "Micorarray analysis of gene expression in zebrafish development"
+"description" "We are performing microarray experiments for expression profiling of zebrafish embryogenesis, both as a baseline for future analysis of mutant and other conditions and to validate our microarray technology. For our purpose we used the Affymetrix zebrafish array which contains approximately 15,000 genes. This represents about 50 % of the estimated number of zebrafish genes. Total RNA was collected from embryos at 16 different stages (zygote, shield stage, 75 % epiboly, 90 [...]
+"release_date" 2005-08-18
+"submission_date" 2005-08-18
+"submitter" "Marc Saric; Martina Konantz "
+"organization" "Max-Planck-Institute for Developmental Biology, T�bingen, Germany"
+"publication_title" "Micorarray analysis of gene expression in zebrafish development"
+"authors" "Martina Konantz; Georg-Wilhelm Otto; Christian Weiler; Marc Saric; Robert Geisler"
+
+
+"# Protocol for labeling is P-AFFY-2 with standard-parameters as found in ArrayExpress"
+"Protocol section" " " " " " " " "
+"accession " "text " "name " "type " "parameters"
+"P-TABM-55" "T�bingen wildtype-fish were maintained as described in N�sslein-Volhard and Dahm: Zebrafish, PAS Oxford Univ. Press ISBN 019963808 X. Staging was done by hours postfertilization (Kimmel et al) in liquid nitrogen and embryos were stored at - 80 �C. " "Zebrafish growth" "grow, sacrifice, store" "degree_C"
+"P-TABM-56" "Briefly, total RNAs were prepared from every stages using TRIzol reagent (Invitrogen GmbH, Germany) with an added chlorophorm extraction before precipitation. Quality of embryonic RNA was assesed using the NanoDrop ND-1000 UV-Bis Spectrophotometer (NanoDrop Technologies, Wilmington, DE, USA). Samples where stored at -80�C" "RNA extraction" "nucleic_acid_extraction, store" "degree_C"
+"P-AFFY-2" "Affymetrix_IVT_protocol In vitro transcription cRNA labeling" "Affymetrix_IVT_protocol"
+
+"Hybridization section"
+"File[raw] " "File[normalized] " "File[exp] " "File[cdf] " "Array[accession] " "Array[serial]" "Protocol[grow]" "Protocol[extraction]" "Protocol[labeling]" "BioSource" "BioSourceMaterial" "BioMaterialCharacteristics[StrainOrLine]" "BioMaterialCharacteristics[Genotype]" "BioMaterialCharacteristics[Organism]" "FactorValue[Time]" "BioMaterialCharacteristics[Age]" "BioMaterialCharacteristics[TimeUnit]" "BioMaterialCharacteristics[InitialTimePoint]" "FactorValue[DevelopmentalStage]" "Bio [...]
+"Tue-10h-1.CEL" "Tue-10h-1.CHP" "Tue-10h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_10h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "10h" 10 "hours" "fertilization" "Gastrula:Bud" "Gastrula:Bud" "frozen_sample"
+"Tue-10h-2.CEL" "Tue-10h-2.CHP" "Tue-10h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_10h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "10h" 10 "hours" "fertilization" "Gastrula:Bud" "Gastrula:Bud" "frozen_sample"
+"Tue-11.7h-1.CEL" "Tue-11.7h-1.CHP" "Tue-11.7h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_11.7h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "11.7h" 11.7 "hours" "fertilization" "Segmentation:5-9 somites" "Segmentation:5-9 somites" "frozen_sample"
+"Tue-11.7h-2.CEL" "Tue-11.7h-2.CHP" "Tue-11.7h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_11.7h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "11.7h" 11.7 "hours" "fertilization" "Segmentation:5-9 somites" "Segmentation:5-9 somites" "frozen_sample"
+"Tue-14d-1.CEL" "Tue-14d-1.CHP" "Tue-14d-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_14d-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "14d" 14 "days" "fertilization" "Larval:Days 14-20" "Larval:Days 14-20" "frozen_sample"
+"Tue-14d-2.CEL" "Tue-14d-2.CHP" "Tue-14d-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_14d_2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "14d" 14 "days" "fertilization" "Larval:Days 14-20" "Larval:Days 14-20" "frozen_sample"
+"Tue-16h-1.CEL" "Tue-16h-1.CHP" "Tue-16h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_16h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "16h" 16 "hours" "fertilization" "Segmentation:14-19 somites" "Segmentation:14-19 somites" "frozen_sample"
+"Tue-16h-2.CEL" "Tue-16h-2.CHP" "Tue-16h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_16h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "16h" 16 "hours" "fertilization" "Segmentation:14-19 somites" "Segmentation:14-19 somites" "frozen_sample"
+"Tue-1c-1.CEL" "Tue-1c-1.CHP" "Tue-1c-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_1c-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "0.25h" 0.25 "hours" "fertilization" "Zygote:1-cell" "Zygote:1-cell" "frozen_sample"
+"Tue-1c-2.CEL" "Tue-1c-2.CHP" "Tue-1c-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_1c-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "0.25h" 0.25 "hours" "fertilization" "Zygote:1-cell" "Zygote:1-cell" "frozen_sample"
+"Tue-24h-1.CEL" "Tue-24h-1.CHP" "Tue-24h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_24h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "24h" 24 "hours" "fertilization" "Pharyngula:Prim-5" "Pharyngula:Prim-5" "frozen_sample"
+"Tue-24h-2.CEL" "Tue-24h-2.CHP" "Tue-24h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_24h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "24h" 24 "hours" "fertilization" "Pharyngula:Prim-5" "Pharyngula:Prim-5" "frozen_sample"
+"Tue-30d-1.CEL" "Tue-30d-1.CHP" "Tue-30d-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_30d-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "30d" 30 "days" "fertilization" "Juvenile:Days 30-44" "Juvenile:Days 30-44" "frozen_sample"
+"Tue-30d-2.CEL" "Tue-30d-2.CHP" "Tue-30d-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_30d-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "30d" 30 "days" "fertilization" "Juvenile:Days 30-44" "Juvenile:Days 30-44" "frozen_sample"
+"Tue-32h-1.CEL" "Tue-32h-1.CHP" "Tue-32h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_32h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "32h" 32 "hours" "fertilization" "Pharyngula:Prim-15" "Pharyngula:Prim-15" "frozen_sample"
+"Tue-32h-2.CEL" "Tue-32h-2.CHP" "Tue-32h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_32h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "32h" 32 "hours" "fertilization" "Pharyngula:Prim-15" "Pharyngula:Prim-15" "frozen_sample"
+"Tue-48h-1.CEL" "Tue-48h-1.CHP" "Tue-48h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_48h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "48h" 48 "hours" "fertilization" "Hatching:Long-pec" "Hatching:Long-pec" "frozen_sample"
+"Tue-48h-2.CEL" "Tue-48h-2.CHP" "Tue-48h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_48h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "48h" 48 "hours" "fertilization" "Hatching:Long-pec" "Hatching:Long-pec" "frozen_sample"
+"Tue-4d-1.CEL" "Tue-4d-1.CHP" "Tue-4d-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_4d-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "4d" 4 "days" "fertilization" "Larval:Day 4" "Larval:Day 4" "frozen_sample"
+"Tue-4d-2.CEL" "Tue-4d-2.CHP" "Tue-4d-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_4d-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "4d" 4 "days" "fertilization" "Larval:Day 4" "Larval:Day 4" "frozen_sample"
+"Tue-5d-1.CEL" "Tue-5d-1.CHP" "Tue-5d-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_5d-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "5d" 5 "days" "fertilization" "Larval:Day 5" "Larval:Day 5" "frozen_sample"
+"Tue-5d-2.CEL" "Tue-5d-2.CHP" "Tue-5d-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_5d-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "5d" 5 "days" "fertilization" "Larval:Day 5" "Larval:Day 5" "frozen_sample"
+"Tue-6h-1.CEL" "Tue-6h-1.CHP" "Tue-6h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_6h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "6h" 6 "hours" "fertilization" "Gastrula:Shield" "Gastrula:Shield" "frozen_sample"
+"Tue-6h-2.CEL" "Tue-6h-2.CHP" "Tue-6h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_6h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "6h" 6 "hours" "fertilization" "Gastrula:Shield" "Gastrula:Shield" "frozen_sample"
+"Tue-8h-1.CEL" "Tue-8h-1.CHP" "Tue-8h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_8h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "8h" 8 "hours" "fertilization" "Gastrula:75%-epiboly" "Gastrula:75%-epiboly" "frozen_sample"
+"Tue-8h-2.CEL" "Tue-8h-2.CHP" "Tue-8h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_8h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "8h" 8 "hours" "fertilization" "Gastrula:75%-epiboly" "Gastrula:75%-epiboly" "frozen_sample"
+"Tue-90d-1.CEL" "Tue-90d-1.CHP" "Tue-90d-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_90d-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "90d" 90 "days" "fertilization" "Adult" "Adult" "frozen_sample"
+"Tue-90d-2.CEL" "Tue-90d-2.CHP" "Tue-90d-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_90d-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "90d" 90 "days" "fertilization" "Adult" "Adult" "frozen_sample"
+"Tue-9h-1.CEL" "Tue-9h-1.CHP" "Tue-9h-1.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_9h-1" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "9h" 9 "hours" "fertilization" "Gastrula:90%-epiboly" "Gastrula:90%-epiboly" "frozen_sample"
+"Tue-9h-2.CEL" "Tue-9h-2.CHP" "Tue-9h-2.EXP" "Zebrafish.cdf" "A-AFFY-38" 4009543 "P-TABM-55" "P-TABM-56" "P-AFFY-2" "Danio rerio Tuebingen wildtype_9h-2" "whole_organism" "Tuebingen" "wild_type" "Danio rerio" "9h" 9 "hours" "fertilization" "Gastrula:90%-epiboly" "Gastrula:90%-epiboly" "frozen_sample"
diff --git a/examples/real/E-TABM-35.png b/examples/real/E-TABM-35.png
new file mode 100644
index 0000000..b46deea
Binary files /dev/null and b/examples/real/E-TABM-35.png differ
diff --git a/examples/real/E-TABM-35.txt b/examples/real/E-TABM-35.txt
new file mode 100755
index 0000000..3edc589
--- /dev/null
+++ b/examples/real/E-TABM-35.txt
@@ -0,0 +1,94 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-35
+quality_control
+experiment_design_type "strain_or_line_design, comparative_genome_hybridization_design"
+name RML C. burnetii CGH analysis
+description Analysis of genetic diversity of 25 Coxiella burnetii isolates with relative to the Nine Mile (RSA493) reference isolate.
+release_date 2006-06-21
+submission_date
+submitter Paul Beare
+organization "Rocky Mountain Laboratories, Laboratory of Intracellular Parasites, National Institute of Allergy and Infectious Diseases, National Institute of Health"
+publication_title Genetic Diversity of the Q Fever agent Coxiella burnetii Assessed by Microarray-based Whole Genome Comparisons
+authors "P. A. Beare; J. E. Samuel; D. Howe; K. Virtaneva; S. F. Porcella; R. A. Heinzen"
+journal submitted to Genome Research
+volume
+issue
+pages
+year
+
+Protocol section
+accession text name type
+P-TABM-61 C. burnetii isolates were propagated in embryonated hen�s eggs for 8 days. egg propagation grow
+P-TABM-62 C. burnetii isolates were propagated in African green monkey kidney (Vero) fibroblasts (CCL-81; American Type Culture Collection) grown in RPMI medium (Invitrogen) supplemented with 2% fetal bovine serum for 14 days. fibroblast propagation grow
+P-TABM-63 "Organisms were purified from infected cells by Renografin or sucrose density gradient centrifugation. Total DNA was extracted using the UltraClean microbial DNA isolation kit (Mo Bio Laboratories Inc) as recommended by the supplier, with an additional heating step (85�C for 10 min) before physical disruption of the bacterial cells. " method A nucleic_acid_extraction
+P-TABM-64 "Organisms were purified from infected cells by Renografin or sucrose density gradient centrifugation. Purified C. burnetii organisms (60 to 70 mg, dry weight) were suspended in 50 mM NaCl-20 mM Tris (pH 9.1) and incubated for 60 min at 37 degrees celsius with 100ug of thermolysin (10). Lysis was completed by adding sodium dodecyl sulfate (SDS) to a 1% final concentration. The plasmid DNA in this lysate was separated from chromosomal DNA by cesium chloride-ethidium bromide (CsC [...]
+P-TABM-65 "Total DNA was extracted from crude samples using the UltraClean microbial DNA isolation kit (Mo Bio Laboratories Inc) as recommended by the supplier, with an additional heating step (85�C for 10 min) before physical disruption of the bacterial cells. gDNA was resuspended in 80 ul of distilled H2O, and quantified using a Beckman Du 530 spectrophotometer (Beckman Coulter, Inc.)." method C nucleic_acid_extraction
+P-TABM-66 "Total DNA was extracted from crude samples using the UltraClean microbial DNA isolation kit (Mo Bio Laboratories Inc) as recommended by the supplier, with an additional heating step (85�C for 10 min) before physical disruption of the bacterial cells. Whole genome amplification (WGA) was conducted on total DNA from 4 ul of a infected yolk sac or 4 ul of a infected guinea pig spleen homogenate . Amplifications employed a GenomiPhi DNA Amplification Kit (Amersham Biosciences). Sp [...]
+P-TABM-67 "C. burnetii replicative vacuoles where harvested from infected Vero cells on 12-mm glass coverslips by micromanipulation. At 5 days post-infection, coverslips were transferred to a 90 cm diameter Petri dish containing approximately 10 ml of RPMI medium. Individual vacuoles were extracted from infected monolayers using a Micromanipulator 5171 (Eppendorf, Westbury, N.Y.), a CellTram vario manual microinjector (Eppendorf), and TransferTips MDS (Eppendorf). The micromanipulator [...]
+P-TABM-68 "1. Concentrate gDNA samples (Total DNA from purified gDNA (7 ug), gDNA from infected yolk sacs (10 ug), or WGA DNA from purified gDNA (7 ug), C. burnetii vacuoles (7 ug), infected yolk sacs (10 ug) or infected guinea pig spleen homogenates (30 ug)) down to 40 uL at room temperature under vacuum in a DNA 110 speedvac (Thermo Savant). Place samples on ice. 2. Add 5 ul of DNaseI 10x buffer 3. Add 5 ul of 0.01 ul/ug of DNaseI (Roche Diagnostics Corp.) 4. Incubate samples at 37 deg [...]
+P-TABM-69 "The difference in intensity value for each probe pair was calculated by subtracting the MM probe intensity value from the PM probe intensity value. The average difference in intensity values for the entire 16 probe pairs for each ORF was calculated using the Statistical Expression Algorithm incorporated into GCOS. Normalization between arrays was achieved by implementing array-specific scaling factors that were calculated as a ratio of 500 divided by the average of the average [...]
+
+Hybridization section
+BioSource BioSourceMaterial BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] Protocol[grow] Sample Protocol[extraction] Extract ExtractMaterial Protocol[labeling] LabeledExtract LabeledExtractMaterial File[raw] File[normalized] File[exp] File[cdf] Array[accession] FactorValue[StrainOrLine] Bioassay data transformation
+Dugway 5G61-63 whole_organism Coxiella burnetii Dugway 5G61-63 P-TABM-62 Dugway 5G61-63 A P-TABM-63 Dugway 5G61-63 A genomic_DNA P-TABM-68 Dugway 5G61-63 A synthetic_DNA PAB-1-Cburnetti-Dugway 5G61-63 A.CEL PAB-1-Cburnetti-Dugway 5G61-63 A.CHP PAB-1-Cburnetti-Dugway 5G61-63 A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Dugway 5G61-63 P-TABM-69
+Dugway 5G61-63 whole_organism Coxiella burnetii Dugway 5G61-63 P-TABM-62 Dugway 5G61-63 B P-TABM-63 Dugway 5G61-63 B genomic_DNA P-TABM-68 Dugway 5G61-63 B synthetic_DNA PAB-1-Cburnetti-Dugway 5G61-63 B.CEL PAB-1-Cburnetti-Dugway 5G61-63 B.CHP PAB-1-Cburnetti-Dugway 5G61-63 B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Dugway 5G61-63 P-TABM-69
+Dugway 7E9-12 whole_organism Coxiella burnetii Dugway 7E9-12 P-TABM-61 Dugway 7E9-12 A P-TABM-64 Dugway 7E9-12 A genomic_DNA P-TABM-68 Dugway 7E9-12 A synthetic_DNA PAB-1-Cburnetti-Dugway 7E9-12 A.CEL PAB-1-Cburnetti-Dugway 7E9-12 A.CHP PAB-1-Cburnetti-Dugway 7E9-12 A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Dugway 7E9-12 P-TABM-69
+Dugway 7E9-12 whole_organism Coxiella burnetii Dugway 7E9-12 P-TABM-61 Dugway 7E9-12 B P-TABM-64 Dugway 7E9-12 B genomic_DNA P-TABM-68 Dugway 7E9-12 B synthetic_DNA PAB-1-Cburnetti-Dugway 7E9-12 B.CEL PAB-1-Cburnetti-Dugway 7E9-12 B.CHP PAB-1-Cburnetti-Dugway 7E9-12 B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Dugway 7E9-12 P-TABM-69
+African whole_organism Coxiella burnetii African P-TABM-61 African A P-TABM-64 African A genomic_DNA P-TABM-68 African A synthetic_DNA PAB-1-Cburnetti-African A.CEL PAB-1-Cburnetti-African A.CHP PAB-1-Cburnetti-African A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 African P-TABM-69
+African whole_organism Coxiella burnetii African P-TABM-61 African B P-TABM-64 African B genomic_DNA P-TABM-68 African B synthetic_DNA PAB-1-Cburnetti-African B.CEL PAB-1-Cburnetti-African B.CHP PAB-1-Cburnetti-African B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 African P-TABM-69
+African whole_organism Coxiella burnetii African P-TABM-61 African C P-TABM-64 African C genomic_DNA P-TABM-68 African C synthetic_DNA PAB-1-Cburnetti-African C.CEL PAB-1-Cburnetti-African C.CHP PAB-1-Cburnetti-African C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 African P-TABM-69
+Australia whole_organism Coxiella burnetii Australian P-TABM-62 Australian A P-TABM-63 Australian A genomic_DNA P-TABM-68 Australian A synthetic_DNA PAB-1-Cburnetti-Australian A.CEL PAB-1-Cburnetti-Australian A.CHP PAB-1-Cburnetti-Australian A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Australian P-TABM-69
+Australia whole_organism Coxiella burnetii Australian P-TABM-62 Australian B P-TABM-63 Australian B genomic_DNA P-TABM-68 Australian B synthetic_DNA PAB-1-Cburnetti-Australian B.CEL PAB-1-Cburnetti-Australian B.CHP PAB-1-Cburnetti-Australian B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Australian P-TABM-69
+BDT whole_organism Coxiella burnetii BDT P-TABM-62 BDT A P-TABM-66 BDT A genomic_DNA P-TABM-68 BDT A synthetic_DNA PAB-1-Cburnetti-BDT A.CEL PAB-1-Cburnetti-BDT A.CHP PAB-1-Cburnetti-BDT A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 BDT P-TABM-69
+BDT whole_organism Coxiella burnetii BDT P-TABM-62 BDT B P-TABM-66 BDT B genomic_DNA P-TABM-68 BDT B synthetic_DNA PAB-1-Cburnetti-BDT B.CEL PAB-1-Cburnetti-BDT B.CHP PAB-1-Cburnetti-BDT B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 BDT P-TABM-69
+California 16 whole_organism Coxiella burnetii California 16 P-TABM-61 California 16 P-TABM-64 California 16 genomic_DNA P-TABM-68 California 16 synthetic_DNA PAB-1-Cburnetti-California 16.CEL PAB-1-Cburnetti-California 16.CHP PAB-1-Cburnetti-California 16.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 California 16 P-TABM-69
+California 33 whole_organism Coxiella burnetii California 33 P-TABM-61 California 33 P-TABM-64 California 33 genomic_DNA P-TABM-68 California 33 synthetic_DNA PAB-1-Cburnetti-California 33.CEL PAB-1-Cburnetti-California 33.CHP PAB-1-Cburnetti-California 33.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 California 33 P-TABM-69
+Crazy whole_organism Coxiella burnetii Crazy P-TABM-62 Crazy A P-TABM-63 Crazy A genomic_DNA P-TABM-68 Crazy A synthetic_DNA PAB-1-Cburnetti-Crazy A.CEL PAB-1-Cburnetti-Crazy A.CHP PAB-1-Cburnetti-Crazy A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Crazy P-TABM-69
+Crazy whole_organism Coxiella burnetii Crazy P-TABM-62 Crazy B P-TABM-63 Crazy B genomic_DNA P-TABM-68 Crazy B synthetic_DNA PAB-1-Cburnetti-Crazy B.CEL PAB-1-Cburnetti-Crazy B.CHP PAB-1-Cburnetti-Crazy B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Crazy P-TABM-69
+D whole_organism Coxiella burnetii D P-TABM-61 D A P-TABM-64 D A genomic_DNA P-TABM-68 D A synthetic_DNA PAB-1-Cburnetti-D A.CEL PAB-1-Cburnetti-D A.CHP PAB-1-Cburnetti-D A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 D P-TABM-69
+D whole_organism Coxiella burnetii D P-TABM-61 D B P-TABM-64 D B genomic_DNA P-TABM-68 D B synthetic_DNA PAB-1-Cburnetti-D B.CEL PAB-1-Cburnetti-D B.CHP PAB-1-Cburnetti-D B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 D P-TABM-69
+El Tayeb whole_organism Coxiella burnetii El Tayeb P-TABM-61 El Tayeb A P-TABM-64 El Tayeb A genomic_DNA P-TABM-68 El Tayeb A synthetic_DNA PAB-1-Cburnetti-El Tayeb A.CEL PAB-1-Cburnetti-El Tayeb A.CHP PAB-1-Cburnetti-El Tayeb A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 El Tayeb P-TABM-69
+El Tayeb whole_organism Coxiella burnetii El Tayeb P-TABM-61 El Tayeb B P-TABM-64 El Tayeb B genomic_DNA P-TABM-68 El Tayeb B synthetic_DNA PAB-1-Cburnetti-El Tayeb B.CEL PAB-1-Cburnetti-El Tayeb B.CHP PAB-1-Cburnetti-El Tayeb B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 El Tayeb P-TABM-69
+G whole_organism Coxiella burnetii G P-TABM-62 G A P-TABM-63 G A genomic_DNA P-TABM-68 G A synthetic_DNA PAB-1-Cburnetti-G A.CEL PAB-1-Cburnetti-G A.CHP PAB-1-Cburnetti-G A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 G P-TABM-69
+G whole_organism Coxiella burnetii G P-TABM-62 G B P-TABM-63 G B genomic_DNA P-TABM-68 G B synthetic_DNA PAB-1-Cburnetti-G B.CEL PAB-1-Cburnetti-G B.CHP PAB-1-Cburnetti-G B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 G P-TABM-69
+G whole_organism Coxiella burnetii G P-TABM-61 G C P-TABM-65 G C genomic_DNA P-TABM-68 G C synthetic_DNA PAB-1-Cburnetti-G C.CEL PAB-1-Cburnetti-G C.CHP PAB-1-Cburnetti-G C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 G P-TABM-69
+G whole_organism Coxiella burnetii G P-TABM-61 G D P-TABM-66 G D genomic_DNA P-TABM-68 G D synthetic_DNA PAB-1-Cburnetti-G D.CEL PAB-1-Cburnetti-G D.CHP PAB-1-Cburnetti-G D.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 G P-TABM-69
+Giroud whole_organism Coxiella burnetii Giroud P-TABM-61 Giroud A P-TABM-64 Giroud A genomic_DNA P-TABM-68 Giroud A synthetic_DNA PAB-1-Cburnetti-Giroud A.CEL PAB-1-Cburnetti-Giroud A.CHP PAB-1-Cburnetti-Giroud A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Giroud P-TABM-69
+Giroud whole_organism Coxiella burnetii Giroud P-TABM-61 Giroud B P-TABM-64 Giroud B genomic_DNA P-TABM-68 Giroud B synthetic_DNA PAB-1-Cburnetti-Giroud B.CEL PAB-1-Cburnetti-Giroud B.CHP PAB-1-Cburnetti-Giroud B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Giroud P-TABM-69
+He whole_organism Coxiella burnetii He P-TABM-61 He A P-TABM-64 He A genomic_DNA P-TABM-68 He A synthetic_DNA PAB-1-Cburnetti-He A.CEL PAB-1-Cburnetti-He A.CHP PAB-1-Cburnetti-He A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 He P-TABM-69
+He whole_organism Coxiella burnetii He P-TABM-61 He B P-TABM-64 He B genomic_DNA P-TABM-68 He B synthetic_DNA PAB-1-Cburnetti-He B.CEL PAB-1-Cburnetti-He B.CHP PAB-1-Cburnetti-He B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 He P-TABM-69
+Idaho whole_organism Coxiella burnetii Idaho P-TABM-62 Idaho A P-TABM-63 Idaho A genomic_DNA P-TABM-68 Idaho A synthetic_DNA PAB-1-Cburnetti-Idaho A.CEL PAB-1-Cburnetti-Idaho A.CHP PAB-1-Cburnetti-Idaho A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Idaho P-TABM-69
+Idaho whole_organism Coxiella burnetii Idaho P-TABM-62 Idaho B P-TABM-63 Idaho B genomic_DNA P-TABM-68 Idaho B synthetic_DNA PAB-1-Cburnetti-Idaho B.CEL PAB-1-Cburnetti-Idaho B.CHP PAB-1-Cburnetti-Idaho B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Idaho P-TABM-69
+K whole_organism Coxiella burnetii K P-TABM-61 K A P-TABM-64 K A genomic_DNA P-TABM-68 K A synthetic_DNA PAB-1-Cburnetti-K A.CEL PAB-1-Cburnetti-K A.CHP PAB-1-Cburnetti-K A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 K P-TABM-69
+K whole_organism Coxiella burnetii K P-TABM-61 K B P-TABM-64 K B genomic_DNA P-TABM-68 K B synthetic_DNA PAB-1-Cburnetti-K B.CEL PAB-1-Cburnetti-K B.CHP PAB-1-Cburnetti-K B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 K P-TABM-69
+K whole_organism Coxiella burnetii K P-TABM-61 K C P-TABM-64 K C genomic_DNA P-TABM-68 K C synthetic_DNA PAB-1-Cburnetti-K C.CEL PAB-1-Cburnetti-K C.CHP PAB-1-Cburnetti-K C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 K P-TABM-69
+KO whole_organism Coxiella burnetii KO P-TABM-62 KO A P-TABM-63 KO A genomic_DNA P-TABM-68 KO A synthetic_DNA PAB-1-Cburnetti-KO A.CEL PAB-1-Cburnetti-KO A.CHP PAB-1-Cburnetti-KO A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 KO P-TABM-69
+KO whole_organism Coxiella burnetii KO P-TABM-62 KO B P-TABM-63 KO B genomic_DNA P-TABM-68 KO B synthetic_DNA PAB-1-Cburnetti-KO B.CEL PAB-1-Cburnetti-KO B.CHP PAB-1-Cburnetti-KO B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 KO P-TABM-69
+KO whole_organism Coxiella burnetii KO P-TABM-62 KO C P-TABM-63 KO C genomic_DNA P-TABM-68 KO C synthetic_DNA PAB-1-Cburnetti-KO C.CEL PAB-1-Cburnetti-KO C.CHP PAB-1-Cburnetti-KO C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 KO P-TABM-69
+Le B whole_organism Coxiella burnetii Le B P-TABM-62 Le B A P-TABM-63 Le B A genomic_DNA P-TABM-68 Le B A synthetic_DNA PAB-1-Cburnetti-LeB A.CEL PAB-1-Cburnetti-LeB A.CHP PAB-1-Cburnetti-LeB A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Le B P-TABM-69
+Le B whole_organism Coxiella burnetii Le B P-TABM-62 Le B B P-TABM-63 Le B B genomic_DNA P-TABM-68 Le B B synthetic_DNA PAB-1-Cburnetti-LeB B.CEL PAB-1-Cburnetti-LeB B.CHP PAB-1-Cburnetti-LeB B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Le B P-TABM-69
+MSU whole_organism Coxiella burnetii MSU P-TABM-62 MSU A P-TABM-63 MSU A genomic_DNA P-TABM-68 MSU A synthetic_DNA PAB-1-Cburnetti-MSU A.CEL PAB-1-Cburnetti-MSU A.CHP PAB-1-Cburnetti-MSU A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 MSU P-TABM-69
+MSU whole_organism Coxiella burnetii MSU P-TABM-62 MSU B P-TABM-63 MSU B genomic_DNA P-TABM-68 MSU B synthetic_DNA PAB-1-Cburnetti-MSU B.CEL PAB-1-Cburnetti-MSU B.CHP PAB-1-Cburnetti-MSU B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 MSU P-TABM-69
+MSU whole_organism Coxiella burnetii MSU P-TABM-62 MSU C P-TABM-63 MSU C genomic_DNA P-TABM-68 MSU C synthetic_DNA PAB-1-Cburnetti-MSU C.CEL PAB-1-Cburnetti-MSU C.CHP PAB-1-Cburnetti-MSU C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 MSU P-TABM-69
+NMI whole_organism Coxiella burnetii NMI P-TABM-62 NMI A P-TABM-63 NMI A genomic_DNA P-TABM-68 NMI A synthetic_DNA PAB-1-Cburnetti-NMI A.CEL PAB-1-Cburnetti-NMI A.CHP PAB-1-Cburnetti-NMI A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 NMI P-TABM-69
+NMI whole_organism Coxiella burnetii NMI P-TABM-62 NMI B P-TABM-63 NMI B genomic_DNA P-TABM-68 NMI B synthetic_DNA PAB-1-Cburnetti-NMI B.CEL PAB-1-Cburnetti-NMI B.CHP PAB-1-Cburnetti-NMI B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 NMI P-TABM-69
+NMI whole_organism Coxiella burnetii NMI P-TABM-62 NMI C P-TABM-63 NMI C genomic_DNA P-TABM-68 NMI C synthetic_DNA PAB-1-Cburnetti-NMI C.CEL PAB-1-Cburnetti-NMI C.CHP PAB-1-Cburnetti-NMI C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 NMI P-TABM-69
+NMI whole_organism Coxiella burnetii NMI P-TABM-62 NMI D P-TABM-63 NMI D genomic_DNA P-TABM-68 NMI D synthetic_DNA PAB-1-Cburnetti-NMI D.CEL PAB-1-Cburnetti-NMI D.CHP PAB-1-Cburnetti-NMI D.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 NMI P-TABM-69
+NMII whole_organism Coxiella burnetii NMII P-TABM-62 NMII A P-TABM-63 NMII A genomic_DNA P-TABM-68 NMII A synthetic_DNA PAB-1-Cburnetti-NMII A.CEL PAB-1-Cburnetti-NMII A.CHP PAB-1-Cburnetti-NMII A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 NMII P-TABM-69
+NMII whole_organism Coxiella burnetii NMII P-TABM-62 NMII B P-TABM-67 NMII B genomic_DNA P-TABM-68 NMII B synthetic_DNA PAB-1-Cburnetti-NMII B.CEL PAB-1-Cburnetti-NMII B.CHP PAB-1-Cburnetti-NMII B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 NMII P-TABM-69
+Ohio whole_organism Coxiella burnetii Ohio P-TABM-61 Ohio A P-TABM-64 Ohio A genomic_DNA P-TABM-68 Ohio A synthetic_DNA PAB-1-Cburnetti-Ohio A.CEL PAB-1-Cburnetti-Ohio A.CHP PAB-1-Cburnetti-Ohio A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Ohio P-TABM-69
+Ohio whole_organism Coxiella burnetii Ohio P-TABM-61 Ohio B P-TABM-64 Ohio B genomic_DNA P-TABM-68 Ohio B synthetic_DNA PAB-1-Cburnetti-Ohio B.CEL PAB-1-Cburnetti-Ohio B.CHP PAB-1-Cburnetti-Ohio B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Ohio P-TABM-69
+P whole_organism Coxiella burnetii P P-TABM-61 P A P-TABM-64 P A genomic_DNA P-TABM-68 P A synthetic_DNA PAB-1-Cburnetti-P A.CEL PAB-1-Cburnetti-P A.CHP PAB-1-Cburnetti-P A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 P P-TABM-69
+P whole_organism Coxiella burnetii P P-TABM-61 P B P-TABM-64 P B genomic_DNA P-TABM-68 P B synthetic_DNA PAB-1-Cburnetti-P B.CEL PAB-1-Cburnetti-P B.CHP PAB-1-Cburnetti-P B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 P P-TABM-69
+P whole_organism Coxiella burnetii P P-TABM-61 P C P-TABM-64 P C genomic_DNA P-TABM-68 P C synthetic_DNA PAB-1-Cburnetti-P C.CEL PAB-1-Cburnetti-P C.CHP PAB-1-Cburnetti-P C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 P P-TABM-69
+Panama whole_organism Coxiella burnetii Panama P-TABM-61 Panama A P-TABM-64 Panama A genomic_DNA P-TABM-68 Panama A synthetic_DNA PAB-1-Cburnetti-Panama A.CEL PAB-1-Cburnetti-Panama A.CHP PAB-1-Cburnetti-Panama A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Panama P-TABM-69
+Panama whole_organism Coxiella burnetii Panama P-TABM-61 Panama B P-TABM-64 Panama B genomic_DNA P-TABM-68 Panama B synthetic_DNA PAB-1-Cburnetti-Panama B.CEL PAB-1-Cburnetti-Panama B.CHP PAB-1-Cburnetti-Panama B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Panama P-TABM-69
+Q321 whole_organism Coxiella burnetii Q321 P-TABM-62 Q321 A P-TABM-63 Q321 A genomic_DNA P-TABM-68 Q321 A synthetic_DNA PAB-1-Cburnetti-Q321 A.CEL PAB-1-Cburnetti-Q321 A.CHP PAB-1-Cburnetti-Q321 A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Q321 P-TABM-69
+Q321 whole_organism Coxiella burnetii Q321 P-TABM-62 Q321 B P-TABM-63 Q321 B genomic_DNA P-TABM-68 Q321 B synthetic_DNA PAB-1-Cburnetti-Q321 B.CEL PAB-1-Cburnetti-Q321 B.CHP PAB-1-Cburnetti-Q321 B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Q321 P-TABM-69
+Q321 whole_organism Coxiella burnetii Q321 P-TABM-62 Q321 C P-TABM-63 Q321 C genomic_DNA P-TABM-68 Q321 C synthetic_DNA PAB-1-Cburnetti-Q321 C.CEL PAB-1-Cburnetti-Q321 C.CHP PAB-1-Cburnetti-Q321 C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Q321 P-TABM-69
+S whole_organism Coxiella burnetii S P-TABM-62 S A P-TABM-63 S A genomic_DNA P-TABM-68 S A synthetic_DNA PAB-1-Cburnetti-S A.CEL PAB-1-Cburnetti-S A.CHP PAB-1-Cburnetti-S A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 S P-TABM-69
+S whole_organism Coxiella burnetii S P-TABM-62 S B P-TABM-63 S B genomic_DNA P-TABM-68 S B synthetic_DNA PAB-1-Cburnetti-S B.CEL PAB-1-Cburnetti-S B.CHP PAB-1-Cburnetti-S B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 S P-TABM-69
+S whole_organism Coxiella burnetii S P-TABM-62 S C P-TABM-63 S C genomic_DNA P-TABM-68 S C synthetic_DNA PAB-1-Cburnetti-S C.CEL PAB-1-Cburnetti-S C.CHP PAB-1-Cburnetti-S C.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 S P-TABM-69
+Turkey whole_organism Coxiella burnetii Turkey P-TABM-61 Turkey A P-TABM-64 Turkey A genomic_DNA P-TABM-68 Turkey A synthetic_DNA PAB-1-Cburnetti-Turkey A.CEL PAB-1-Cburnetti-Turkey A.CHP PAB-1-Cburnetti-Turkey A.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Turkey P-TABM-69
+Turkey whole_organism Coxiella burnetii Turkey P-TABM-61 Turkey B P-TABM-64 Turkey B genomic_DNA P-TABM-68 Turkey B synthetic_DNA PAB-1-Cburnetti-Turkey B.CEL PAB-1-Cburnetti-Turkey B.CHP PAB-1-Cburnetti-Turkey B.EXP /net/nfs6/vol4/ma-db/shoja/Affy_Data/RML-chipa510998.CDF A-AFFY-48 Turkey P-TABM-69
diff --git a/examples/real/E-TABM-54.png b/examples/real/E-TABM-54.png
new file mode 100644
index 0000000..8d91b05
Binary files /dev/null and b/examples/real/E-TABM-54.png differ
diff --git a/examples/real/E-TABM-54.txt b/examples/real/E-TABM-54.txt
new file mode 100755
index 0000000..e21d985
--- /dev/null
+++ b/examples/real/E-TABM-54.txt
@@ -0,0 +1,287 @@
+Experiment section
+accession E-TABM-54
+domain www.stanford.edu
+experiment_design_type strain_or_line_design;comparative_genome_hybridization
+name Bordetella pertussis comparative genome hybridization
+description Comparative genome hybridization (CGH) of sample of 137 Bordetella pertussis strains
+release_date 2005-12-31
+submitter Craig Cummings
+submitter_email cummings at pmgm2.stanford.edu
+quality_control technical_replicate; biological_replicate
+
+Hybridization section
+BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioSource BioSourceMaterial SampleMaterial ExtractMaterial Sample Extract LabeledExtract LabeledExtractMaterial Dye Protocol[grow] Protocol[extraction] Protocol[pool] Protocol[labeling] Protocol[hybridization] Protocol[scanning] Protocol[normalization] FactorValue[StrainOrLine] FactorValue[clinical_information] File[raw] File[normalized] Array[accession] Array[serial] Hybridization Normalization
+Bordetella pertussis Bpe60 Bpe60 whole_organism whole_organism genomic_DNA Bpe60 Bpe60 Bpe60 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe60 Tohama I laboratory strain b23n061.txt b23n061_norm.txt A-MEXP-234 b23n061 b23n061 b23n061
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe60 Tohama I laboratory strain b23n061.txt b23n061_norm.txt A-MEXP-234 b23n061 b23n061 b23n061
+Bordetella pertussis Bpe317 Bpe317 whole_organism whole_organism genomic_DNA Bpe317 Bpe317 Bpe317 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe317 GMT-1 recent lab strain b29n119.txt b29n119_norm.txt A-MEXP-236 b29n119 b29n119 b29n119
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe317 GMT-1 recent lab strain b29n119.txt b29n119_norm.txt A-MEXP-236 b29n119 b29n119 b29n119
+Bordetella pertussis Bpe55 Bpe55 whole_organism whole_organism genomic_DNA Bpe55 Bpe55 Bpe55 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe55 Tohama II laboratory strain b25n048.txt b25n048_norm.txt A-MEXP-234 b25n048 b25n048 b25n048
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe55 Tohama II laboratory strain b25n048.txt b25n048_norm.txt A-MEXP-234 b25n048 b25n048 b25n048
+Bordetella pertussis Bpe243 Bpe243 whole_organism whole_organism genomic_DNA Bpe243 Bpe243 Bpe243 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe243 US pre-vaccine b29n112.txt b29n112_norm.txt A-MEXP-236 b29n112 b29n112 b29n112
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe243 US pre-vaccine b29n112.txt b29n112_norm.txt A-MEXP-236 b29n112 b29n112 b29n112
+Bordetella pertussis Bpe244 Bpe244 whole_organism whole_organism genomic_DNA Bpe244 Bpe244 Bpe244 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe244 US pre-vaccine b29n113.txt b29n113_norm.txt A-MEXP-236 b29n113 b29n113 b29n113
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe244 US pre-vaccine b29n113.txt b29n113_norm.txt A-MEXP-236 b29n113 b29n113 b29n113
+Bordetella pertussis Bpe245 Bpe245 whole_organism whole_organism genomic_DNA Bpe245 Bpe245 Bpe245 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe245 US pre-vaccine b29n114.txt b29n114_norm.txt A-MEXP-236 b29n114 b29n114 b29n114
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe245 US pre-vaccine b29n114.txt b29n114_norm.txt A-MEXP-236 b29n114 b29n114 b29n114
+Bordetella pertussis Bpe247 Bpe247 whole_organism whole_organism genomic_DNA Bpe247 Bpe247 Bpe247 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe247 US pre-vaccine b29n116.txt b29n116_norm.txt A-MEXP-236 b29n116 b29n116 b29n116
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe247 US pre-vaccine b29n116.txt b29n116_norm.txt A-MEXP-236 b29n116 b29n116 b29n116
+Bordetella pertussis Bpe41 Bpe41 whole_organism whole_organism genomic_DNA Bpe41 Bpe41 Bpe41 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe41 US Cincinnati outbreak b23n124.txt b23n124_norm.txt A-MEXP-234 b23n124 b23n124 b23n124
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe41 US Cincinnati outbreak b23n124.txt b23n124_norm.txt A-MEXP-234 b23n124 b23n124 b23n124
+Bordetella pertussis Bpe51 Bpe51 whole_organism whole_organism genomic_DNA Bpe51 Bpe51 Bpe51 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe51 US Cincinnati outbreak b24n122.txt b24n122_norm.txt A-MEXP-234 b24n122 b24n122 b24n122
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe51 US Cincinnati outbreak b24n122.txt b24n122_norm.txt A-MEXP-234 b24n122 b24n122 b24n122
+Bordetella pertussis Bpe43 Bpe43 whole_organism whole_organism genomic_DNA Bpe43 Bpe43 Bpe43 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe43 US Cincinnati outbreak b24n016.txt b24n016_norm.txt A-MEXP-234 b24n016 b24n016 b24n016
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe43 US Cincinnati outbreak b24n016.txt b24n016_norm.txt A-MEXP-234 b24n016 b24n016 b24n016
+vBordetella pertussis Bpe44 Bpe44 whole_organism whole_organism genomic_DNA Bpe44 Bpe44 Bpe44 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe44 US Cincinnati outbreak b24n017.txt b24n017_norm.txt A-MEXP-234 b24n017 b24n017 b24n017
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe44 US Cincinnati outbreak b24n017.txt b24n017_norm.txt A-MEXP-234 b24n017 b24n017 b24n017
+Bordetella pertussis Bpe48 Bpe48 whole_organism whole_organism genomic_DNA Bpe48 Bpe48 Bpe48 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe48 US Cincinnati outbreak b24n020.txt b24n020_norm.txt A-MEXP-234 b24n020 b24n020 b24n020
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe48 US Cincinnati outbreak b24n020.txt b24n020_norm.txt A-MEXP-234 b24n020 b24n020 b24n020
+Bordetella pertussis Bpe50 Bpe50 whole_organism whole_organism genomic_DNA Bpe50 Bpe50 Bpe50 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe50 US Cincinnati non-outbreak b29n105.txt b29n105_norm.txt A-MEXP-236 b29n105 b29n105 b29n105
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe50 US Cincinnati non-outbreak b29n105.txt b29n105_norm.txt A-MEXP-236 b29n105 b29n105 b29n105
+Bordetella pertussis Bpe52 Bpe52 whole_organism whole_organism genomic_DNA Bpe52 Bpe52 Bpe52 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe52 US Cincinnati non-outbreak b24n052.txt b24n052_norm.txt A-MEXP-234 b24n052 b24n052 b24n052
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe52 US Cincinnati non-outbreak b24n052.txt b24n052_norm.txt A-MEXP-234 b24n052 b24n052 b24n052
+Bordetella pertussis Bpe37 Bpe37 whole_organism whole_organism genomic_DNA Bpe37 Bpe37 Bpe37 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe37 US Cincinnati non-outbreak b24n054.txt b24n054_norm.txt A-MEXP-234 b24n054 b24n054 b24n054
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe37 US Cincinnati non-outbreak b24n054.txt b24n054_norm.txt A-MEXP-234 b24n054 b24n054 b24n054
+Bordetella pertussis Bpe33 Bpe33 whole_organism whole_organism genomic_DNA Bpe33 Bpe33 Bpe33 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe33 US Cincinnati non-outbreak b24n113.txt b24n113_norm.txt A-MEXP-234 b24n113 b24n113 b24n113
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe33 US Cincinnati non-outbreak b24n113.txt b24n113_norm.txt A-MEXP-234 b24n113 b24n113 b24n113
+Bordetella pertussis Bpe34 Bpe34 whole_organism whole_organism genomic_DNA Bpe34 Bpe34 Bpe34 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe34 US Cincinnati non-outbreak b24n114.txt b24n114_norm.txt A-MEXP-234 b24n114 b24n114 b24n114
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe34 US Cincinnati non-outbreak b24n114.txt b24n114_norm.txt A-MEXP-234 b24n114 b24n114 b24n114
+Bordetella pertussis Bpe35 Bpe35 whole_organism whole_organism genomic_DNA Bpe35 Bpe35 Bpe35 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe35 US Cincinnati non-outbreak b24n115.txt b24n115_norm.txt A-MEXP-234 b24n115 b24n115 b24n115
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe35 US Cincinnati non-outbreak b24n115.txt b24n115_norm.txt A-MEXP-234 b24n115 b24n115 b24n115
+Bordetella pertussis Bpe36 Bpe36 whole_organism whole_organism genomic_DNA Bpe36 Bpe36 Bpe36 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe36 US Cincinnati non-outbreak b24n116.txt b24n116_norm.txt A-MEXP-234 b24n116 b24n116 b24n116
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe36 US Cincinnati non-outbreak b24n116.txt b24n116_norm.txt A-MEXP-234 b24n116 b24n116 b24n116
+Bordetella pertussis Bpe38 Bpe38 whole_organism whole_organism genomic_DNA Bpe38 Bpe38 Bpe38 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe38 US Cincinnati non-outbreak b24n117.txt b24n117_norm.txt A-MEXP-234 b24n117 b24n117 b24n117
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe38 US Cincinnati non-outbreak b24n117.txt b24n117_norm.txt A-MEXP-234 b24n117 b24n117 b24n117
+Bordetella pertussis Bpe40 Bpe40 whole_organism whole_organism genomic_DNA Bpe40 Bpe40 Bpe40 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe40 US Cincinnati non-outbreak b24n118.txt b24n118_norm.txt A-MEXP-234 b24n118 b24n118 b24n118
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe40 US Cincinnati non-outbreak b24n118.txt b24n118_norm.txt A-MEXP-234 b24n118 b24n118 b24n118
+Bordetella pertussis Bpe47 Bpe47 whole_organism whole_organism genomic_DNA Bpe47 Bpe47 Bpe47 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe47 US Cincinnati non-outbreak b24n120.txt b24n120_norm.txt A-MEXP-234 b24n120 b24n120 b24n120
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe47 US Cincinnati non-outbreak b24n120.txt b24n120_norm.txt A-MEXP-234 b24n120 b24n120 b24n120
+Bordetella pertussis Bpe49 Bpe49 whole_organism whole_organism genomic_DNA Bpe49 Bpe49 Bpe49 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe49 US Cincinnati non-outbreak b24n121.txt b24n121_norm.txt A-MEXP-234 b24n121 b24n121 b24n121
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe49 US Cincinnati non-outbreak b24n121.txt b24n121_norm.txt A-MEXP-234 b24n121 b24n121 b24n121
+Bordetella pertussis Bpe59 Bpe59 whole_organism whole_organism genomic_DNA Bpe59 Bpe59 Bpe59 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe59 US Cincinnati non-outbreak b29n108.txt b29n108_norm.txt A-MEXP-236 b29n108 b29n108 b29n108
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe59 US Cincinnati non-outbreak b29n108.txt b29n108_norm.txt A-MEXP-236 b29n108 b29n108 b29n108
+Bordetella pertussis Bpe58 Bpe58 whole_organism whole_organism genomic_DNA Bpe58 Bpe58 Bpe58 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe58 Other US post-vaccine b29n106.txt b29n106_norm.txt A-MEXP-236 b29n106 b29n106 b29n106
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe58 Other US post-vaccine b29n106.txt b29n106_norm.txt A-MEXP-236 b29n106 b29n106 b29n106
+Bordetella pertussis Bpe45 Bpe45 whole_organism whole_organism genomic_DNA Bpe45 Bpe45 Bpe45 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe45 Other US post-vaccine b24n032.txt b24n032_norm.txt A-MEXP-234 b24n032 b24n032 b24n032
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe45 Other US post-vaccine b24n032.txt b24n032_norm.txt A-MEXP-234 b24n032 b24n032 b24n032
+Bordetella pertussis Bpe329 Bpe329 whole_organism whole_organism genomic_DNA Bpe329 Bpe329 Bpe329 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe329 Other US post-vaccine b29n132.txt b29n132_norm.txt A-MEXP-236 b29n132 b29n132 b29n132
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe329 Other US post-vaccine b29n132.txt b29n132_norm.txt A-MEXP-236 b29n132 b29n132 b29n132
+Bordetella pertussis Bpe39 Bpe39 whole_organism whole_organism genomic_DNA Bpe39 Bpe39 Bpe39 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe39 Other US post-vaccine b23n123.txt b23n123_norm.txt A-MEXP-234 b23n123 b23n123 b23n123
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe39 Other US post-vaccine b23n123.txt b23n123_norm.txt A-MEXP-234 b23n123 b23n123 b23n123
+Bordetella pertussis Bpe42 Bpe42 whole_organism whole_organism genomic_DNA Bpe42 Bpe42 Bpe42 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe42 Other US post-vaccine b23n125.txt b23n125_norm.txt A-MEXP-234 b23n125 b23n125 b23n125
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe42 Other US post-vaccine b23n125.txt b23n125_norm.txt A-MEXP-234 b23n125 b23n125 b23n125
+Bordetella pertussis Bpe241 Bpe241 whole_organism whole_organism genomic_DNA Bpe241 Bpe241 Bpe241 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe241 Other US post-vaccine b29n111.txt b29n111_norm.txt A-MEXP-236 b29n111 b29n111 b29n111
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe241 Other US post-vaccine b29n111.txt b29n111_norm.txt A-MEXP-236 b29n111 b29n111 b29n111
+Bordetella pertussis Bpe46 Bpe46 whole_organism whole_organism genomic_DNA Bpe46 Bpe46 Bpe46 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe46 Other US post-vaccine b24n119.txt b24n119_norm.txt A-MEXP-234 b24n119 b24n119 b24n119
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe46 Other US post-vaccine b24n119.txt b24n119_norm.txt A-MEXP-234 b24n119 b24n119 b24n119
+Bordetella pertussis Bpe280 Bpe280 whole_organism whole_organism genomic_DNA Bpe280 Bpe280 Bpe280 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe280 Other US post-vaccine b29n117.txt b29n117_norm.txt A-MEXP-236 b29n117 b29n117 b29n117
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe280 Other US post-vaccine b29n117.txt b29n117_norm.txt A-MEXP-236 b29n117 b29n117 b29n117
+Bordetella pertussis Bpe281 Bpe281 whole_organism whole_organism genomic_DNA Bpe281 Bpe281 Bpe281 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe281 Other US post-vaccine b29n118.txt b29n118_norm.txt A-MEXP-236 b29n118 b29n118 b29n118
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe281 Other US post-vaccine b29n118.txt b29n118_norm.txt A-MEXP-236 b29n118 b29n118 b29n118
+Bordetella pertussis Bpe319 Bpe319 whole_organism whole_organism genomic_DNA Bpe319 Bpe319 Bpe319 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe319 Other US post-vaccine b29n120.txt b29n120_norm.txt A-MEXP-236 b29n120 b29n120 b29n120
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe319 Other US post-vaccine b29n120.txt b29n120_norm.txt A-MEXP-236 b29n120 b29n120 b29n120
+Bordetella pertussis Bpe320 Bpe320 whole_organism whole_organism genomic_DNA Bpe320 Bpe320 Bpe320 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe320 Other US post-vaccine b29n121.txt b29n121_norm.txt A-MEXP-236 b29n121 b29n121 b29n121
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe320 Other US post-vaccine b29n121.txt b29n121_norm.txt A-MEXP-236 b29n121 b29n121 b29n121
+Bordetella pertussis Bpe321 Bpe321 whole_organism whole_organism genomic_DNA Bpe321 Bpe321 Bpe321 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe321 Other US post-vaccine b29n122.txt b29n122_norm.txt A-MEXP-236 b29n122 b29n122 b29n122
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe321 Other US post-vaccine b29n122.txt b29n122_norm.txt A-MEXP-236 b29n122 b29n122 b29n122
+Bordetella pertussis Bpe322 Bpe322 whole_organism whole_organism genomic_DNA Bpe322 Bpe322 Bpe322 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe322 Other US post-vaccine b29n125.txt b29n125_norm.txt A-MEXP-236 b29n125 b29n125 b29n125
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe322 Other US post-vaccine b29n125.txt b29n125_norm.txt A-MEXP-236 b29n125 b29n125 b29n125
+Bordetella pertussis Bpe323 Bpe323 whole_organism whole_organism genomic_DNA Bpe323 Bpe323 Bpe323 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe323 Other US post-vaccine b29n126.txt b29n126_norm.txt A-MEXP-236 b29n126 b29n126 b29n126
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe323 Other US post-vaccine b29n126.txt b29n126_norm.txt A-MEXP-236 b29n126 b29n126 b29n126
+Bordetella pertussis Bpe324 Bpe324 whole_organism whole_organism genomic_DNA Bpe324 Bpe324 Bpe324 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe324 Other US post-vaccine b29n127.txt b29n127_norm.txt A-MEXP-236 b29n127 b29n127 b29n127
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe324 Other US post-vaccine b29n127.txt b29n127_norm.txt A-MEXP-236 b29n127 b29n127 b29n127
+Bordetella pertussis Bpe325 Bpe325 whole_organism whole_organism genomic_DNA Bpe325 Bpe325 Bpe325 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe325 Other US post-vaccine b29n128.txt b29n128_norm.txt A-MEXP-236 b29n128 b29n128 b29n128
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe325 Other US post-vaccine b29n128.txt b29n128_norm.txt A-MEXP-236 b29n128 b29n128 b29n128
+Bordetella pertussis Bpe326 Bpe326 whole_organism whole_organism genomic_DNA Bpe326 Bpe326 Bpe326 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe326 Other US post-vaccine b29n129.txt b29n129_norm.txt A-MEXP-236 b29n129 b29n129 b29n129
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe326 Other US post-vaccine b29n129.txt b29n129_norm.txt A-MEXP-236 b29n129 b29n129 b29n129
+Bordetella pertussis Bpe327 Bpe327 whole_organism whole_organism genomic_DNA Bpe327 Bpe327 Bpe327 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe327 Other US post-vaccine b29n130.txt b29n130_norm.txt A-MEXP-236 b29n130 b29n130 b29n130
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe327 Other US post-vaccine b29n130.txt b29n130_norm.txt A-MEXP-236 b29n130 b29n130 b29n130
+Bordetella pertussis Bpe328 Bpe328 whole_organism whole_organism genomic_DNA Bpe328 Bpe328 Bpe328 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe328 Other US post-vaccine b29n131.txt b29n131_norm.txt A-MEXP-236 b29n131 b29n131 b29n131
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe328 Other US post-vaccine b29n131.txt b29n131_norm.txt A-MEXP-236 b29n131 b29n131 b29n131
+Bordetella pertussis Bpe330 Bpe330 whole_organism whole_organism genomic_DNA Bpe330 Bpe330 Bpe330 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe330 Other US post-vaccine b31n040.txt b31n040_norm.txt a-mexp-281! b31n040 b31n040 b31n040
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe330 Other US post-vaccine b31n040.txt b31n040_norm.txt a-mexp-281! b31n040 b31n040 b31n040
+Bordetella pertussis Bpe331 Bpe331 whole_organism whole_organism genomic_DNA Bpe331 Bpe331 Bpe331 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe331 Other US post-vaccine b31n041.txt b31n041_norm.txt a-mexp-281! b31n041 b31n041 b31n041
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe331 Other US post-vaccine b31n041.txt b31n041_norm.txt a-mexp-281! b31n041 b31n041 b31n041
+Bordetella pertussis Bpe332 Bpe332 whole_organism whole_organism genomic_DNA Bpe332 Bpe332 Bpe332 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe332 Other US post-vaccine b31n042.txt b31n042_norm.txt a-mexp-281! b31n042 b31n042 b31n042
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe332 Other US post-vaccine b31n042.txt b31n042_norm.txt a-mexp-281! b31n042 b31n042 b31n042
+Bordetella pertussis Bpe333 Bpe333 whole_organism whole_organism genomic_DNA Bpe333 Bpe333 Bpe333 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe333 Other US post-vaccine b31n043.txt b31n043_norm.txt a-mexp-281! b31n043 b31n043 b31n043
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe333 Other US post-vaccine b31n043.txt b31n043_norm.txt a-mexp-281! b31n043 b31n043 b31n043
+Bordetella pertussis Bpe334 Bpe334 whole_organism whole_organism genomic_DNA Bpe334 Bpe334 Bpe334 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe334 Other US post-vaccine b31n044.txt b31n044_norm.txt a-mexp-281! b31n044 b31n044 b31n044
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe334 Other US post-vaccine b31n044.txt b31n044_norm.txt a-mexp-281! b31n044 b31n044 b31n044
+Bordetella pertussis Bpe335 Bpe335 whole_organism whole_organism genomic_DNA Bpe335 Bpe335 Bpe335 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe335 Other US post-vaccine b31n045.txt b31n045_norm.txt a-mexp-281! b31n045 b31n045 b31n045
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe335 Other US post-vaccine b31n045.txt b31n045_norm.txt a-mexp-281! b31n045 b31n045 b31n045
+Bordetella pertussis Bpe336 Bpe336 whole_organism whole_organism genomic_DNA Bpe336 Bpe336 Bpe336 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe336 Other US post-vaccine b31n046.txt b31n046_norm.txt a-mexp-281! b31n046 b31n046 b31n046
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe336 Other US post-vaccine b31n046.txt b31n046_norm.txt a-mexp-281! b31n046 b31n046 b31n046
+Bordetella pertussis Bpe337 Bpe337 whole_organism whole_organism genomic_DNA Bpe337 Bpe337 Bpe337 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe337 Other US post-vaccine b31n047.txt b31n047_norm.txt a-mexp-281! b31n047 b31n047 b31n047
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe337 Other US post-vaccine b31n047.txt b31n047_norm.txt a-mexp-281! b31n047 b31n047 b31n047
+Bordetella pertussis Bpe338 Bpe338 whole_organism whole_organism genomic_DNA Bpe338 Bpe338 Bpe338 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe338 Other US post-vaccine b31n048.txt b31n048_norm.txt a-mexp-281! b31n048 b31n048 b31n048
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe338 Other US post-vaccine b31n048.txt b31n048_norm.txt a-mexp-281! b31n048 b31n048 b31n048
+Bordetella pertussis Bpe339 Bpe339 whole_organism whole_organism genomic_DNA Bpe339 Bpe339 Bpe339 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe339 Other US post-vaccine b31n049.txt b31n049_norm.txt a-mexp-281! b31n049 b31n049 b31n049
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe339 Other US post-vaccine b31n049.txt b31n049_norm.txt a-mexp-281! b31n049 b31n049 b31n049
+Bordetella pertussis Bpe340 Bpe340 whole_organism whole_organism genomic_DNA Bpe340 Bpe340 Bpe340 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe340 Other US post-vaccine b31n050.txt b31n050_norm.txt a-mexp-281! b31n050 b31n050 b31n050
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe340 Other US post-vaccine b31n050.txt b31n050_norm.txt a-mexp-281! b31n050 b31n050 b31n050
+Bordetella pertussis Bpe341 Bpe341 whole_organism whole_organism genomic_DNA Bpe341 Bpe341 Bpe341 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe341 Other US post-vaccine b31n051.txt b31n051_norm.txt a-mexp-281! b31n051 b31n051 b31n051
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe341 Other US post-vaccine b31n051.txt b31n051_norm.txt a-mexp-281! b31n051 b31n051 b31n051
+Bordetella pertussis Bpe342 Bpe342 whole_organism whole_organism genomic_DNA Bpe342 Bpe342 Bpe342 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe342 Other US post-vaccine b31n052.txt b31n052_norm.txt a-mexp-281! b31n052 b31n052 b31n052
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe342 Other US post-vaccine b31n052.txt b31n052_norm.txt a-mexp-281! b31n052 b31n052 b31n052
+Bordetella pertussis Bpe343 Bpe343 whole_organism whole_organism genomic_DNA Bpe343 Bpe343 Bpe343 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe343 Other US post-vaccine b31n053.txt b31n053_norm.txt a-mexp-281! b31n053 b31n053 b31n053
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe343 Other US post-vaccine b31n053.txt b31n053_norm.txt a-mexp-281! b31n053 b31n053 b31n053
+Bordetella pertussis Bpe344 Bpe344 whole_organism whole_organism genomic_DNA Bpe344 Bpe344 Bpe344 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe344 Other US post-vaccine b31n054.txt b31n054_norm.txt a-mexp-281! b31n054 b31n054 b31n054
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe344 Other US post-vaccine b31n054.txt b31n054_norm.txt a-mexp-281! b31n054 b31n054 b31n054
+Bordetella pertussis Bpe345 Bpe345 whole_organism whole_organism genomic_DNA Bpe345 Bpe345 Bpe345 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe345 Other US post-vaccine b31n055.txt b31n055_norm.txt a-mexp-281! b31n055 b31n055 b31n055
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe345 Other US post-vaccine b31n055.txt b31n055_norm.txt a-mexp-281! b31n055 b31n055 b31n055
+Bordetella pertussis Bpe346 Bpe346 whole_organism whole_organism genomic_DNA Bpe346 Bpe346 Bpe346 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe346 Other US post-vaccine b31n056.txt b31n056_norm.txt a-mexp-281! b31n056 b31n056 b31n056
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe346 Other US post-vaccine b31n056.txt b31n056_norm.txt a-mexp-281! b31n056 b31n056 b31n056
+Bordetella pertussis Bpe347 Bpe347 whole_organism whole_organism genomic_DNA Bpe347 Bpe347 Bpe347 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe347 Other US post-vaccine b31n057.txt b31n057_norm.txt a-mexp-281! b31n057 b31n057 b31n057
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe347 Other US post-vaccine b31n057.txt b31n057_norm.txt a-mexp-281! b31n057 b31n057 b31n057
+Bordetella pertussis Bpe348 Bpe348 whole_organism whole_organism genomic_DNA Bpe348 Bpe348 Bpe348 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe348 Other US post-vaccine b31n058.txt b31n058_norm.txt a-mexp-281! b31n058 b31n058 b31n058
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe348 Other US post-vaccine b31n058.txt b31n058_norm.txt a-mexp-281! b31n058 b31n058 b31n058
+Bordetella pertussis Bpe349 Bpe349 whole_organism whole_organism genomic_DNA Bpe349 Bpe349 Bpe349 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe349 Other US post-vaccine b31n059.txt b31n059_norm.txt a-mexp-281! b31n059 b31n059 b31n059
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe349 Other US post-vaccine b31n059.txt b31n059_norm.txt a-mexp-281! b31n059 b31n059 b31n059
+Bordetella pertussis Bpe350 Bpe350 whole_organism whole_organism genomic_DNA Bpe350 Bpe350 Bpe350 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe350 Other US post-vaccine b31n060.txt b31n060_norm.txt a-mexp-281! b31n060 b31n060 b31n060
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe350 Other US post-vaccine b31n060.txt b31n060_norm.txt a-mexp-281! b31n060 b31n060 b31n060
+Bordetella pertussis Bpe351 Bpe351 whole_organism whole_organism genomic_DNA Bpe351 Bpe351 Bpe351 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe351 Other US post-vaccine b31n061.txt b31n061_norm.txt a-mexp-281! b31n061 b31n061 b31n061
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe351 Other US post-vaccine b31n061.txt b31n061_norm.txt a-mexp-281! b31n061 b31n061 b31n061
+Bordetella pertussis Bpe352 Bpe352 whole_organism whole_organism genomic_DNA Bpe352 Bpe352 Bpe352 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe352 Other US post-vaccine b31n062.txt b31n062_norm.txt a-mexp-281! b31n062 b31n062 b31n062
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe352 Other US post-vaccine b31n062.txt b31n062_norm.txt a-mexp-281! b31n062 b31n062 b31n062
+Bordetella pertussis Bpe353 Bpe353 whole_organism whole_organism genomic_DNA Bpe353 Bpe353 Bpe353 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe353 Other US post-vaccine b31n063.txt b31n063_norm.txt a-mexp-281! b31n063 b31n063 b31n063
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe353 Other US post-vaccine b31n063.txt b31n063_norm.txt a-mexp-281! b31n063 b31n063 b31n063
+Bordetella pertussis Bpe159 Bpe159 whole_organism whole_organism genomic_DNA Bpe159 Bpe159 Bpe159 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe159 Other US post-vaccine b23n006.txt b23n006_norm.txt A-MEXP-234 b23n006 b23n006 b23n006
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe159 Other US post-vaccine b23n006.txt b23n006_norm.txt A-MEXP-234 b23n006 b23n006 b23n006
+Bordetella pertussis Bpe205 Bpe205 whole_organism whole_organism genomic_DNA Bpe205 Bpe205 Bpe205 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe205 Dutch pre-vaccine b23n083.txt b23n083_norm.txt A-MEXP-234 b23n083 b23n083 b23n083
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe205 Dutch pre-vaccine b23n083.txt b23n083_norm.txt A-MEXP-234 b23n083 b23n083 b23n083
+Bordetella pertussis Bpe186 Bpe186 whole_organism whole_organism genomic_DNA Bpe186 Bpe186 Bpe186 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe186 Dutch pre-vaccine b23n109.txt b23n109_norm.txt A-MEXP-234 b23n109 b23n109 b23n109
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe186 Dutch pre-vaccine b23n109.txt b23n109_norm.txt A-MEXP-234 b23n109 b23n109 b23n109
+Bordetella pertussis Bpe191 Bpe191 whole_organism whole_organism genomic_DNA Bpe191 Bpe191 Bpe191 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe191 Dutch pre-vaccine b23n114.txt b23n114_norm.txt A-MEXP-234 b23n114 b23n114 b23n114
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe191 Dutch pre-vaccine b23n114.txt b23n114_norm.txt A-MEXP-234 b23n114 b23n114 b23n114
+Bordetella pertussis Bpe202 Bpe202 whole_organism whole_organism genomic_DNA Bpe202 Bpe202 Bpe202 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe202 Dutch pre-vaccine b24n061.txt b24n061_norm.txt A-MEXP-234 b24n061 b24n061 b24n061
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe202 Dutch pre-vaccine b24n061.txt b24n061_norm.txt A-MEXP-234 b24n061 b24n061 b24n061
+Bordetella pertussis Bpe197 Bpe197 whole_organism whole_organism genomic_DNA Bpe197 Bpe197 Bpe197 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe197 Dutch pre-vaccine b24n056.txt b24n056_norm.txt A-MEXP-234 b24n056 b24n056 b24n056
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe197 Dutch pre-vaccine b24n056.txt b24n056_norm.txt A-MEXP-234 b24n056 b24n056 b24n056
+Bordetella pertussis Bpe182 Bpe182 whole_organism whole_organism genomic_DNA Bpe182 Bpe182 Bpe182 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe182 Dutch pre-vaccine b23n105.txt b23n105_norm.txt A-MEXP-234 b23n105 b23n105 b23n105
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe182 Dutch pre-vaccine b23n105.txt b23n105_norm.txt A-MEXP-234 b23n105 b23n105 b23n105
+Bordetella pertussis Bpe195 Bpe195 whole_organism whole_organism genomic_DNA Bpe195 Bpe195 Bpe195 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe195 Dutch pre-vaccine b23n118.txt b23n118_norm.txt A-MEXP-234 b23n118 b23n118 b23n118
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe195 Dutch pre-vaccine b23n118.txt b23n118_norm.txt A-MEXP-234 b23n118 b23n118 b23n118
+Bordetella pertussis Bpe196 Bpe196 whole_organism whole_organism genomic_DNA Bpe196 Bpe196 Bpe196 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe196 Dutch pre-vaccine b23n119.txt b23n119_norm.txt A-MEXP-234 b23n119 b23n119 b23n119
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe196 Dutch pre-vaccine b23n119.txt b23n119_norm.txt A-MEXP-234 b23n119 b23n119 b23n119
+Bordetella pertussis Bpe179 Bpe179 whole_organism whole_organism genomic_DNA Bpe179 Bpe179 Bpe179 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe179 Dutch pre-vaccine b23n101.txt b23n101_norm.txt A-MEXP-234 b23n101 b23n101 b23n101
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe179 Dutch pre-vaccine b23n101.txt b23n101_norm.txt A-MEXP-234 b23n101 b23n101 b23n101
+Bordetella pertussis Bpe1 Bpe1 whole_organism whole_organism genomic_DNA Bpe1 Bpe1 Bpe1 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe1 Dutch post-vaccine b29n101.txt b29n101_norm.txt A-MEXP-236 b29n101 b29n101 b29n101
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe1 Dutch post-vaccine b29n101.txt b29n101_norm.txt A-MEXP-236 b29n101 b29n101 b29n101
+Bordetella pertussis Bpe11 Bpe11 whole_organism whole_organism genomic_DNA Bpe11 Bpe11 Bpe11 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe11 Dutch post-vaccine b29n102.txt b29n102_norm.txt A-MEXP-236 b29n102 b29n102 b29n102
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe11 Dutch post-vaccine b29n102.txt b29n102_norm.txt A-MEXP-236 b29n102 b29n102 b29n102
+Bordetella pertussis Bpe14 Bpe14 whole_organism whole_organism genomic_DNA Bpe14 Bpe14 Bpe14 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe14 Dutch post-vaccine b29n103.txt b29n103_norm.txt A-MEXP-236 b29n103 b29n103 b29n103
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe14 Dutch post-vaccine b29n103.txt b29n103_norm.txt A-MEXP-236 b29n103 b29n103 b29n103
+Bordetella pertussis Bpe15 Bpe15 whole_organism whole_organism genomic_DNA Bpe15 Bpe15 Bpe15 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe15 Dutch post-vaccine b29n104.txt b29n104_norm.txt A-MEXP-236 b29n104 b29n104 b29n104
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe15 Dutch post-vaccine b29n104.txt b29n104_norm.txt A-MEXP-236 b29n104 b29n104 b29n104
+Bordetella pertussis Bpe206 Bpe206 whole_organism whole_organism genomic_DNA Bpe206 Bpe206 Bpe206 synthetic_DNA Cy5 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9967 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe206 Dutch post-vaccine b23n085.txt b23n085_norm.txt A-MEXP-234 b23n085 b23n085 b23n085
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe206 Dutch post-vaccine b23n085.txt b23n085_norm.txt A-MEXP-234 b23n085 b23n085 b23n085
+Bordetella pertussis Bpe180 Bpe180 whole_organism whole_organism genomic_DNA Bpe180 Bpe180 Bpe180 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe180 Dutch post-vaccine b23n103.txt b23n103_norm.txt A-MEXP-234 b23n103 b23n103 b23n103
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe180 Dutch post-vaccine b23n103.txt b23n103_norm.txt A-MEXP-234 b23n103 b23n103 b23n103
+Bordetella pertussis Bpe181 Bpe181 whole_organism whole_organism genomic_DNA Bpe181 Bpe181 Bpe181 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe181 Dutch post-vaccine b23n104.txt b23n104_norm.txt A-MEXP-234 b23n104 b23n104 b23n104
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe181 Dutch post-vaccine b23n104.txt b23n104_norm.txt A-MEXP-234 b23n104 b23n104 b23n104
+Bordetella pertussis Bpe183 Bpe183 whole_organism whole_organism genomic_DNA Bpe183 Bpe183 Bpe183 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe183 Dutch post-vaccine b23n106.txt b23n106_norm.txt A-MEXP-234 b23n106 b23n106 b23n106
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe183 Dutch post-vaccine b23n106.txt b23n106_norm.txt A-MEXP-234 b23n106 b23n106 b23n106
+Bordetella pertussis Bpe184 Bpe184 whole_organism whole_organism genomic_DNA Bpe184 Bpe184 Bpe184 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe184 Dutch post-vaccine b23n107.txt b23n107_norm.txt A-MEXP-234 b23n107 b23n107 b23n107
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe184 Dutch post-vaccine b23n107.txt b23n107_norm.txt A-MEXP-234 b23n107 b23n107 b23n107
+Bordetella pertussis Bpe185 Bpe185 whole_organism whole_organism genomic_DNA Bpe185 Bpe185 Bpe185 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe185 Dutch post-vaccine b23n108.txt b23n108_norm.txt A-MEXP-234 b23n108 b23n108 b23n108
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe185 Dutch post-vaccine b23n108.txt b23n108_norm.txt A-MEXP-234 b23n108 b23n108 b23n108
+Bordetella pertussis Bpe187 Bpe187 whole_organism whole_organism genomic_DNA Bpe187 Bpe187 Bpe187 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe187 Dutch post-vaccine b23n110.txt b23n110_norm.txt A-MEXP-234 b23n110 b23n110 b23n110
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe187 Dutch post-vaccine b23n110.txt b23n110_norm.txt A-MEXP-234 b23n110 b23n110 b23n110
+Bordetella pertussis Bpe188 Bpe188 whole_organism whole_organism genomic_DNA Bpe188 Bpe188 Bpe188 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe188 Dutch post-vaccine b23n111.txt b23n111_norm.txt A-MEXP-234 b23n111 b23n111 b23n111
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe188 Dutch post-vaccine b23n111.txt b23n111_norm.txt A-MEXP-234 b23n111 b23n111 b23n111
+Bordetella pertussis Bpe189 Bpe189 whole_organism whole_organism genomic_DNA Bpe189 Bpe189 Bpe189 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe189 Dutch post-vaccine b23n112.txt b23n112_norm.txt A-MEXP-234 b23n112 b23n112 b23n112
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe189 Dutch post-vaccine b23n112.txt b23n112_norm.txt A-MEXP-234 b23n112 b23n112 b23n112
+Bordetella pertussis Bpe190 Bpe190 whole_organism whole_organism genomic_DNA Bpe190 Bpe190 Bpe190 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe190 Dutch post-vaccine b23n113.txt b23n113_norm.txt A-MEXP-234 b23n113 b23n113 b23n113
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe190 Dutch post-vaccine b23n113.txt b23n113_norm.txt A-MEXP-234 b23n113 b23n113 b23n113
+Bordetella pertussis Bpe192 Bpe192 whole_organism whole_organism genomic_DNA Bpe192 Bpe192 Bpe192 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe192 Dutch post-vaccine b23n115.txt b23n115_norm.txt A-MEXP-234 b23n115 b23n115 b23n115
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe192 Dutch post-vaccine b23n115.txt b23n115_norm.txt A-MEXP-234 b23n115 b23n115 b23n115
+Bordetella pertussis Bpe193 Bpe193 whole_organism whole_organism genomic_DNA Bpe193 Bpe193 Bpe193 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe193 Dutch post-vaccine b23n116.txt b23n116_norm.txt A-MEXP-234 b23n116 b23n116 b23n116
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe193 Dutch post-vaccine b23n116.txt b23n116_norm.txt A-MEXP-234 b23n116 b23n116 b23n116
+Bordetella pertussis Bpe194 Bpe194 whole_organism whole_organism genomic_DNA Bpe194 Bpe194 Bpe194 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe194 Dutch post-vaccine b23n117.txt b23n117_norm.txt A-MEXP-234 b23n117 b23n117 b23n117
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe194 Dutch post-vaccine b23n117.txt b23n117_norm.txt A-MEXP-234 b23n117 b23n117 b23n117
+Bordetella pertussis Bpe204 Bpe204 whole_organism whole_organism genomic_DNA Bpe204 Bpe204 Bpe204 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe204 Dutch post-vaccine b24n009.txt b24n009_norm.txt A-MEXP-234 b24n009 b24n009 b24n009
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe204 Dutch post-vaccine b24n009.txt b24n009_norm.txt A-MEXP-234 b24n009 b24n009 b24n009
+Bordetella pertussis Bpe198 Bpe198 whole_organism whole_organism genomic_DNA Bpe198 Bpe198 Bpe198 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe198 Dutch post-vaccine b24n057.txt b24n057_norm.txt A-MEXP-234 b24n057 b24n057 b24n057
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe198 Dutch post-vaccine b24n057.txt b24n057_norm.txt A-MEXP-234 b24n057 b24n057 b24n057
+Bordetella pertussis Bpe203 Bpe203 whole_organism whole_organism genomic_DNA Bpe203 Bpe203 Bpe203 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe203 Dutch post-vaccine b29n110.txt b29n110_norm.txt A-MEXP-236 b29n110 b29n110 b29n110
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe203 Dutch post-vaccine b29n110.txt b29n110_norm.txt A-MEXP-236 b29n110 b29n110 b29n110
+Bordetella pertussis Bpe199 Bpe199 whole_organism whole_organism genomic_DNA Bpe199 Bpe199 Bpe199 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe199 Dutch post-vaccine b24n058.txt b24n058_norm.txt A-MEXP-234 b24n058 b24n058 b24n058
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe199 Dutch post-vaccine b24n058.txt b24n058_norm.txt A-MEXP-234 b24n058 b24n058 b24n058
+Bordetella pertussis Bpe200 Bpe200 whole_organism whole_organism genomic_DNA Bpe200 Bpe200 Bpe200 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965v Bpe200 Dutch post-vaccine b24n059.txt b24n059_norm.txt A-MEXP-234 b24n059 b24n059 b24n059
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe200 Dutch post-vaccine b24n059.txt b24n059_norm.txt A-MEXP-234 b24n059 b24n059 b24n059
+Bordetella pertussis WCH01 WCH01 whole_organism whole_organism genomic_DNA WCH01 WCH01 WCH01 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH01 Australian b23n086.txt b23n086_norm.txt A-MEXP-234 b23n086 b23n086 b23n086
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH01 Australian b23n086.txt b23n086_norm.txt A-MEXP-234 b23n086 b23n086 b23n086
+Bordetella pertussis WCH02 WCH02 whole_organism whole_organism genomic_DNA WCH02 WCH02 WCH02 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH02 Australian b23n087.txt b23n087_norm.txt A-MEXP-234 b23n087 b23n087 b23n087
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH02 Australian b23n087.txt b23n087_norm.txt A-MEXP-234 b23n087 b23n087 b23n087
+Bordetella pertussis WCH03 WCH03 whole_organism whole_organism genomic_DNA WCH03 WCH03 WCH03 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH03 Australian b23n088.txt b23n088_norm.txt A-MEXP-234 b23n088 b23n088 b23n088
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH03 Australian b23n088.txt b23n088_norm.txt A-MEXP-234 b23n088 b23n088 b23n088
+Bordetella pertussis WCH04 WCH04 whole_organism whole_organism genomic_DNA WCH04 WCH04 WCH04 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH04 Australian b23n089.txt b23n089_norm.txt A-MEXP-234 b23n089 b23n089 b23n089
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH04 Australian b23n089.txt b23n089_norm.txt A-MEXP-234 b23n089 b23n089 b23n089
+Bordetella pertussis WCH07 WCH07 whole_organism whole_organism genomic_DNA WCH07 WCH07 WCH07 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH07 Australian b23n090.txt b23n090_norm.txt A-MEXP-234 b23n090 b23n090 b23n090
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH07 Australian b23n090.txt b23n090_norm.txt A-MEXP-234 b23n090 b23n090 b23n090
+Bordetella pertussis WCH08 WCH08 whole_organism whole_organism genomic_DNA WCH08 WCH08 WCH08 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH08 Australian b23n091.txt b23n091_norm.txt A-MEXP-234 b23n091 b23n091 b23n091
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH08 Australian b23n091.txt b23n091_norm.txt A-MEXP-234 b23n091 b23n091 b23n091
+Bordetella pertussis WCH23 WCH23 whole_organism whole_organism genomic_DNA WCH23 WCH23 WCH23 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH23 Australian b23n092.txt b23n092_norm.txt A-MEXP-234 b23n092 b23n092 b23n092
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH23 Australian b23n092.txt b23n092_norm.txt A-MEXP-234 b23n092 b23n092 b23n092
+Bordetella pertussis WCH05 WCH05 whole_organism whole_organism genomic_DNA WCH05 WCH05 WCH05 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH05 Australian b24n085.txt b24n085_norm.txt A-MEXP-234 b24n085 b24n085 b24n085
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH05 Australian b24n085.txt b24n085_norm.txt A-MEXP-234 b24n085 b24n085 b24n085
+Bordetella pertussis WCH06 WCH06 whole_organism whole_organism genomic_DNA WCH06 WCH06 WCH06 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH06 Australian b24n086.txt b24n086_norm.txt A-MEXP-234 b24n086 b24n086 b24n086
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH06 Australian b24n086.txt b24n086_norm.txt A-MEXP-234 b24n086 b24n086 b24n086
+Bordetella pertussis WCH11 WCH11 whole_organism whole_organism genomic_DNA WCH11 WCH11 WCH11 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH11 Australian b24n087.txt b24n087_norm.txt A-MEXP-234 b24n087 b24n087 b24n087
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH11 Australian b24n087.txt b24n087_norm.txt A-MEXP-234 b24n087 b24n087 b24n087
+Bordetella pertussis WCH12 WCH12 whole_organism whole_organism genomic_DNA WCH12 WCH12 WCH12 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH12 Australian b24n088.txt b24n088_norm.txt A-MEXP-234 b24n088 b24n088 b24n088
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH12 Australian b24n088.txt b24n088_norm.txt A-MEXP-234 b24n088 b24n088 b24n088
+Bordetella pertussis WCH19 WCH19 whole_organism whole_organism genomic_DNA WCH19 WCH19 WCH19 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH19 Australian b24n089.txt b24n089_norm.txt A-MEXP-234 b24n089 b24n089 b24n089
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH19 Australian b24n089.txt b24n089_norm.txt A-MEXP-234 b24n089 b24n089 b24n089
+Bordetella pertussis WCH20 WCH20 whole_organism whole_organism genomic_DNA WCH20 WCH20 WCH20 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH20 Australian b24n090.txt b24n090_norm.txt A-MEXP-234 b24n090 b24n090 b24n090
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH20 Australian b24n090.txt b24n090_norm.txt A-MEXP-234 b24n090 b24n090 b24n090
+Bordetella pertussis WCH21 WCH21 whole_organism whole_organism genomic_DNA WCH21 WCH21 WCH21 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH21 Australian b24n091.txt b24n091_norm.txt A-MEXP-234 b24n091 b24n091 b24n091
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH21 Australian b24n091.txt b24n091_norm.txt A-MEXP-234 b24n091 b24n091 b24n091
+Bordetella pertussis WCH22 WCH22 whole_organism whole_organism genomic_DNA WCH22 WCH22 WCH22 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH22 Australian b24n092.txt b24n092_norm.txt A-MEXP-234 b24n092 b24n092 b24n092
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH22 Australian b24n092.txt b24n092_norm.txt A-MEXP-234 b24n092 b24n092 b24n092
+Bordetella pertussis WCH24 WCH24 whole_organism whole_organism genomic_DNA WCH24 WCH24 WCH24 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH24 Australian b24n093.txt b24n093_norm.txt A-MEXP-234 b24n093 b24n093 b24n093
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 WCH24 Australian b24n093.txt b24n093_norm.txt A-MEXP-234 b24n093 b24n093 b24n093
+Bordetella pertussis 3588 3588 whole_organism whole_organism genomic_DNA 3588 3588 3588 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 3588 Italian (unvaccinated children) b23n043.txt b23n043_norm.txt A-MEXP-234 b23n043 b23n043 b23n043
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 3588 Italian (unvaccinated children) b23n043.txt b23n043_norm.txt A-MEXP-234 b23n043 b23n043 b23n043
+Bordetella pertussis 10641 10641 whole_organism whole_organism genomic_DNA 10641 10641 10641 synthetic_DvNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 10641 Italian (unvaccinated children) b23n044.txt b23n044_norm.txt A-MEXP-234 b23n044 b23n044 b23n044
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 10641 Italian (unvaccinated children) b23n044.txt b23n044_norm.txt A-MEXP-234 b23n044 b23n044 b23n044
+Bordetella pertussis 15550 15550 whole_organism whole_organism genomic_DNA 15550 15550 15550 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 15550 Italian (unvaccinated children) b23n047.txt b23n047_norm.txt A-MEXP-234 b23n047 b23n047 b23n047
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 15550 Italian (unvaccinated children) b23n047.txt b23n047_norm.txt A-MEXP-234 b23n047 b23n047 b23n047
+Bordetella pertussis 9859 9859 whole_organism whole_organism genomic_DNA 9859 9859 9859 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 9859 Italian (unvaccinated children) b23n051.txt b23n051_norm.txt A-MEXP-234 b23n051 b23n051 b23n051
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 9859 Italian (unvaccinated children) b23n051.txt b23n051_norm.txt A-MEXP-234 b23n051 b23n051 b23n051
+Bordetella pertussis 4558 4558 whole_organism whole_organism genomic_DNA 4558 4558 4558 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 4558 Italian (unvaccinated children) b23n058.txt b23n058_norm.txt A-MEXP-234 b23n058 b23n058 b23n058
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 4558 Italian (unvaccinated children) b23n058.txt b23n058_norm.txt A-MEXP-234 b23n058 b23n058 b23n058
+Bordetella pertussis Bp01568 Bp01568 whole_organism whole_organism genomic_DNA Bp01568 Bp01568 Bp01568 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp01568 Italian (unvaccinated children) b24n011.txt b24n011_norm.txt A-MEXP-234 b24n011 b24n011 b24n011
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp01568 Italian (unvaccinated children) b24n011.txt b24n011_norm.txt A-MEXP-234 b24n011 b24n011 b24n011
+Bordetella pertussis Bp12892 Bp12892 whole_organism whole_organism genomic_DNA Bp12892 Bp12892 Bp12892 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp12892 Italian (unvaccinated children) b24n012.txt b24n012_norm.txt A-MEXP-234 b24n012 b24n012 b24n012
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp12892 Italian (unvaccinated children) b24n012.txt b24n012_norm.txt A-MEXP-234 b24n012 b24n012 b24n012
+Bordetella pertussis Bp05275 Bp05275 whole_organism whole_organism genomic_DNA Bp05275 Bp05275 Bp05275 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp05275 Italian (unvaccinated children) b24n013.txt b24n013_norm.txt A-MEXP-234 b24n013 b24n013 b24n013
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp05275 Italian (unvaccinated children) b24n013.txt b24n013_norm.txt A-MEXP-234 b24n013 b24n013 b24n013
+Bordetella pertussis Bp13732 Bp13732 whole_organism whole_organism genomic_DNA Bp13732 Bp13732 Bp13732 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp13732 Italian (unvaccinated children) b24n080.txt b24n080_norm.txt A-MEXP-234 b24n080 b24n080 b24n080
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp13732 Italian (unvaccinated children) b24n080.txt b24n080_norm.txt A-MEXP-234 b24n080 b24n080 b24n080
+Bordetella pertussis Bp12262 Bp12262 whole_organism whole_organism genomic_DNA Bp12262 Bp12262 Bp12262 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp12262 Italian (unvaccinated children) b24n082.txt b24n082_norm.txt A-MEXP-234 b24n082 b24n082 b24n082
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp12262 Italian (unvaccinated children) b24n082.txt b24n082_norm.txt A-MEXP-234 b24n082 b24n082 b24n082
+Bordetella pertussis Bp15622 Bp15622 whole_organism whole_organism genomic_DNA Bp15622 Bp15622 Bp15622 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp15622 Italian (unvaccinated children) b24n083.txt b24n083_norm.txt A-MEXP-234 b24n083 b24n083 b24n083
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp15622 Italian (unvaccinated children) b24n083.txt b24n083_norm.txt A-MEXP-234 b24n083 b24n083 b24n083
+Bordetella pertussis 3520 3520 whole_organism whole_organism genomic_DNA 3520 3520 3520 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 3520 Italian (vaccinated children) b23n041.txt b23n041_norm.txt A-MEXP-234 b23n041 b23n041 b23n041
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 3520 Italian (vaccinated children) b23n041.txt b23n041_norm.txt A-MEXP-234 b23n041 b23n041 b23n041
+Bordetella pertussis 9878 9878 whole_organism whole_organism genomic_DNA 9878 9878 9878 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 9878 Italian (vaccinated children) b23n042.txt b23n042_norm.txt A-MEXP-234 b23n042 b23n042 b23n042
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 9878 Italian (vaccinated children) b23n042.txt b23n042_norm.txt A-MEXP-234 b23n042 b23n042 b23n042
+Bordetella pertussis 15637 15637 whole_organism whole_organism genomic_DNA 15637 15637 15637 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 15637 Italian (vaccinated children) b23n050.txt b23n050_norm.txt A-MEXP-234 b23n050 b23n050 b23n050
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 15637 Italian (vaccinated children) b23n050.txt b23n050_norm.txt A-MEXP-234 b23n050 b23n050 b23n050
+Bordetella pertussis 6453 6453 whole_organism whole_organism genomic_DNA 6453 6453 6453 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 6453 Italian (vaccinated children) b23n052.txt b23n052_norm.txt A-MEXP-234 b23n052 b23n052 b23n052
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 6453 Italian (vaccinated children) b23n052.txt b23n052_norm.txt A-MEXP-234 b23n052 b23n052 b23n052
+Bordetella pertussis 9585 9585 whole_organism whole_organism genomic_DNA 9585 9585 9585 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 9585 Italian (vaccinated children) b23n056.txt b23n056_norm.txt A-MEXP-234 b23n056 b23n056 b23n056
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 9585 Italian (vaccinated children) b23n056.txt b23n056_norm.txt A-MEXP-234 b23n056 b23n056 b23n056
+Bordetella pertussis 14782 14782 whole_organism whole_organism genomic_DNA 14782 14782 14782 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 14782 Italian (vaccinated children) b23n057.txt b23n057_norm.txt A-MEXP-234 b23n057 b23n057 b23n057
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 14782 Italian (vaccinated children) b23n057.txt b23n057_norm.txt A-MEXP-234 b23n057 b23n057 b23n057
+Bordetella pertussis 12464 12464 whole_organism whole_organism genomic_DNA 12464 12464 12464 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 12464 Italian (vaccinated children) b23n059.txt b23n059_norm.txt A-MEXP-234 b23n059 b23n059 b23n059
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 12464 Italian (vaccinated children) b23n059.txt b23n059_norm.txt A-MEXP-234 b23n059 b23n059 b23n059
+Bordetella pertussis Bp01994 Bp01994 whole_organism whole_organism genomic_DNA Bp01994 Bp01994 Bp01994 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp01994 Italian (vaccinated children) b24n076.txt b24n076_norm.txt A-MEXP-234 b24n076 b24n076 b24n076
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp01994 Italian (vaccinated children) b24n076.txt b24n076_norm.txt A-MEXP-234 b24n076 b24n076 b24n076
+Bordetella pertussis Bp03975 Bp03975 whole_organism whole_organism genomic_DNA Bp03975 Bp03975 Bp03975 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp03975 Italian (vaccinated children) b24n081.txt b24n081_norm.txt A-MEXP-234 b24n081 b24n081 b24n081
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp03975 Italian (vaccinated children) b24n081.txt b24n081_norm.txt A-MEXP-234 b24n081 b24n081 b24n081
+Bordetella pertussis Bp04844 Bp04844 whole_organism whole_organism genomic_DNA Bp04844 Bp04844 Bp04844 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp04844 Italian (vaccinated children) b24n084.txt b24n084_norm.txt A-MEXP-234 b24n084 b24n084 b24n084
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bp04844 Italian (vaccinated children) b24n084.txt b24n084_norm.txt A-MEXP-234 b24n084 b24n084 b24n084
+Bordetella pertussis Bpe53 Bpe53 whole_organism whole_organism genomic_DNA Bpe53 Bpe53 Bpe53 synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe53 18323 laboratory strain b23n093.txt b23n093_norm.txt A-MEXP-234 b23n093 b23n093 b23n093
+B. bronchiseptica + B. parapertussis + B. pertussis Bordetella reference strains Bordetella reference strains whole_organism whole_organism genomic_DNA DNA reference DNA reference DNA reference synthetic_DNA Cy3 P-MEXP-9739 P-MEXP-9744 P-MEXP-9958 P-MEXP-9745 P-MEXP-9746 P-MEXP-9747 P-MEXP-9965 Bpe53 18323 laboratory strain b23n093.txt b23n093_norm.txt A-MEXP-234 b23n093 b23n093 b23n093
diff --git a/examples/real/E-TABM-66.png b/examples/real/E-TABM-66.png
new file mode 100644
index 0000000..14689e0
Binary files /dev/null and b/examples/real/E-TABM-66.png differ
diff --git a/examples/real/E-TABM-66.txt b/examples/real/E-TABM-66.txt
new file mode 100755
index 0000000..9d60e7f
--- /dev/null
+++ b/examples/real/E-TABM-66.txt
@@ -0,0 +1,184 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-66
+quality_control
+experiment_design_type cell_type_comparison_design
+name Comparison of normal and malignant human breast epithelial cells
+description "Multiple genome-wide microarrays have been used to analyse the transcriptomes of immunomagnetically separated normal human luminal epithelial and myoepithelial cells, as well as primary malignant breast epithelial cells."
+release_date
+submission_date
+submitter Anita Grigoriadis
+organization
+publication_title
+authors
+journal
+volume
+issue
+pages
+year
+
+Protocol section
+accession text name type parameters
+P-MEXP-10843 "100 ug total RNA of 352,453,463,621,688,697,723,729,794,1098 were combined." Myoepithelial pool pool
+P-MEXP-10844 "100 ug total RNA of 723,729,697,822,1016,1031,1040,1053,1123,1130 were combined." Luminal_epithelial_pool pool
+P-MEXP-10845 "50ug total RNA of 489,552,593,695,725,842,845,896,983,1000,1033,1045,1052,1062,1063 were combined" F19_breast_tumour_pool pool
+P-TABM-AnitaG-10115 "1. One round of amplification through to dye coupling reaction performed according to Amino Allyl MessageAmp aRNA Kit protocol (Ambion, cat #1752 http://www.ambion.com/techlib/prot/fm_1752.pdf ). Cy3 monoreactive dye (Amersham, cat# PA23001) or Cy5 (Amersham, cat# PA25001) used to label samples.<br>2. Rneasy Minelute Cleanup Kit (QIAGEN, cat #74204 http://www1.qiagen.com/literature/handbooks/PDF/RNACleanupAndConcentration/RNY_MinElute_Cleanup/1023760_HBRNYME0303W [...]
+P-MEXP-14985 "For detail see CodeLink Gene Expression System:Manual Labelled cRNA Target Preparation:<br><br>Assessment of total RNA<br><br>1.1 See Appendix 2 and 3 to assess the quantity and quality of total RNA starting material prior to use in .rst-strand cDNA synthesis reaction.<br><br>Synthesis of first-strand cDNA<br><br>2.1 Prepare a working solution of bacterial control mRNAs (Appendix 4).<br><br>2.2 Prepare each total RNA sample for manual target preparation: 0.2 .2 ug total RNA [...]
+P-MEXP-14987 "For details see CodeLink Gene Expression System: Single-Assay Bioarray Hybridization and Detection<br><br>Protocol for hybridization and detection<br><br>1<br><br>Fragmentation of cRNA<br><br>1.1 For each bioarray to be loaded, bring 10 ug of cRNA (from step 6.13 of CodeLink target preparation protocol) to a final volume of 20 ul with nuclease-free water in a<br><br>thin-walled microcentrifuge tube.<br><br>1.2 Add 5 ul of 5fragmentation buffer for each bioarray. Place tube [...]
+P-MEXP-14988 "GenePix scanner, 5 micron resolution, 600PMT, 100%Power" CodeLink Scanning image_acquisition
+
+Hybridization section
+index BioSource BioSourceMaterial BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[BioSourceProvider] BioMaterialCharacteristics[DevelopmentalStage] BioMaterialCharacteristics[Age] BioMaterialCharacteristics[TimeUnit] BioMaterialCharacteristics[OrganismPart] BioMaterialCharacteristics[Sex] BioMaterialCharacteristics[Individual] BioMaterialCharacteristics[DiseaseState] FactorValue[DiseaseState] BioMaterialCharacteristics[CellType] BioMaterialCharacteristics[ClinicalHistory] [...]
+23 SAMPLE_human_822 organism_part Homo sapiens "Royal Free Hospital, London,UK" adult mammary gland female Human_822 normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy3_agil1 P-TABM-AnitaG-10115 Cy3 L_pool_cy3_agil1 L_pool_cy3_agil1 251239114659.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+4 SAMPLE_human_621 organism_part Homo sapiens "Middlesex University Hospital, London,UK" adult 38 years mammary gland female Human_621 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy3_agil1 P-TABM-AnitaG-10115 Cy3 M_pool_cy3_agil1 M_pool_cy3_agil1 251239114660.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+42 SAMPLE_human_695 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_695 invasive lobular carcinoma invasive lobular carcinoma breast epithelial "grad1,size42, 9/15 lymph node, ESR1 positive, progesteron receptor negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy3_agil1 P-TABM-AnitaG-10115 Cy3 T_pool_cy3_agil1 T_pool_cy3_agil1 251239114661.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+26 SAMPLE_human_1040 organism_part Homo sapiens "Middlesex Hospital, London,UK" adult 33 years mammary gland female Human_1040 normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy5_agil1 P-TABM-AnitaG-10115 Cy5 L_pool_cy5_agil1 L_pool_cy5_agil1 251239119797.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+7 SAMPLE_human_723 organism_part Homo sapiens "St. George's University Hospital, London,UK" adult 32 years mammary gland female Human_723 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy5_agil1 P-TABM-AnitaG-10115 Cy5 M_pool_cy5_agil1 M_pool_cy5_agil1 251239119798.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+45 SAMPLE_human_845 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_845 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size50,vascular invasion, 2/9 lymph node,ESR1 positive, progesteron receptor negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy5_agil1 P-TABM-AnitaG-10115 Cy5 T_pool_cy5_agil1 T_pool_cy5_agil1 251239119800.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+27 SAMPLE_human_1053 organism_part Homo sapiens "Ashstead Hospital, London,UK" adult 22 years mammary gland female Human_1053 normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy5_agil2 P-TABM-AnitaG-10115 Cy5 L_pool_cy5_agil2 L_pool_cy5_agil2 251239125237.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+8 SAMPLE_human_729 organism_part Homo sapiens "St. George's Hospital, London,UK" adult 31 years mammary gland female Human_729 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy5_agil2 P-TABM-AnitaG-10115 Cy5 M_pool_cy5_agil2 M_pool_cy5_agil2 251239125238.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+46 SAMPLE_human_896 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_896 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "size45, 0/2 lymph node" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy5_agil2 P-TABM-AnitaG-10115 Cy5 T_pool_cy5_agil2 T_pool_cy5_agil2 251239125239.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+9 SAMPLE_human_1098 organism_part Homo sapiens "Queen Victoria Hospital, London,UK" adult 26 years mammary gland female Human_1098 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy5_agil3 P-TABM-AnitaG-10115 Cy5 M_pool_cy5_agil3 M_pool_cy5_agil3 251239125240.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+47 SAMPLE_human_983 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_983 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size15,vascular invasion, ESR1 negative, HER2 positive, progesteron receptor negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy5_agil3 P-TABM-AnitaG-10115 Cy5 T_pool_cy5_agil3 T_pool_cy5_agil3 251239125243.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+24 SAMPLE_human_1016 organism_part Homo sapiens "St. Anthony's Hospital, London,UK" adult 30 years mammary gland female Human_1016 normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy3_agil2 P-TABM-AnitaG-10115 Cy3 L_pool_cy3_agil2 L_pool_cy3_agil2 251239125464.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+5 SAMPLE_human_688 organism_part Homo sapiens "St. George's Hospital, London,UK" adult 26 years mammary gland female Human_688 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy3_agil2 P-TABM-AnitaG-10115 Cy3 M_pool_cy3_agil2 M_pool_cy3_agil2 251239125465.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+28 SAMPLE_human_1123 organism_part Homo sapiens "University College London Hospital, London,UK" adult 32 years mammary gland female Human_1123 normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy5_agil3 P-TABM-AnitaG-10115 Cy5 L_pool_cy5_agil3 L_pool_cy5_agil3 251239125466.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+43 SAMPLE_human_725 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_725 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size35,vascular invasion, 1/15 lymph node, ESR1 positive" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy3_agil2 P-TABM-AnitaG-10115 Cy3 T_pool_cy3_agil2 T_pool_cy3_agil2 251239125467.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+25 SAMPLE_human_1031 organism_part Homo sapiens "St. Elisabeth Hospital, London,UK" adult 33 years mammary gland female Human_1031 normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy3_agil3 P-TABM-AnitaG-10115 Cy3 L_pool_cy3_agil3 L_pool_cy3_agil3 251239125951.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+6 SAMPLE_human_697 organism_part Homo sapiens "St. George's University Hospital, London,UK" adult 29 years mammary gland female Human_697 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy3_agil3 P-TABM-AnitaG-10115 Cy3 M_pool_cy3_agil3 M_pool_cy3_agil3 251239125953.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+44 SAMPLE_human_842 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_842 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,vascular invasion, 1/10 lymph node, ESR1 positive, progesteron receptor positive" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy3_agil3 P-TABM-AnitaG-10115 Cy3 T_pool_cy3_agil3 T_pool_cy3_agil3 251239125954.gpr A-MEXP-218 P-MEXP-6162 P-MEXP-10145
+32 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 L_pool_cy5_breakthrough1 L_pool_cy5_breakthrough1 90000310.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+13 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 M_pool_cy5_breakthrough1 M_pool_cy5_breakthrough1 90000311.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+51 SAMPLE_human_1052 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_1052 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size35, ESR1 negative, progesteron receptor negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 T_pool_cy5_breakthrough1 T_pool_cy5_breakthrough1 90000312.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+29 SAMPLE_human_1130 organism_part Homo sapiens "University College London Hospital, London,UK" adult 32 years mammary gland female Human_1130 normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 L_pool_cy3_breakthrough1 L_pool_cy3_breakthrough1 90000337.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+10 SAMPLE_human_794 organism_part Homo sapiens "Ashstead Hospital, London,UK" adult 39 years mammary gland female Human_794 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 M_pool_cy3_breakthrough1 M_pool_cy3_breakthrough1 90000338.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+48 SAMPLE_human_1000 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_1000 invasive ductual carcinoma invasive ductual carcinoma breast epithelial breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 T_pool_cy3_breakthrough1 T_pool_cy3_breakthrough1 90000339.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+33 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 L_pool_cy5_breakthrough2 L_pool_cy5_breakthrough2 90000340.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+14 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 M_pool_cy5_breakthrough2 M_pool_cy5_breakthrough2 90000341.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+52 SAMPLE_human_1062 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_1062 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad2,size20,vascular invasion, 1/25 lymph node, ESR1 positive, HER2 negative, progesteron receptor negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 T_pool_cy5_breakthrough2 T_pool_cy5_breakthrough2 90000342.gpr A- [...]
+30 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 L_pool_cy3_breakthrough2 L_pool_cy3_breakthrough2 90000343.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+11 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 M_pool_cy3_breakthrough2 M_pool_cy3_breakthrough2 90000344.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+49 SAMPLE_human_1033 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_1033 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size58, 1/15 lymph node, ESR1 negative, HER2 negative, progesteron receptor negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 T_pool_cy3_breakthrough2 T_pool_cy3_breakthrough2 90000345.gpr A-MEXP-259 P-MEXP-10 [...]
+34 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 L_pool_cy5_breakthrough3 L_pool_cy5_breakthrough3 90000418.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+15 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 M_pool_cy5_breakthrough3 M_pool_cy5_breakthrough3 90000419.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+53 SAMPLE_human_1063 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_1063 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size45,vascular invasion, ESR1 positive, HER2 positive, progesteron receptor positive" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 T_pool_cy5_breakthrough3 T_pool_cy5_breakthrough3 90000420.gpr A-MEXP-259 P-MEXP-1 [...]
+31 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 L_pool_cy3_breakthrough3 L_pool_cy3_breakthrough3 90000421.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+12 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 M_pool_cy3_breakthrough3 M_pool_cy3_breakthrough3 90000422.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+50 SAMPLE_human_1045 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_1045 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad2,size50, ESR1 negative, HER2 negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 T_pool_cy3_breakthrough3 T_pool_cy3_breakthrough3 90000423.gpr A-MEXP-259 P-MEXP-10140 P-MEXP-10145
+106 normal luminal E_luminal_pool 2nd_L_pool_L_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy3_breakthrough1 2nd_L_pool_cy3_breakthrough1 95000061.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+138 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy3_breakthrough1 2nd_L_pool_cy3_breakthrough1 95000061.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+107 normal luminal E_luminal_pool 2nd_L_pool_L_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy3_breakthrough2 2nd_L_pool_cy3_breakthrough2 95000062.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+139 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy3_breakthrough2 2nd_L_pool_cy3_breakthrough2 95000062.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+94 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy3_breakthrough1 2nd_M_pool_cy3_breakthrough1 95000063.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+130 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy3_breakthrough1 2nd_M_pool_cy3_breakthrough1 95000063.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+95 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy3_breakthrough2 2nd_M_pool_cy3_breakthrough2 95000064.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+131 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy3_breakthrough2 2nd_M_pool_cy3_breakthrough2 95000064.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+118 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy3_breakthrough95000065 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy3_breakthrough95000065 2nd_T_pool_cy3_breakthrough95000065 95000065.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+146 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough95000065 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy3_breakthrough95000065 2nd_T_pool_cy3_breakthrough95000065 95000065.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+119 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy3_breakthrough95000066 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy3_breakthrough95000066 2nd_T_pool_cy3_breakthrough95000066 95000066.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+147 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough95000066 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy3_breakthrough95000066 2nd_T_pool_cy3_breakthrough95000066 95000066.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+110 normal luminal E_luminal_pool 2nd_L_pool_L_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy5_breakthrough1 2nd_L_pool_cy5_breakthrough1 95000067.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+142 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy5_breakthrough1 2nd_L_pool_cy5_breakthrough1 95000067.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+111 normal luminal E_luminal_pool 2nd_L_pool_L_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy5_breakthrough2 2nd_L_pool_cy5_breakthrough2 95000068.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+143 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy5_breakthrough2 2nd_L_pool_cy5_breakthrough2 95000068.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+98 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy5_breakthrough1 2nd_M_pool_cy5_breakthrough1 95000069.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+134 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy5_breakthrough1 2nd_M_pool_cy5_breakthrough1 95000069.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+99 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy5_breakthrough2 2nd_M_pool_cy5_breakthrough2 95000070.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+135 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy5_breakthrough2 2nd_M_pool_cy5_breakthrough2 95000070.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+122 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy5_breakthrough1 2nd_T_pool_cy5_breakthrough1 95000071.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+150 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy5_breakthrough1 2nd_T_pool_cy5_breakthrough1 95000071.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+123 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy5_breakthrough2 2nd_T_pool_cy5_breakthrough2 95000072.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+151 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy5_breakthrough2 2nd_T_pool_cy5_breakthrough2 95000072.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+112 normal luminal E_luminal_pool 2nd_L_pool_L_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy5_breakthrough3 2nd_L_pool_cy5_breakthrough3 95000073.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+144 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy5_breakthrough3 2nd_L_pool_cy5_breakthrough3 95000073.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+113 normal luminal E_luminal_pool 2nd_L_pool_L_cy5_breakthrough4 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy5_breakthrough4 2nd_L_pool_cy5_breakthrough4 95000074.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+145 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough4 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy5_breakthrough4 2nd_L_pool_cy5_breakthrough4 95000074.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+100 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy5_breakthrough3 2nd_M_pool_cy5_breakthrough3 95000075.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+136 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy5_breakthrough3 2nd_M_pool_cy5_breakthrough3 95000075.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+101 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy5_breakthrough4 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy5_breakthrough4 2nd_M_pool_cy5_breakthrough4 95000076.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+137 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough4 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy5_breakthrough4 2nd_M_pool_cy5_breakthrough4 95000076.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+124 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy5_breakthrough3 2nd_T_pool_cy5_breakthrough3 95000077.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+152 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy5_breakthrough3 2nd_T_pool_cy5_breakthrough3 95000077.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+125 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy5_breakthrough4 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy5_breakthrough4 2nd_T_pool_cy5_breakthrough4 95000078.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+153 REF sources E_REF 2nd_REF_pool_L_cy3_breakthrough4 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy5_breakthrough4 2nd_T_pool_cy5_breakthrough4 95000078.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+108 normal luminal E_luminal_pool 2nd_L_pool_L_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy3_breakthrough3 2nd_L_pool_cy3_breakthrough3 95000079.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+140 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy3_breakthrough3 2nd_L_pool_cy3_breakthrough3 95000079.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+109 normal luminal E_luminal_pool 2nd_L_pool_L_cy3_breakthrough4 P-TABM-AnitaG-10115 Cy3 2nd_L_pool_cy3_breakthrough4 2nd_L_pool_cy3_breakthrough4 95000080.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+141 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough4 P-TABM-AnitaG-10115 Cy5 2nd_L_pool_cy3_breakthrough4 2nd_L_pool_cy3_breakthrough4 95000080.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+96 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy3_breakthrough3 2nd_M_pool_cy3_breakthrough3 95000081.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+132 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy3_breakthrough3 2nd_M_pool_cy3_breakthrough3 95000081.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+97 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_L_cy3_breakthrough4 P-TABM-AnitaG-10115 Cy3 2nd_M_pool_cy3_breakthrough4 2nd_M_pool_cy3_breakthrough4 95000082.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+133 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough4 P-TABM-AnitaG-10115 Cy5 2nd_M_pool_cy3_breakthrough4 2nd_M_pool_cy3_breakthrough4 95000082.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+120 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy3_breakthrough95000083 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy3_breakthrough95000083 2nd_T_pool_cy3_breakthrough95000083 95000083.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+148 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough95000083 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy3_breakthrough95000083 2nd_T_pool_cy3_breakthrough95000083 95000083.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+121 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_L_cy3_breakthrough95000084 P-TABM-AnitaG-10115 Cy3 2nd_T_pool_cy3_breakthrough95000084 2nd_T_pool_cy3_breakthrough95000084 95000084.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+149 REF sources E_REF 2nd_REF_pool_L_cy5_breakthrough95000084 P-TABM-AnitaG-10115 Cy5 2nd_T_pool_cy3_breakthrough95000084 2nd_T_pool_cy3_breakthrough95000084 95000084.gpr A-MEXP-339 P-MEXP-10140 P-MEXP-10145
+20 SAMPLE_human_723L organism_part Homo sapiens "St. George's Hospital, London,UK" adult 32 years mammary gland female Human_723L normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_affy1 P-AFFY-2 biotin L_pool_affy1 L_pool_affy1 L1.CEL L1.EXP A-AFFY-44
+21 SAMPLE_human_729L organism_part Homo sapiens "St. George's Hospital, London,UK" adult 31 years mammary gland female Human_729L normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_affy2 P-AFFY-2 biotin L_pool_affy2 L_pool_affy2 L2.CEL L2.EXP A-AFFY-44
+22 SAMPLE_human_697L organism_part Homo sapiens "St. George's Hospital, London,UK" adult 29 years mammary gland female Human_697L normal normal luminal epithelial Reduction mammoplasties luminal epithelial P-MEXP-975 P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_affy3 P-AFFY-2 biotin L_pool_affy3 L_pool_affy3 L3.CEL L3.EXP A-AFFY-44
+1 SAMPLE_human_352 organism_part Homo sapiens "Queen Mary's University Hospital, London,UK" adult 30 years mammary gland female Human_352 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_affy1 P-AFFY-2 biotin M_pool_affy1 M_pool_affy1 M1.CEL M1.EXP A-AFFY-44
+2 SAMPLE_human_453 organism_part Homo sapiens "Queen Mary's University Hospital, London,UK" adult 22 years mammary gland female Human_453 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_affy2 P-AFFY-2 biotin M_pool_affy2 M_pool_affy2 M2.CEL M2.EXP A-AFFY-44
+3 SAMPLE_human_463 organism_part Homo sapiens "Queen Mary's University Hospital, London,UK" adult 31 years mammary gland female Human_463 normal normal myoepithelial Reduction mammoplasties myoepithelial P-MEXP-976 P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_affy3 P-AFFY-2 biotin M_pool_affy3 M_pool_affy3 M3.CEL M3.EXP A-AFFY-44
+39 SAMPLE_human_489 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_489 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size25,vascular invasion, ESR1 positive, HER2 negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_affy1 P-AFFY-2 biotin T_pool_affy1 T_pool_affy1 T1.CEL T1.EXP A-AFFY-44
+40 SAMPLE_human_552 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_552 invasive ductual carcinoma invasive ductual carcinoma breast epithelial "grad3,size40,vascular invasion, 8/9 lymph node, ESR1 negative, HER2 negative" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_affy2 P-AFFY-2 biotin T_pool_affy2 T_pool_affy2 T2.CEL T2.EXP A-AFFY-44
+41 SAMPLE_human_593 organism_part Homo sapiens "University College London Hospital, London,UK" adult mammary gland female Human_593 invasive lobular carcinoma invasive lobular carcinoma breast epithelial "grad2,size35, 1/9 lymph node, ESR1 positive" breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_affy3 P-AFFY-2 biotin T_pool_affy3 T_pool_affy3 T3.CEL T3.EXP A-AFFY-44
+116 normal luminal E_luminal_pool 2nd_L_pool_amersham3 P-MEXP-14985 Cy5 2nd_L_pool_amersham3 2nd_L_pool_amersham3 T00250228.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+117 normal luminal E_luminal_pool 2nd_L_pool_amersham4 P-MEXP-14985 Cy5 2nd_L_pool_amersham4 2nd_L_pool_amersham4 T00250229.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+104 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_amersham3 P-MEXP-14985 Cy5 2nd_M_pool_amersham3 2nd_M_pool_amersham3 T00250230.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+105 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_amersham4 P-MEXP-14985 Cy5 2nd_M_pool_amersham4 2nd_M_pool_amersham4 T00250231.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+128 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_amersham3 P-MEXP-14985 Cy5 2nd_T_pool_amersham3 2nd_T_pool_amersham3 T00250232.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+129 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_amersham4 P-MEXP-14985 Cy5 2nd_T_pool_amersham4 2nd_T_pool_amersham4 T00250233.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+114 normal luminal E_luminal_pool 2nd_L_pool_amersham1 P-MEXP-14985 Cy5 2nd_L_pool_amersham1 2nd_L_pool_amersham1 T00253433.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+115 normal luminal E_luminal_pool 2nd_L_pool_amersham2 P-MEXP-14985 Cy5 2nd_L_pool_amersham2 2nd_L_pool_amersham2 T00253434.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+102 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_amersham1 P-MEXP-14985 Cy5 2nd_M_pool_amersham1 2nd_M_pool_amersham1 T00253435.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+103 normal myoepithelial E_myoepithelial_pool 2nd_M_pool_amersham2 P-MEXP-14985 Cy5 2nd_M_pool_amersham2 2nd_M_pool_amersham2 T00253436.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+126 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_amersham1 P-MEXP-14985 Cy5 2nd_T_pool_amersham1 2nd_T_pool_amersham1 T00253437.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+127 invasive ductual carcinoma breast epithelial E_tumour_pool 2nd_T_pool_amersham2 P-MEXP-14985 Cy5 2nd_T_pool_amersham2 2nd_T_pool_amersham2 T00253438.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+35 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_amersham1 P-MEXP-14985 Cy5 L_pool_amersham1 L_pool_amersham1 T00297797.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+36 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_amersham2 P-MEXP-14985 Cy5 L_pool_amersham2 L_pool_amersham2 T00297799.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+37 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_amersham3 P-MEXP-14985 Cy5 L_pool_amersham3 L_pool_amersham3 T00297800.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+38 normal luminal epithelial P-MEXP-972 P-MEXP-10844 E_luminal_pool L_pool_amersham4 P-MEXP-14985 Cy5 L_pool_amersham4 L_pool_amersham4 T00297801.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+16 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_amersham1 P-MEXP-14985 Cy5 M_pool_amersham1 M_pool_amersham1 T00297802.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+17 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_amersham2 P-MEXP-14985 Cy5 M_pool_amersham2 M_pool_amersham2 T00297803.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+18 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_amersham3 P-MEXP-14985 Cy5 M_pool_amersham3 M_pool_amersham3 T00297804.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+19 normal myoepithelial P-MEXP-972 P-MEXP-10843 E_myoepithelial_pool M_pool_amersham4 P-MEXP-14985 Cy5 M_pool_amersham4 M_pool_amersham4 T00297805.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+54 invasive ductual carcinoma breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_amersham1 P-MEXP-14985 Cy5 T_pool_amersham1 T_pool_amersham1 T00297808.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+55 invasive ductual carcinoma breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_amersham2 P-MEXP-14985 Cy5 T_pool_amersham2 T_pool_amersham2 T00297810.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+56 invasive ductual carcinoma breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_amersham3 P-MEXP-14985 Cy5 T_pool_amersham3 T_pool_amersham3 T00297811.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+57 invasive ductual carcinoma breast epithelial P-MEXP-972 P-MEXP-10845 E_tumour_pool T_pool_amersham4 P-MEXP-14985 Cy5 T_pool_amersham4 T_pool_amersham4 T00297812.txt A-GEHB-1 P-MEXP-14987 P-MEXP-14988
+58 REF sources E_REF REF_pool_cy5_agil1 P-TABM-AnitaG-10115 Cy5 M_pool_cy3_agil1 M_pool_cy3_agil1
+59 REF sources E_REF REF_pool_cy5_agil2 P-TABM-AnitaG-10115 Cy5 M_pool_cy3_agil2 M_pool_cy3_agil2
+60 REF sources E_REF REF_pool_cy5_agil3 P-TABM-AnitaG-10115 Cy5 M_pool_cy3_agil3 M_pool_cy3_agil3
+61 REF sources E_REF REF_pool_cy3_agil1 P-TABM-AnitaG-10115 Cy3 M_pool_cy5_agil1 M_pool_cy5_agil1
+62 REF sources E_REF REF_pool_cy3_agil2 P-TABM-AnitaG-10115 Cy3 M_pool_cy5_agil2 M_pool_cy5_agil2
+63 REF sources E_REF REF_pool_cy3_agil3 P-TABM-AnitaG-10115 Cy3 M_pool_cy5_agil3 M_pool_cy5_agil3
+64 REF sources E_REF REF_pool_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 M_pool_cy3_breakthrough1 M_pool_cy3_breakthrough1
+65 REF sources E_REF REF_pool_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 M_pool_cy3_breakthrough2 M_pool_cy3_breakthrough2
+66 REF sources E_REF REF_pool_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 M_pool_cy3_breakthrough3 M_pool_cy3_breakthrough3
+67 REF sources E_REF REF_pool_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 M_pool_cy5_breakthrough1 M_pool_cy5_breakthrough1
+68 REF sources E_REF REF_pool_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 M_pool_cy5_breakthrough2 M_pool_cy5_breakthrough2
+69 REF sources E_REF REF_pool_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 M_pool_cy5_breakthrough3 M_pool_cy5_breakthrough3
+70 REF sources E_REF REF_pool_cy5_agil1 P-TABM-AnitaG-10115 Cy5 L_pool_cy3_agil1 L_pool_cy3_agil1
+71 REF sources E_REF REF_pool_cy5_agil2 P-TABM-AnitaG-10115 Cy5 L_pool_cy3_agil2 L_pool_cy3_agil2
+72 REF sources E_REF REF_pool_cy5_agil3 P-TABM-AnitaG-10115 Cy5 L_pool_cy3_agil3 L_pool_cy3_agil3
+73 REF sources E_REF REF_pool_cy3_agil1 P-TABM-AnitaG-10115 Cy3 L_pool_cy5_agil1 L_pool_cy5_agil1
+74 REF sources E_REF REF_pool_cy3_agil2 P-TABM-AnitaG-10115 Cy3 L_pool_cy5_agil2 L_pool_cy5_agil2
+75 REF sources E_REF REF_pool_cy3_agil3 P-TABM-AnitaG-10115 Cy3 L_pool_cy5_agil3 L_pool_cy5_agil3
+76 REF sources E_REF REF_pool_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 L_pool_cy3_breakthrough1 L_pool_cy3_breakthrough1
+77 REF sources E_REF REF_pool_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 L_pool_cy3_breakthrough2 L_pool_cy3_breakthrough2
+78 REF sources E_REF REF_pool_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 L_pool_cy3_breakthrough3 L_pool_cy3_breakthrough3
+79 REF sources E_REF REF_pool_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 L_pool_cy5_breakthrough1 L_pool_cy5_breakthrough1
+80 REF sources E_REF REF_pool_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 L_pool_cy5_breakthrough2 L_pool_cy5_breakthrough2
+81 REF sources E_REF REF_pool_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 L_pool_cy5_breakthrough3 L_pool_cy5_breakthrough3
+82 REF sources E_REF REF_pool_cy5_agil1 P-TABM-AnitaG-10115 Cy5 T_pool_cy3_agil1 T_pool_cy3_agil1
+83 REF sources E_REF REF_pool_cy5_agil2 P-TABM-AnitaG-10115 Cy5 T_pool_cy3_agil2 T_pool_cy3_agil2
+84 REF sources E_REF REF_pool_cy5_agil3 P-TABM-AnitaG-10115 Cy5 T_pool_cy3_agil3 T_pool_cy3_agil3
+85 REF sources E_REF REF_pool_cy3_agil1 P-TABM-AnitaG-10115 Cy3 T_pool_cy5_agil1 T_pool_cy5_agil1
+86 REF sources E_REF REF_pool_cy3_agil2 P-TABM-AnitaG-10115 Cy3 T_pool_cy5_agil2 T_pool_cy5_agil2
+87 REF sources E_REF REF_pool_cy3_agil3 P-TABM-AnitaG-10115 Cy3 T_pool_cy5_agil3 T_pool_cy5_agil3
+88 REF sources E_REF REF_pool_cy5_breakthrough1 P-TABM-AnitaG-10115 Cy5 T_pool_cy3_breakthrough1 T_pool_cy3_breakthrough1
+89 REF sources E_REF REF_pool_cy5_breakthrough2 P-TABM-AnitaG-10115 Cy5 T_pool_cy3_breakthrough2 T_pool_cy3_breakthrough2
+90 REF sources E_REF REF_pool_cy5_breakthrough3 P-TABM-AnitaG-10115 Cy5 T_pool_cy3_breakthrough3 T_pool_cy3_breakthrough3
+91 REF sources E_REF REF_pool_cy3_breakthrough1 P-TABM-AnitaG-10115 Cy3 T_pool_cy5_breakthrough1 T_pool_cy5_breakthrough1
+92 REF sources E_REF REF_pool_cy3_breakthrough2 P-TABM-AnitaG-10115 Cy3 T_pool_cy5_breakthrough2 T_pool_cy5_breakthrough2
+93 REF sources E_REF REF_pool_cy3_breakthrough3 P-TABM-AnitaG-10115 Cy3 T_pool_cy5_breakthrough3 T_pool_cy5_breakthrough3
diff --git a/examples/real/E-TABM-70.png b/examples/real/E-TABM-70.png
new file mode 100644
index 0000000..a847c87
Binary files /dev/null and b/examples/real/E-TABM-70.png differ
diff --git a/examples/real/E-TABM-70.txt b/examples/real/E-TABM-70.txt
new file mode 100755
index 0000000..3f5443c
--- /dev/null
+++ b/examples/real/E-TABM-70.txt
@@ -0,0 +1,101 @@
+Experiment section
+domain ebi.ac.uk
+accession E-TABM-70
+quality_control dye_swap_quality_control
+experiment_design_type compound_treatment_design
+name IGR_PLOIDY_STUDY_GK
+description "During the oncogenic process, tetraploidy is a candidate intermediate stage leading from diploidy to aneuploidy. The aim of our experiment was to establish tetraploid clones and to characterize their transcriptome."
+release_date 2006-01-23
+submission_date 2005-12-19
+submitter Philippe Dessen
+submitter_email dessen at igr.fr
+publication_title Apoptosis regulation in tetraploid cells
+authors Maria Castedo; Arud Coquelle; Sonia Vivet; Audrey Kauffmann; Marie O. Pequignot; Noelia Casares; Alexander Valent; Shahul Mouhamad; Elise Schmitt; Nazanine Modjtahedi; William Vainchenker; Laurence Zitvogel; Vladimir Lazar; Carmen Garrido; Guido Kroemer
+address "Centre Natiol de la Recherche Scientifique, UMR8125,�; Unit� de G�nomique Fonctionnelle, Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; INSERM U-517, Faculty of Medicine and Pharmacy, 7 Boulevard Jeanne d;Arc, 21033 Dijon, France�; INSERM U362,Institut Gustave Roussy, 39 rue Camille-Desmoulins, F-94805 Villejuif, France; Unit� d�Immunologie, ERM0208 INSERM, IFR54, Institut Gustave Roussy, Villejuif, France"
+journal EMBO J
+volume revision
+issue
+pages
+year 2006
+
+Protocol section
+accession type text parameters
+P_IGR_SAMPLE_04_TETRA_1 compound_based_treatment "RKO cells were grown in McCoy' 5A medium supplemented with 10%FCS. In order to generate tetraploid and diploid clones, the cell line RKO containing ~5% tetraploid cells was subcloned by limiting dilution into diploid and tetraploid clones. One diploid clone was transfected with a cDNA encoding H2B-GFP, selected in blasticidine (20 �g/ml), FACS-separated into subsets of cells enriched in a diploid or tetraploid DNA content to generate dipl [...]
+P_IGR_SAMPLE_04_TETRA_2 compound_based_treatment "Tetraploid HCT116 clones were generated upon treatment with cytochalasin D (0.6 �g/ml, 48h) or nocodazole (100nM, 48h). For microarray assay, established diploid and tetraploid clones were collected during exponential growth with a cell scrapper in 1ml RLT lysis buffer with beta-mercaptoethanol (10ml/ml)."
+P_IGR_EXTRACT_04 nucleic_acid_extraction "Total RNA was isolated from using Rneasy micro elute kit (Qiagen, Courtaboeuf, France) according to manufacturer instructions. Briefly Cells was mixed with 350 ul RLT buffer with Beta Mercaptoethanol 1%. Then add 350 ul ethanol 70% and mixed thoroughly by"
+P_IGR_POOL_04A pool "Pools of RNA (Pool1 or Pool2), obtained by mixing equal amounts (500 ng) of total RNA from each of the 8 cell line samples, constituted the reference in each group. "
+P_IGR_POOL_04B pool "Pools of RNA (Pool1 or Pool2), obtained by mixing equal amounts (500 ng) of total RNA from each of the 8 cell line samples, constituted the reference in each group. "
+P_IGR_LABEL_CY3_04 labeling "(500 ng, Amplification=RNA polymerases). Agilent oligo Cy5 or Cy3 probes labeling protocol. Kit used for probe labeling: Agilent Fluorescent Low input Linear Amplification kit (G2554A) adapted for small amount of total RNA (500 ng total RNA per reaction)."
+P_IGR_LABEL_CY5_04 labeling "(500 ng, Amplification=RNA polymerases). Agilent oligo Cy5 or Cy3 probes labeling protocol. Kit used for probe labeling: Agilent Fluorescent Low input Linear Amplification kit (G2554A) adapted for small amount of total RNA (500 ng total RNA per reaction)."
+P_IGR_HYBRID_04 hybridization Agilent Hybridization Protocol (Chamber type: Agilent SureHyb Chamber; Quantity of labelled extract: 1 ug; duration: 17 hours; volume: 650 ul ; Temperature in �C: 60). Add the following components in clean 1.5 ml tubes. 1 ug linearly amplified cRNA la
+P_IGR_SCANNING_04 image_acquisition "Scanning was performed with a Agilent 2565 AA DNA Microarray scanner using defaults parameters (100� PMT, 10 um resolution, at 20�C in low ozone concentration environment. Microarray images were alysed by using Feature Extraction software version A.7.5.1 from Agilent technologies. Defaults settings were used."
+P_IGR_TRANSFORM_04 Transformation protocol "Raw data files from the Agilent Feature Extraction software A.6.1.1 for image alysis were then imported into Resolver(TM) system for gene expression data alysis from Rosetta Inpharmatics. Then combined experiments were generated, to obtain average values from the replicates and dye swap experiments in order to avoid dye incorporation bias. Microarray data were processed and combined using the Rosetta Resolver system described in Stoughton and D [...]
+
+
+
+Hybridization section
+File[raw] Array[accession] Array[serial] Protocol[treatment] Protocol[extraction] Protocol[pool] Protocol[labeling] Protocol[hybridization] Protocol[scanning] Protocol[transformation] BioSource Sample Extract LabeledExtract Dye Hybridization File[transformed] TransformationType BioSourceMaterial ExtractMaterial LabeledExtractMaterial FactorValue[CellLine] FactorValue[ploidy] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[Provider] BioMaterialCharacteristics[CellLine] Bio [...]
+US14702370_251239119523_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DW DW sample DW extract DW Cy5 Cy5 DW Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119523_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 DW Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119541_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DW DW sample DW extract DW Cy3 Cy3 DW Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119541_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 DW Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119609_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DX DX sample DX extract DX Cy5 Cy5 DX Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119609_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 DX Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119524_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DX DX sample DX extract DX Cy3 Cy3 DX Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119524_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 DX Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119530_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DY DY sample DY extract DY Cy5 Cy5 DY Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119530_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 DY Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119525_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DY DY sample DY extract DY Cy3 Cy3 DY Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119525_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 DY Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119531_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DZ DZ sample DZ extract DZ Cy5 Cy5 DZ Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119531_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 DZ Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119614_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 DZ DZ sample DZ extract DZ Cy3 Cy3 DZ Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as diploid diploid
+US14702370_251239119614_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 DZ Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119519_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TA TA sample TA extract TA Cy5 Cy5 TA Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119519_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 TA Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119487_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TA TA sample TA extract TA Cy3 Cy3 TA Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119487_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 TA Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119520_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TB TB sample TB extract TB Cy5 Cy5 TB Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119520_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 TB Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119489_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TB TB sample TB extract TB Cy3 Cy3 TB Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119489_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 TB Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119608_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TC TC sample TC extract TC Cy5 Cy5 TC Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119608_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 TC Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119538_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TC TC sample TC extract TC Cy3 Cy3 TC Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119538_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 TC Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119522_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TD TD sample TD extract TD Cy5 Cy5 TD Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119522_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy3 Cy3 TD Cy5_Pool1 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239119540_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 TD TD sample TD extract TD Cy3 Cy3 TD Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO clone selection as tetraploid tetraploid
+US14702370_251239119540_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_1 P_IGR_EXTRACT_04 P_IGR_POOL_04A P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool1 Pool1 sample Pool1 extract Pool1 Cy5 Cy5 TD Cy3_Pool1 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA colon RKO Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" colon RKO
+US14702370_251239124865_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co3 Co3 sample Co3 extract Co3 Cy5 Cy5 Co3 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124865_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 Co3 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124928_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co3 Co3 sample Co3 extract Co3 Cy3 Cy3 Co3 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124928_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 Co3 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124866_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co4 Co4 sample Co4 extract Co4 Cy5 Cy5 Co4 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124866_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 Co4 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124929_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co4 Co4 sample Co4 extract Co4 Cy3 Cy3 Co4 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124929_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 Co4 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124869_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co1 Co1 sample Co1 extract Co1 Cy5 Cy5 Co1 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124869_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 Co1 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124932_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co1 Co1 sample Co1 extract Co1 Cy3 Cy3 Co1 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124932_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 Co1 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124925_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co2 Co2 sample Co2 extract Co2 Cy5 Cy5 Co2 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124925_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 Co2 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124933_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Co2 Co2 sample Co2 extract Co2 Cy3 Cy3 Co2 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 diploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 diploid
+US14702370_251239124933_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 Co2 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124926_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 C1 C1 sample C1 extract C1 Cy5 Cy5 C1 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 cytochalasin D tetraploid
+US14702370_251239124926_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 C1 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124934_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 C1 C1 sample C1 extract C1 Cy3 Cy3 C1 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 cytochalasin D tetraploid
+US14702370_251239124934_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 C1 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124927_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 C2 C2 sample C2 extract C2 Cy5 Cy5 C2 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 cytochalasin D tetraploid
+US14702370_251239124927_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 C2 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124940_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 C2 C2 sample C2 extract C2 Cy3 Cy3 C2 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 cytochalasin D tetraploid
+US14702370_251239124940_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 C2 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124867_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 N1 N1 sample N1 extract N1 Cy5 Cy5 N1 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 nocodazole tetraploid
+US14702370_251239124867_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 N1 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124930_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 N1 N1 sample N1 extract N1 Cy3 Cy3 N1 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 nocodazole tetraploid
+US14702370_251239124930_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 N1 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124868_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 N2 N2 sample N2 extract N2 Cy5 Cy5 N2 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 nocodazole tetraploid
+US14702370_251239124868_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY5_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy5 Cy5 N2 Cy5-Pool2 Cy5 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
+US14702370_251239124931_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 N2 N2 sample N2 extract N2 Cy3 Cy3 N2 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 tetraploid Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116 nocodazole tetraploid
+US14702370_251239124931_S01_A01.txt A-MEXP-304 2.51E+11 P_IGR_SAMPLE_04_TETRA_2 P_IGR_EXTRACT_04 P_IGR_POOL_04B P_IGR_LABEL_CY3_04 P_IGR_HYBRID_04 P_IGR_SCANNING_04 P_IGR_TRANSFORM_04 Pool2 Pool2 sample Pool2 extract Pool2 Cy3 Cy3 N2 Cy3-Pool2 Cy3 hyb igr_ploidy_gk_all_seqs_all_exps_hp.txt lowess cell line total_RNA synthetic_RNA HCT116 Homo sapiens "CNRS-UMR8125, Institut Gustave Roussy, 94805 Villejuif, France" HCT116
diff --git a/examples/reference/Data1.txt b/examples/reference/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/reference/Data1.txt
@@ -0,0 +1,37 @@
+ATF 1.0
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 0.475 0.481 0.494 0.491 0.129 0.413 0.731 52 376 621 616 -1.074 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 0.494 0.496 0.500 0.599 0.409 0.433 0.777 120 778 1979 1904 -1.019 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 0.501 0.490 0.493 0.641 1.280 0.460 0.837 120 810 1379 1491 -0.998 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 0.780 0.782 0.819 1.372 3.252 0.675 0.722 120 796 1696 1663 -0.359 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 0.455 0.460 0.450 0.600 0.632 0.382 0.751 120 930 1334 1251 -1.137 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 0.420 0.431 0.473 0.696 2.121 0.362 0.589 120 914 426 438 -1.252 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 0.223 0.229 0.226 0.313 0.289 0.160 0.283 80 552 126 134 -2.163 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 0.525 0.503 0.522 0.741 1.710 0.445 0.754 120 816 1089 1052 -0.929 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 0.388 0.384 0.368 0.443 0.369 0.351 0.721 120 796 408 418 -1.367 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 0.885 0.912 0.923 1.477 3.031 0.751 0.698 120 810 1757 1648 -0.176 825 932 786 862 0
diff --git a/examples/reference/Data2.txt b/examples/reference/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/reference/Data2.txt
@@ -0,0 +1,40 @@
+ATF 1.0
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 0.447 0.473 0.502 0.428 2.467 0.472 0.332 80 656 204 221 -1.162 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 0.530 0.484 0.477 0.503 3.009 0.418 0.611 120 820 1042 1177 -0.916 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 0.669 0.588 0.592 0.604 3.170 0.511 0.592 120 820 611 851 -0.579 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 0.742 0.684 0.755 0.675 3.312 0.576 0.412 120 820 385 608 -0.430 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 0.479 0.514 0.506 0.502 2.251 0.507 0.593 120 820 935 984 -1.061 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 0.506 0.525 0.546 0.488 3.047 0.532 0.398 120 810 244 241 -0.982 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 0.592 0.612 1.025 0.705 2.930 1.819 0.071 52 340 78 79 -0.757 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 0.497 0.506 0.543 0.522 3.195 0.482 0.645 120 820 834 845 -1.008 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 0.606 0.624 0.669 0.614 2.841 0.577 0.414 80 616 302 328 -0.722 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 0.584 0.503 0.457 0.508 2.361 0.481 0.581 120 930 507 708 -0.775 187 320 237 471 0
diff --git a/examples/reference/Data3.txt b/examples/reference/Data3.txt
new file mode 100644
index 0000000..c07040f
--- /dev/null
+++ b/examples/reference/Data3.txt
@@ -0,0 +1,11 @@
+MetaColumn MetaRow Column Row Name ID X Y Dia. F635 Median F635 Mean F635 SD B635 Median B635 Mean B635 SD % > B635+1SD % > B635+2SD F635 % Sat. F532 Median F532 Mean F532 SD B532 Median B532 Mean B532 SD % > B532+1SD % > B532+2SD F532 % Sat. Ratio of Medians (635/532) Ratio of Means (635/532) Median of Ratios (635/532) Mean of Ratios (635/532) Ratios SD (635/532) Rgn Ratio (635/532) Rgn R2 (635/532) F Pixels B Pixels Sum of Medians (635/532) Sum of Means (635/532) Log Ratio (635/532) F6 [...]
+1 1 1 1 iYAR047C-1 8 2310 13480 130 2247 2246 594 59 64 55 100 100 0 1545 1594 427 69 73 43 100 100 0 1.482 1.434 1.458 1.431 1.368 1.453 0.926 120 930 3664 3712 0.568 2188 1476 2187 1525 0
+1 1 2 1 iYARCdelta8-1 16 2580 13480 130 4636 4620 697 59 82 190 100 100 0 3868 3838 726 68 95 209 100 100 0 1.204 1.21 1.215 1.226 1.188 1.181 0.961 120 930 8377 8331 0.268 4577 3800 4561 3770 0
+1 1 3 1 iYAR060C 24 2860 13480 130 2696 2751 499 59 84 202 100 100 0 2228 2284 440 67 97 211 100 100 0 1.22 1.214 1.218 1.217 1.115 1.21 0.951 120 930 4798 4909 0.287 2637 2161 2692 2217 0
+1 1 4 1 iYBL106C 104 3130 13480 130 926 895 171 57 72 104 100 100 0 739 735 136 68 89 152 100 97 0 1.295 1.256 1.279 1.255 1.249 1.249 0.932 120 930 1540 1505 0.373 869 671 838 667 0
+1 1 5 1 iYBL102W 112 3440 13480 110 424 440 171 55 60 23 100 98 0 312 330 111 68 70 21 100 100 0 1.512 1.469 1.449 1.417 1.574 1.554 0.826 80 648 613 647 0.597 369 244 385 262 0
+1 1 6 1 iYBL099W 120 3680 13480 130 1260 1258 176 53 62 71 100 100 0 947 951 128 66 76 76 100 100 0 1.37 1.362 1.349 1.363 1.166 1.347 0.94 120 916 2088 2090 0.454 1207 881 1205 885 0
+1 1 7 1 iYBL081W 200 3960 13490 120 459 462 102 53 53 14 100 100 0 378 386 98 64 66 17 100 100 0 1.293 1.27 1.278 1.3 1.361 1.251 0.886 120 820 720 731 0.371 406 314 409 322 0
+1 1 8 1 iYBL076C 208 4250 13490 130 597 592 108 52 55 25 100 100 0 458 472 91 63 66 22 100 100 0 1.38 1.32 1.332 1.325 1.216 1.346 0.897 120 924 940 949 0.464 545 395 540 409 0
+1 1 9 1 iYBL072C 216 4510 13490 140 1377 1442 565 51 53 16 100 100 0 972 1019 418 62 64 18 100 100 0 1.457 1.454 1.484 1.471 1.377 1.447 0.9 156 1098 2236 2348 0.543 1326 910 1391 957 0
+1 1 10 1 iYBL052C 296 4800 13490 120 356 377 155 50 52 15 100 100 0 286 291 101 63 65 17 100 100 0 1.372 1.434 1.46 1.409 1.498 1.516 0.832 120 814 529 555 0.456 306 223 327 228 0
diff --git a/examples/reference/reference.png b/examples/reference/reference.png
new file mode 100644
index 0000000..59a924e
Binary files /dev/null and b/examples/reference/reference.png differ
diff --git a/examples/reference/reference.txt b/examples/reference/reference.txt
new file mode 100644
index 0000000..6ae99ef
--- /dev/null
+++ b/examples/reference/reference.txt
@@ -0,0 +1,42 @@
+# Simple example summary spreadsheet for tab2mage.pl script.
+# This file illustrates how pooling of samples into a reference channel may be accomplished.
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-5
+quality_control dye_swap_quality_control
+experiment_design_type strain_or_line_design
+name Transcriptome analysis of invasive verses non-invasive strains of budding yeast
+description An experiment was performed to...
+release_date 2004-08-30
+submission_date 2004-07-28
+submitter John Falstaff
+# The publication details below are fictitious
+publication_title Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast
+authors John Falstaff; Robin Goodfellow
+journal Nature Genetics
+volume 12
+issue 4
+pages 123-456
+year 2004
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-EXML-1 Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8 Yeast growth
+P-EXML-2 Your protocol text here Yeast cell harvesting
+P-EXML-3 Your protocol text here Cell lysis and RNA prep
+P-EXML-4 Your protocol text here cDNA labeling
+P-EXML-5 Your protocol text here Hybridization
+P-EXML-6 Your protocol text here Scanning
+P-EXML-7 Your protocol text here Extract pooling
+
+Hybridization section
+BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Sex] BioSource BioSourceMaterial SampleMaterial ExtractMaterial Extract LabeledExtractMaterial Dye Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[pool] Protocol[labeling] Protocol[hybridization] Protocol[scanning] FactorValue[Time(h)] File[raw] Array[accession] Array[serial]
+Saccharomyces cerevisiae S288C mating_type_a Time1 whole_organism whole_organism total_RNA Time1 synthetic_DNA Cy3 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 1 Data1.txt A-EXML-1 244531
+Saccharomyces cerevisiae S288C mating_type_a Time1 whole_organism whole_organism total_RNA Reference extract synthetic_DNA Cy5 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-7 P-EXML-4 P-EXML-5 P-EXML-6 Data1.txt A-EXML-1 244531
+Saccharomyces cerevisiae S288C mating_type_a Time2 whole_organism whole_organism total_RNA Time2 synthetic_DNA Cy3 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 2 Data2.txt A-EXML-1 244532
+Saccharomyces cerevisiae S288C mating_type_a Time2 whole_organism whole_organism total_RNA Reference extract synthetic_DNA Cy5 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-7 P-EXML-4 P-EXML-5 P-EXML-6 Data2.txt A-EXML-1 244532
+Saccharomyces cerevisiae S288C mating_type_a Time3 whole_organism whole_organism total_RNA Time3 synthetic_DNA Cy3 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 3 Data3.txt A-EXML-1 244533
+Saccharomyces cerevisiae S288C mating_type_a Time3 whole_organism whole_organism total_RNA Reference extract synthetic_DNA Cy5 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-7 P-EXML-4 P-EXML-5 P-EXML-6 Data3.txt A-EXML-1 244533
diff --git a/examples/two_color/Data1.txt b/examples/two_color/Data1.txt
new file mode 100644
index 0000000..157be04
--- /dev/null
+++ b/examples/two_color/Data1.txt
@@ -0,0 +1,37 @@
+ATF 1.0
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 0.475 0.481 0.494 0.491 0.129 0.413 0.731 52 376 621 616 -1.074 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 0.494 0.496 0.500 0.599 0.409 0.433 0.777 120 778 1979 1904 -1.019 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 0.501 0.490 0.493 0.641 1.280 0.460 0.837 120 810 1379 1491 -0.998 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 0.780 0.782 0.819 1.372 3.252 0.675 0.722 120 796 1696 1663 -0.359 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 0.455 0.460 0.450 0.600 0.632 0.382 0.751 120 930 1334 1251 -1.137 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 0.420 0.431 0.473 0.696 2.121 0.362 0.589 120 914 426 438 -1.252 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 0.223 0.229 0.226 0.313 0.289 0.160 0.283 80 552 126 134 -2.163 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 0.525 0.503 0.522 0.741 1.710 0.445 0.754 120 816 1089 1052 -0.929 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 0.388 0.384 0.368 0.443 0.369 0.351 0.721 120 796 408 418 -1.367 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 0.885 0.912 0.923 1.477 3.031 0.751 0.698 120 810 1757 1648 -0.176 825 932 786 862 0
diff --git a/examples/two_color/Data2.txt b/examples/two_color/Data2.txt
new file mode 100644
index 0000000..f26132d
--- /dev/null
+++ b/examples/two_color/Data2.txt
@@ -0,0 +1,40 @@
+ATF 1.0
+27 43
+"Type=GenePix Results 1.4"
+"DateTime=2003/05/19 15:39:10"
+"Settings=E:\settings files\arrays.gps"
+"GalFile="
+"Scanner=GenePix 4000A [54807]"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data2.tif"
+"PMTVolts=680 610"
+"ScanPower=100 100"
+"FocusPosition=0"
+"NormalizationFactor:RatioOfMedians=0.93502"
+"NormalizationFactor:RatioOfMeans=0.917873"
+"NormalizationFactor:MedianOfRatios=0.933921"
+"NormalizationFactor:MeanOfRatios=0.954675"
+"NormalizationFactor:RegressionRatio=0.787143"
+"JpegImage=\\Server\Data2.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=560, 5360"
+"JpegOrigin=1650, 6810"
+"Creator=GenePix Pro 3.0.6.90"
+"Temperature=41.3353"
+"LaserPower=1.50975 1.13071"
+"LaserOnTime=41930 44902"
+"Supplier="
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532 Median" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - B53 [...]
+1 1 1 "" "" 2040 7130 110 112 120 55 49 53 24 80 63 0 191 200 78 50 51 13 100 100 0 0.447 0.473 0.502 0.428 2.467 0.472 0.332 80 656 204 221 -1.162 63 141 71 150 0
+1 2 1 "" "" 2330 7120 120 412 435 197 51 55 26 95 95 0 731 843 375 50 51 11 99 99 0 0.530 0.484 0.477 0.503 3.009 0.418 0.611 120 820 1042 1177 -0.916 361 681 384 793 0
+1 3 1 "" "" 2610 7130 120 297 367 232 52 56 25 95 90 0 417 587 397 51 52 12 97 95 0 0.669 0.588 0.592 0.604 3.170 0.511 0.592 120 820 611 851 -0.579 245 366 315 536 0
+1 4 1 "" "" 2900 7120 120 214 297 230 50 54 25 92 88 0 272 412 327 51 52 12 94 93 0 0.742 0.684 0.755 0.675 3.312 0.576 0.412 120 820 385 608 -0.430 164 221 247 361 0
+1 5 1 "" "" 3200 7130 120 355 386 203 52 56 26 92 91 0 683 701 245 51 52 12 100 100 0 0.479 0.514 0.506 0.502 2.251 0.507 0.593 120 820 935 984 -1.061 303 632 334 650 0
+1 6 1 "" "" 3500 7110 120 134 135 56 52 55 26 82 71 0 213 209 61 51 52 14 95 93 0 0.506 0.525 0.546 0.488 3.047 0.532 0.398 120 810 244 241 -0.982 82 162 83 158 0
+1 7 1 "" "" 3770 7120 80 79 80 33 50 54 27 51 26 0 100 100 26 51 52 11 98 82 0 0.592 0.612 1.025 0.705 2.930 1.819 0.071 52 340 78 79 -0.757 29 49 30 49 0
+1 8 1 "" "" 4070 7120 120 332 339 152 55 59 27 92 90 0 609 613 227 52 52 12 95 95 0 0.497 0.506 0.543 0.522 3.195 0.482 0.645 120 820 834 845 -1.008 277 557 284 561 0
+1 9 1 "" "" 4370 7090 110 167 179 82 53 57 26 91 86 0 240 254 102 52 54 15 97 96 0 0.606 0.624 0.669 0.614 2.841 0.577 0.414 80 616 302 328 -0.722 114 188 126 202 0
+1 10 1 "" "" 4650 7110 130 241 291 200 54 57 25 90 85 0 371 522 320 51 52 12 99 99 0 0.584 0.503 0.457 0.508 2.361 0.481 0.581 120 930 507 708 -0.775 187 320 237 471 0
diff --git a/examples/two_color/two_color.png b/examples/two_color/two_color.png
new file mode 100644
index 0000000..df1137c
Binary files /dev/null and b/examples/two_color/two_color.png differ
diff --git a/examples/two_color/two_color.txt b/examples/two_color/two_color.txt
new file mode 100644
index 0000000..23a547c
--- /dev/null
+++ b/examples/two_color/two_color.txt
@@ -0,0 +1,45 @@
+# Simple example summary spreadsheet for tab2mage.pl script.
+# This file illustrates how channels should be combined when describing a two-color hybridization (e.g. Cy3/Cy5).
+# Lines starting with a '#' are treated as comments and are ignored.
+
+Experiment section
+domain ebi.ac.uk
+accession E-EXML-1
+quality_control dye_swap_quality_control
+experiment_design_type strain_or_line_design;co-expression_design
+name Transcriptome analysis of invasive verses non-invasive strains of budding yeast
+description An experiment was performed to...
+release_date 2004-08-30
+submission_date 2004-07-28
+submitter John Falstaff
+submitter_email jfalstaff at wagglespike.com
+investigator Bill Shakespeare
+investigator_email bills at wagglespike.com
+organization Windsor Laboratories
+address Windsor, Ontario, Canada
+# The publication details below are fictitious
+publication_title Comparison of the transcriptomes of invasive verses non-invasive strains of budding yeast
+authors John Falstaff; Robin Goodfellow; Bill Shakespeare
+journal Nature Genetics
+volume 12
+issue 4
+pages 123-456
+year 2004
+pubmed_id 12345678
+
+# Protocol text may be formatted using standard HTML tags
+Protocol section
+accession text name
+P-EXML-1 Cells were grown in YPD (1% yeast extract/2% peptone/2% glucose) to an OD600 of approximately 0.8 Yeast growth
+P-EXML-2 Your protocol text here Yeast cell harvesting
+P-EXML-3 Your protocol text here Cell lysis and RNA prep
+P-EXML-4 Your protocol text here cDNA labeling
+P-EXML-5 Your protocol text here Hybridization
+P-EXML-6 Your protocol text here Scanning
+
+Hybridization section
+File[raw] Array[accession] Array[serial] Protocol[grow] Protocol[treatment] Protocol[extraction] Protocol[labeling] Protocol[hybridization] Protocol[scanning] BioSource Sample Extract LabeledExtract Hybridization BioSourceMaterial SampleMaterial ExtractMaterial LabeledExtractMaterial Dye FactorValue[StrainOrLine] BioMaterialCharacteristics[Organism] BioMaterialCharacteristics[StrainOrLine] BioMaterialCharacteristics[Sex]
+Data1.txt A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 S288C S288C S288C S288C Cy5 Dye Swap 1 whole_organism whole_organism total_RNA synthetic_DNA Cy5 S288C Saccharomyces cerevisiae S288C mating_type_a
+Data1.txt A-EXML-1 244532 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 Sigma1278b Sigma1278b Sigma1278b Sigma1278b Cy3 Dye Swap 1 whole_organism whole_organism total_RNA synthetic_DNA Cy3 Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
+Data2.txt A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 S288C S288C S288C S288C Cy3 Dye Swap 2 whole_organism whole_organism total_RNA synthetic_DNA Cy3 S288C Saccharomyces cerevisiae S288C mating_type_a
+Data2.txt A-EXML-1 244533 P-EXML-1 P-EXML-2 P-EXML-3 P-EXML-4 P-EXML-5 P-EXML-6 Sigma1278b Sigma1278b Sigma1278b Sigma1278b Cy5 Dye Swap 2 whole_organism whole_organism total_RNA synthetic_DNA Cy5 Sigma1278b Saccharomyces cerevisiae Sigma1278b mating_type_a
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..714345a
--- /dev/null
+++ b/index.html
@@ -0,0 +1,356 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>Tab2MAGE</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" href="docs/style.css">
+ </head>
+
+ <body>
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="270"><img src="docs/T2M_logo.png" border="0" height="50" alt="Tab2MAGE logo"> <img src="docs/MAGETAB_logo.png" border="0" height="50" alt="MAGE-TAB logo"></td>
+ <td class="pagetitle">Home page</td>
+ </tr>
+ </table>
+ </div>
+
+ <div CLASS="section">
+ <table class="layout">
+ <tr>
+ <td class="layout">
+
+ <ul>
+ <li><a href="#intro">Introduction</a></li>
+ <li><a href="#tab2mage">Tab2MAGE MAGE-ML generation</a></li>
+ <li><a href="#magetab">MAGE-TAB MAGE-ML generation</a></li>
+ <li><a href="#expt_check">Experiment checking script</a></li>
+ <li><a href="#visualize">Experiment visualization</a></li>
+ <li><a href="#other_packages">Additional packages</a></li>
+ <li><a href="#contact">Contact details</a></li>
+ </ul>
+
+ <ul>
+ <li><a href="docs/spreadsheet.html"><b>How to create a Tab2MAGE spreadsheet</b></a> (<a href="docs/spreadsheet.html#realexamples">examples</a>)</li>
+ <li><a href="docs/aesubs.html"><b>Tab2MAGE submission to ArrayExpress</b></a></li>
+ <li><a href="docs/magetab_subs.html"><b>MAGE-TAB submission to ArrayExpress</b></a></li>
+ <li><a href="docs/index.html">Full Documentation (spreadsheets, usage, APIs)</a></li>
+ <li><a href="http://sourceforge.net/projects/tab2mage/">Tab2MAGE SourceForge summary</a></li>
+ <li><a href="http://sourceforge.net/project/showfiles.php?group_id=120325">Tab2MAGE downloads</a></li>
+ </ul>
+
+ </td>
+ <td class="layout" valign="top">
+ <div CLASS="imghead">
+ <a href="http://www.ebi.ac.uk/arrayexpress/">
+ <img src="docs/aelogo.png" border="0" alt="ArrayExpress logo">
+ </a>
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <div CLASS="section">
+
+ <div CLASS="sectionhead"><a name="intro"></a>Introduction</div>
+
+ <p CLASS="text">Tab2MAGE is a software package written and
+ supported by the <a
+ href="http://www.ebi.ac.uk/arrayexpress/">ArrayExpress</a>
+ curation team, which aims to ease the process of submitting
+ large microarray experiment datasets to our public repository
+ database. To this end, Tab2MAGE currently includes two tools,
+ the <a href="#tab2mage">tab2mage.pl</a> script itself, and a
+ data file checking script, <a
+ href="#expt_check">expt_check.pl</a>. With these scripts it is
+ possible to perform an initial data file validation against an
+ array design (e.g., in the form of an "Array Description File"
+ or ADF), and then to generate MAGE-ML using these data files
+ alongside a separate spreadsheet providing MIAME-compliant
+ sample annotation.</p>
+
+ <p class="text">As of version 1.9.9, the package also includes
+ tools to <a href="#expt_check">validate MAGE-TAB documents</a>
+ and <a href="#magetab">convert them into MAGE-ML</a>.</p>
+
+ <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div CLASS="section">
+
+ <div CLASS="sectionhead"><a name="tab2mage"></a>Tab2MAGE MAGE-ML generation</div>
+
+ <p CLASS="text">The tab2mage.pl script is currently under active
+ development. The latest version released on SourceForge is aimed
+ at the moderately experienced user. For the time being it is
+ still recommended that small to medium-sized experiment data
+ submissions be provided via the <a
+ href="http://www.ebi.ac.uk/miamexpress/">MIAMExpress</a> web
+ interface. The primary aim of the Tab2MAGE project is to provide
+ an easy-to-use method for submitting large datasets (e.g.,
+ greater than around 50 hybridizations) to
+ ArrayExpress.</p>
+
+ <p CLASS="text">This script is currently capable of parsing data
+ files in a number of formats. These formats include
+ tab-delimited text files such as those produced by GenePix,
+ BlueFuse, ScanAlyze, ScanArray or Agilent software, and also
+ the data files produced by Affymetrix scanning software. The
+ generic MetaColumn/MetaRow format favoured by ArrayExpress is of
+ course also supported. As of version 1.9.4, Tab2MAGE now also
+ supports most data files produced by the <a
+ href="http://www.illumina.com/">Illumina BeadStudio</a>
+ software package.</p>
+
+ <p CLASS="text">To use the script a sample annotation
+ spreadsheet must be provided. This experiment summary file has a
+ flexible format based around a set of predefined column
+ headings. Here is an <a
+ href="examples/two_color/two_color.txt">example</a> of the
+ format of this file, which corresponds to the experiment design
+ <a href="examples/two_color/two_color.png">depicted
+ here</a>. The spreadsheet consists of three sections,
+ separated by one or more blank lines: <em>Experiment</em>,
+ <em>Protocol</em> and <em>Hybridization</em>.</p>
+
+ <p CLASS="text"><a class="subhead" href="docs/spreadsheet.html#experiment">Experiment
+ section</a><br> This section contains top-level information
+ about the experiment, such as the title, description and
+ accession number, organized by row.</p>
+
+ <p CLASS="text"><a class="subhead" href="docs/spreadsheet.html#protocol">Protocol
+ section</a><br>Protocols are defined as needed in this
+ section, with one protocol per row. If all of the protocols used
+ in the experiment have previously been loaded into ArrayExpress
+ and given accession numbers, this whole section can be
+ omitted.</p>
+
+ <p CLASS="text"><a class="subhead" href="docs/spreadsheet.html#hybridization">Hybridization
+ section</a><br>This section contains the bulk of the
+ experiment information. At its simplest, each row describes the
+ route taken from BioSource to output data file. Multichannel
+ (e.g., two-colour) data can be described by entering each
+ channel as a separate line. Pooling can be described at multiple
+ levels by using as many lines as necessary to describe all the
+ relationships between upstream and downstream samples. In a
+ sense this table can be compared to an SQL database table in the
+ way that it provides links between MAGE objects.</p>
+
+ <p CLASS="text">Further <a
+ href="docs/spreadsheet.html#examples">examples</a> of the
+ Tab2MAGE spreadsheet format are included in the <a
+ href="docs/index.html">online package documentation</a>,
+ alongside detailed information on <a
+ href="docs/ArrayExpress/Curator/MAGE/Definitions.html#supported_row_and_column_headings">allowed column
+ and row names</a>. A set of <a
+ href="docs/usage.html">tab2mage.pl usage notes</a> is also
+ included, giving examples of how to invoke the script.</p>
+
+ <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div CLASS="section">
+
+ <div CLASS="sectionhead"><a name="magetab"></a>MAGE-TAB MAGE-ML generation</div>
+
+ <p class="text">New in version 1.9.7, the Tab2MAGE package now
+ includes a script, <a href="docs/magetab.html">magetab.pl</a>,
+ for generating MAGE-ML from a MAGE-TAB document. Most, but not
+ all, of the requirements from the specification have now been
+ implemented. Please see the MAGETAB.txt file included in the
+ package for implementation notes and a discussion of current
+ limitations. The script is typically be invoked as follows:</p>
+
+ <pre>magetab.pl -i <IDF file> -n <experiment accession> -t <output directory></pre>
+
+ <p class="text">The MAGE-TAB parser is currently based on the
+ version 1.0 specification for MAGE-TAB, available for download
+ <a
+ href="http://www.ebi.ac.uk/systems-srv/mp/file-exchange/MAGE-TABv1.0.tar.gz">from
+ the EBI website</a>. See these <a
+ href="docs/magetab_subs.html">MAGE-TAB help notes</a> for
+ information on submitting MAGE-TAB documents to
+ ArrayExpress.</p>
+
+ <p class="text">See also the <a
+ href="#other_packages">Additional packages</a> section for
+ information on converting from Tab2MAGE to MAGE-TAB format.</p>
+
+ <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div CLASS="section">
+
+ <div CLASS="sectionhead"><a name="expt_check"></a>Experiment checker script</div>
+
+ <p CLASS="text">The expt_check.pl script can be used to validate microarray
+ experiment data files and associated MIAME metadata for common errors.
+ The script can be used in any of four different modes, depending on
+ your needs: Tab2MAGE, MAGE-TAB, MIAMExpress or full stand-alone mode. <a
+ href="docs/expt_check.html">Full documentation</a> is
+ available; in summary, the four forms are shown here:</p>
+
+ <p class="subhead">Tab2MAGE mode</p>
+ <pre>expt_check.pl -e <Tab2MAGE spreadsheet file></pre>
+
+ <p class="subhead">MAGE-TAB mode</p>
+ <pre>expt_check.pl -i <MAGE-TAB IDF file></pre>
+
+ <p class="text">Unless the "-s" ("stand-alone") option is used,
+ the script will attempt to connect to the ArrayExpress database to
+ retrieve information on the array design(s) used in the experiment.
+ The array information is used to check the data files for consistency.
+ This behaviour may also be supressed by supplying the name of
+ an ADF file to the script, using the -a option (see below).</p>
+
+ <p class="subhead">MIAMExpress mode (local installations only)</p>
+ <pre>expt_check.pl -l <login username> -t <experiment title></pre>
+
+ <p class="text">Note that if you wish to use the expt_check.pl
+ script with a local installation of MIAMExpress, you will need
+ to edit the database connection parameters in the included <a
+ href="docs/ArrayExpress/Curator/Config.html">ArrayExpress::Curator::Config
+ module</a>.</p>
+
+ <p class="subhead">Full stand-alone mode</p>
+ <pre>expt_check.pl -s -a <ADF file> <list of data files></pre>
+
+ <p CLASS="text">If an ADF is
+ supplied to the script in stand-alone mode, the data files will be checked for
+ features missing from that ADF (please see the <a
+ href="http://www.ebi.ac.uk/miamexpress/help/adf/">MIAMExpress ADF
+ help notes</a> for information on the ADF format). A full list
+ of the tests performed by the experiment checker script is
+ available in the documentation.</p>
+
+ <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div CLASS="section">
+
+ <div CLASS="sectionhead"><a name="other_packages"></a>Additional packages</div>
+
+ <p CLASS="text">There are several additional packages related to
+ Tab2MAGE which are available for download from the <a
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325">SourceForge
+ project pages</a>:</p>
+
+ <p class="text"><a name="visualize"
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=163063"
+ class="subhead">MAGE-ML Visualize</a><br/>A popular download,
+ this MAGE-ML visualization script developed by Anna Farne at the
+ EBI will read any MAGE-ML file and generate a graph showing the
+ links between BioMaterials all the way through to BioAssays, and
+ include information on sample characteristics and experimental
+ factor values in the output. Here is <a
+ href="examples/final_data_matrix/E-EXML-7.xml.dot.jpg">an
+ example of a MAGE-ML Visualize graph</a>.
+ </p>
+
+ <p class="text">Alternatively, the <a
+ href="#expt_check">expt_check.pl</a> script may be used to
+ visualize either Tab2MAGE or MAGE-TAB encoded experiments. A
+ command-line option, "-x" has been provided for omission of data
+ file checking, allowing for quick visualization of the graph
+ contained in a spreadsheet. The output from this script is a
+ more minimalistic version of that given by MAGE-ML Visualize (<a
+ href="examples/final_data_matrix/final_data_matrix.png">expt_check.pl
+ graph example</a>). </p>
+
+ <p class="text">Both these scripts use the Graphviz software
+ package to generate the graph output. Please see the <a
+ href="http://www.graphviz.org/">Graphviz website</a> for
+ installation instructions.</p>
+
+ <p class="text"><a name="adfchecker"
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=201790"
+ class="subhead">ADF Checker</a><br/>The ADF checker script, also
+ developed by Anna Farne, can be used to validate the format and
+ content of an <a
+ href="http://www.ebi.ac.uk/miamexpress/help/adf/index.html">Array Description Format
+ file</a> (used to describe an array design in tabular
+ format). An online version of this tool is also available on the
+ <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/ADF_checker_page.pl">EBI
+ website</a>.</p>
+
+ <p class="text"><a name="tabconverter"
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=226041"
+ class="subhead">Tab2MAGE to MAGE-TAB converter</a><br/>Faisal
+ Ibne Rezwan at the EBI has written this script to convert
+ Tab2MAGE spreadsheets to MAGE-TAB IDF and SDRF component
+ documents.</p>
+
+ <p class="text"><a name="geoimport"
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=243607"
+ class="subhead">GEOImport</a><br/>Faisal Ibne Rezwan has also
+ written this package in collaboration with Margus Lukk (also at
+ the EBI) to import experiments from the <a
+ href="http://www.ncbi.nlm.nih.gov/geo/">NCBI GEO database</a>
+ into Tab2MAGE format. These scripts are being used to facilitate
+ data transfer between GEO and ArrayExpress. At the time of
+ writing, over 250 experiments have been processed using this
+ package. Currently only Affymetrix-based experiments are
+ supported, but we hope to add support for other platforms
+ shortly.</p>
+
+ <p class="text"><a name="oe2magetab"
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=281911"
+ class="subhead">OE2magetab</a><br/>A MAGE-TAB post-hoc ontology matching
+ utility. Xiangqun Zheng Bradley at the EBI has created this
+ package to match sample annotation terms from an input MAGE-TAB
+ document to an ontology supplied in either OBO or flat-file
+ format. This script generates an output MAGE-TAB document
+ containing the appropriate "Term Source REF" and "Term Source
+ Accession" values.</p>
+
+ <p class="text"><a name="mage2tab"
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=219611"
+ class="subhead">mage2tab</a><br/>The mage2tab project is
+ maintained by Junmin Liu at <a
+ href="http://www.cbil.upenn.edu/">CBIL</a>. Its aim is to
+ provide a simple means to convert MAGE-ML documents to MAGE-TAB
+ format. Further information on mage2tab is available on the <a
+ href="https://www.cbil.upenn.edu/magewiki/index.php/mage2tab">CBIL
+ wiki page</a>.</p>
+
+ <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <div CLASS="section">
+
+ <div CLASS="sectionhead"><a name="contact"></a>Contact details</div>
+
+ <p CLASS="text">Please contact the
+ <script type="text/javascript">
+ <!--
+ var aname = "miamexpress@ebi.ac.uk";
+ document.write("<a href='" + "mail" + "to:" + aname + "'>ArrayExpress curators<" + "/a>")
+ //-->
+ </script> for help with data submission, or for more information on local installation and use of Tab2MAGE.
+ </p>
+
+ <p CLASS="sectionfoot">[ <a href="#top">Top of page</a> ]</p>
+
+ </div>
+
+ <hr>
+
+ <a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" />
+ </a>
+ <br>
+<!-- Created: Wed Sep 29 09:26:35 BST 2004 -->
+<!-- hhmts start -->
+Last modified: Mon Apr 21 12:21:19 BST 2008
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/lib/ArrayExpress/ArrayMAGE.pm b/lib/ArrayExpress/ArrayMAGE.pm
new file mode 100644
index 0000000..28ed571
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE.pm
@@ -0,0 +1,658 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayMAGE.pm 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../index.html">
+ <img src="../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: ArrayMAGE.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::ArrayMAGE.pm - an OO module providing methods
+for writing MAGE-ML for array designs.
+
+=head1 SYNOPSIS
+
+ # Import all the ArrayMAGE classes.
+ use ArrayExpress::ArrayMAGE qw(:ALL);
+
+ # Instantiate the objects we require.
+ my @classes = qw(
+ Feature
+ Reporter
+ BioSequence
+ FeatureReporterMap
+ ArrayDesign
+ Zone
+ );
+ my %object;
+ foreach my $type (@classes) {
+ if ( $type eq 'ArrayDesign' ) {
+
+ # ArrayDesign requires an accession.
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new({
+ accession => $acc,
+ });
+ }
+ else {
+ $object{$type} = "ArrayExpress::ArrayMAGE::$type"->new();
+ }
+ }
+
+ # ReporterGroup and CompositeGroup are typically handled
+ # slightly differently, because multiple groups may be
+ # instantiated during a run (see below).
+ my ( %reporter_group, %composite_group );
+
+ # Add objects, e.g. while scanning through an ADF or CDF file.
+ foreach my $row_hashref ( @list_of_lines ) {
+ $object{Feature}->add({
+ metacolumn => $row->{'MetaColumn'},
+ metarow => $row->{'MetaRow'},
+ column => $row->{'Column'},
+ row => $row->{'Row'},
+ });
+
+ # ReporterGroups and CompositeGroups are handled in similar
+ # ways (only ReporterGroup is shown here):
+ while ( my ( $rname, $rvalue ) = each %$row ) {
+ if ( my ($type) = ( $rname =~ m!ReporterGroup\[(.*?)\]!i ) ) {
+ my $key = "$type.$rvalue";
+ $reporter_group{$key}
+ ||= ArrayExpress::ArrayMAGE::ReporterGroup->new({
+ identifier => $key,
+ tag => $rvalue,
+ is_species => ( $type =~ m!species!i ) || 0,
+ });
+ $reporter_group{$key}->add({
+ identifier => $row->{'Reporter Identifier'},
+ });
+ }
+ }
+ }
+
+ # Write out the MAGE-ML to STDOUT.
+ ArrayExpress::ArrayMAGE->combine_parts(
+ \%objects,
+ \%reporter_group,
+ \%composite_group,
+ \*STDOUT,
+ );
+
+=head1 DESCRIPTION
+
+This is a module designed to allow the creation of MAGE-ML for array
+designs in a memory-efficient fashion. This module is the abstract
+superclass for a series of MAGE-like classes representing parts of the
+MAGE-ML document to be written. These classes, when instantiated, all
+point to temporary filehandles into which is written the XML
+describing MAGE object instances. Each subclass implements an 'add'
+method in addition to those inherited from this base
+class. Instantiating the object with new() opens up the XML tags for a
+list of objects, the 'add' adds objects based on the values it is
+passed (no surprise there) and finally the XML tags are closed and the
+MAGE-ML written out by calling the class method combine_parts() in this
+top-level class.
+
+=head1 PUBLIC METHODS
+
+=over 2
+
+=item C<combine_parts( \%obj, \%rg, \%cg, $fh )>
+
+Class method. Takes: a hashref of ArrayMAGE objects, a hashref of
+ReporterGroups, a hashref of CompositeGroups, and an open output
+filehandle, and writes the completed MAGE-ML to that filehandle.
+
+=item C<set_namespace("namespace")>
+
+Class method. Allows the user to set the identifier namespace for the
+output MAGE objects.
+
+=item C<set_design("design")>
+
+Class method. Allows the user to set the design name used in
+identifiers for the output MAGE objects. Typically this will be the
+same as the array accession, but e.g. for Affymetrix arrays will
+correspond to the design name.
+
+=item C<set_separator("separator")>
+
+Class method. Allows the user to set the identifier separator for the
+output MAGE objects. Usually this will be q{:} or q{.}.
+
+=item C<set_programname("programname")>
+
+Class method. Allows the user to set the name of the program (this is
+not particularly useful).
+
+=back
+
+=head1 SUBCLASSES
+
+=head2 ArrayExpress::ArrayMAGE::ArrayDesign
+
+=over 2
+
+=item new() required attribute: C<accession>
+
+The accession attribute becomes the MAGE PhysicalArrayDesign
+identifier.
+
+=item add() is not implemented for this class.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::BioSequence
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create the
+BioSequence identifier.
+
+=item add() optional attribute: C<dbrefs>
+
+A hashref with db accessions as keys, and db tags as values
+(e.g. C<{ A12345 =E<gt> 'embl' }> ). Typically used for Reporter
+BioSequence Database Entry ADF columns.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::CompositeGroup
+
+=over 2
+
+=item new() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create a
+CompositeGroup identifier.
+
+=item new() required attribute: C<tag>
+
+This tag is used as the CompositeGroup name. If C<is_species> is set,
+then it will also be used in a Species_assn element as the species
+name.
+
+=item new() optional attribute: C<is_species>
+
+A flag indicating whether this is a grouping based on species or not.
+
+=item add() required attribute: C<id_ref>
+
+This identifier is combined with the class namespace to create a
+CompositeSequence_ref identifier.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::CompositeSequence
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create the
+CompositeSequence identifier.
+
+=item add() optional attribute: C<name>
+
+The name of the CompositeSequence.
+
+=item add() optional attribute: C<biosequences>
+
+An arrayref of BioSequence identifiers to be attached to this
+CompositeSequence.
+
+=item add() optional attribute: C<dbrefs>
+
+A hashref with db accessions as keys, and db tags as values
+(e.g. C<{ A12345 =E<gt> 'embl' }> ). Typically used for CompositeSequence
+Description Database Entry ADF columns.
+
+=item add() optional attribute: C<controltype>
+
+A ControlType ontology term to be attached to this CompositeSequence.
+
+=item add() optional attribute: C<comment>
+
+A comment to be added to this CompositeSequence as a Description_assn.
+
+=item add() optional attribute: C<cs_only>
+
+A flag indicating that we are not creating Reporters (and can
+therefore skip ReporterCompositeMap creation).
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Database
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<tag>
+
+This tag is combined with the class namespace to create the Database
+identifier. Typically this will be a standard database tag,
+e.g. C<embl>.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Feature
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<metacolumn>
+
+=item add() required attribute: C<metarow>
+
+=item add() required attribute: C<column>
+
+=item add() required attribute: C<row>
+
+Feature coordinates, used in the Feature and Zone_ref identifiers and
+in FeatureLocation.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::FeatureReporterMap
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+The Reporter identifier to which Features should be mapped.
+
+=item add() required attribute: C<features>
+
+An arrayref of Feature identifiers mapped to the Reporter.
+
+=item add() optional attribute: C<mismatch_info>
+
+A hashref of hashrefs, keyed by Feature identifier and then by
+C<start_coord>, C<new_sequence> and C<replaced_length>, used to create
+a MismatchInformation_assnlist (useful for Affymetrix array designs).
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Reporter
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create the
+Reporter identifier.
+
+=item add() optional attribute: C<name>
+
+The name of the Reporter.
+
+=item add() optional attribute: C<biosequences>
+
+An arrayref of BioSequence identifiers to be attached to this
+Reporter.
+
+=item add() optional attribute: C<dbrefs>
+
+A hashref with db accessions as keys, and db tags as values
+(e.g. C<{ A12345 =E<gt> 'embl' }> ). Typically used for Reporter
+Description Database Entry ADF columns.
+
+=item add() optional attribute: C<controltype>
+
+A ControlType ontology term to be attached to this Reporter.
+
+=item add() optional attribute: C<warningtype>
+
+A WarningType ontology term to be attached to this Reporter.
+
+=item add() optional attribute: C<failtype>
+
+A FailType ontology term to be attached to this Reporter.
+
+=item add() optional attribute: C<comment>
+
+A comment to be added to this Reporter as a Description_assn.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::ReporterCompositeMap
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<identifier>
+
+The CompositeSequence identifier to which Reporters should be mapped.
+
+=item add() required attribute: C<reporters>
+
+An arrayref of Reporter identifiers mapped to the CompositeSequence.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::ReporterGroup
+
+=over 2
+
+=item new() required attribute: C<identifier>
+
+This identifier is combined with the class namespace to create a
+ReporterGroup identifier.
+
+=item new() required attribute: C<tag>
+
+This tag is used as the ReporterGroup name. If C<is_species> is set,
+then it will also be used in a Species_assn element as the species
+name.
+
+=item new() optional attribute: C<is_species>
+
+A flag indicating whether this is a grouping based on species or not.
+
+=item add() required attribute: C<id_ref>
+
+This identifier is combined with the class namespace to create a
+Reporter_ref identifier.
+
+=back
+
+=head2 ArrayExpress::ArrayMAGE::Zone
+
+=over 2
+
+=item new() requires no attributes.
+
+=item add() required attribute: C<column>
+
+=item add() required attribute: C<row>
+
+Zone coordinates, used in the Zone identifier and in its row and
+column attributes.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::ArrayMAGE;
+
+use IO::File;
+use Carp;
+use Class::Std;
+
+my %filehandle : ATTR( :set<filehandle>, :default<undef> );
+
+# These are class variables used to set global behaviour.
+my %ClassOptions = (
+ programname => q{ArrayMAGE},
+ separator => q{.},
+ design => undef,
+);
+
+$ClassOptions{namespace} = sprintf( "ebi.ac.uk:%s", $ClassOptions{programname} );
+
+# Define instance/class methods to set and get options.
+{
+ ## no critic ProhibitNoStrict
+ no strict qw(refs);
+ ## use critic ProhibitNoStrict
+
+ foreach my $method ( qw(programname namespace separator design) ) {
+ my $setter = "set_$method";
+ my $getter = "get_$method";
+
+ *{$setter} = sub {
+ my ( $class, @args ) = @_;
+
+ unless ( scalar @args && defined $args[0] ) {
+ croak("Error: no value passed to $method setter.\n");
+ }
+
+ $ClassOptions{$method} = $args[0];
+
+ return;
+ };
+
+ *{$getter} = sub {
+
+ my ( $class ) = @_;
+
+ return $ClassOptions{$method};
+ }
+ }
+}
+
+# Convenience import method.
+sub import {
+
+ my ( $pkg, @tags ) = @_;
+
+ foreach (@tags) {
+ if ( $_ =~ m/ALL/i ) {
+ import_all();
+ }
+ }
+
+ return;
+}
+
+sub import_all {
+
+ eval {
+ require ArrayExpress::ArrayMAGE::ArrayDesign;
+ require ArrayExpress::ArrayMAGE::BioSequence;
+ require ArrayExpress::ArrayMAGE::CompositeGroup;
+ require ArrayExpress::ArrayMAGE::CompositeSequence;
+ require ArrayExpress::ArrayMAGE::Database;
+ require ArrayExpress::ArrayMAGE::Feature;
+ require ArrayExpress::ArrayMAGE::FeatureReporterMap;
+ require ArrayExpress::ArrayMAGE::Reporter;
+ require ArrayExpress::ArrayMAGE::ReporterCompositeMap;
+ require ArrayExpress::ArrayMAGE::ReporterGroup;
+ require ArrayExpress::ArrayMAGE::Zone;
+ require ArrayExpress::ArrayMAGE::ProgressBar;
+ };
+
+ if ($@) {
+ croak "ArrayMAGE load error: $@";
+ }
+
+ return;
+}
+
+sub combine_parts {
+
+ # Given hashrefs to a general set of ArrayMAGEs and the
+ # ReporterGroups ArrayMAGEs, merge everything with a bit of glue
+ # and output to the passed filehandle.
+
+ my ( $class, $objects, $reporter_group, $composite_group, $fh ) = @_;
+
+ # Close out the various XML tags.
+ foreach my $object ( values %{ $objects },
+ values %{ $reporter_group },
+ values %{ $composite_group } ) {
+ $object->end();
+ }
+
+ my $namespace = $class->get_namespace();
+ my $design = $class->get_design();
+ my $separator = $class->get_separator();
+
+ print STDOUT ("\nCombining MAGE-ML parts into output file...\n");
+
+ print $fh <<"MAGE_OUT";
+<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
+<!DOCTYPE MAGE-ML PUBLIC "-//OMG//DTD MAGE-ML 1.1//EN" "MAGE-ML.dtd">
+<MAGE-ML identifier="MAGE:$namespace:$design">
+MAGE_OUT
+
+ foreach my $type qw(Database BioSequence) {
+ $objects->{$type}->output($fh) if $objects->{$type};
+ }
+
+ print $fh <<"MAGE_OUT";
+ <DesignElement_package>
+MAGE_OUT
+
+ foreach my $type
+ qw(CompositeSequence Reporter ReporterCompositeMap FeatureReporterMap)
+ {
+ $objects->{$type}->output($fh) if $objects->{$type};
+ }
+
+ print $fh <<"MAGE_OUT";
+ </DesignElement_package>
+MAGE_OUT
+
+ print $fh <<"MAGE_OUT";
+ <ArrayDesign_package>
+MAGE_OUT
+
+ if ( scalar( grep { defined $_ } values %$reporter_group ) ) {
+ print $fh <<"MAGE_OUT";
+ <ReporterGroup_assnlist>
+MAGE_OUT
+
+ foreach my $group ( values %$reporter_group ) {
+ $group->output($fh);
+ }
+ print $fh <<"MAGE_OUT";
+ </ReporterGroup_assnlist>
+MAGE_OUT
+ }
+
+ if ( scalar( grep { defined $_ } values %$composite_group ) ) {
+ print $fh <<"MAGE_OUT";
+ <CompositeGroup_assnlist>
+MAGE_OUT
+ foreach my $group ( values %$composite_group ) {
+ $group->output($fh);
+ }
+ print $fh <<"MAGE_OUT";
+ </CompositeGroup_assnlist>
+MAGE_OUT
+ }
+
+ foreach my $type qw(ArrayDesign Feature) {
+ $objects->{$type}->output($fh) if $objects->{$type};
+ }
+
+ if ( scalar( grep { defined $_ } values %$reporter_group ) ) {
+ print $fh <<"MAGE_OUT";
+ <ReporterGroups_assnreflist>
+MAGE_OUT
+ foreach my $key ( keys %$reporter_group ) {
+ print $fh <<"MAGE_OUT";
+ <ReporterGroup_ref identifier="$namespace:ReporterGroup:$design$separator$key"/>
+MAGE_OUT
+ }
+ print $fh <<"MAGE_OUT";
+ </ReporterGroups_assnreflist>
+MAGE_OUT
+ }
+
+ if ( scalar( grep { defined $_ } values %$composite_group ) ) {
+ print $fh <<"MAGE_OUT";
+ <CompositeGroups_assnreflist>
+MAGE_OUT
+ foreach my $key ( keys %$composite_group ) {
+ print $fh <<"MAGE_OUT";
+ <CompositeGroup_ref identifier="$namespace:CompositeGroup:$design$separator$key"/>
+MAGE_OUT
+ }
+ print $fh <<"MAGE_OUT";
+ </CompositeGroups_assnreflist>
+MAGE_OUT
+ }
+
+ foreach my $type qw(Zone) {
+ $objects->{$type}->output($fh) if $objects->{$type};
+ }
+
+ print $fh <<"MAGE_OUT";
+ </PhysicalArrayDesign>
+ </ArrayDesign_assnlist>
+ </ArrayDesign_package>
+</MAGE-ML>
+MAGE_OUT
+
+ return;
+}
+
+sub get_fh : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ unless ( $filehandle{ident $self} ) {
+ $filehandle{ident $self} = IO::File->new_tmpfile();
+ }
+ return $filehandle{ident $self};
+}
+
+sub output : RESTRICTED {
+
+ my ( $self, $output_fh ) = @_;
+
+ my $fh = $self->get_fh();
+ $fh->isa('IO::File') or die("Error: object filehandle uninitialized.");
+ seek( $fh, 0, 0 );
+ while ( my $line = <$fh> ) {
+ print $output_fh $line;
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm b/lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm
new file mode 100644
index 0000000..de3d604
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ArrayDesign.pm
@@ -0,0 +1,42 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesign.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+###############
+# ArrayDesign #
+###############
+package ArrayExpress::ArrayMAGE::ArrayDesign;
+use base 'ArrayExpress::ArrayMAGE';
+
+my %accession : ATTR( :get<accession>, :init_arg<accession>, :default<undef> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $identifier = $self->get_accession();
+ defined( $identifier )
+ or croak("Error: ArrayDesign accession must be set");
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <ArrayDesign_assnlist>
+ <PhysicalArrayDesign identifier="$identifier">
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/BioSequence.pm b/lib/ArrayExpress/ArrayMAGE/BioSequence.pm
new file mode 100644
index 0000000..27ed7e4
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/BioSequence.pm
@@ -0,0 +1,96 @@
+#!/usr/bin/env perl
+#
+# $Id: BioSequence.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+###############
+# BioSequence #
+###############
+package ArrayExpress::ArrayMAGE::BioSequence;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <BioSequence_package>
+ <BioSequence_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $fh = $self->get_fh();
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ defined $args->{identifier} or die;
+
+ print $fh
+ " <BioSequence identifier=\"$namespace:BioSequence:$design$separator$args->{identifier}\"";
+ $args->{sequence} && do {
+ my $seqlen = length $args->{sequence};
+ print $fh
+ "\n sequence=\"$args->{sequence}\"\n length=\"$seqlen\"";
+ };
+ print $fh ">\n";
+
+ if ( $args->{dbrefs}
+ && scalar( grep { defined $_ } values %{ $args->{dbrefs} } ) ) {
+ ref $args->{dbrefs} eq 'HASH' or die;
+ print $fh " <SequenceDatabases_assnlist>\n";
+ while ( my ( $acc, $db ) = each %{ $args->{dbrefs} } ) {
+ print $fh <<"MAGE_OUT";
+ <DatabaseEntry accession="$acc">
+ <Database_assnref>
+ <Database_ref identifier="ebi.ac.uk:Database:$db"/>
+ </Database_assnref>
+ </DatabaseEntry>
+MAGE_OUT
+ }
+ print $fh " </SequenceDatabases_assnlist>\n";
+ };
+
+ $args->{polymertype} && print $fh <<"MAGE_OUT";
+ <PolymerType_assn>
+ <OntologyEntry category="PolymerType"
+ value="$args->{polymertype}">
+ </OntologyEntry>
+ </PolymerType_assn>
+MAGE_OUT
+
+ $args->{type} && print $fh <<"MAGE_OUT";
+ <Type_assn>
+ <OntologyEntry category="BioSequenceType"
+ value="$args->{type}">
+ </OntologyEntry>
+ </Type_assn>
+MAGE_OUT
+
+ print $fh " </BioSequence>\n";
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </BioSequence_assnlist>
+ </BioSequence_package>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm b/lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm
new file mode 100644
index 0000000..c91b19a
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/CompositeGroup.pm
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+#
+# $Id: CompositeGroup.pm 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+##################
+# CompositeGroup #
+##################
+package ArrayExpress::ArrayMAGE::CompositeGroup;
+use base 'ArrayExpress::ArrayMAGE';
+
+my %identifier : ATTR( :get<identifier>, :init_arg<identifier>, :default<undef> );
+my %tag : ATTR( :get<tag>, :init_arg<tag>, :default<undef> );
+my %is_species : ATTR( :get<is_species>, :init_arg<is_species>, :default<undef> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $identifier = $self->get_identifier();
+ defined( $identifier )
+ or croak("Error: CompositeGroup identifier must be set");
+ my $tag = $self->get_tag();
+ defined( $tag )
+ or croak("Error: CompositeGroup tag must be set");
+
+ my $fh = $self->get_fh();
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ print $fh <<"MAGE_OUT";
+ <CompositeGroup identifier="$namespace:CompositeGroup:$design$separator$identifier"
+ name="$tag">
+MAGE_OUT
+
+ $self->get_is_species() && print $fh <<"MAGE_OUT";
+ <Species_assn>
+ <OntologyEntry category="Organism"
+ value="$tag">
+ </OntologyEntry>
+ </Species_assn>
+MAGE_OUT
+
+ print $fh <<"MAGE_OUT";
+ <CompositeSequences_assnreflist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ defined $args->{id_ref} or die;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <CompositeSequence_ref identifier="$namespace:CompositeSequence:$design$separator$args->{id_ref}"/>
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </CompositeSequences_assnreflist>
+ </CompositeGroup>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm b/lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm
new file mode 100644
index 0000000..1cdf34a
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/CompositeSequence.pm
@@ -0,0 +1,119 @@
+#!/usr/bin/env perl
+#
+# $Id: CompositeSequence.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+#####################
+# CompositeSequence #
+#####################
+package ArrayExpress::ArrayMAGE::CompositeSequence;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <CompositeSequence_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $fh = $self->get_fh();
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ defined $args->{identifier} or die;
+
+ print $fh
+ " <CompositeSequence identifier=\"$namespace:CompositeSequence:$design$separator$args->{identifier}\"";
+ $args->{name} && print $fh "\n name=\"$args->{name}\"";
+ print $fh ">\n";
+
+ my $db_refs_provided =
+ $args->{dbrefs}
+ ? scalar( grep { defined $_ } values %{ $args->{dbrefs} } )
+ : undef;
+
+ ( $args->{comment} || $db_refs_provided ) && do {
+ print $fh " <Descriptions_assnlist>\n <Description";
+ $args->{comment} && print $fh " text=\"$args->{comment}\"";
+ print $fh ">\n";
+ $db_refs_provided && do {
+ print $fh " <DatabaseReferences_assnlist>\n";
+ while ( my ( $acc, $db ) = each %{ $args->{dbrefs} } ) {
+ print $fh <<"MAGE_OUT";
+ <DatabaseEntry accession="$acc">
+ <Database_assnref>
+ <Database_ref identifier="ebi.ac.uk:Database:$db"/>
+ </Database_assnref>
+ </DatabaseEntry>
+MAGE_OUT
+ }
+ print $fh " </DatabaseReferences_assnlist>\n";
+ };
+ print $fh <<"MAGE_OUT";
+ </Description>
+ </Descriptions_assnlist>
+MAGE_OUT
+ };
+
+ $args->{controltype} && print $fh <<"MAGE_OUT";
+ <ControlType_assn>
+ <OntologyEntry category="ControlType"
+ value="$args->{controltype}">
+ </OntologyEntry>
+ </ControlType_assn>
+MAGE_OUT
+
+ ( $args->{biosequences} && @{ $args->{biosequences} } ) && do {
+ print $fh <<"MAGE_OUT";
+ <BiologicalCharacteristics_assnreflist>
+MAGE_OUT
+ foreach my $bsid ( @{ $args->{biosequences} } ) {
+ print $fh <<"MAGE_OUT";
+ <BioSequence_ref identifier="$namespace:BioSequence:$design$separator$bsid"/>
+MAGE_OUT
+ }
+ print $fh <<"MAGE_OUT";
+ </BiologicalCharacteristics_assnreflist>
+MAGE_OUT
+ };
+
+ # Skip RCM if we're only generating CSs:
+ unless ( $args->{cs_only} ) {
+
+ # assume this is 1:1 CompositeSequence:FRM for now (1:n FRM:Feature)
+ print $fh <<"MAGE_OUT";
+ <ReporterCompositeMaps_assnreflist>
+ <ReporterCompositeMap_ref identifier="$namespace:ReporterCompositeMap:$design$separator$args->{identifier}"/>
+ </ReporterCompositeMaps_assnreflist>
+MAGE_OUT
+ }
+
+ print $fh " </CompositeSequence>\n";
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </CompositeSequence_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Database.pm b/lib/ArrayExpress/ArrayMAGE/Database.pm
new file mode 100644
index 0000000..9a1c5a9
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Database.pm
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+#
+# $Id: Database.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+############
+# Database #
+############
+package ArrayExpress::ArrayMAGE::Database;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <Description_package>
+ <Database_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ defined $args->{tag} or die;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <Database identifier="ebi.ac.uk:Database:$args->{tag}"
+ name="$args->{tag}">
+ </Database>
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </Database_assnlist>
+ </Description_package>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Feature.pm b/lib/ArrayExpress/ArrayMAGE/Feature.pm
new file mode 100644
index 0000000..f7a0b91
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Feature.pm
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+#
+# $Id: Feature.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+###########
+# Feature #
+###########
+package ArrayExpress::ArrayMAGE::Feature;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+
+ # Omitting TechnologyType OE here FIXME
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <FeatureGroups_assnlist>
+ <FeatureGroup identifier="$namespace:FeatureGroup:$design">
+ <Features_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $fh = $self->get_fh();
+
+ defined( $args->{row} ) or die;
+ defined( $args->{column} ) or die;
+ defined( $args->{metarow} ) or die;
+ defined( $args->{metacolumn} ) or die;
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ my $identifier = "$namespace:Feature:$design$separator";
+
+ $identifier .= $args->{identifier}
+ || "$args->{metacolumn}.$args->{metarow}.$args->{column}.$args->{row}";
+
+ print $fh <<"MAGE_OUT";
+ <Feature identifier="$identifier">
+ <Zone_assnref>
+ <Zone_ref identifier="$namespace:Zone:$design$separator$args->{metacolumn}.$args->{metarow}"/>
+ </Zone_assnref>
+ <FeatureLocation_assn>
+ <FeatureLocation column="$args->{column}"
+ row="$args->{row}">
+ </FeatureLocation>
+ </FeatureLocation_assn>
+ </Feature>
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </Features_assnlist>
+ </FeatureGroup>
+ </FeatureGroups_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm b/lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm
new file mode 100644
index 0000000..035e6fb
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/FeatureReporterMap.pm
@@ -0,0 +1,88 @@
+#!/usr/bin/env perl
+#
+# $Id: FeatureReporterMap.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+######################
+# FeatureReporterMap #
+######################
+package ArrayExpress::ArrayMAGE::FeatureReporterMap;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <FeatureReporterMap_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $fh = $self->get_fh();
+
+ defined $args->{identifier} or die;
+ defined $args->{features} or die;
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ print $fh <<"MAGE_OUT";
+ <FeatureReporterMap identifier="$namespace:FeatureReporterMap:$design$separator$args->{identifier}">
+ <Reporter_assnref>
+ <Reporter_ref identifier="$namespace:Reporter:$design$separator$args->{identifier}"/>
+ </Reporter_assnref>
+ <FeatureInformationSources_assnlist>
+MAGE_OUT
+
+ foreach my $identifier ( @{ $args->{features} } ) {
+ print $fh <<"MAGE_OUT";
+ <FeatureInformation>
+ <Feature_assnref>
+ <Feature_ref identifier="$namespace:Feature:$design$separator$identifier"/>
+ </Feature_assnref>
+MAGE_OUT
+
+ if ( $args->{mismatch_info} && $args->{mismatch_info}{$identifier} ) {
+ my %info = %{ $args->{mismatch_info}{$identifier} };
+ print $fh <<"MAGE_OUT";
+ <MismatchInformation_assnlist>
+ <MismatchInformation startCoord="$info{start_coord}"
+ newSequence="$info{new_sequence}"
+ replacedLength="$info{replaced_length}"/>
+ </MismatchInformation_assnlist>
+MAGE_OUT
+ }
+ print $fh <<"MAGE_OUT";
+ </FeatureInformation>
+MAGE_OUT
+ }
+
+ print $fh <<"MAGE_OUT";
+ </FeatureInformationSources_assnlist>
+ </FeatureReporterMap>
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </FeatureReporterMap_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/NullObject.pm b/lib/ArrayExpress/ArrayMAGE/NullObject.pm
new file mode 100644
index 0000000..909211f
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/NullObject.pm
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+#
+# $Id: NullObject.pm 2088 2008-06-25 13:23:09Z tfrayner $
+
+use strict;
+use warnings;
+
+######################
+# NullObject package #
+######################
+
+package ArrayExpress::ArrayMAGE::NullObject;
+
+use Class::Std;
+
+# This package provides empty objects which don't store information
+# when e.g. only exporting CompositeSequences for very large CDFs.
+
+sub add {}
+
+sub end {}
+
+sub output {}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ProgressBar.pm b/lib/ArrayExpress/ArrayMAGE/ProgressBar.pm
new file mode 100644
index 0000000..af02999
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ProgressBar.pm
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+#
+# $Id: ProgressBar.pm 1858 2007-12-17 12:47:08Z tfrayner $
+
+use strict;
+use warnings;
+
+###############
+# ProgressBar #
+###############
+# Progress bar code
+# Credit to perlmonks user tmoertel (see PM node 396857)
+# TGM 2004-10-05
+
+package ArrayExpress::ArrayMAGE::ProgressBar;
+use Class::Struct;
+use List::Util qw( min );
+
+struct( width => '$', portion => '$', max_val => '$' );
+
+sub draw {
+
+ my ( $self, $x, $max_val ) = @_;
+
+ local $| = 1;
+
+ $max_val ||= $self->max_val;
+ my $old_portion = $self->portion || 0;
+ my $new_portion = int( $self->width * $x / $max_val + 0.5 );
+ print "\b" x ( 6 + $self->width - min( $new_portion, $old_portion ) )
+ if defined $self->portion;
+ print "*" x ( $new_portion - $old_portion ),
+ " " x ( $self->width - $new_portion ), sprintf " %3d%% ",
+ int( 100 * $x / $max_val + 0.5 );
+ $self->portion($new_portion);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Reporter.pm b/lib/ArrayExpress/ArrayMAGE/Reporter.pm
new file mode 100644
index 0000000..39d3b27
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Reporter.pm
@@ -0,0 +1,131 @@
+#!/usr/bin/env perl
+#
+# $Id: Reporter.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+############
+# Reporter #
+############
+package ArrayExpress::ArrayMAGE::Reporter;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <Reporter_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $fh = $self->get_fh();
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ defined $args->{identifier} or die;
+
+ print $fh
+ " <Reporter identifier=\"$namespace:Reporter:$design$separator$args->{identifier}\"";
+ $args->{name} && print $fh "\n name=\"$args->{name}\"";
+ print $fh ">\n";
+
+ my $db_refs_provided =
+ $args->{dbrefs}
+ ? scalar( grep { defined $_ } values %{ $args->{dbrefs} } )
+ : undef;
+
+ ( $args->{comment} || $db_refs_provided ) && do {
+ print $fh " <Descriptions_assnlist>\n <Description";
+ $args->{comment} && print $fh " text=\"$args->{comment}\"";
+ print $fh ">\n";
+ $db_refs_provided && do {
+ print $fh " <DatabaseReferences_assnlist>\n";
+ while ( my ( $acc, $db ) = each %{ $args->{dbrefs} } ) {
+ print $fh <<"MAGE_OUT";
+ <DatabaseEntry accession="$acc">
+ <Database_assnref>
+ <Database_ref identifier="ebi.ac.uk:Database:$db"/>
+ </Database_assnref>
+ </DatabaseEntry>
+MAGE_OUT
+ }
+ print $fh " </DatabaseReferences_assnlist>\n";
+ };
+ print $fh <<"MAGE_OUT";
+ </Description>
+ </Descriptions_assnlist>
+MAGE_OUT
+ };
+
+ $args->{controltype} && print $fh <<"MAGE_OUT";
+ <ControlType_assn>
+ <OntologyEntry category="ControlType"
+ value="$args->{controltype}">
+ </OntologyEntry>
+ </ControlType_assn>
+MAGE_OUT
+
+ ( $args->{biosequences} && @{ $args->{biosequences} } ) && do {
+ print $fh <<"MAGE_OUT";
+ <ImmobilizedCharacteristics_assnreflist>
+MAGE_OUT
+ foreach my $bsid ( @{ $args->{biosequences} } ) {
+ print $fh <<"MAGE_OUT";
+ <BioSequence_ref identifier="$namespace:BioSequence:$design$separator$bsid"/>
+MAGE_OUT
+ }
+ print $fh <<"MAGE_OUT";
+ </ImmobilizedCharacteristics_assnreflist>
+MAGE_OUT
+ };
+
+ $args->{warningtype} && print $fh <<"MAGE_OUT";
+ <WarningType_assn>
+ <OntologyEntry category="WarningType"
+ value="$args->{warningtype}">
+ </OntologyEntry>
+ </WarningType_assn>
+MAGE_OUT
+
+ $args->{failtype} && print $fh <<"MAGE_OUT";
+ <FailTypes_assnlist>
+ <OntologyEntry category="FailType"
+ value="$args->{failtype}">
+ </OntologyEntry>
+ </FailTypes_assnlist>
+MAGE_OUT
+
+ # assume this is 1:1 Reporter:FRM for now (1:n FRM:Feature)
+ print $fh <<"MAGE_OUT";
+ <FeatureReporterMaps_assnreflist>
+ <FeatureReporterMap_ref identifier="$namespace:FeatureReporterMap:$design$separator$args->{identifier}"/>
+ </FeatureReporterMaps_assnreflist>
+MAGE_OUT
+
+ print $fh " </Reporter>\n";
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </Reporter_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm b/lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm
new file mode 100644
index 0000000..30ede8e
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ReporterCompositeMap.pm
@@ -0,0 +1,75 @@
+#!/usr/bin/env perl
+#
+# $Id: ReporterCompositeMap.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+########################
+# ReporterCompositeMap #
+########################
+package ArrayExpress::ArrayMAGE::ReporterCompositeMap;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <ReporterCompositeMap_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $fh = $self->get_fh();
+
+ defined $args->{identifier} or die;
+ defined $args->{reporters} or die;
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ print $fh <<"MAGE_OUT";
+ <ReporterCompositeMap identifier="$namespace:ReporterCompositeMap:$design$separator$args->{identifier}">
+ <CompositeSequence_assnref>
+ <CompositeSequence_ref identifier="$namespace:CompositeSequence:$design$separator$args->{identifier}"/>
+ </CompositeSequence_assnref>
+ <ReporterPositionSources_assnlist>
+MAGE_OUT
+
+ foreach my $reporter ( @{ $args->{reporters} } ) {
+ print $fh <<"MAGE_OUT";
+ <ReporterPosition>
+ <Reporter_assnref>
+ <Reporter_ref identifier="$namespace:Reporter:$design$separator$reporter"/>
+ </Reporter_assnref>
+ </ReporterPosition>
+MAGE_OUT
+ }
+
+ print $fh <<"MAGE_OUT";
+ </ReporterPositionSources_assnlist>
+ </ReporterCompositeMap>
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </ReporterCompositeMap_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm b/lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm
new file mode 100644
index 0000000..27e3e7a
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/ReporterGroup.pm
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+#
+# $Id: ReporterGroup.pm 1938 2008-02-10 18:34:20Z tfrayner $
+
+use strict;
+use warnings;
+
+#################
+# ReporterGroup #
+#################
+package ArrayExpress::ArrayMAGE::ReporterGroup;
+use base 'ArrayExpress::ArrayMAGE';
+
+my %identifier : ATTR( :get<identifier>, :init_arg<identifier>, :default<undef> );
+my %tag : ATTR( :get<tag>, :init_arg<tag>, :default<undef> );
+my %is_species : ATTR( :get<is_species>, :init_arg<is_species>, :default<undef> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $identifier = $self->get_identifier();
+ defined( $identifier )
+ or croak("Error: ReporterGroup identifier must be set");
+ my $tag = $self->get_tag();
+ defined( $tag )
+ or croak("Error: ReporterGroup tag must be set");
+
+ my $fh = $self->get_fh();
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ print $fh <<"MAGE_OUT";
+ <ReporterGroup identifier="$namespace:ReporterGroup:$design$separator$identifier"
+ name="$tag">
+MAGE_OUT
+
+ $self->get_is_species() && print $fh <<"MAGE_OUT";
+ <Species_assn>
+ <OntologyEntry category="Organism"
+ value="$tag">
+ </OntologyEntry>
+ </Species_assn>
+MAGE_OUT
+
+ print $fh <<"MAGE_OUT";
+ <Reporters_assnreflist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ defined $args->{id_ref} or die;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <Reporter_ref identifier="$namespace:Reporter:$design$separator$args->{id_ref}"/>
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </Reporters_assnreflist>
+ </ReporterGroup>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/ArrayMAGE/Zone.pm b/lib/ArrayExpress/ArrayMAGE/Zone.pm
new file mode 100644
index 0000000..6d29268
--- /dev/null
+++ b/lib/ArrayExpress/ArrayMAGE/Zone.pm
@@ -0,0 +1,62 @@
+#!/usr/bin/env perl
+#
+# $Id: Zone.pm 1937 2008-02-10 13:15:30Z tfrayner $
+
+use strict;
+use warnings;
+
+########
+# Zone #
+########
+package ArrayExpress::ArrayMAGE::Zone;
+use base 'ArrayExpress::ArrayMAGE';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ # Missing zonesPerX, zonesPerY FIXME
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <ZoneGroups_assnlist>
+ <ZoneGroup>
+ <ZoneLocations_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+sub add {
+
+ my ( $self, $args ) = @_;
+
+ defined $args->{row} or die;
+ defined $args->{column} or die;
+
+ my $namespace = $self->get_namespace();
+ my $design = $self->get_design();
+ my $separator = $self->get_separator();
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ <Zone column="$args->{column}"
+ row="$args->{row}"
+ identifier="$namespace:Zone:$design$separator$args->{column}.$args->{row}">
+ </Zone>
+MAGE_OUT
+
+ return;
+}
+
+sub end : RESTRICTED {
+
+ my $self = shift;
+
+ print { $self->get_fh() } <<"MAGE_OUT";
+ </ZoneLocations_assnlist>
+ </ZoneGroup>
+ </ZoneGroups_assnlist>
+MAGE_OUT
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Creator.pm b/lib/ArrayExpress/AutoSubmission/Creator.pm
new file mode 100644
index 0000000..4cf791b
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Creator.pm
@@ -0,0 +1,514 @@
+#!/usr/bin/env perl
+#
+# Module used to create new submissions in the absence of a Webform.
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Creator.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::AutoSubmission::Creator;
+
+use strict;
+use warnings;
+
+use Carp;
+use Class::Std;
+use File::Copy;
+use File::Basename;
+use File::Spec;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::User;
+require ArrayExpress::AutoSubmission::DB::Organism;
+require ArrayExpress::AutoSubmission::DB::DataFile;
+require ArrayExpress::AutoSubmission::DB::Spreadsheet;
+
+my %login : ATTR(:name<login>, :init_arg<login>, :default<undef>);
+my %name : ATTR(:name<name>, :init_arg<name>, :default<undef>);
+my %spreadsheet : ATTR(:name<spreadsheet>, :init_arg<spreadsheet>, :default<undef>);
+my %data_files : ATTR(:name<data_files>, :init_arg<data_files>, :default<[]>);
+my %accession : ATTR(:name<accession>, :init_arg<accession>, :default<undef>);
+my %comment : ATTR(:name<comment>, :init_arg<comment>, :default<undef>);
+my %experiment_type : ATTR(:name<experiment_type>, :init_arg<experiment_type>, :default<undef>);
+my %clobber : ATTR(:name<clobber>, :init_arg<clobber>, :default<0>);
+my %organisms : ATTR(:name<organisms>, :init_arg<organisms>, :default<[]>);
+my %user : ATTR();
+my %experiment : ATTR();
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ # Basic usage validation.
+ croak("Error: no login set")
+ unless ( $self->get_login() );
+
+ croak("Error: no name set")
+ unless ( $self->get_name() );
+
+ croak("Error: no spreadsheet set")
+ unless ( $self->get_spreadsheet() );
+
+ croak("Error: no experiment_type set")
+ unless ( $self->get_experiment_type() );
+
+ croak("Error: data_files must be an array reference")
+ unless ( ref( $self->get_data_files() ) eq 'ARRAY' );
+
+ croak("Error: organisms must be an array reference")
+ unless ( ref( $self->get_organisms() ) eq 'ARRAY' );
+
+ return;
+}
+
+sub _get_user_approval : PRIVATE {
+
+ my ( $self, $message ) = @_;
+
+ print STDERR $message;
+ chomp( my $response = lc <STDIN> );
+
+ if ($response eq 'y') { return 1 }
+
+ return;
+}
+
+{ # Create a closure over this array.
+ my @pass_chars = ( 'A'..'Z', 'a'..'z', 0..9 );
+
+ sub mk_passwd {
+
+ my $self = shift;
+
+ # Simply returns a random string suitable for use as a
+ # password. Can pass an optional length argument, otherwise
+ # defaults to eight chars.
+
+ my $length = shift || 8;
+
+ my $password = q{};
+ for ( 1..$length ) {
+ $password .= $pass_chars[int rand(scalar @pass_chars)];
+ }
+ return $password;
+ }
+}
+
+sub insert_spreadsheet {
+
+ my $self = shift;
+
+ my $expt = $self->get_experiment();
+
+ my $spreadsheet = basename($self->get_spreadsheet());
+
+ my $target_file = File::Spec->catfile(
+ $expt->filesystem_directory(),
+ $spreadsheet,
+ );
+
+ # Ask what to do for preexisting files.
+ if ( -e $target_file ) {
+
+ if ( $self->get_clobber()
+ || $self->_get_user_approval(
+ "Warning: submission has pre-existing file $target_file. Overwrite [y/N] ? ") ) {
+
+ unlink($target_file)
+ or die("Error removing old spreadsheet $target_file: $!");
+ }
+ else{
+ printf STDERR ("Skipping insertion of spreadsheet $spreadsheet.\n");
+ return;
+ }
+ }
+ # Make sure we don't end up with multiple spreadsheets mapped to
+ # the submission. Note that we don't clean up the filesystem, just
+ # the DB mapping.
+ my $ss_iterator = $expt->spreadsheets(is_deleted => 0);
+ while ( my $old_spreadsheet = $ss_iterator->next() ) {
+ $old_spreadsheet->set('is_deleted' => 1);
+ $old_spreadsheet->update();
+ }
+
+ # Write out the new spreadsheet, enter it into the DB and fix the
+ # permissions (no basename on copy).
+ copy($self->get_spreadsheet(), $target_file)
+ or die(
+ sprintf(
+ "Error copying spreadsheet %s to submissions directory: %s",
+ $self->get_spreadsheet(),
+ $!,
+ )
+ );
+ chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $target_file)
+ or warn ("Warning: problem setting permissions on spreadsheet $target_file: $!");
+ my $db_spreadsheet = ArrayExpress::AutoSubmission::DB::Spreadsheet->insert({
+ experiment_id => $expt,
+ name => $spreadsheet,
+ is_deleted => 0,
+ });
+
+ # Make a backup as well.
+ my $backup = "$target_file.backup." . date_now();
+ copy($target_file, $backup)
+ or warn ("Error backing up spreadsheet $spreadsheet ($backup) in submissions directory: $!");
+ chmod(oct('0444'), $backup)
+ or warn ("Warning: problem setting permissions on backup $backup: $!");
+
+ return;
+}
+
+sub insert_data_files {
+
+ my $self = shift;
+
+ my $expt = $self->get_experiment();
+
+ DATAFILE:
+ foreach my $data_file ( @{ $self->get_data_files() } ) {
+
+ my $target_file = File::Spec->catfile(
+ $expt->filesystem_directory(),
+ basename($data_file),
+ );
+
+ # Ask what to do for preexisting files.
+ if ( -e $target_file ) {
+ if ( $self->get_clobber()
+ || $self->_get_user_approval(
+ "Warning: submission has pre-existing file $target_file. Overwrite [y/N] ? ") ) {
+
+ unlink($target_file)
+ or die("Error removing old data file $target_file: $!");
+ }
+ else {
+ print STDERR ("Skipping insertion of data file $data_file.\n");
+ next DATAFILE;
+ }
+ }
+
+ # Write out the new data file, enter it into the DB and fix the permissions.
+ copy($data_file, $target_file)
+ or die("Error copying data file $data_file to submissions directory: $!");
+ chmod (oct($CONFIG->get_FILE_PERMISSIONS()), $target_file)
+ or warn("Warning: problem setting permissions on data archive $target_file: $!");
+ my $db_file = ArrayExpress::AutoSubmission::DB::DataFile->find_or_create(
+ experiment_id => $expt,
+ name => basename($data_file),
+ is_deleted => 0,
+ );
+ $db_file->set(is_unpacked => 0);
+ $db_file->update();
+ }
+
+ return;
+}
+
+sub get_user {
+
+ my $self = shift;
+
+ # Return any previously assigned user object.
+ return $user{ident $self} if $user{ident $self};
+
+ # Sort out our user account, creating one if necessary.
+ $user{ident $self} = ArrayExpress::AutoSubmission::DB::User->retrieve(
+ login => $self->get_login(),
+ is_deleted => 0,
+ );
+ unless ($user{ident $self}) {
+
+ if ( $self->get_clobber()
+ || $self->_get_user_approval(
+ sprintf("Warning: user %s does not yet exist. Create [y/N] ? ",
+ $self->get_login()))) {
+
+ print STDERR ("Creating user...\n");
+ $user{ident $self} = ArrayExpress::AutoSubmission::DB::User->find_or_create(
+ login => $self->get_login(),
+ name => $self->get_login(),
+ email => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+ password => $self->mk_passwd(),
+ created_at => date_now(),
+ is_deleted => 0,
+ ) or die(
+ sprintf("Error creating user %s in database: %s",
+ $self->get_login(), $!)
+ );
+ }
+ else {
+ die("Cannot insert a submission without a user account. Exiting.\n");
+ }
+ }
+
+ return $user{ident $self};
+}
+
+sub get_experiment {
+
+ my $self = shift;
+
+ # Return any previously assigned experiment object.
+ return $experiment{ident $self} if $experiment{ident $self};
+
+ # Check pre-existing accession, update if needed.
+ if ( $self->get_accession() ) {
+ $experiment{ident $self} = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+ accession => $self->get_accession(),
+ is_deleted => 0,
+ );
+
+ # Update pre-existing experiment.
+ if ( $experiment{ident $self} ) {
+
+ # We're now allowing experiments to switch between types,
+ # but we don't want duplicated accessions. As a result, if
+ # we try and create an experiment with a pre-existing
+ # accession, but of the wrong type, we want to catch that
+ # here.
+ unless ( $experiment{ident $self}->experiment_type()
+ eq $self->get_experiment_type() ) {
+ croak(
+ sprintf(
+ qq{Error: Pre-existing experiment in database is not of type "%s".\n},
+ $self->get_experiment_type(),
+ ),
+ );
+ }
+
+ # Make sure it's okay to overwrite stuff.
+ if ( $self->get_clobber()
+ || $self->_get_user_approval(
+ sprintf("Experiment with accession %s already exists. "
+ . "Update this experiment (this may overwrite an old spreadsheet) [y/N] ? ",
+ $self->get_accession())
+ ) ) {
+
+ # postpone checking until insertion complete.
+ $experiment{ident $self}->set(
+ in_curation => 0,
+ date_submitted => date_now(),
+ name => $self->get_name(),
+ user_id => $self->get_user(),
+ );
+
+ # FIXME allow updates of e.g. organisms here.
+ $experiment{ident $self}->update();
+ }
+ else {
+ die(sprintf(
+ "Will not create duplicate experiment %s. Quitting.\n",
+ $self->get_accession(),
+ ));
+ }
+ }
+ }
+
+ # New experiment created here.
+ unless ( $experiment{ident $self} ) {
+
+ if ( ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+ name => $self->get_name(),
+ is_deleted => 0,
+ ) ) {
+ die(qq{Error: pre-existing experiment in database with name "}
+ . $self->get_name() . qq{". Exiting.\n});
+ }
+
+ print STDERR ("Creating experiment...\n");
+ $experiment{ident $self} = ArrayExpress::AutoSubmission::DB::Experiment->insert({
+ name => $self->get_name(),
+ user_id => $self->get_user(),
+ accession => $self->get_accession(),
+ in_curation => 0, # Don't start checking just yet!
+ num_submissions => 0,
+ experiment_type => $self->get_experiment_type(),
+ comment => $self->get_comment(),
+ date_submitted => date_now(),
+ is_deleted => 0,
+ });
+
+ # Add any species that we know about.
+ foreach my $species_name ( @{ $self->get_organisms() } ) {
+ if ( my @organisms
+ = ArrayExpress::AutoSubmission::DB::Organism->search(
+ scientific_name => $species_name,
+ is_deleted => 0, ) ) {
+
+ if ( scalar @organisms == 1 ) {
+ $experiment{ident $self}->add_to_organism_instances({
+ experiment_id => $experiment{ident $self},
+ organism_id => $organisms[0],
+ });
+ }
+ else {
+ croak(qq{Warning: Multiple organisms named "$species_name" found in database.\n});
+ }
+ }
+ else {
+ croak(qq{Error: Organism name "$species_name" not found in database.\n});
+ }
+ }
+ $experiment{ident $self}->update();
+ }
+
+ return $experiment{ident $self};
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Creator.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::Creator - A class providing convenience
+methods for experiment object creation and insertion into the
+autosubmissions tracking database.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::Creator;
+ my $creator = ArrayExpress::AutoSubmission::Creator->new({
+ login => $login,
+ name => $expt_name,
+ spreadsheet => $startfile,
+ data_files => \@datafiles,
+ accession => $accession,
+ experiment_type => 'MAGE-TAB',
+ comment => 'Submission inserted manually',
+ clobber => 0,
+ });
+
+ # Create the experiment and insert the spreadsheet.
+ my $expt = $creator->get_experiment();
+
+ # Copy the files to the submissions directory.
+ print STDERR ("Copying files...\n");
+ $creator->insert_spreadsheet();
+ $creator->insert_data_files();
+
+=head1 DESCRIPTION
+
+This module provides some basic convenience methods to automate error
+handling when inserting new experiments into the tracking database.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new()>
+
+Object constructor. Takes a hashref of options, including the following:
+
+=over 4
+
+=item C<login>
+
+The login name of the user to which the experiment should be assigned.
+
+=item C<name>
+
+The name of the experiment
+
+=item C<spreadsheet>
+
+The path to spreadsheet file to be inserted.
+
+=item C<data_files>
+
+An arrayref listing the paths to the data files to be inserted.
+
+=item C<accession>
+
+The accession number for the experiment.
+
+=item C<comment>
+
+Any comments to be attached to the experiment.
+
+=item C<experiment_type>
+
+The experiment type. This should typically be one of the following:
+MAGE-TAB, Tab2MAGE, MIAMExpress, GEO, MUGEN, Unknown.
+
+=item C<clobber>
+
+A flag indicating whether to overwrite files without prompting the user.
+
+=item C<organisms>
+
+An arrayref of organism scientific names to associate with this
+experiment.
+
+=back
+
+=item C<mk_passwd>
+
+Returns a random string suitable for use as an account password. Can
+pass an optional length argument, otherwise defaults to eight characters.
+
+=item C<insert_spreadsheet>
+
+Copy the spreadsheet file into the appropriate filesystem location and
+insert a record into the database.
+
+=item C<insert_data_files>
+
+Copy the data files into the appropriate filesystem location and
+insert records into the database.
+
+=item C<get_user>
+
+Returns the appropriate Class::DBI user object as retrieved from (or
+inserted into) the database.
+
+=item C<get_experiment>
+
+Returns the appropriate Class::DBI experiment object as retrieved from
+(or inserted into) the database.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB.pm b/lib/ArrayExpress/AutoSubmission/DB.pm
new file mode 100644
index 0000000..8ad8829
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB.pm
@@ -0,0 +1,3539 @@
+#!/usr/bin/env perl
+#
+# Module used to interact with the back-end submissions DB. The table
+# classes are defined in the DB/ subdirectory.
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: DB.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use strict;
+use warnings;
+use Class::DBI;
+
+package ArrayExpress::AutoSubmission::DB;
+use base 'Class::DBI';
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+# DUMMY used to fool our test scripts (connection requires a DSN).
+ArrayExpress::AutoSubmission::DB->connection(
+ ($CONFIG->get_AUTOSUBS_DSN() || 'DUMMY'),
+ $CONFIG->get_AUTOSUBS_USERNAME(),
+ $CONFIG->get_AUTOSUBS_PASSWORD(),
+ $CONFIG->get_AUTOSUBS_DBPARAMS(),
+);
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: DB.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::DB - Class::DBI based interface to the
+autosubmissions tracking database.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::DB::Experiment;
+ my $expt = ArrayExpress::AutoSubmission::DB::Experiment->retrieve( $id );
+
+=head1 DESCRIPTION
+
+This module is the abstract superclass for a set of Class::DBI - based
+modules, which are used as an object-relational mapping to the
+underlying submissions tracking database (currently implemented using
+MySQL). You should not use this class directly; rather, you should use
+the relevant table subclasses to query the database (see L</SYNOPSIS>
+for an example). Please refer to the Class::DBI documentation for
+information on query syntax.
+
+=head1 TABLES
+
+Included below are auto-generated descriptions of the MySQL
+tables. This listing was generated using the
+SQL::Translator::Producer::POD module.
+
+=head2 array_designs
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 miamexpress_subid
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 miamexpress_login
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 status
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 data_warehouse_ready
+
+=over 4
+
+=item * char(15)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 date_last_processed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 miame_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 in_data_warehouse
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 annotation_source
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 annotation_version
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 biomart_table_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 release_date
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_released
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = miamexpress_subid
+
+=back
+
+=head2 array_designs_experiments
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 array_design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = array_design_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = array_design_id
+
+=item * Reference Table = L</array_designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 array_designs_organisms
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 organism_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 array_design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = organism_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = array_design_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = organism_id
+
+=item * Reference Table = L</organisms>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = array_design_id
+
+=item * Reference Table = L</array_designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 categories
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 ontology_term
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 display_label
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_common
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_bmc
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_fv
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 categories_designs
+
+=head3 FIELDS
+
+=head4 category_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = category_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = design_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = category_id
+
+=item * Reference Table = L</categories>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = design_id
+
+=item * Reference Table = L</designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 categories_materials
+
+=head3 FIELDS
+
+=head4 category_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 material_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = category_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = material_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = category_id
+
+=item * Reference Table = L</categories>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = material_id
+
+=item * Reference Table = L</materials>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 categories_taxons
+
+=head3 FIELDS
+
+=head4 category_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 taxon_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = category_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = taxon_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = category_id
+
+=item * Reference Table = L</categories>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = taxon_id
+
+=item * Reference Table = L</taxons>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 data_files
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_unpacked
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 data_formats
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = name
+
+=back
+
+=head2 design_instances
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = design_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = design_id
+
+=item * Reference Table = L</designs>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 designs
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 display_label
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 ontology_category
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 ontology_value
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 design_type
+
+=over 4
+
+=item * char(15)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 events
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 array_design_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 event_type
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 was_successful
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 source_db
+
+=over 4
+
+=item * varchar(30)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 target_db
+
+=over 4
+
+=item * varchar(30)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 start_time
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 end_time
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 machine
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 operator
+
+=over 4
+
+=item * varchar(30)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 log_file
+
+=over 4
+
+=item * varchar(511)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 jobregister_dbid
+
+=over 4
+
+=item * int(15)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = array_design_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = array_design_id
+
+=item * Reference Table = L</array_designs>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 experiments
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 user_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 checker_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 software
+
+=over 4
+
+=item * varchar(100)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 status
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 data_warehouse_ready
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 date_last_edited
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 date_submitted
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 date_last_processed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 in_curation
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 curator
+
+=over 4
+
+=item * char(30)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 experiment_type
+
+=over 4
+
+=item * char(30)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 miamexpress_login
+
+=over 4
+
+=item * char(30)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 miamexpress_subid
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_affymetrix
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_mx_batchloader
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 miame_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 in_data_warehouse
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 num_submissions
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 submitter_description
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 curated_name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 num_samples
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 num_hybridizations
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 has_raw_data
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 has_processed_data
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 release_date
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_released
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 ae_miame_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 ae_data_warehouse_score
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = user_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = user_id
+
+=item * Reference Table = L</users>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 experiments_factors
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 factor_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = factor_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = factor_id
+
+=item * Reference Table = L</factors>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 experiments_loaded_data
+
+=head3 FIELDS
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 loaded_data_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=item * Reference Table = L</loaded_data>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 experiments_quantitation_types
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 quantitation_type_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = quantitation_type_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = quantitation_type_id
+
+=item * Reference Table = L</quantitation_types>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 factors
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(128)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 loaded_data
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 identifier
+
+=over 4
+
+=item * varchar(255)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 md5_hash
+
+=over 4
+
+=item * char(35)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 data_format_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 platform_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 needs_metrics_calculation
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 date_hashed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = data_format_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = platform_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = platform_id
+
+=item * Reference Table = L</platforms>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = data_format_id
+
+=item * Reference Table = L</data_formats>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 loaded_data_quality_metrics
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 value
+
+=over 4
+
+=item * decimal(12,5)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 quality_metric_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 loaded_data_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 date_calculated
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = quality_metric_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = loaded_data_id
+
+=item * Reference Table = L</loaded_data>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = quality_metric_id
+
+=item * Reference Table = L</quality_metrics>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 material_instances
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 material_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = material_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = material_id
+
+=item * Reference Table = L</materials>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 materials
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 display_label
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 ontology_category
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 ontology_value
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 meta
+
+=head3 FIELDS
+
+=head4 name
+
+=over 4
+
+=item * varchar(128)
+
+=item * PRIMARY KEY
+
+=item * Default ''
+
+=item * Nullable 'No'
+
+=back
+
+=head4 value
+
+=over 4
+
+=item * varchar(128)
+
+=item * Default ''
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = name
+
+=back
+
+=head2 organism_instances
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 organism_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = organism_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = organism_id
+
+=item * Reference Table = L</organisms>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 organisms
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 scientific_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 common_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 taxon_id
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = taxon_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = taxon_id
+
+=item * Reference Table = L</taxons>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 permissions
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 info
+
+=over 4
+
+=item * varchar(80)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 permissions_roles
+
+=head3 FIELDS
+
+=head4 role_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 permission_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = role_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = permission_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = role_id
+
+=item * Reference Table = L</roles>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = permission_id
+
+=item * Reference Table = L</permissions>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 platforms
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = name
+
+=back
+
+=head2 protocols
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * char(15)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 user_accession
+
+=over 4
+
+=item * varchar(100)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 expt_accession
+
+=over 4
+
+=item * char(15)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 date_last_processed
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 comment
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = accession
+
+=back
+
+=head2 quality_metrics
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 type
+
+=over 4
+
+=item * varchar(50)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 description
+
+=over 4
+
+=item * text(65535)
+
+=item * Nullable 'Yes'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = type
+
+=back
+
+=head2 quantitation_types
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(128)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 roles
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 info
+
+=over 4
+
+=item * varchar(80)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 roles_users
+
+=head3 FIELDS
+
+=head4 user_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 role_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = user_id
+
+=back
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = role_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = user_id
+
+=item * Reference Table = L</users>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = role_id
+
+=item * Reference Table = L</roles>
+
+=item * Reference Fields = L</id>
+
+=item * On delete = CASCADE
+
+=back
+
+=head2 spreadsheets
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 experiment_id
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(255)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 INDICES
+
+=head4 NORMAL
+
+=over 4
+
+=item * Fields = experiment_id
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 FOREIGN KEY
+
+=over 4
+
+=item * Fields = experiment_id
+
+=item * Reference Table = L</experiments>
+
+=item * Reference Fields = L</id>
+
+=back
+
+=head2 taxons
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 scientific_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 common_name
+
+=over 4
+
+=item * varchar(50)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 accession
+
+=over 4
+
+=item * int(11)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head2 users
+
+=head3 FIELDS
+
+=head4 id
+
+=over 4
+
+=item * int(11)
+
+=item * PRIMARY KEY
+
+=item * Nullable 'No'
+
+=back
+
+=head4 login
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 name
+
+=over 4
+
+=item * varchar(40)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 password
+
+=over 4
+
+=item * varchar(40)
+
+=item * Nullable 'No'
+
+=back
+
+=head4 email
+
+=over 4
+
+=item * varchar(100)
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 modified_at
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 created_at
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 access
+
+=over 4
+
+=item * datetime
+
+=item * Default 'NULL'
+
+=item * Nullable 'Yes'
+
+=back
+
+=head4 is_deleted
+
+=over 4
+
+=item * int(11)
+
+=item * Nullable 'No'
+
+=back
+
+=head3 CONSTRAINTS
+
+=head4 PRIMARY KEY
+
+=over 4
+
+=item * Fields = id
+
+=back
+
+=head4 UNIQUE
+
+=over 4
+
+=item * Fields = login
+
+=back
+
+=head1 PRODUCED BY
+
+SQL::Translator::Producer::POD
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm b/lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm
new file mode 100644
index 0000000..7682c7f
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Accessionable.pm
@@ -0,0 +1,56 @@
+#!/usr/bin/env perl
+#
+# $Id: Accessionable.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+####################################################################
+# Superclass for experiment, array, protocol which have accessions #
+####################################################################
+package ArrayExpress::AutoSubmission::DB::Accessionable;
+
+# Abstract package without corresponding DB table
+use base 'Class::Data::Inheritable';
+use List::Util qw(max);
+use Carp;
+
+__PACKAGE__->mk_classdata('accession_prefix');
+
+sub next_accession { # Class method.
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my $prefix = $class->accession_prefix()
+ or croak("Error: $class->accession_prefix() not set.");
+
+ # Only use the accessions matching the prefix pattern.
+ my @accessions;
+ my $iterator = $class->retrieve_all();
+ while (my $object = $iterator->next() ) {
+ my $acc = $object->accession();
+ push(@accessions, $acc) if ($acc && $acc =~ s/^$prefix//);
+ }
+
+ return ( $prefix . ( ( max(@accessions) || 0 ) + 1 ) );
+}
+
+sub get_accession { # Instance method.
+ my ($self, $values) = @_;
+
+ # Set any additional accessors passed.
+ if ($values) {
+ ref $values eq 'HASH'
+ or croak("Error: get_accession() needs a hash ref.");
+ $self->set( %{ $values } );
+ }
+
+ # Grab the accession, or generate a new one.
+ unless ( $self->accession() ) {
+ $self->set( accession => $self->next_accession() );
+ }
+ $self->update();
+ return ( $self->accession() );
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm
new file mode 100644
index 0000000..29c9f44
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesign.pm
@@ -0,0 +1,59 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesign.pm 1898 2008-01-18 20:13:25Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ArrayDesign;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::Accessionable';
+__PACKAGE__->table('array_designs');
+__PACKAGE__->columns(
+ Primary => qw(
+ id
+ )
+);
+__PACKAGE__->columns(
+ Essential => qw(
+ miamexpress_subid
+ accession
+ name
+ status
+ date_last_processed
+ release_date
+ is_deleted
+ )
+);
+__PACKAGE__->columns(
+ Others => qw(
+ miamexpress_login
+ miame_score
+ data_warehouse_ready
+ in_data_warehouse
+ annotation_source
+ annotation_version
+ biomart_table_name
+ is_released
+ comment
+ )
+);
+__PACKAGE__->has_many(
+ organisms => [
+ 'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism' => 'organism_id'
+ ]
+);
+__PACKAGE__->has_many(
+ organism_instances =>
+ 'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism'
+);
+__PACKAGE__->has_many(
+ experiments => [
+ 'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment' => 'experiment_id'
+ ]
+);
+__PACKAGE__->has_many(
+ events => 'ArrayExpress::AutoSubmission::DB::Event'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm
new file mode 100644
index 0000000..eeb6d55
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignExperiment.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesignExperiment.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('array_designs_experiments');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ experiment_id
+ array_design_id
+ )
+);
+__PACKAGE__->has_a(
+ experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_a(
+ array_design_id => 'ArrayExpress::AutoSubmission::DB::ArrayDesign' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm
new file mode 100644
index 0000000..dbb1ab6
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ArrayDesignOrganism.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ArrayDesignOrganism.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('array_designs_organisms');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ organism_id
+ array_design_id
+ )
+);
+__PACKAGE__->has_a(
+ organism_id => 'ArrayExpress::AutoSubmission::DB::Organism' );
+__PACKAGE__->has_a(
+ array_design_id => 'ArrayExpress::AutoSubmission::DB::ArrayDesign' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Category.pm b/lib/ArrayExpress/AutoSubmission/DB/Category.pm
new file mode 100644
index 0000000..234067c
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Category.pm
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+#
+# $Id: Category.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Category;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ ontology_term
+ display_label
+ is_common
+ is_bmc
+ is_fv
+ is_deleted
+ )
+);
+__PACKAGE__->has_many( taxons =>
+ [ 'ArrayExpress::AutoSubmission::DB::CategoryTaxon' => 'taxon_id' ] );
+__PACKAGE__->has_many( designs =>
+ [ 'ArrayExpress::AutoSubmission::DB::CategoryDesign' => 'design_id' ]
+);
+__PACKAGE__->has_many(
+ materials => [
+ 'ArrayExpress::AutoSubmission::DB::CategoryMaterial' => 'material_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm b/lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm
new file mode 100644
index 0000000..d25bb40
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/CategoryDesign.pm
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+#
+# $Id: CategoryDesign.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::CategoryDesign;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories_designs');
+__PACKAGE__->columns(
+ Primary => qw(
+ category_id
+ design_id
+ )
+);
+__PACKAGE__->has_a(
+ category_id => 'ArrayExpress::AutoSubmission::DB::Category' );
+__PACKAGE__->has_a( design_id => 'ArrayExpress::AutoSubmission::DB::Design' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm b/lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm
new file mode 100644
index 0000000..66e3c78
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/CategoryMaterial.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: CategoryMaterial.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::CategoryMaterial;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories_materials');
+__PACKAGE__->columns(
+ Primary => qw(
+ category_id
+ material_id
+ )
+);
+__PACKAGE__->has_a(
+ category_id => 'ArrayExpress::AutoSubmission::DB::Category' );
+__PACKAGE__->has_a(
+ material_id => 'ArrayExpress::AutoSubmission::DB::Material' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm b/lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm
new file mode 100644
index 0000000..8755bd0
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/CategoryTaxon.pm
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+#
+# $Id: CategoryTaxon.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::CategoryTaxon;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('categories_taxons');
+__PACKAGE__->columns(
+ Primary => qw(
+ category_id
+ taxon_id
+ )
+);
+__PACKAGE__->has_a(
+ category_id => 'ArrayExpress::AutoSubmission::DB::Category' );
+__PACKAGE__->has_a( taxon_id => 'ArrayExpress::AutoSubmission::DB::Taxon' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/DataFile.pm b/lib/ArrayExpress/AutoSubmission/DB/DataFile.pm
new file mode 100644
index 0000000..4449d69
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/DataFile.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: DataFile.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::DataFile;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::File';
+__PACKAGE__->table('data_files');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ experiment_id
+ name
+ is_unpacked
+ is_deleted
+ )
+);
+__PACKAGE__->has_a( experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm b/lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm
new file mode 100644
index 0000000..cd1c3d3
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/DataFormat.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: DataFormat.pm 1936 2008-02-08 19:13:37Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::DataFormat;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('data_formats');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ name
+ )
+);
+
+__PACKAGE__->has_many(
+ loaded_data => 'ArrayExpress::AutoSubmission::DB::LoadedData'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Design.pm b/lib/ArrayExpress/AutoSubmission/DB/Design.pm
new file mode 100644
index 0000000..e24e40c
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Design.pm
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+#
+# $Id: Design.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Design;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('designs');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ display_label
+ ontology_category
+ ontology_value
+ design_type
+ is_deleted
+ )
+);
+__PACKAGE__->has_many(
+ categories => [
+ 'ArrayExpress::AutoSubmission::DB::CategoryDesign' => 'category_id'
+ ]
+);
+__PACKAGE__->has_many(
+ experiments => [
+ 'ArrayExpress::AutoSubmission::DB::DesignInstance' => 'experiment_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm
new file mode 100644
index 0000000..7a1bcb1
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/DesignInstance.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: DesignInstance.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::DesignInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('design_instances');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ design_id
+ experiment_id
+ )
+);
+__PACKAGE__->has_a( design_id => 'ArrayExpress::AutoSubmission::DB::Design' );
+__PACKAGE__->has_a(
+ experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Event.pm b/lib/ArrayExpress/AutoSubmission/DB/Event.pm
new file mode 100644
index 0000000..5477b79
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Event.pm
@@ -0,0 +1,49 @@
+#!/usr/bin/env perl
+#
+# $Id: Event.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Event;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('events');
+__PACKAGE__->columns(
+ Primary => qw(
+ id
+ )
+);
+__PACKAGE__->columns(
+ Essential => qw(
+ array_design_id
+ experiment_id
+ event_type
+ jobregister_dbid
+ is_deleted
+ )
+);
+__PACKAGE__->columns(
+ Others => qw(
+ was_successful
+ source_db
+ target_db
+ start_time
+ end_time
+ machine
+ operator
+ log_file
+ comment
+ )
+);
+__PACKAGE__->has_a(
+ array_design_id => 'ArrayExpress::AutoSubmission::DB::ArrayDesign',
+);
+__PACKAGE__->has_a(
+ experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment',
+);
+
+__PACKAGE__->set_sql(
+ last_jobid => "SELECT MAX(jobregister_dbid) FROM __TABLE__ WHERE %s = ?",
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Experiment.pm b/lib/ArrayExpress/AutoSubmission/DB/Experiment.pm
new file mode 100644
index 0000000..97b8f8e
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Experiment.pm
@@ -0,0 +1,324 @@
+#!/usr/bin/env perl
+#
+# $Id: Experiment.pm 2012 2008-03-30 17:27:46Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Experiment;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::Accessionable';
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(untaint);
+use File::Spec;
+use File::Path;
+use File::Basename;
+use Carp;
+
+__PACKAGE__->table('experiments');
+__PACKAGE__->columns(
+ Primary => qw(
+ id
+ )
+);
+
+# These are columns used in the main submissions tracking system, so
+# are generally essential.
+__PACKAGE__->columns(
+ Essential => qw(
+ accession
+ name
+ user_id
+ date_last_edited
+ date_submitted
+ date_last_processed
+ in_curation
+ status
+ experiment_type
+ is_affymetrix
+ miamexpress_subid
+ release_date
+ is_released
+ curated_name
+ is_deleted
+ )
+);
+
+# These are less-used columns which may be retrieved for more extended
+# tracking purposes.
+__PACKAGE__->columns(
+ Others => qw(
+ checker_score
+ miame_score
+ software
+ data_warehouse_ready
+ in_data_warehouse
+ num_submissions
+ curator
+ miamexpress_login
+ is_mx_batchloader
+ submitter_description
+ num_samples
+ num_hybridizations
+ has_raw_data
+ has_processed_data
+ ae_miame_score
+ ae_data_warehouse_score
+ comment
+ )
+);
+__PACKAGE__->has_a( user_id => 'ArrayExpress::AutoSubmission::DB::User' );
+__PACKAGE__->has_many(
+ array_designs => [
+ 'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment' => 'array_design_id'
+ ]
+);
+__PACKAGE__->has_many(
+ array_design_instances =>
+ 'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment'
+);
+__PACKAGE__->has_many(
+ designs => [
+ 'ArrayExpress::AutoSubmission::DB::DesignInstance' => 'design_id'
+ ]
+);
+__PACKAGE__->has_many(
+ materials => [
+ 'ArrayExpress::AutoSubmission::DB::MaterialInstance' => 'material_id'
+ ]
+);
+__PACKAGE__->has_many(
+ organisms => [
+ 'ArrayExpress::AutoSubmission::DB::OrganismInstance' => 'organism_id'
+ ]
+);
+__PACKAGE__->has_many(
+ design_instances =>
+ 'ArrayExpress::AutoSubmission::DB::DesignInstance'
+);
+__PACKAGE__->has_many(
+ material_instances =>
+ 'ArrayExpress::AutoSubmission::DB::MaterialInstance'
+);
+__PACKAGE__->has_many(
+ organism_instances =>
+ 'ArrayExpress::AutoSubmission::DB::OrganismInstance'
+);
+__PACKAGE__->has_many(
+ data_files =>
+ 'ArrayExpress::AutoSubmission::DB::DataFile',
+ # Order by ID so that more recent uploads are unpacked later.
+ { order_by => 'id' }
+);
+__PACKAGE__->has_many(
+ spreadsheets =>
+ 'ArrayExpress::AutoSubmission::DB::Spreadsheet'
+);
+__PACKAGE__->has_many(
+ factors => [
+ 'ArrayExpress::AutoSubmission::DB::ExperimentFactor' => 'factor_id'
+ ]
+);
+__PACKAGE__->has_many(
+ factor_instances =>
+ 'ArrayExpress::AutoSubmission::DB::ExperimentFactor'
+);
+__PACKAGE__->has_many(
+ quantitation_types => [
+ 'ArrayExpress::AutoSubmission::DB::ExperimentQT' => 'quantitation_type_id'
+ ]
+);
+__PACKAGE__->has_many(
+ quantitation_type_instances =>
+ 'ArrayExpress::AutoSubmission::DB::ExperimentQT'
+);
+__PACKAGE__->has_many(
+ events => 'ArrayExpress::AutoSubmission::DB::Event'
+);
+
+__PACKAGE__->has_many(
+ loaded_data => [
+ 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData' => 'loaded_data_id'
+ ]
+);
+__PACKAGE__->has_many(
+ loaded_data_instances => 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData'
+);
+
+__PACKAGE__->set_sql(
+ status_for_update => "SELECT status FROM __TABLE__ WHERE id = ? FOR UPDATE",
+);
+
+sub update_designs {
+
+ my ($self, $new_assns) = @_;
+
+ my @old_assn_instances = $self->design_instances();
+
+ my %new = map { $_->id() => 1 } @$new_assns;
+ my %old = map { $_->design_id() => 1 } @old_assn_instances;
+
+ foreach my $assn (@old_assn_instances) {
+ unless ($new{$assn->design_id()}) {
+ $assn->delete();
+ }
+ }
+ foreach my $assn (@$new_assns) {
+ unless ($old{$assn->id()}) {
+ $self->add_to_design_instances({
+ design_id => $assn,
+ });
+ }
+ }
+ $self->update;
+
+ return;
+}
+
+sub update_materials {
+
+ my ($self, $new_assns) = @_;
+
+ my @old_assn_instances = $self->material_instances();
+
+ my %new = map { $_->id() => 1 } @$new_assns;
+ my %old = map { $_->material_id() => 1 } @old_assn_instances;
+
+ foreach my $assn (@old_assn_instances) {
+ unless ($new{$assn->material_id()}) {
+ $assn->delete();
+ }
+ }
+ foreach my $assn (@$new_assns) {
+ unless ($old{$assn->id()}) {
+ $self->add_to_material_instances({
+ material_id => $assn,
+ });
+ }
+ }
+ $self->update;
+
+ return;
+}
+
+sub update_organisms {
+
+ my ($self, $new_assns) = @_;
+
+ my @old_assn_instances = $self->organism_instances();
+
+ my %new = map { $_->id() => 1 } @$new_assns;
+ my %old = map { $_->organism_id() => 1 } @old_assn_instances;
+
+ foreach my $assn (@old_assn_instances) {
+ unless ($new{$assn->organism_id()}) {
+ $assn->delete();
+ }
+ }
+ foreach my $assn (@$new_assns) {
+ unless ($old{$assn->id()}) {
+ $self->add_to_organism_instances({
+ organism_id => $assn,
+ });
+ }
+ }
+ $self->update;
+
+ return;
+}
+
+sub filesystem_directory {
+
+ # Returns the top-level submissions directory containing
+ # spreadsheets and data file archives, having first created it.
+ my $self = shift;
+
+ # Check we have a valid experiment (MIAMExpress submissions are
+ # handled separately, in MIAMExpress.pm).
+ unless ( $self->user_id()
+ && $self->user_id()->login()
+ && $self->experiment_type()
+ && $self->id() ) {
+ croak("Error: Invalid Experiment object for filesystem directory creation.");
+ }
+ my $dir = File::Spec->catdir(
+ $CONFIG->get_AUTOSUBMISSIONS_FILEBASE(),
+ untaint($self->user_id()->login()),
+ untaint($self->experiment_type()) . q{_} . untaint($self->id()),
+ );
+
+ # Check for pre-existence because otherwise we can be stuck trying
+ # to change permissions on a pre-existing directory which may not
+ # belong to us.
+ unless ( -e $dir ) {
+
+ # Attempt to create submissions directory.
+ eval {
+
+ # Handle the umask.
+ my $original_umask = umask;
+ umask(0);
+
+ # Needed because Webform.pm changes this to parse the
+ # filename; we however need this to be based on the server
+ # OS. File::Path::mkpath() uses File::Basename.
+ my $old_fstype = fileparse_set_fstype($^O);
+
+ # Do the deed.
+ mkpath($dir, undef, oct(777));
+ chmod(oct($CONFIG->get_DIR_PERMISSIONS()), $dir)
+ or croak("Error changing permissions on $dir: $!");
+
+ # Reset the original settings.
+ fileparse_set_fstype($old_fstype) if $old_fstype;
+ umask($original_umask);
+ };
+
+ # If it failed, check whether we can at least rwx the dir.
+ if ($@) {
+ unless ( -d $dir && -r _ && -w _ && -x _ ) {
+ die("Error creating submissions directory.\n");
+ }
+ }
+ }
+
+ return $dir;
+}
+
+sub unpack_directory {
+
+ # Returns the directory into which the data archives will be
+ # unpacked, having first created it.
+ my $self = shift;
+
+ my $dir = File::Spec->catdir(
+ $self->filesystem_directory(), 'unpacked'
+ );
+
+ # Check for pre-existence because otherwise we can be stuck trying
+ # to change permissions on a pre-existing directory which may not
+ # belong to us.
+ unless ( -e $dir ) {
+
+ # Attempt to create unpacking directory.
+ eval {
+ my $original_umask = umask;
+ umask(0);
+ mkpath($dir, undef, oct(777));
+ chmod(oct($CONFIG->get_DIR_PERMISSIONS()), $dir)
+ or croak("Error changing permissions on $dir: $!");
+ umask($original_umask);
+ };
+
+ # If it failed, check whether we can at least rwx the dir.
+ if ($@) {
+ unless ( -d $dir && -r _ && -w _ && -x _ ) {
+ die("Error creating submissions directory.\n");
+ }
+ }
+ }
+
+ return $dir;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm b/lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm
new file mode 100644
index 0000000..62f65aa
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ExperimentFactor.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ExperimentFactor.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ExperimentFactor;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('experiments_factors');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ factor_id
+ experiment_id
+ )
+);
+__PACKAGE__->has_a(
+ experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_a(
+ factor_id => 'ArrayExpress::AutoSubmission::DB::Factor' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm b/lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm
new file mode 100644
index 0000000..06c6d3d
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ExperimentLoadedData.pm
@@ -0,0 +1,20 @@
+#!/usr/bin/env perl
+#
+# $Id: ExperimentLoadedData.pm 1936 2008-02-08 19:13:37Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ExperimentLoadedData;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('experiments_loaded_data');
+__PACKAGE__->columns(
+ Primary => qw(
+ loaded_data_id
+ experiment_id
+ )
+);
+__PACKAGE__->has_a( loaded_data_id => 'ArrayExpress::AutoSubmission::DB::LoadedData' );
+__PACKAGE__->has_a( experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm b/lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm
new file mode 100644
index 0000000..a1237ae
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/ExperimentQT.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: ExperimentQT.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::ExperimentQT;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('experiments_quantitation_types');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ quantitation_type_id
+ experiment_id
+ )
+);
+__PACKAGE__->has_a(
+ experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_a(
+ quantitation_type_id => 'ArrayExpress::AutoSubmission::DB::QuantitationType' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Factor.pm b/lib/ArrayExpress/AutoSubmission/DB/Factor.pm
new file mode 100644
index 0000000..417a232
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Factor.pm
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl
+#
+# $Id: Factor.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Factor;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('factors');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ name
+ is_deleted
+ )
+);
+__PACKAGE__->has_many(
+ experiments => [
+ 'ArrayExpress::AutoSubmission::DB::ExperimentFactor' => 'experiment_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/File.pm b/lib/ArrayExpress/AutoSubmission/DB/File.pm
new file mode 100644
index 0000000..0fcdf2c
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/File.pm
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+#
+# $Id: File.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::File;
+
+# Abstract package without corresponding DB table
+use File::Spec;
+use Carp;
+
+sub filesystem_path {
+ my $self = shift;
+
+ my $experiment;
+ unless ( $experiment = $self->experiment_id() ) {
+ croak(
+ sprintf(
+ "Error: Orphaned file has no experiment: %s",
+ $self->name()
+ )
+ );
+ }
+
+ my $path = File::Spec->catfile(
+ $experiment->filesystem_directory(),
+ $self->name()
+ );
+
+ return $path;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm b/lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm
new file mode 100644
index 0000000..fdeea69
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/LoadedData.pm
@@ -0,0 +1,44 @@
+#!/usr/bin/env perl
+#
+# $Id: LoadedData.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::LoadedData;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('loaded_data');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ identifier
+ md5_hash
+ data_format_id
+ platform_id
+ needs_metrics_calculation
+ date_hashed
+ is_deleted
+ )
+);
+
+__PACKAGE__->has_a( data_format_id => 'ArrayExpress::AutoSubmission::DB::DataFormat' );
+__PACKAGE__->has_a( platform_id => 'ArrayExpress::AutoSubmission::DB::Platform' );
+__PACKAGE__->has_many(
+ experiments => [
+ 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData' => 'experiment_id'
+ ]
+);
+__PACKAGE__->has_many(
+ loaded_data_instances => 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData'
+);
+__PACKAGE__->has_many(
+ quality_metrics => [
+ 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance' => 'quality_metric_id'
+ ]
+);
+__PACKAGE__->has_many(
+ quality_metric_instances => 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Material.pm b/lib/ArrayExpress/AutoSubmission/DB/Material.pm
new file mode 100644
index 0000000..2a6ea5e
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Material.pm
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+#
+# $Id: Material.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Material;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('materials');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ display_label
+ ontology_category
+ ontology_value
+ is_deleted
+ )
+);
+__PACKAGE__->has_many(
+ categories => [
+ 'ArrayExpress::AutoSubmission::DB::CategoryMaterial' => 'category_id'
+ ]
+);
+__PACKAGE__->has_many(
+ experiments => [
+ 'ArrayExpress::AutoSubmission::DB::MaterialInstance' =>
+ 'experiment_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm
new file mode 100644
index 0000000..1ec17ee
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/MaterialInstance.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: MaterialInstance.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::MaterialInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('material_instances');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ material_id
+ experiment_id
+ )
+);
+__PACKAGE__->has_a(
+ material_id => 'ArrayExpress::AutoSubmission::DB::Material' );
+__PACKAGE__->has_a(
+ experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Organism.pm b/lib/ArrayExpress/AutoSubmission/DB/Organism.pm
new file mode 100644
index 0000000..b9dc226
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Organism.pm
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+#
+# $Id: Organism.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Organism;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('organisms');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ scientific_name
+ common_name
+ accession
+ taxon_id
+ is_deleted
+ )
+);
+__PACKAGE__->has_a( taxon_id => 'ArrayExpress::AutoSubmission::DB::Taxon' );
+__PACKAGE__->has_many(
+ experiments => [
+ 'ArrayExpress::AutoSubmission::DB::OrganismInstance' =>
+ 'experiment_id'
+ ]
+);
+__PACKAGE__->has_many(
+ array_designs => [
+ 'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism' => 'array_design_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm
new file mode 100644
index 0000000..5805efe
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/OrganismInstance.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: OrganismInstance.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::OrganismInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('organism_instances');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ organism_id
+ experiment_id
+ )
+);
+__PACKAGE__->has_a(
+ organism_id => 'ArrayExpress::AutoSubmission::DB::Organism' );
+__PACKAGE__->has_a(
+ experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Permission.pm b/lib/ArrayExpress/AutoSubmission/DB/Permission.pm
new file mode 100644
index 0000000..8de9e55
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Permission.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: Permission.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Permission;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('permissions');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ name
+ info
+ is_deleted
+ )
+);
+__PACKAGE__->has_many( roles =>
+ [ 'ArrayExpress::AutoSubmission::DB::PermissionRole' => 'role_id' ] );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm b/lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm
new file mode 100644
index 0000000..21e69c5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/PermissionRole.pm
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+#
+# $Id: PermissionRole.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::PermissionRole;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('permissions_roles');
+__PACKAGE__->columns(
+ Primary => qw(
+ role_id
+ permission_id
+ )
+);
+__PACKAGE__->has_a( role_id => 'ArrayExpress::AutoSubmission::DB::Role' );
+__PACKAGE__->has_a(
+ permission_id => 'ArrayExpress::AutoSubmission::DB::Permission' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Platform.pm b/lib/ArrayExpress/AutoSubmission/DB/Platform.pm
new file mode 100644
index 0000000..734f432
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Platform.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: Platform.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Platform;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('platforms');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ name
+ )
+);
+
+__PACKAGE__->has_many(
+ loaded_data => 'ArrayExpress::AutoSubmission::DB::LoadedData'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Protocol.pm b/lib/ArrayExpress/AutoSubmission/DB/Protocol.pm
new file mode 100644
index 0000000..6313481
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Protocol.pm
@@ -0,0 +1,115 @@
+#!/usr/bin/env perl
+#
+# $Id: Protocol.pm 2024 2008-04-14 10:23:23Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Protocol;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::Accessionable';
+
+use ArrayExpress::Curator::Common qw(date_now);
+
+__PACKAGE__->table('protocols');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ accession
+ user_accession
+ expt_accession
+ name
+ date_last_processed
+ comment
+ is_deleted
+ )
+);
+
+sub reassign_protocol { # Class method.
+
+ my ( $class, $user_accession, $expt_accession, $protocol_name ) = @_;
+
+ # This is non-essential, but ends up in the database.
+ $protocol_name ||= q{Unknown};
+
+ # Search for (user_accession eq accession), then connects to AE if
+ # $CONFIG allows it to check for (preloaded accession eq
+ # user_accession). Finally checks for (user_accession eq
+ # user_accession and expt_accession eq expt_accession). Creates a
+ # new protocol in db and assigns accession if not found.
+ my $prot_accession;
+
+ # First look for simple reuse of DB accessions
+ my $db_protocol
+ = ArrayExpress::AutoSubmission::DB::Protocol->retrieve(
+ accession => $user_accession,
+ is_deleted => 0,
+ );
+
+ if ( $db_protocol ) {
+ $prot_accession = $db_protocol->accession();
+ }
+ else {
+
+ # Otherwise, check that ArrayExpress doesn't already have the
+ # protocol, if possible.
+ require ArrayExpress::Curator::Database;
+ ArrayExpress::Curator::Database->import('get_ae_dbh');
+
+ if ( my $dbh = get_ae_dbh() ) {
+
+ print STDOUT (
+ "Querying ArrayExpress for protocol accession $user_accession\n"
+ );
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct i.identifier
+from tt_identifiable i, tt_protocol p
+where i.id=p.id and i.identifier=?
+QUERY
+
+ # Query on protocol identifier, return the user
+ # accession if it's found.
+ $sth->execute( $user_accession )
+ or croak($sth->errstr() . " $DBI::errstr");
+ my $arrayref = $sth->fetchall_arrayref;
+ if ( scalar @$arrayref ) {
+ print STDOUT (
+ "Using preexisting ArrayExpress protocol accession $user_accession\n"
+ );
+ $prot_accession = $user_accession;
+ }
+ }
+
+ # If no AE accession, check user and expt accessions,
+ # creating a new protocol if necessary.
+ unless ($prot_accession) {
+ unless ($db_protocol) {
+ $db_protocol
+ = ArrayExpress::AutoSubmission::DB::Protocol->find_or_create(
+ user_accession => $user_accession,
+ expt_accession => $expt_accession,
+ is_deleted => 0,
+ );
+ unless ($db_protocol->name()) {
+ $db_protocol->set(
+ name => $protocol_name,
+ );
+ }
+ }
+ $prot_accession = $db_protocol->get_accession();
+ }
+ }
+
+ # If we're using a protocol from the autosubs system, set the date
+ # to record its usage.
+ if ( $db_protocol ) {
+ $db_protocol->set(
+ date_last_processed => date_now(),
+ );
+ $db_protocol->update();
+ }
+
+ return $prot_accession;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm b/lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm
new file mode 100644
index 0000000..83b2c70
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/QualityMetric.pm
@@ -0,0 +1,29 @@
+#!/usr/bin/env perl
+#
+# $Id: QualityMetric.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::QualityMetric;
+use base 'ArrayExpress::AutoSubmission::DB';
+
+__PACKAGE__->table('quality_metrics');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ type
+ description
+ )
+);
+
+__PACKAGE__->has_many(
+ loaded_data => [
+ 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance' => 'loaded_data_id'
+ ]
+);
+__PACKAGE__->has_many(
+ quality_metric_instances => 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance'
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm b/lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm
new file mode 100644
index 0000000..57037ba
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/QualityMetricInstance.pm
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+# $Id: QualityMetricInstance.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::QualityMetricInstance;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('loaded_data_quality_metrics');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ value
+ date_calculated
+ loaded_data_id
+ quality_metric_id
+ )
+);
+__PACKAGE__->has_a( loaded_data_id => 'ArrayExpress::AutoSubmission::DB::LoadedData' );
+__PACKAGE__->has_a( quality_metric_id => 'ArrayExpress::AutoSubmission::DB::QualityMetric' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm b/lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm
new file mode 100644
index 0000000..596e5fa
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/QuantitationType.pm
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl
+#
+# $Id: QuantitationType.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::QuantitationType;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('quantitation_types');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ name
+ is_deleted
+ )
+);
+__PACKAGE__->has_many(
+ experiments => [
+ 'ArrayExpress::AutoSubmission::DB::ExperimentQT' => 'experiment_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Role.pm b/lib/ArrayExpress/AutoSubmission/DB/Role.pm
new file mode 100644
index 0000000..baab88a
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Role.pm
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+#
+# $Id: Role.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Role;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('roles');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ name
+ info
+ is_deleted
+ )
+);
+__PACKAGE__->has_many(
+ users => [ 'ArrayExpress::AutoSubmission::DB::RoleUser' => 'user_id' ] );
+__PACKAGE__->has_many(
+ permissions => [
+ 'ArrayExpress::AutoSubmission::DB::PermissionRole' => 'permission_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm b/lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm
new file mode 100644
index 0000000..82a6299
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/RoleUser.pm
@@ -0,0 +1,20 @@
+#!/usr/bin/env perl
+#
+# $Id: RoleUser.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::RoleUser;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('roles_users');
+__PACKAGE__->columns(
+ Primary => qw(
+ user_id
+ role_id
+ )
+);
+__PACKAGE__->has_a( role_id => 'ArrayExpress::AutoSubmission::DB::Role' );
+__PACKAGE__->has_a( user_id => 'ArrayExpress::AutoSubmission::DB::User' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm b/lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm
new file mode 100644
index 0000000..d793711
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Spreadsheet.pm
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl
+#
+# $Id: Spreadsheet.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Spreadsheet;
+use base 'ArrayExpress::AutoSubmission::DB';
+use base 'ArrayExpress::AutoSubmission::DB::File';
+__PACKAGE__->table('spreadsheets');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ experiment_id
+ name
+ is_deleted
+ )
+);
+__PACKAGE__->has_a( experiment_id => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/Taxon.pm b/lib/ArrayExpress/AutoSubmission/DB/Taxon.pm
new file mode 100644
index 0000000..7cb884d
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/Taxon.pm
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+#
+# $Id: Taxon.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::Taxon;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('taxons');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ scientific_name
+ common_name
+ accession
+ is_deleted
+ )
+);
+__PACKAGE__->has_many(
+ organisms => 'ArrayExpress::AutoSubmission::DB::Organism' );
+__PACKAGE__->has_many(
+ categories => [
+ 'ArrayExpress::AutoSubmission::DB::CategoryTaxon' => 'category_id'
+ ]
+);
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/DB/User.pm b/lib/ArrayExpress/AutoSubmission/DB/User.pm
new file mode 100644
index 0000000..980b054
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/DB/User.pm
@@ -0,0 +1,29 @@
+#!/usr/bin/env perl
+#
+# $Id: User.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::DB::User;
+use base 'ArrayExpress::AutoSubmission::DB';
+__PACKAGE__->table('users');
+__PACKAGE__->columns(
+ All => qw(
+ id
+ login
+ name
+ password
+ email
+ modified_at
+ created_at
+ access
+ is_deleted
+ )
+);
+__PACKAGE__->has_many(
+ experiments => 'ArrayExpress::AutoSubmission::DB::Experiment' );
+__PACKAGE__->has_many(
+ roles => [ 'ArrayExpress::AutoSubmission::DB::RoleUser' => 'role_id' ] );
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon.pm b/lib/ArrayExpress/AutoSubmission/Daemon.pm
new file mode 100644
index 0000000..48963a7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon.pm
@@ -0,0 +1,435 @@
+#!/usr/bin/env perl
+#
+# Module used to construct checker and exporter daemon objects
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Daemon.pm 2017 2008-04-01 21:24:23Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon;
+
+use Carp;
+use Class::Std;
+use MIME::Lite;
+use Proc::Daemon;
+use POSIX qw(:sys_wait_h);
+use Socket;
+use IO::Socket;
+use IO::Select;
+use IO::Handle;
+use Scalar::Util qw(looks_like_number);
+use English qw( -no_match_vars );
+use Sys::Hostname;
+
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+
+# These are only set upon initialization
+my %polling_interval : ATTR( :get<polling_interval>, :init_arg<polling_interval> );
+my %experiment_type : ATTR( :get<experiment_type>, :init_arg<experiment_type> );
+my %accession_prefix : ATTR( :get<accession_prefix>, :init_arg<accession_prefix>, :default<undef> );
+my %autosubs_admin : ATTR( :get<autosubs_admin>, :init_arg<autosubs_admin> );
+
+# Optional for exporter daemons; the checker daemons will check that
+# this is defined.
+my %pidfile : ATTR( :get<pidfile>, :init_arg<pidfile>, :default<undef> );
+my %checker_threshold : ATTR( :get<checker_threshold>, :init_arg<checker_threshold>, :default<undef> );
+
+# The log file names are manipulated post-init.
+my %logfile : ATTR( :name<logfile>, :init_arg<logfile>, :default<q{}> );
+
+# Optional means to control the set of QTs used by the daemons.
+my %qt_filename : ATTR( :get<qt_filename>, :init_arg<qt_filename>, :default<undef> );
+
+# Attribute holding a flag notifying the process that it should quit.
+my %quit_when_done : ATTR( :name<quit_when_done>, :default<undef> );
+
+# Initialization subroutine.
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ croak("Error: no polling_interval set")
+ unless ( $self->get_polling_interval() );
+
+ croak("Error: no experiment_type set")
+ unless ( $self->get_experiment_type() );
+
+ croak("Error: no autosubs_admin email set.")
+ unless ( $self->get_autosubs_admin() );
+
+ return;
+}
+
+sub run {
+
+ my ( $self ) = @_;
+
+ if (my $prefix = $self->get_accession_prefix() ) {
+ ArrayExpress::AutoSubmission::DB::Experiment->accession_prefix(
+ $prefix,
+ );
+ }
+
+ # Check that there are no other processes running, if a pidfile
+ # has been provided. Typically the checker daemons have a pidfile,
+ # but the exporter daemons do not.
+ if ( $self->get_pidfile() && -f $self->get_pidfile() ) {
+ die(
+ sprintf(
+"Error: Old pidfile %s exists; another checker daemon may be running.\n",
+ $self->get_pidfile() )
+ );
+ }
+
+ # Check that the right user has launched the process; otherwise we
+ # end up with multiple copies running under multiple accounts and
+ # it's a nightmare to get control again.
+ if ( my $admin_user = $CONFIG->get_AUTOSUBS_ADMIN_USERNAME() ) {
+ unless ( getlogin() eq $admin_user ) {
+ die( "Error: Non-admin user attempting to launch daemon processes"
+ . " (you should be $admin_user). Please ensure AUTOSUBS_ADMIN_USERNAME"
+ . " is set to the correct login in the site configuration file.\n");
+ }
+ }
+ else {
+ die( "Error: AUTOSUBS_ADMIN_USERNAME must be set to an admin"
+ . " username in the site configuration file.\n");
+ }
+
+ # Run as a daemon
+ printf STDOUT ( "Starting %s %s daemon...\n",
+ $self->get_experiment_type(), ref($self) );
+ Proc::Daemon::Init;
+
+ ##########################
+ ### Exception handlers ###
+ ##########################
+ $SIG{TERM} = sub { $self->alert_on_signal(@_) }; # 15
+ $SIG{QUIT} = sub { $self->alert_on_signal(@_) }; # 3
+ $SIG{SEGV} = sub { $self->alert_on_signal(@_) }; # 11
+ $SIG{INT} = sub { $self->alert_on_signal(@_) }; # 2
+ $SIG{USR1} = sub { $self->set_quit_when_done(1) }; # 16
+
+ # From here, all die()s should mail us a notification.
+ $SIG{__DIE__} = sub { $self->alert_admin(@_) };
+
+ if ( $self->get_pidfile() ) {
+
+ # Create a file containing the process id ($$). This has to
+ # happen after daemon forking, above.
+ open( my $pid, ">", $self->get_pidfile() )
+ or die(sprintf("Unable to open pidfile %s: %s\n",
+ $self->get_pidfile(), $!));
+ print $pid "$PROCESS_ID\n";
+ close $pid or die("$!\n");
+ }
+
+ # The daemon starts via this entry point method.
+ $self->monitor_submissions();
+
+ return;
+}
+
+{
+ my ( $oldout, $olderr );
+
+ sub redirect_stdout_to_file : RESTRICTED {
+
+ my ( $self, $logfile ) = @_;
+
+ unless ( defined($logfile) ) {
+ die("Error: Undefined log file name.");
+ }
+
+ open( $oldout, '>&', \*STDOUT ) and seek( $oldout, 0, 0 );
+ open( $olderr, '>&', \*STDERR ) and seek( $olderr, 0, 0 );
+
+ open( STDOUT, '>', $logfile )
+ or die("Can't redirect STDOUT to logfile $logfile\n");
+ open( STDERR, '>&', \*STDOUT )
+ or die("Can't redirect STDERR to STDOUT");
+ *STDERR->autoflush(); # make unbuffered
+ *STDOUT->autoflush(); # make unbuffered
+
+ return;
+ }
+
+ sub restore_stdout_from_file : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ unless ( $oldout && $olderr ) {
+ die("Error: STDOUT and STDERR not cached;"
+ . " have you called redirect_stdout_to_file() yet?");
+ }
+
+ ( close(STDOUT) and open( STDOUT, '>&', $oldout ) )
+ or die("Can't restore STDOUT: $!\n");
+ ( close(STDERR) and open( STDERR, '>&', $olderr ) )
+ or die("Can't restore STDERR: $!\n");
+
+ return;
+ }
+}
+
+sub expt_check_submission : RESTRICTED {
+
+ my ( $self, $submission, $checker ) = @_;
+
+ # Set up our sockets for parent-child communication
+ socketpair( CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
+ or die "Cannot create socketpair: $!";
+
+ CHILD->autoflush(1);
+ PARENT->autoflush(1);
+
+ my $pid;
+ my $selector = IO::Select->new();
+ $selector->add( \*CHILD );
+
+ # Launch a subprocess to check the submission and return a score.
+ my ( $checker_score,
+ $consensus_software,
+ $dw_score,
+ $miame_score,
+ $comment );
+
+ my $start_time = date_now();
+
+ if ( $pid = fork ) {
+
+ # Parent process code
+
+ close PARENT;
+
+ my $checker_signal;
+ while ( my @handles = $selector->can_read ) {
+ foreach my $handle (@handles) {
+ recv( $handle, $checker_signal, 1024, 0 );
+ }
+ last;
+ }
+
+ # Post-process the child signal.
+ chomp $checker_signal;
+ ( $checker_score,
+ $consensus_software,
+ $dw_score,
+ $miame_score,
+ $comment ) =
+ split /\t/, $checker_signal;
+
+ close CHILD;
+ waitpid( $pid, 0 );
+
+ }
+
+ else {
+
+ # Child process code; must end with an exit
+
+ die "Error: Cannot fork: $!" unless defined $pid;
+ close CHILD;
+ $PROGRAM_NAME .= '_child';
+
+ $checker->set_clobber(1);
+
+ # Redirect STDOUT and STDERR to our log file.
+ my $logfile = $self->get_logfile()
+ or croak("Error: log file name not set.");
+
+ $self->redirect_stdout_to_file( $logfile );
+
+ # Run all the checks
+ eval { $checker->check(); };
+
+ # Communicate results with parent process
+ if ($EVAL_ERROR) {
+
+ # Checker score of 512 is reserved for checker crashes.
+ print PARENT (
+ join( "\t",
+ $CONFIG->get_ERROR_CHECKERCRASH(),
+ q{Unknown},
+ q{},
+ q{},
+ $EVAL_ERROR,
+ ),
+ "\n" );
+ }
+ else {
+
+ # Print out to the log.
+ print STDOUT ( "\n\nExperiment checker exit code: "
+ . $checker->get_error()
+ . "\n\n" );
+
+ # Pass the results to parent.
+ print PARENT (
+ join( "\t",
+ $checker->get_error(),
+ $checker->get_miamexpress_software_type() || q{},
+ $checker->get_aedw_score(),
+ $checker->get_miame(),
+ q{}
+ ),
+ "\n"
+ );
+ }
+ close PARENT;
+
+ # Restore STDOUT and STDERR.
+ $self->restore_stdout_from_file();
+
+ exit; # quit the child
+
+ }
+
+ my $end_time = date_now();
+
+ #################################################################
+ # Post-checking update of the database record for a submission. #
+ #################################################################
+
+ # Checker score 512 is reserved for checker crashes
+ if ( $checker_score & $CONFIG->get_ERROR_CHECKERCRASH() ) {
+ $submission->set(
+ status => $CONFIG->get_STATUS_CRASHED(),
+ comment => $submission->comment()
+ . ( $comment ? "\n\n$comment\n" : q{} ),
+ date_last_processed => $end_time,
+ );
+ $submission->update();
+
+ # Give up at this point.
+ return;
+ }
+
+ # Update the cache with the checker score etc.
+ my $accession = $submission->get_accession({
+ checker_score => $checker_score,
+ software => ( $consensus_software || 'Unknown' ),
+ data_warehouse_ready => $dw_score,
+ miame_score => $miame_score,
+ comment => $submission->comment()
+ . ( $comment ? "\n\n$comment\n" : q{} ),
+ date_last_processed => $end_time,
+ });
+ $submission->update();
+
+ # Add an event to record this run.
+ $submission->add_to_events({
+ event_type => 'Experiment Checker',
+ was_successful => ( $checker_score & $CONFIG->get_ERROR_CHECKERCRASH() ? 0 : 1 ),
+ source_db => $submission->experiment_type(),
+ start_time => $start_time,
+ end_time => $end_time,
+ machine => hostname(),
+ operator => $submission->curator(),
+ log_file => $self->get_logfile(),
+ is_deleted => 0,
+ });
+
+ # Check the returned info, act as necessary.
+ if ( looks_like_number($checker_score)
+ && ( $checker_score <= $self->get_checker_threshold() ) ) {
+
+ # Submission passes checks - mark for export.
+ $submission->set(
+ status => $CONFIG->get_STATUS_PASSED(),
+ date_last_processed => $end_time,
+ );
+ $submission->update();
+ }
+ else {
+
+ # Submission failed checks.
+ $submission->set(
+ status => $CONFIG->get_STATUS_FAILED(),
+ date_last_processed => $end_time,
+ );
+ $submission->update();
+ }
+
+ return;
+}
+
+sub is_magetab_doc : RESTRICTED {
+
+ # Tests for combined MAGETAB IDF+SDRF document versus IDF only,
+ # returns true in the former case, false for the latter. Used in
+ # both Daemon::Checker and Daemon::Exporter subclasses.
+
+ my ( $self, $doc ) = @_;
+
+ open( my $fh, '<', $doc )
+ or croak(qq{Error: Unable to open document "$doc": $!\n});
+
+ my ( $idf_found, $sdrf_found );
+ while ( my $line = <$fh> ) {
+ $idf_found++ if ( $line =~ m/\A \s* \"? \s* \[IDF\]/ixms );
+ $sdrf_found++ if ( $line =~ m/\A \s* \"? \s* \[SDRF\]/ixms );
+ }
+
+ close( $fh ) or croak(qq{Error: Unable to close document "$doc": $!\n});
+
+ if ( $idf_found && $sdrf_found ) {
+ return 1;
+ }
+
+ return;
+}
+
+sub get_curation_experiments : RESTRICTED {
+ confess("Error: stub method called in abstract parent class.");
+}
+
+sub monitor_submissions : RESTRICTED {
+ confess("Error: stub method called in abstract parent class.");
+}
+
+# Signal handling subroutines (not OO as such)
+sub alert_admin : PRIVATE {
+
+ my ($self, $error) = @_;
+
+ my $mailbody =
+ "The automatic experiment checking system ($PROCESS_ID) "
+ . "has crashed with the following error message: \n\n $error\n\n";
+
+ my $mail = MIME::Lite->new(
+ From => $self->get_autosubs_admin(),
+ To => $self->get_autosubs_admin(),
+ Subject => 'Crash notification',
+ Type => 'text/plain',
+ Encoding => 'quoted-printable',
+ Data => $mailbody,
+ );
+ $mail->send();
+
+ return;
+}
+
+sub alert_on_signal : PRIVATE {
+
+ my ($self, $signal) = @_;
+
+ # Delete our PID file, if we're using one.
+ my $pidfile = $self->get_pidfile();
+ unlink $pidfile if (defined $pidfile);
+
+ # Call for help. NB if we die here we get two emails instead of
+ # one. I assume that when this signal handler returns a die() is
+ # invoked, not sure about that though.
+ $self->alert_admin("Error: Signal SIG$signal received.\n");
+
+ # FIXME this is currently undetected by any parent processes.
+ exit(255);
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm
new file mode 100644
index 0000000..47a8c4a
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/Checker.pm
@@ -0,0 +1,295 @@
+#!/usr/bin/env perl
+#
+# Module used to construct spreadsheet checker daemon objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Checker.pm 2012 2008-03-30 17:27:46Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::Checker;
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use Class::Std;
+use Archive::Tar;
+use Archive::Zip;
+use IO::Zlib;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use File::Spec;
+use File::Find;
+use File::Copy;
+use Carp;
+use Cwd;
+use English qw( -no_match_vars );
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ croak("Error: no checker_threshold set.")
+ unless ( defined( $self->get_checker_threshold() ) );
+
+ return;
+}
+
+sub unpack_archive : PRIVATE {
+
+ my ($self, $file, $submission) = @_;
+
+ my $filepath = $file->filesystem_path();
+ my $unpack_dir = $submission->unpack_directory();
+
+ # Unpack the files into the appropriate directory. This should not
+ # create a significant memory problem - files are written directly
+ # to disk.
+ my $starting_dir = getcwd();
+ chdir $unpack_dir
+ or croak ("Error changing to unpack directory $unpack_dir: $!");
+
+ # Previously we used Archive::Any; however, memory requirements
+ # necessitated that we make the selection ourselves. Maybe add
+ # File::MMagic processing here as well as file extensions FIXME?
+ if ( $filepath =~ m/\. (?:tar.gz|tgz|tar) \z/ixms) {
+
+ # Tar archive
+
+ # In general the tar command suffers from fewer memory issues
+ # than Archive::Tar, which loads each archived data file
+ # completely into memory (not good for large files). We only
+ # fall back to Archive::Tar when tar isn't available.
+ my $rc = system("tar xvzf $filepath");
+
+ if ( $rc ) {
+
+ # Try again, using Archive::Tar. Trap any errors;
+ # otherwise this daemon will crash silently.
+ local $EVAL_ERROR;
+
+ # Apparently this call dies quite a lot; presumably
+ # trapped with an eval. However, to cut out the spurious
+ # alert emails we temporarily deactivate the die signal
+ # handler.
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+
+ eval {
+ Archive::Tar->extract_archive($filepath);
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+ if ($EVAL_ERROR) {
+ croak ("Error extracting archive $filepath: $EVAL_ERROR");
+ }
+ }
+ }
+ elsif ( $filepath =~ m/\. (?:zip) \z/ixms) {
+
+ # Zip archive
+ my $zip = Archive::Zip->new();
+ my $rc = $zip->read($filepath);
+ if ($rc) {
+ croak("Error reading zip file $filepath: $rc");
+ }
+ $rc = $zip->extractTree();
+ if ($rc) {
+ croak("Error extracting zip file $filepath: $rc");
+ }
+ }
+ else {
+ croak("Error: unrecognized file type for $filepath");
+ }
+
+ # We now want to flatten the directory structure.
+ find(
+ sub {
+ if ( -f $_ && -o $_ ) {
+ chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $_)
+ or croak("Error changing permissions on $_: $!");
+ move($_, $unpack_dir)
+ or croak("Error moving file $_: $!");
+ }
+ elsif ( -d $_ && -o $_ ) {
+ chmod(oct($CONFIG->get_DIR_PERMISSIONS()), $_)
+ or croak("Error changing permissions on $_: $!");
+ }
+ },
+ $unpack_dir,
+ );
+
+ # Back out of the unpacking directory.
+ chdir $starting_dir
+ or croak ("Error changing back to original directory: $!");
+
+ return;
+}
+
+sub monitor_submissions : RESTRICTED {
+ my $self = shift;
+
+ # Loop forever
+ EVENT_LOOP:
+ while (1) {
+
+ # Get the list of all experiments in curation.
+ my $new_results = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ in_curation => 1,
+ status => $CONFIG->get_STATUS_PENDING(),
+ experiment_type => $self->get_experiment_type(),
+ is_deleted => 0,
+ );
+
+ # Process the new submissions in order of id.
+ SUBMISSION:
+ while ( my $submission = $new_results->next() ) {
+
+ # Skip submissions by test user, or submissions without any user.
+ next SUBMISSION if ( ! $submission->user_id()
+ || $submission->user_id()->login() eq 'test' );
+
+ # Start a transaction.
+ my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+ $dbh->begin_work();
+
+ # Get current status and a row-level read lock.
+ my $current_status =
+ ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+ ->select_val( $submission->id() ) || q{};
+
+ # Double-check, in case something changes in the meantime.
+ unless ( $current_status eq $CONFIG->get_STATUS_PENDING() ) {
+ $dbh->commit();
+ next SUBMISSION;
+ }
+
+ # Mark the submission as in checking.
+ $submission->set(
+ status => $CONFIG->get_STATUS_CHECKING(),
+ date_last_processed => date_now(),
+ );
+ $submission->update();
+
+ # End the transaction.
+ $dbh->commit();
+
+ # Iterate over all the data file archives, unpacking them.
+ my $datafiles = $submission->data_files(is_deleted => 0);
+
+ # Note that data files are returned in order of their database id.
+ FILE:
+ while ( my $file = $datafiles->next() ) {
+
+ # Don't unpack a file more than once.
+ next FILE if $file->is_unpacked();
+
+ my $filepath = $file->filesystem_path();
+
+ # If the file exists, do something with it.
+ if ( -f $filepath ) {
+
+ # This area is prone to crashes, we want to trap them locally.
+ local $EVAL_ERROR;
+
+ # Process those files that Archive::Any can handle.
+ if ( $filepath =~ m/\. (?:tar.gz|tgz|tar|zip) \z/ixms ) {
+ eval {
+ $self->unpack_archive( $file, $submission )
+ };
+ }
+
+ # Gzipped files can be mistaken for tarred
+ # files. We handle them separately here.
+ elsif (my ($outfile) = ($filepath =~ m/(.*)\. (?:gz) \z/ixms) ) {
+ eval {
+ my $zlib_fh = new IO::Zlib;
+ if ( $zlib_fh->open($filepath, "rb") ) {
+ open (my $output_fh, ">", $outfile)
+ or croak("Error opening gzip output file $outfile: $!");
+ while ( my $line = <$zlib_fh> ) {
+ print $output_fh $line;
+ }
+
+ $zlib_fh->close();
+ close( $output_fh )
+ or croak("Error closing output file: $!");
+ }
+ move($outfile, $submission->unpack_directory())
+ or croak("Error moving file $outfile: $!");
+ };
+ }
+
+ # All uncompressed or unrecognized files are just
+ # copied to the unpack directory.
+ else {
+ eval{copy($filepath, $submission->unpack_directory())
+ or croak("Error copying file $filepath: $!")};
+ }
+
+ # Check that there were no problems.
+ if ( $EVAL_ERROR ) {
+ $submission->set(
+ status => $CONFIG->get_STATUS_CRASHED(),
+ comment => $submission->comment()
+ . "\n\nError: Unable to unpack file or archive: $filepath\n",
+ date_last_processed => date_now(),
+ );
+ $submission->update();
+ next SUBMISSION;
+ }
+
+ # Mark the file as unpacked in the database.
+ $file->set(is_unpacked => 1);
+ $file->update();
+ }
+ else {
+
+ # File not found. Mark the submission as bad, skip to next one.
+ $submission->set(
+ status => $CONFIG->get_STATUS_CRASHED(),
+ comment => $submission->comment()
+ . "\n\nError: File in database not found on filesystem: $filepath\n",
+ date_last_processed => date_now(),
+ );
+ $submission->update();
+ next SUBMISSION;
+ }
+ }
+
+ # Only one spreadsheet per submission at this point.
+ my $spreadsheet
+ = $submission->spreadsheets(is_deleted => 0)->next();
+
+ # Sort out the STDOUT logfile.
+ my $logfile_string = $spreadsheet->name();
+ $logfile_string =~ s/\.\w{3,4}$//; # strip off the extension
+ $self->set_logfile(
+ File::Spec->catfile(
+ $submission->filesystem_directory(),
+ "expt_${logfile_string}_stdout.log"
+ )
+ );
+
+ # Actually check the submission for errors. This also
+ # updates the autosubs db.
+ $self->check( $submission, $spreadsheet );
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ sleep( $self->get_polling_interval() * 60 );
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ return;
+}
+
+sub check : RESTRICTED {
+ confess("Error: stub method called in abstract superclass.");
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm
new file mode 100644
index 0000000..3d38470
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/Exporter.pm
@@ -0,0 +1,219 @@
+#!/usr/bin/env perl
+#
+# Module used to construct spreadsheet checker exporter objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Exporter.pm 2012 2008-03-30 17:27:46Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::Exporter;
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use Class::Std;
+use English qw( -no_match_vars );
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use Carp;
+use POSIX qw(:sys_wait_h);
+use Socket;
+use IO::Socket;
+use IO::Select;
+use Sys::Hostname;
+
+# This is simply passed through to the Tab2MAGE object.
+my %keep_protocol_accns : ATTR( :get<keep_protocol_accns>, :init_arg<keep_protocol_accns>, :default<undef> );
+
+# Track which subdirectory to write the output to.
+my %pipeline_subdir : ATTR( :get<pipeline_subdir>, :init_arg<pipeline_subdir>, :default<undef> );
+
+sub export_tab2mage : PRIVATE {
+
+ my ( $self, $submission ) = @_;
+
+ # Set up our sockets for parent-child communication
+ socketpair( CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
+ or die "Cannot create socketpair: $!";
+
+ CHILD->autoflush(1);
+ PARENT->autoflush(1);
+
+ my $pid;
+ my $selector = IO::Select->new();
+ $selector->add( \*CHILD );
+
+ my $exporter_signal;
+
+ if ( $pid = fork ) {
+
+ # Parent process code
+
+ close PARENT;
+
+ while ( my @handles = $selector->can_read ) {
+ foreach my $handle (@handles) {
+ recv( $handle, $exporter_signal, 1024, 0 );
+ }
+ last;
+ }
+ chomp $exporter_signal;
+
+ close CHILD;
+ waitpid( $pid, 0 );
+ }
+
+ else {
+
+ # Child process code; must end with an exit
+
+ die "Error: Cannot fork: $!" unless defined $pid;
+ close CHILD;
+ $PROGRAM_NAME .= '_child';
+
+ my $spreadsheet
+ = $submission->spreadsheets(is_deleted => 0)->next();
+
+ # Create the Tab2MAGE or MAGE-TAB exporter object.
+ my $exporter = $self->create_exporter( $submission, $spreadsheet );
+
+ # Here we actually do the MAGE-ML export.
+ eval {
+
+ # Handle the combined MAGETAB doc case here.
+ if ( $exporter->can('parse_magetab_doc')
+ && $exporter->get_magetab_doc() ) {
+ $exporter->parse_magetab_doc();
+ }
+
+ # Actually export the MAGE-ML.
+ $exporter->write_mageml();
+ };
+
+ print PARENT ("$EVAL_ERROR\n");
+
+ close PARENT;
+
+ exit;
+ }
+
+ return $exporter_signal; # non-zero is failure
+}
+
+sub monitor_submissions : RESTRICTED {
+ my $self = shift;
+
+ # Loop forever
+ EVENT_LOOP:
+ while (1) {
+
+ # Create the accession cache object.
+ my $iterator
+ = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ experiment_type => $self->get_experiment_type(),
+ status => $CONFIG->get_STATUS_PASSED(),
+ is_deleted => 0,
+ );
+
+ SUBMISSION:
+ while ( my $submission = $iterator->next() ) {
+
+ # Skip submissions by test user, or submissions without any user.
+ next SUBMISSION if ( ! $submission->user_id()
+ || $submission->user_id()->login() eq 'test' );
+
+ # Start a transaction.
+ my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+ $dbh->begin_work();
+
+ # Get current status and a row-level read lock.
+ my $current_status =
+ ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+ ->select_val( $submission->id() ) || q{};
+
+ # Double-check, in case something changes in the meantime.
+ unless ( $current_status eq $CONFIG->get_STATUS_PASSED() ) {
+ $dbh->commit();
+ next SUBMISSION;
+ }
+
+ # Set the status so a parallel daemon doesn't pick up.
+ $submission->set(
+ status => $CONFIG->get_STATUS_EXPORT(),
+ date_last_processed => date_now(),
+ );
+ $submission->update();
+
+ # End the transaction.
+ $dbh->commit();
+
+ # Actually export the MAGE-ML.
+ my $start_time = date_now();
+ my $rc = $self->export_tab2mage( $submission );
+ my $end_time = date_now();
+
+ if ( ! $rc ) { # Test for export success.
+ $submission->set(
+ status => $CONFIG->get_STATUS_COMPLETE(),
+ date_last_processed => $end_time,
+ );
+ }
+ else { # Export failed
+ $submission->set(
+ status => $CONFIG->get_STATUS_EXPORT_ERROR(),
+ date_last_processed => $end_time,
+ comment => $submission->comment()
+ . "\n\n$rc\n",
+ );
+ }
+ $submission->update();
+
+ # Add an event to record this run.
+ $submission->add_to_events({
+ event_type => 'MAGE-ML Export',
+ was_successful => ( $rc ? 0 : 1 ),
+ source_db => $submission->experiment_type(),
+ start_time => $start_time,
+ end_time => $end_time,
+ machine => hostname(),
+ operator => $submission->curator(),
+ log_file => $self->get_logfile(),
+ is_deleted => 0,
+ });
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ sleep( $self->get_polling_interval() * 60 );
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ return;
+}
+
+sub get_targetdir : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ my $targetdir = $CONFIG->get_AUTOSUBMISSIONS_TARGET();
+ if ( my $subdir = $self->get_pipeline_subdir() ) {
+ $targetdir = File::Spec->catdir(
+ $targetdir,
+ $subdir,
+ );
+ }
+
+ return $targetdir;
+}
+
+sub create_exporter : RESTRICTED {
+ confess("Error: stub method called in abstract superclass");
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm
new file mode 100644
index 0000000..e09f2a7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABChecker.pm
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+#
+# $Id: MAGETABChecker.pm 2020 2008-04-08 13:15:33Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MAGETABChecker;
+use base 'ArrayExpress::AutoSubmission::Daemon::Checker';
+
+require ArrayExpress::MAGETAB::Checker;
+
+use Class::Std;
+
+sub check : RESTRICTED {
+
+ my ( $self, $submission, $spreadsheet ) = @_;
+
+ my $file = $spreadsheet->filesystem_path();
+
+ # For MIAMExpress submissions converted to MAGE-TAB, we don't want
+ # to repeat the data file checks. Here we assume that the
+ # miamexpress_subid column is a good indicator (which, for the
+ # most part, it should be).
+ my $skip_data = $submission->miamexpress_subid() ? 1 : 0;
+
+ # Parameters passed to ExperimentChecker object.
+ my %checker_params;
+ if ( $self->is_magetab_doc( $file ) ) {
+ %checker_params = (
+ magetab_doc => $file,
+ source_directory => $submission->unpack_directory(),
+ log_to_current_dir => $submission->filesystem_directory(),
+ qt_filename => $self->get_qt_filename(),
+ skip_data_checks => $skip_data,
+ safe_filechecks => 1,
+ );
+ }
+ else {
+ %checker_params = (
+ idf => $file,
+ source_directory => $submission->unpack_directory(),
+ log_to_current_dir => $submission->filesystem_directory(),
+ qt_filename => $self->get_qt_filename(),
+ skip_data_checks => $skip_data,
+ safe_filechecks => 1,
+ );
+ }
+ $self->expt_check_submission(
+ $submission,
+ ArrayExpress::MAGETAB::Checker->new(\%checker_params),
+ );
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm
new file mode 100644
index 0000000..e3462f7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MAGETABExporter.pm
@@ -0,0 +1,64 @@
+#!/usr/bin/env perl
+#
+# $Id: MAGETABExporter.pm 1868 2008-01-03 14:26:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MAGETABExporter;
+use base 'ArrayExpress::AutoSubmission::Daemon::Exporter';
+
+use Class::Std;
+use File::Spec;
+require ArrayExpress::MAGETAB;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+sub create_exporter : RESTRICTED {
+
+ my ( $self, $submission, $spreadsheet ) = @_;
+
+ my %common_opts = (
+ authority => $CONFIG->get_AUTOSUBS_DOMAIN(),
+ namespace => 'MAGETabulator',
+ expt_accession => $submission->get_accession(),
+ output_file => $submission->get_accession() . '.xml',
+ target_directory => File::Spec->catdir(
+ $self->get_targetdir(),
+ $submission->accession(),
+ ),
+ source_directory => $submission->unpack_directory(),
+ use_plain_text => 0,
+ protocol_accession_prefix => 'P-MTAB-',
+ keep_protocol_accns => $self->get_keep_protocol_accns(),
+ clobber => 1,
+ qt_filename => $self->get_qt_filename(),
+
+ # Note that if we change this we must also revert the
+ # ExperimentChecker code to flag unknown QTs as
+ # PARSEBAD.
+ keep_all_qts => 1, # review this FIXME
+ );
+
+ # Decide whether this is a combined MAGETAB (IDF+SDRF) or IDF-only
+ # document.
+ my $magetab;
+ my $file = $spreadsheet->filesystem_path();
+ if ( $self->is_magetab_doc( $file ) ) {
+ $magetab = ArrayExpress::MAGETAB->new({
+ magetab_doc => $file,
+ %common_opts,
+ });
+ }
+ else {
+ $magetab = ArrayExpress::MAGETAB->new({
+ idf => $file,
+ %common_opts,
+ });
+ }
+
+ $self->set_logfile( $magetab->logfiles('magetab') );
+
+ return $magetab;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm
new file mode 100644
index 0000000..fac69a7
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MXChecker.pm
@@ -0,0 +1,271 @@
+#!/usr/bin/env perl
+#
+# Module used to construct MIAMExpress checker daemon objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: MXChecker.pm 2013 2008-03-31 17:23:14Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MXChecker;
+
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use Class::Std;
+use DBI;
+use Carp;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now mx2tab);
+
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::Curator::MIAMExpress;
+
+my %mx_dsn : ATTR( :get<mx_dsn>, :init_arg<mx_dsn> );
+my %mx_username : ATTR( :get<mx_username>, :init_arg<mx_username> );
+my %mx_password : ATTR( :get<mx_password>, :init_arg<mx_password> );
+my %mx_dbparams : ATTR( :get<mx_dbparams>, :init_arg<mx_dbparams> );
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ croak("Error: no mx_dsn set.")
+ unless ( $self->get_mx_dsn() );
+
+ croak("Error: no mx_username set.")
+ unless ( $self->get_mx_username() );
+
+ croak("Error: no mx_password set.")
+ unless ( $self->get_mx_password() );
+
+ croak("Error: no mx_dbparams set.")
+ unless ( $self->get_mx_dbparams() );
+
+ croak("Error: no checker_threshold set.")
+ unless ( defined( $self->get_checker_threshold() ) );
+
+ return;
+}
+
+sub get_curation_expts : PRIVATE {
+ my $self = shift;
+
+ my $dbh = DBI->connect(
+ $self->get_mx_dsn(), $self->get_mx_username(),
+ $self->get_mx_password(), $self->get_mx_dbparams(),
+ ) or die("Database connection error: $DBI::errstr\n");
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_SYSUID from TSUBMIS, TEXPRMNT
+where TSUBMIS_PROC_STATUS in ('C','S')
+and TSUBMIS_COMP_STATUS='U'
+and TSUBMIS_SYSUID=TEXPRMNT_SUBID
+and TSUBMIS_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TSUBMIS_LOGIN!='test'
+QUERY
+
+ $sth->execute() or die($sth->errstr);
+
+ my $results = $sth->fetchall_hashref('TSUBMIS_SYSUID');
+
+ $dbh->disconnect()
+ or die( "Database disconnection error: " . $dbh->errstr() . "\n" );
+
+ return [ sort keys %{$results} ];
+
+}
+
+sub info_from_subid : PRIVATE {
+
+ my ( $self, $subid ) = @_;
+
+ my $dbh = DBI->connect(
+ $self->get_mx_dsn(), $self->get_mx_username(),
+ $self->get_mx_password(), $self->get_mx_dbparams(),
+ ) or die("Database connection error: $DBI::errstr\n");
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_LOGIN, TSUBMIS_SUB_DESCR, TSUBMIS_LAST_CHANGE, TEXPRMNT_VARS
+from TSUBMIS, TEXPRMNT
+where TSUBMIS_SYSUID=?
+and TSUBMIS_SYSUID=TEXPRMNT_SUBID
+and TEXPRMNT_DEL_STATUS='U'
+and TSUBMIS_DEL_STATUS='U'
+QUERY
+
+ $sth->execute($subid) or die($sth->errstr);
+
+ my $results = $sth->fetchall_arrayref;
+
+ $sth->finish();
+
+ if ( scalar(@$results) != 1 ) {
+ die(
+ "Error: submission id $subid maps to "
+ . scalar(@$results)
+ . " submissions in the database!\n"
+ );
+ }
+
+ my ( $login, $title, $date, $vars ) = @{ $results->[0] };
+
+ my $is_mxbl = 0;
+ if ( $vars && $vars =~ m/BatchUploadTool/ixms ) {
+ $is_mxbl = 1;
+ }
+
+ # Get the experiment filebase, while we have a connection.
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='experiment_datafiles_path'
+QUERY
+
+ $sth->execute() or die($sth->errstr);
+
+ # FIXME we're assuming the query worked here.
+ my $subsdir = File::Spec->catdir(
+ $sth->fetchrow_arrayref()->[0], $login, "submission$subid",
+ );
+
+ $dbh->disconnect()
+ or die( "Database disconnection error: " . $dbh->errstr() . "\n" );
+
+ return ( $login, $title, $subsdir, $date, $is_mxbl );
+
+}
+
+sub monitor_submissions : RESTRICTED {
+ my $self = shift;
+
+ # Loop forever
+ EVENT_LOOP:
+ while (1) {
+
+ # Get the list of all experiments in curation.
+ my $new_results = $self->get_curation_expts();
+
+ # Process the new submissions in order of id.
+ SUBMISSION:
+ foreach my $subid ( @{$new_results} ) {
+
+ # Retrieve each experiment record in turn, creating
+ # records where none exist.
+ my $submission =
+ ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+ miamexpress_subid => $subid,
+ is_deleted => 0,
+ )
+ ||
+ ArrayExpress::AutoSubmission::DB::Experiment->insert({
+ miamexpress_subid => $subid,
+ status => $CONFIG->get_STATUS_PENDING(),
+ experiment_type => $self->get_experiment_type(),
+ is_deleted => 0,
+ });
+
+ # We avoid checking non-MX submissions with MX submission
+ # IDs here (these will typically have been switched to
+ # e.g. MAGE-TAB).
+ next SUBMISSION
+ if ( $submission->experiment_type() ne $self->get_experiment_type() );
+
+ # Start a transaction.
+ my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+ $dbh->begin_work();
+
+ # Get current status and a row-level read lock.
+ my $current_status =
+ ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+ ->select_val( $submission->id() ) || q{};
+
+ # Skip the submission if it has a non-pending status
+ # already in the database. Occasionally checking crashes
+ # due to e.g. AE being down, so all submissions pending
+ # checking (or with NULL status) are fair game. Crashed
+ # subs are no longer checked.
+ # Double-check, in case something changes in the meantime.
+ unless ( $current_status eq $CONFIG->get_STATUS_PENDING() ) {
+ $dbh->commit();
+ next SUBMISSION;
+ }
+
+ $submission->set(
+ status => $CONFIG->get_STATUS_DB_RETRIEVAL(),
+ date_last_processed => date_now(),
+ );
+ $submission->update();
+
+ # End the transaction.
+ $dbh->commit();
+
+ my ( $login, $title, $subsdir, $subsdate, $is_mxbl )
+ = $self->info_from_subid($subid);
+
+ # This is specific to the current MIAMExpress version.
+ $self->set_logfile(
+ File::Spec->catfile(
+ $subsdir,
+ "expt_${login}_sub${subid}_stdout.log"
+ )
+ );
+
+ # Enter the login and expt name information.
+ $submission->set(
+ miamexpress_login => $login,
+ name => $title,
+ status => $CONFIG->get_STATUS_CHECKING(),
+ date_submitted => $subsdate,
+ date_last_processed => date_now(),
+ is_mx_batchloader => $is_mxbl,
+ );
+ $submission->update();
+
+ # Actually check the submission for errors. This also
+ # updates the autosubs db.
+ $self->expt_check_submission(
+ $submission,
+ ArrayExpress::Curator::MIAMExpress->new({
+ mx_login => $login,
+ mx_title => $title,
+ }),
+ );
+
+ # FIXME maybe bring the status changes from mx2tab into
+ # this module so that they can be more easily managed
+ # within a transaction. NOTE however that when I tried
+ # wrapping this call in a transaction quite odd things
+ # happened; possibly a DBD::mysql bug? The script failed
+ # while complaining that the DBD driver hadn't implemented
+ # the AutoCommit attribute (which, of course, it
+ # has). Note also that the transaction earlier works just
+ # fine, maybe there's a cryptic interaction between the
+ # two.
+ my $new_status =
+ ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+ ->select_val( $submission->id() ) || q{};
+
+ # If checks passed, we now switch the submission to
+ # MAGE-TAB.
+ if ( $new_status eq $CONFIG->get_STATUS_PASSED() ) {
+ eval {
+ mx2tab($submission);
+ };
+ }
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ sleep( $self->get_polling_interval() * 60 );
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm
new file mode 100644
index 0000000..8fc41c8
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/MXExporter.pm
@@ -0,0 +1,247 @@
+#!/usr/bin/env perl
+#
+# Module used to construct MIAMExpress exporter daemon objects
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: MXExporter.pm 2012 2008-03-30 17:27:46Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::MXExporter;
+use base 'ArrayExpress::AutoSubmission::Daemon';
+
+use English qw( -no_match_vars );
+use Class::Std;
+use DBI;
+use File::Spec;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use Carp;
+use POSIX qw(:sys_wait_h);
+use Socket;
+use IO::Socket;
+use IO::Select;
+use Sys::Hostname;
+
+my %mx_export_command : ATTR( :get<mx_export_command>, :init_arg<mx_export_command> );
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ croak("Error: no mx_export_command set.")
+ unless ( $self->get_mx_export_command() );
+
+ return;
+}
+
+sub export_mx_mageml : PRIVATE {
+
+ my ( $self, $title, $software, $accession, $login, $subid ) = @_;
+
+ unless ( $self->get_mx_export_command() ) {
+ die("Error: MIAMExpress export command not defined.\n");
+ }
+
+ # Set up our sockets for parent-child communication
+ socketpair( CHILD, PARENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
+ or die "Cannot create socketpair: $!";
+
+ CHILD->autoflush(1);
+ PARENT->autoflush(1);
+
+ my $pid;
+ my $selector = IO::Select->new();
+ $selector->add( \*CHILD );
+
+ my ( $exporter_signal, $logfile );
+
+ if ( $pid = fork ) {
+
+ # Parent process code
+
+ close PARENT;
+
+ my $child_signal;
+ while ( my @handles = $selector->can_read ) {
+ foreach my $handle (@handles) {
+ recv( $handle, $child_signal, 1024, 0 );
+ }
+ last;
+ }
+ chomp $child_signal;
+
+ # It's important to know where our logfile is.
+ ( $logfile, $exporter_signal ) = split /\t/, $child_signal;
+
+ close CHILD;
+ waitpid( $pid, 0 );
+ }
+
+ else {
+
+ # Child process code; must end with an exit
+
+ die "Error: Cannot fork: $!" unless defined $pid;
+ close CHILD;
+ $PROGRAM_NAME .= '_child';
+
+ # First, sort out a log file for STDOUT/STDERR redirection.
+ my $dbh = DBI->connect(
+ $CONFIG->get_MX_DSN(), $CONFIG->get_MX_USERNAME(),
+ $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS(),
+ ) or die("Database connection error: $DBI::errstr\n");
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='experiment_datafiles_path'
+QUERY
+
+ $sth->execute() or die($sth->errstr);
+
+ # FIXME we're assuming the query worked here.
+ my $logfile = File::Spec->catfile(
+ $sth->fetchrow_arrayref()->[0],
+ $login,
+ "submission$subid",
+ "expt_${login}_sub${subid}_mxexporter.log",
+ );
+
+ $dbh->disconnect();
+
+ my $sys_command = $self->get_mx_export_command()
+ . qq{ "$title" "$software" "$accession"};
+
+ $self->redirect_stdout_to_file( $logfile );
+ print STDOUT "Running command:\n\n$sys_command\n\n";
+
+ my $rc = system($sys_command);
+
+ # Pass the logfile back to our parent.
+ print PARENT ("$logfile\t$rc\n");
+
+ close PARENT;
+
+ $self->restore_stdout_from_file();
+
+ exit;
+ }
+
+ return ( $logfile, $exporter_signal ); # non-zero is failure
+}
+
+sub monitor_submissions : RESTRICTED {
+ my $self = shift;
+
+ # Loop forever
+ EVENT_LOOP:
+ while (1) {
+
+ # Create the accession cache iterators.
+ my $passed
+ = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ experiment_type => $self->get_experiment_type(),
+ status => $CONFIG->get_STATUS_PASSED(),
+ is_deleted => 0,
+ );
+ my $postponed
+ = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ experiment_type => $self->get_experiment_type(),
+ status => $CONFIG->get_STATUS_EXPORT_POSTPONED(),
+ is_deleted => 0,
+ );
+
+ SUBMISSION:
+ while ( my $submission = ( $passed->next() || $postponed->next() ) ) {
+
+ # Start a transaction.
+ my $dbh = ArrayExpress::AutoSubmission::DB::Experiment->db_Main();
+ $dbh->begin_work();
+
+ # Get current status and a row-level read lock.
+ my $current_status =
+ ArrayExpress::AutoSubmission::DB::Experiment->sql_status_for_update()
+ ->select_val( $submission->id() ) || q{};
+
+ # Double-check, in case something changes in the meantime.
+ unless ( $current_status eq $CONFIG->get_STATUS_PASSED()
+ || $current_status eq $CONFIG->get_STATUS_EXPORT_POSTPONED() ) {
+ $dbh->commit();
+ next SUBMISSION;
+ }
+
+ $submission->set(
+ status => $CONFIG->get_STATUS_EXPORT(),
+ date_last_processed => date_now(),
+ );
+ $submission->update();
+
+ # End the transaction.
+ $dbh->commit();
+
+ my $start_time = date_now();
+ my ( $logfile, $rc ) = $self->export_mx_mageml(
+ $submission->name(),
+ $submission->software(),
+ $submission->accession(),
+ $submission->miamexpress_login(),
+ $submission->miamexpress_subid(),
+ );
+ my $end_time = date_now();
+
+ if ( ! $rc ) {
+
+ # False == success.
+ $submission->set(
+ status => $CONFIG->get_STATUS_COMPLETE(),
+ date_last_processed => $end_time,
+ );
+ }
+ elsif ( (unpack "c", pack "C", $rc >> 8) == -7 ) {
+
+ # Export failed due to connectivity problem ("exit -7").
+ $submission->set(
+ status => $CONFIG->get_STATUS_EXPORT_POSTPONED(),
+ date_last_processed => $end_time,
+ );
+ }
+ else {
+
+ # Export failed.
+ $submission->set(
+ status => $CONFIG->get_STATUS_EXPORT_ERROR(),
+ date_last_processed => $end_time,
+ );
+ }
+ $submission->update();
+
+ # Add an event to record this run.
+ $submission->add_to_events({
+ event_type => 'MAGE-ML Export',
+ was_successful => ( $rc ? 0 : 1 ),
+ source_db => $submission->experiment_type(),
+ start_time => $start_time,
+ end_time => $end_time,
+ machine => hostname(),
+ operator => $submission->curator(),
+ log_file => $logfile,
+ is_deleted => 0,
+ });
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ sleep( $self->get_polling_interval() * 60 );
+
+ last EVENT_LOOP if $self->get_quit_when_done();
+
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm b/lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm
new file mode 100644
index 0000000..9970b4f
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/T2MChecker.pm
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+#
+# $Id: T2MChecker.pm 2020 2008-04-08 13:15:33Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::T2MChecker;
+use base 'ArrayExpress::AutoSubmission::Daemon::Checker';
+
+require ArrayExpress::Curator::Validate;
+
+use Class::Std;
+
+sub check : RESTRICTED {
+
+ my ( $self, $submission, $spreadsheet ) = @_;
+
+ $self->expt_check_submission(
+ $submission,
+ ArrayExpress::Curator::Validate->new({
+ spreadsheet_filename => $spreadsheet->filesystem_path(),
+ source_directory => $submission->unpack_directory(),
+ qt_filename => $self->get_qt_filename(),
+ safe_filechecks => 1,
+ }),
+ );
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm b/lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm
new file mode 100644
index 0000000..968f3d5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Daemon/T2MExporter.pm
@@ -0,0 +1,47 @@
+#!/usr/bin/env perl
+#
+# $Id: T2MExporter.pm 1868 2008-01-03 14:26:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::AutoSubmission::Daemon::T2MExporter;
+use base 'ArrayExpress::AutoSubmission::Daemon::Exporter';
+
+use Class::Std;
+use File::Spec;
+require ArrayExpress::Curator::Tab2MAGE;
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+sub create_exporter : RESTRICTED {
+
+ my ( $self, $submission, $spreadsheet ) = @_;
+
+ my $tab2mage = ArrayExpress::Curator::Tab2MAGE->new({
+
+ # Only one spreadsheet per submission at the moment.
+ spreadsheet_filename => $spreadsheet->filesystem_path(),
+ target_directory => File::Spec->catdir(
+ $self->get_targetdir(),
+ $submission->accession(),
+ ),
+ source_directory => $submission->unpack_directory(),
+ external_accession => $submission->get_accession(),
+ external_domain => $CONFIG->get_AUTOSUBS_DOMAIN,
+ clobber => 1,
+ binary_datafiles => 1,
+ keep_protocol_accns => $self->get_keep_protocol_accns(),
+ qt_filename => $self->get_qt_filename(),
+
+ # Note that if we change this we must also revert the
+ # ExperimentChecker code to flag unknown QTs as
+ # PARSEBAD.
+ keep_all_qts => 1, # review this FIXME
+ });
+
+ $self->set_logfile( $tab2mage->logfiles('tab2mage') );
+
+ return $tab2mage;
+}
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/Spreadsheet.pm b/lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
new file mode 100644
index 0000000..422cfb5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
@@ -0,0 +1,1079 @@
+#!/usr/bin/env perl
+#
+# Module used to generate spreadsheet templates for Tab2MAGE submissions
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Spreadsheet.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::AutoSubmission::Spreadsheet;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use Readonly;
+use ArrayExpress::Curator::Common qw(decamelize);
+require ArrayExpress::AutoSubmission::DB::Category;
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $EDF_EXPTACCESSION
+ $EDF_EXPTDOMAIN
+ $EDF_EXPTNAME
+ $EDF_EXPTDESCRIPTION
+ $EDF_EXPTRELEASEDATE
+ $EDF_EXPTSUBMITTER
+ $EDF_EXPTDATACODER
+ $EDF_EXPTCURATOR
+ $EDF_EXPTINVESTIGATOR
+ $EDF_EXPTORGANIZATION
+ $EDF_EXPTADDRESS
+ $EDF_EXPTSUBMITTER_EMAIL
+ $EDF_EXPTCURATOR_EMAIL
+ $EDF_EXPTINVESTIGATOR_EMAIL
+ $EDF_EXPTDATACODER_EMAIL
+ $EDF_EXPTSUBMISSIONDATE
+ $EDF_EXPTDESIGNTYPE
+ $EDF_EXPTQUALITYCONTROL
+ $EDF_EXPTCURATEDNAME
+
+ $EDF_PUBTITLE
+ $EDF_PUBPAGES
+ $EDF_PUBJOURNAL
+ $EDF_PUBVOLUME
+ $EDF_PUBISSUE
+ $EDF_PUBYEAR
+ $EDF_PUBAUTHORS
+ $EDF_PUBURI
+ $EDF_PUBMEDID
+
+ $EDF_PROTOCOLACCESSION
+ $EDF_PROTOCOLNAME
+ $EDF_PROTOCOLTYPE
+ $EDF_PROTOCOLTEXT
+ $EDF_PROTOCOLPARAMS
+
+ $OE_VAL_CHIP_CHIP
+);
+
+my %experiment : ATTR( :init_arg<experiment>, :get<experiment>, :default<undef> );
+my %url : ATTR( :init_arg<url>, :get<url>, :default<undef> );
+my %date : ATTR( :init_arg<date>, :get<date>, :default<undef> );
+
+# 1 = all spreadsheets; 2 = dye-swap only; 4 = Affy only, 8 = ChIP
+Readonly my $ALL => 1;
+Readonly my $DYESWAP => 2;
+Readonly my $AFFY => 4;
+Readonly my $CHIPCHIP => 8;
+
+Readonly my $MGED_ONTOLOGY => 'MGED Ontology';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $experiment = $self->get_experiment();
+ unless ( $experiment
+ && $experiment->isa('ArrayExpress::AutoSubmission::DB::Experiment') ) {
+ croak('Error: experiment attribute not set to an'
+ . ' ArrayExpress::AutoSubmission::DB::Experiment object.');
+ }
+
+ unless ( $self->get_url() ) {
+ carp('Warning: URL attribute not set.');
+ }
+ unless ( $self->get_date() ) {
+ carp('Warning: Date attribute not set.');
+ }
+
+ return;
+}
+
+sub _tab2mage_expt_section : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $experiment = $self->get_experiment();
+
+ my $experiment_name = $experiment->name();
+ my $experiment_type = $experiment->experiment_type();
+ my $submitter = $experiment->user_id()->name();
+ my $submitter_email = $experiment->user_id()->email();
+
+ my $url = $self->get_url() || q{};
+ my $date = $self->get_date() || q{};
+
+ my $design_string =
+ join( ', ', map { $_->ontology_value() } $experiment->designs() );
+
+ my $template = <<"EXPTSECTION";
+#
+# $experiment_type Submission '$experiment_name', generated at $date
+#
+# Please save this page to disk, and edit it using any spreadsheet program (e.g.,
+# Microsoft Excel, OpenOffice.org Calc). This file can be imported into either of
+# these applications as a tab-delimited text file. It is highly recommended that
+# you set the cell format to plain text throughout the spreadsheet, to avoid
+# 'helpful' changes made by the spreadsheet application. Once you have completed
+# this spreadsheet please upload it using the submissions webpage alongside your
+# data files:
+# $url
+#
+# Please see http://tab2mage.sourceforge.net/docs/spreadsheet.html for
+# information on filling in the spreadsheet.
+#
+# All lines beginning with the '#' character are treated as comments and ignored.
+# Blank lines may only be used between Experiment, Protocol and Hybridization sections.
+#
+#
+#
+# This section contains the top-level information for your experiment.
+Experiment section
+$EDF_EXPTNAME\t$experiment_name
+$EDF_EXPTDESCRIPTION\t
+$EDF_EXPTDESIGNTYPE\t$design_string
+# $EDF_EXPTQUALITYCONTROL examples: dye_swap_quality_control, biological_replicate, technical_replicate
+$EDF_EXPTQUALITYCONTROL\t
+# Dates should be entered in the form YYYY-MM-DD. If you are using MS Excel,
+# it is recommended that you set your spreadsheet to 'text' format throughout
+# to help avoid any unwanted changes made by Excel.
+$EDF_EXPTRELEASEDATE\t
+$EDF_EXPTSUBMISSIONDATE\t
+$EDF_EXPTSUBMITTER\t$submitter
+$EDF_EXPTORGANIZATION\t
+$EDF_EXPTADDRESS\t
+$EDF_EXPTSUBMITTER_EMAIL\t$submitter_email
+$EDF_PUBTITLE\t
+$EDF_PUBPAGES\t
+$EDF_PUBJOURNAL\t
+$EDF_PUBVOLUME\t
+$EDF_PUBISSUE\t
+$EDF_PUBYEAR\t
+$EDF_PUBAUTHORS\t
+$EDF_PUBMEDID\t
+EXPTSECTION
+
+ return $template;
+}
+
+sub _tab2mage_prot_section : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $experiment = $self->get_experiment();
+ my $is_affymetrix = $experiment->is_affymetrix();
+
+ my $template = <<"PROTHEADER";
+
+# The next section describes the protocols used in your experiment. Please select
+# your own accession codes (e.g. 'GROWTH', 'TREATMENT') for use within this spreadsheet,
+# or re-use ArrayExpress protocols by using the assigned ArrayExpress protocol
+# accession numbers. If you have already submitted protocols to ArrayExpress,
+# you can retrieve these using this web page:
+# http://www.ebi.ac.uk/aerep/
+#
+Protocol section
+$EDF_PROTOCOLACCESSION\t$EDF_PROTOCOLNAME\t$EDF_PROTOCOLTEXT
+GROWTH\tGrowth\t<your growth protocol text here>
+TREATMENT\tTreatment\t<your treatment protocol text here>
+EXTRACTION\tExtraction\t<your nucleic acid extraction protocol text here>
+LABELING\tLabeling\t<your labeling protocol text here>
+PROTHEADER
+
+ if ( $is_affymetrix ) {
+ $template .= <<"PROTAFFY";
+# For Affymetrix experiments you do not need to provide hybridization or scanning
+# protocols. If you are supplying CHP files, you do not need to enter a normalization
+# protocol.
+NORMALIZATION\tNormalization\t<your normalization protocol text here>
+PROTAFFY
+ }
+ else {
+ $template .= <<"PROTDYESWAP";
+HYBRIDIZATION\tHybridization\t<your hybridization protocol text here>
+SCANNING\tScanning\t<your scanning protocol text here>
+NORMALIZATION\tNormalization\t<your normalization protocol text here>
+PROTDYESWAP
+ }
+ $template .= <<"PROTEND";
+TRANSFORMATION\tTransformation\t<your final data transformation protocol text here>
+# Add any other protocols here...
+PROTEND
+
+ return $template;
+}
+
+sub _retrieve_factorvalues : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Retrieve the factor values for the experiment. FIXME consider
+ # caching these?
+
+ my $experiment = $self->get_experiment();
+
+ # FactorValues. The factors are derived only from the design. We
+ # go round the houses here because Class::DBI many-to-many sucks.
+ my @fv_categories;
+ my @design_categories = map { $_->categories() } $experiment->designs();
+ foreach my $cat ( @design_categories ) {
+ push(@fv_categories, $cat) if $cat->is_fv();
+ }
+ my %factorvalue = map { $_ => 1 }
+ map { $_->ontology_term() } @fv_categories;
+
+ return [ keys %factorvalue ];
+}
+
+sub _retrieve_bmcs : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Retrieve the BioMaterialCharacteristics for the
+ # experiment. FIXME consider caching these?
+
+ my $experiment = $self->get_experiment();
+
+ # BioMaterialCharacteristics: Designs.
+ my @all_categories;
+ push( @all_categories,
+ map { $_->categories() } $experiment->designs() );
+
+ # BioMaterialCharacteristics: Materials.
+ push( @all_categories,
+ map { $_->categories() } $experiment->materials() );
+
+ # BioMaterialCharacteristics: Organisms.
+ push( @all_categories,
+ map { $_->categories() }
+ map { $_->taxon_id() } $experiment->organisms() );
+
+ # Filter out the non-BMC categories.
+ my @bmc_categories;
+ foreach my $cat ( @all_categories ) {
+ push(@bmc_categories, $cat) if $cat->is_bmc();
+ }
+
+ # BioMaterialCharacteristics: Common categories
+ push(
+ @bmc_categories,
+ ArrayExpress::AutoSubmission::DB::Category->search(
+ is_common => 1,
+ )
+ );
+
+ # Unique the BMC categories.
+ my %bmc =
+ map { $_->ontology_term() => 1 } @bmc_categories;
+
+ return [ keys %bmc ];
+}
+
+sub _get_tab2mage_hyb_terms : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $experiment = $self->get_experiment();
+ my $is_affymetrix = $experiment->is_affymetrix();
+
+ my $factorvalues = $self->_retrieve_factorvalues();
+ my $characteristics = $self->_retrieve_bmcs();
+
+ # Custom help and example texts for BioMaterialCharacteristics.
+ my %bmc_help = (
+ 'Organism' => 'Sample latin species name',
+ 'Age' => 'Sample Age value, e.g. 20 years',
+ );
+ my $example_organism = ($experiment->organisms())[0];
+ my $example_material = ($experiment->materials())[0];
+ my %bmc_example = (
+ 'Organism' => $example_organism ? $example_organism->scientific_name() : q{},
+ );
+
+ # Here we define the basic structure of the template.
+ my @terms = (
+ { 'BioSource' => {
+ usage => $ALL,
+ help => 'Unique IDs for biosources (experimental starting materials)',
+ example => 'Cell culture 1',
+ } },
+ { 'BioSourceMaterial' => {
+ usage => $ALL,
+ help => 'MaterialType term from MGED ontology',
+ example => $example_material ? $example_material->ontology_value() : q{},
+ } },
+ (
+ map {
+ my $charcol = {
+ "BioMaterialCharacteristics[$_]" =>
+ {
+ usage => $ALL,
+ help => $bmc_help{$_} || "Sample $_ value",
+ example => $bmc_example{$_} || q{},
+ }
+ };
+
+ # Some Characteristics are measurements, not OE values. Return
+ # value determined here:
+ ($_ =~ m/\A Time|Age \z/ixms) ? ( $charcol,
+ {
+ 'BioMaterialCharacteristics[TimeUnit]' => {
+ usage => $ALL,
+ help => 'Time units for Characteristic',
+ example => 'hours',
+ }
+ }
+ )
+ : ($_ =~ m/\A Dose \z/ixms) ? ( $charcol,
+ {
+ 'BioMaterialCharacteristics[ConcentrationUnit]' => {
+ usage => $ALL,
+ help => 'Concentration units for Characteristic',
+ example => 'mg_per_ml',
+ }
+ }
+ )
+ : $charcol;
+ } @$characteristics
+ ),
+ { 'Protocol[grow]' => {
+ usage => $ALL,
+ help => 'Growth protocol, where applicable',
+ example => 'GROWTH',
+ } },
+ { 'Protocol[treatment]' => {
+ usage => $ALL,
+ help => 'Sample treatment protocol',
+ example => 'TREATMENT',
+ } },
+ { 'Sample' => {
+ usage => $DYESWAP,
+ help => 'Unique IDs for samples (after growth and treatment)',
+ example => 'Cell pellet 1',
+ } },
+ { 'Protocol[extraction]' => {
+ usage => $ALL,
+ help => 'RNA/DNA extraction',
+ example => 'EXTRACTION',
+ } },
+ { 'Extract' => {
+ usage => $DYESWAP,
+ help => 'Unique IDs for extracts (RNA or DNA)',
+ example => 'RNA Extract 1',
+ } },
+ { 'Protocol[immunoprecipitate]' => {
+ usage => $CHIPCHIP,
+ help => 'ChIP protocol',
+ } },
+ { 'Immunoprecipitate' => {
+ usage => $CHIPCHIP,
+ help => 'Unique IDs for IPs',
+ example => 'Immunoprecipitate 1'
+ } },
+ { 'Protocol[labeling]' => {
+ usage => $ALL,
+ help => 'RNA/DNA labeling',
+ example => 'LABELING',
+ } },
+ { 'LabeledExtract' => {
+ usage => $DYESWAP,
+ help => 'Unique IDs for labeled extracts',
+ example => 'Labeled Extract 1.' . $is_affymetrix ? 'biotin' : 'Cy3',
+ } },
+ { 'Dye' => {
+ usage => $DYESWAP,
+ help => 'Labeling dye, e.g. Cy3, biotin, 33P',
+ example => $is_affymetrix ? 'biotin' : 'Cy3',
+ } },
+ { 'Protocol[hybridization]' => {
+ usage => $DYESWAP,
+ help => 'Hyb protocol',
+ example => 'HYBRIDIZATION',
+ } },
+ { 'Hybridization' => {
+ usage => $ALL,
+ help => 'Unique IDs for hybs',
+ example => 'Hybridization 1',
+ } },
+ (
+ map {
+ # Some FVs are measurements, not OE values:
+ { ( ($_ =~ m/\A Time|Age|Dose \z/ixms)
+ ? "FactorValue[$_(units)]" : "FactorValue[$_]") =>
+ {
+ usage => $ALL,
+ help => $bmc_help{$_} || "Value for $_ variable",
+ example => $bmc_example{$_} || q{},
+ }
+ }
+ }
+ @$factorvalues
+ ),
+ { 'Protocol[scanning]' => {
+ usage => $DYESWAP,
+ help => 'Image acquisition',
+ example => 'SCANNING',
+ } },
+ { 'Protocol[normalization]' => {
+ usage => $DYESWAP,
+ help => 'Data normalization',
+ example => 'NORMALIZATION',
+ } },
+ { 'Array[accession]' => {
+ usage => $ALL,
+ help => 'ArrayExpress array accession, e.g. A-MEXP-27',
+ } },
+ { 'Array[serial]' => {
+ usage => $ALL,
+ help => 'Array serial number/chip lot',
+ } },
+ { 'File[raw]' => {
+ usage => $ALL,
+ help => 'Unprocessed data file, e.g. CEL, GPR',
+ } },
+ { 'File[normalized]' => {
+ usage => $ALL,
+ help => 'Normalized data',
+ } },
+ { 'File[exp]' => {
+ usage => $AFFY,
+ help => 'EXP file where available',
+ } },
+ { 'File[cdf]' => {
+ usage => $AFFY,
+ help => 'CDF library file (do not upload CDF unless custom array used)',
+ } },
+ { 'Protocol[transformation]' => {
+ usage => $ALL,
+ help => 'Protocol producing final data matrix file',
+ example => 'TRANSFORMATION',
+ } },
+ { 'File[transformed]' => {
+ usage => $ALL,
+ help => 'Final data matrix file(s)',
+ } },
+
+ );
+
+ return \@terms;
+}
+
+sub _table_from_terms : PRIVATE {
+
+ my ( $self, $terms ) = @_;
+
+ my $experiment = $self->get_experiment();
+ my $is_affymetrix = $experiment->is_affymetrix();
+
+ my %designs_include =
+ map { $_->ontology_value() => 1 } $experiment->designs();
+ my (@headings, @explanations, @examples);
+
+ foreach my $term ( @{ $terms } ) {
+ while ( my ( $name, $termhash ) = each %$term ) {
+ if (
+ ( $termhash->{'usage'} & $ALL )
+ || ( $termhash->{'usage'} & $DYESWAP && !$is_affymetrix )
+ || ( $termhash->{'usage'} & $AFFY && $is_affymetrix )
+ || ( $termhash->{'usage'} & $CHIPCHIP
+ && $designs_include{$OE_VAL_CHIP_CHIP} )
+ )
+ {
+ push( @headings, $name );
+ push( @explanations, "# $termhash->{'help'}" || q{} );
+ push( @examples, $termhash->{'example'} || q{} );
+ }
+ }
+ }
+
+ my $template = join( "\t", @headings ) . "\n"
+ . join( "\t", @explanations ) . "\n"
+ . join( "\t", @examples ) . "\n";
+
+ return $template;
+}
+
+sub _tab2mage_hyb_section : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $template = <<"HYBHEADER";
+
+# The Hybridization section contains all the information linking your samples to your
+# data files. Please feel free to add as many new BioMaterialCharacteristics[] or
+# FactorValue[] columns as necessary to completely describe your experiment. See this
+# web page for help with supported data file formats:
+# http://tab2mage.sourceforge.net/docs/datafiles.html
+#
+# The first line below has been filled in with example terms, where possible:
+Hybridization section
+HYBHEADER
+
+ # Get the tab2mage-specific hyb section template data.
+ my $terms = $self->_get_tab2mage_hyb_terms();
+
+ # Generate the table.
+ $template .= $self->_table_from_terms( $terms );
+
+ return $template;
+}
+
+sub tab2mage {
+
+ my ( $self ) = @_;
+
+ # Build the spreadsheet.
+ # Also incorporate simplified Affy form.
+ # FIXME to use the terms from MAGE::Definitions.
+ my $template = $self->_tab2mage_expt_section()
+ . $self->_tab2mage_prot_section()
+ . $self->_tab2mage_hyb_section();
+
+ # Return the spreadsheet as a string.
+ return $template;
+}
+
+sub _stylize_factor_name : PRIVATE {
+
+ my ( $self, $category ) = @_;
+
+ return uc($category);
+}
+
+sub _magetab_idf_factorvalue : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $factorvalues = $self->_retrieve_factorvalues();
+
+ my ( @factornames, @factortypes, @factortermsources );
+
+ # Note that the factorname convention below must be matched in the SDRF.
+ foreach my $category ( @$factorvalues ) {
+ push @factornames, $self->_stylize_factor_name($category);
+
+ # Switch from CamelCase category to underscore_word value
+ push @factortypes, decamelize($category);
+ push @factortermsources, $MGED_ONTOLOGY;
+ }
+ my $template = join("\t", 'Experimental Factor Name', @factornames) . "\n"
+ . join("\t", 'Experimental Factor Type', @factortypes) . "\n";
+
+ return $template;
+}
+
+sub _magetab_idf_protocols : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $experiment = $self->get_experiment();
+ my $is_affymetrix = $experiment->is_affymetrix();
+
+ my @protocolnames = qw(
+ GROWTH
+ TREATMENT
+ EXTRACTION
+ LABELING
+ );
+ my @protocoltypes = qw(
+ grow
+ specified_biomaterial_action
+ nucleic_acid_extraction
+ labeling
+ );
+ my @protocoldescs = (
+ q{<your growth protocol text here>},
+ q{<your treatment protocol text here>},
+ q{<your nucleic acid extraction protocol text here>},
+ q{<your labeling protocol text here>},
+ );
+ my @protocoltermsources = (
+ $MGED_ONTOLOGY,
+ $MGED_ONTOLOGY,
+ $MGED_ONTOLOGY,
+ $MGED_ONTOLOGY,
+ );
+
+ # These will usually be automatically derived from CEL and EXP for
+ # affy subs:
+
+ unless ($is_affymetrix) {
+ push @protocolnames, qw(HYBRIDIZATION SCANNING);
+ push @protocoltypes, qw(hybridization image_acquisition);
+ push @protocoldescs, (
+ q{<your hybridization protocol text here>},
+ q{<your scanning protocol text here>},
+ );
+ push @protocoltermsources, (
+ $MGED_ONTOLOGY,
+ $MGED_ONTOLOGY,
+ );
+ }
+
+ push @protocolnames, qw(NORMALIZATION TRANSFORMATION);
+ push @protocoltypes, qw(bioassay_data_transformation bioassay_data_transformation);
+ push @protocoldescs, (
+ q{<your normalization protocol text here>},
+ q{<your transformation protocol text here>},
+ );
+ push @protocoltermsources, (
+ q{},
+ q{},
+ );
+
+ my $template = join("\t", 'Protocol Name', @protocolnames ) . "\n"
+ . join("\t", 'Protocol Type', @protocoltypes ) . "\n"
+ . join("\t", 'Protocol Description', @protocoldescs ) . "\n";
+
+ return $template;
+}
+
+sub _magetab_idf : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $experiment = $self->get_experiment();
+
+ my $experiment_name = $experiment->name();
+ my $experiment_type = $experiment->experiment_type();
+ my $submitter = $experiment->user_id()->name();
+ my $submitter_email = $experiment->user_id()->email();
+
+ my $url = $self->get_url() || q{};
+ my $date = $self->get_date() || q{};
+
+ my $design_string =
+ join( "\t", map { $_->ontology_value() } $experiment->designs() );
+
+ my $template = <<"EXPTSECTIONSTART";
+#
+# $experiment_type Submission '$experiment_name', generated at $date
+#
+# Please save this page to disk, and edit it using any spreadsheet program (e.g.,
+# Microsoft Excel, OpenOffice.org Calc). This file can be imported into either of
+# these applications as a tab-delimited text file. It is highly recommended that
+# you set the cell format to plain text throughout the spreadsheet, to avoid
+# 'helpful' changes made by the spreadsheet application. Once you have completed
+# this spreadsheet please upload it using the submissions webpage alongside your
+# data files:
+# $url
+#
+# Please see http://tab2mage.sourceforge.net/docs/magetab_subs.html for
+# information on filling in the IDF and SDRF sections below.
+#
+# All lines beginning with the '#' character are treated as comments and ignored.
+
+
+# This section contains the top-level information for your experiment.
+Investigation Title\t$experiment_name
+Experiment Description\t
+Experimental Design\t$design_string
+
+# Please create as many Experimental Factors here as you need to
+# describe the variables investigated by your experiment.
+EXPTSECTIONSTART
+
+ $template .= $self->_magetab_idf_factorvalue();
+
+ $template .= <<"EXPTSECTIONMID";
+
+# Quality Control Type examples: dye_swap_quality_control, biological_replicate, technical_replicate
+Quality Control Type\t
+
+# Dates should be entered in the form YYYY-MM-DD. If you are using MS Excel,
+# it is recommended that you set your spreadsheet to 'text' format throughout
+# to help avoid any unwanted changes made by Excel.
+Public Release Date\t
+
+# Please list contact details in columns, below:
+Person Last Name\t$submitter\t
+Person First Name\t\t
+Person Mid Initials\t\t
+Person Email\t$submitter_email\t
+Person Phone\t\t
+Person Address\t\t
+Person Affiliation\t\t
+Person Roles\tsubmitter\t
+
+# Please list all publications associated with this submission, in columns:
+PubMed ID\t
+Publication Author List\t
+Publication Title\t
+Publication Status\t
+
+# The next section describes the protocols used in your
+# experiment. Please select your own Protocol Names (e.g. 'GROWTH',
+# 'TREATMENT') for use within this spreadsheet, or re-use ArrayExpress
+# protocols by using the assigned ArrayExpress protocol accession
+# numbers in your SDRF. If you have already submitted protocols to
+# ArrayExpress, you can retrieve these using this web page:
+# http://www.ebi.ac.uk/aerep/
+EXPTSECTIONMID
+
+ $template .= $self->_magetab_idf_protocols();
+
+ return $template;
+}
+
+sub _magetab_sdrf : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $experiment = $self->get_experiment();
+
+ my $template = <<"HYBHEADER";
+# The SDRF section contains all the information linking your samples
+# to your data files. Please feel free to add as many new
+# Characteristics[] or FactorValue[] columns as necessary to
+# completely describe your experiment. See this web page for help with
+# supported data file formats:
+# http://tab2mage.sourceforge.net/docs/datafiles.html
+#
+# The first line below has been filled in with example terms, where possible:
+HYBHEADER
+
+ # Get the magetab-specific hyb section template data.
+ my $terms = $self->_get_magetab_hyb_terms();
+
+ # Generate the table.
+ $template .= $self->_table_from_terms( $terms );
+
+ return $template;
+}
+
+sub _get_magetab_hyb_terms : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $experiment = $self->get_experiment();
+ my $is_affymetrix = $experiment->is_affymetrix();
+
+ my $factorvalues = $self->_retrieve_factorvalues();
+ my $characteristics = $self->_retrieve_bmcs();
+
+ # Custom help and example texts for BioMaterialCharacteristics.
+ my %bmc_help = (
+ 'Organism' => 'Sample latin species name',
+ 'Age' => 'Sample Age value, e.g. 20 years',
+ );
+ my $example_organism = ($experiment->organisms())[0];
+ my $example_material = ($experiment->materials())[0];
+ my %bmc_example = (
+ 'Organism' => $example_organism ? $example_organism->scientific_name() : q{},
+ );
+
+ # Here we define the basic structure of the template.
+ my @terms = (
+ { 'Source Name' => {
+ usage => $ALL,
+ help => 'Unique IDs for biosources (experimental starting materials)',
+ example => 'Cell culture 1',
+ } },
+ { 'Material Type' => {
+ usage => $ALL,
+ help => 'MaterialType term from MGED ontology',
+ example => $example_material ? $example_material->ontology_value() : q{},
+ } },
+ (
+ map {
+ my $charcol = {
+ "Characteristics[$_]" =>
+ {
+ usage => $ALL,
+ help => $bmc_help{$_} || "Sample $_ value",
+ example => $bmc_example{$_} || q{},
+ }
+ };
+
+ # Some Characteristics are measurements, not OE values. Return
+ # value determined here:
+ ($_ =~ m/\A Time|Age \z/ixms) ? ( $charcol,
+ {
+ 'Unit[TimeUnit]' => {
+ usage => $ALL,
+ help => 'Time units for Characteristic',
+ example => 'hours',
+ }
+ }
+ )
+ : ($_ =~ m/\A Dose \z/ixms) ? ( $charcol,
+ {
+ 'Unit[ConcentrationUnit]' => {
+ usage => $ALL,
+ help => 'Concentration units for Characteristic',
+ example => 'mg_per_ml',
+ }
+ }
+ )
+ : $charcol;
+ } @$characteristics
+ ),
+ { 'Protocol REF' => {
+ usage => $ALL,
+ help => 'Growth protocol, where applicable',
+ example => 'GROWTH',
+ } },
+ { 'Protocol REF' => {
+ usage => $ALL,
+ help => 'Sample treatment protocol',
+ example => 'TREATMENT',
+ } },
+ { 'Sample Name' => {
+ usage => $DYESWAP,
+ help => 'Unique IDs for samples (after growth and treatment)',
+ example => 'Cell pellet 1',
+ } },
+ { 'Protocol REF' => {
+ usage => $ALL,
+ help => 'RNA/DNA extraction',
+ example => 'EXTRACTION',
+ } },
+ { 'Extract Name' => { # all dye-swap except ChIP-chip.
+ usage => ($DYESWAP & ~ $CHIPCHIP),
+ help => 'Unique IDs for extracts (RNA or DNA)',
+ example => 'RNA Extract 1',
+ } },
+ { 'Sample Name' => {
+ usage => $CHIPCHIP,
+ help => 'Unique IDs for extracts (RNA or DNA)',
+ example => 'RNA Extract 1',
+ } },
+ { 'Protocol REF' => {
+ usage => $CHIPCHIP,
+ help => 'ChIP protocol',
+ } },
+ { 'Extract Name' => {
+ usage => $CHIPCHIP,
+ help => 'Unique IDs for IPs',
+ example => 'Immunoprecipitate 1'
+ } },
+ { 'Protocol REF' => {
+ usage => $ALL,
+ help => 'RNA/DNA labeling',
+ example => 'LABELING',
+ } },
+ { 'Labeled Extract Name' => {
+ usage => $ALL,
+ help => 'Unique IDs for labeled extracts',
+ example => 'Labeled Extract 1.' . ($is_affymetrix ? 'biotin' : 'Cy3'),
+ } },
+ { 'Label' => {
+ usage => $ALL,
+ help => 'Labeling dye, e.g. Cy3, biotin, 33P',
+ example => ($is_affymetrix ? 'biotin' : 'Cy3'),
+ } },
+ { 'Protocol REF' => {
+ usage => ($DYESWAP | $CHIPCHIP),
+ help => 'Hyb protocol',
+ example => 'HYBRIDIZATION',
+ } },
+ { 'Hybridization Name' => {
+ usage => $ALL,
+ help => 'Unique IDs for hybs',
+ example => 'Hybridization 1',
+ } },
+ { 'Array Design REF' => {
+ usage => $ALL,
+ help => 'ArrayExpress array accession, e.g. A-MEXP-27',
+ } },
+ { 'Comment[Array serial number]' => {
+ usage => $ALL,
+ help => 'Array serial number/chip lot',
+ } },
+ { 'Protocol REF' => {
+ usage => ($DYESWAP | $CHIPCHIP),
+ help => 'Image acquisition',
+ example => 'SCANNING',
+ } },
+ { 'Scan Name' => {
+ usage => ($DYESWAP | $CHIPCHIP),
+ help => 'Unique IDs for scans',
+ example => 'Scan 1',
+ } },
+ { 'Array Data File' => {
+ usage => $ALL,
+ help => 'Unprocessed data file, e.g. CEL, GPR',
+ } },
+ { 'Comment[EXP]' => {
+ usage => $AFFY,
+ help => 'EXP file where available',
+ } },
+ { 'Protocol REF' => {
+ usage => $ALL,
+ help => 'Data normalization',
+ example => 'NORMALIZATION',
+ } },
+ { 'Derived Array Data File' => {
+ usage => $ALL,
+ help => 'Normalized data',
+ } },
+ { 'Protocol REF' => {
+ usage => $ALL,
+ help => 'Protocol producing final data matrix file',
+ example => 'TRANSFORMATION',
+ } },
+ { 'Derived Array Data Matrix File' => {
+ usage => $ALL,
+ help => 'Final data matrix file(s)',
+ } },
+ { 'Comment[CDF]' => {
+ usage => $AFFY,
+ help => 'CDF library file (do not upload CDF unless custom array used)',
+ } },
+ (
+ map {
+
+ # $fvname convention must match that in the IDF.
+ my $fvname = $self->_stylize_factor_name($_);
+ my $fvcol = {
+ "Factor Value[$fvname]" => {
+ usage => $ALL,
+ help => $bmc_help{$_} || "Value for $fvname Experimental Factor",
+ example => $bmc_example{$_} || q{},
+ }
+ };
+
+ # Some FVs are measurements, not OE values. Return
+ # value determined here:
+ ($_ =~ m/\A Time|Age \z/ixms) ? ( $fvcol,
+ {
+ 'Unit[TimeUnit]' => {
+ usage => $ALL,
+ help => 'Time units for Factor Value',
+ example => 'hours',
+ }
+ }
+ )
+ : ($_ =~ m/\A Dose \z/ixms) ? ( $fvcol,
+ {
+ 'Unit[ConcentrationUnit]' => {
+ usage => $ALL,
+ help => 'Concentration units for Factor Value',
+ example => 'mg_per_ml',
+ }
+ }
+ )
+ : $fvcol;
+ }
+ @$factorvalues
+ ),
+
+ );
+
+ return \@terms;
+}
+
+sub magetab {
+
+ my ( $self ) = @_;
+
+ my $template = "#####################################################\n[IDF]\n"
+ . $self->_magetab_idf()
+ . "\n#####################################################\n[SDRF]\n"
+ . $self->_magetab_sdrf();
+
+ return $template;
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Spreadsheet.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::Spreadsheet - A Tab2MAGE/MAGE-TAB
+document templating class.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::Spreadsheet;
+ my $ss = ArrayExpress::AutoSubmission::Spreadsheet->new({
+ experiment => $expt,
+ url => $self->query()->url(),
+ date => date_now(),
+ });
+
+ $ss_content = $spreadsheet->magetab();
+
+=head1 DESCRIPTION
+
+This module provides Tab2MAGE and MAGE-TAB document templating
+functions. The functions take Class::DBI experiment objects from the
+tracking database and generate the appropriate output strings which
+can then be printed to file.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new>
+
+Object constructor, taking the following arguments:
+
+=over 4
+
+=item C<experiment>
+
+The Class::DBI experiment object to use in template generation.
+
+=item C<url>
+
+The URL of the page to which completed templates should be submitted
+(this is embedded in the template for convenience).
+
+=item C<date>
+
+The date of template generation.
+
+=back
+
+=item C<magetab>
+
+Return a string representing the MAGE-TAB template for this experiment.
+
+=item C<tab2mage>
+
+Return a string representing the Tab2MAGE template for this experiment.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
+
diff --git a/lib/ArrayExpress/AutoSubmission/WebForm.pm b/lib/ArrayExpress/AutoSubmission/WebForm.pm
new file mode 100644
index 0000000..c779aff
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/WebForm.pm
@@ -0,0 +1,1601 @@
+#!/usr/bin/env perl
+#
+# Module used to generate and control web submission forms
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: WebForm.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::AutoSubmission::WebForm;
+
+use strict;
+use warnings;
+
+use base 'CGI::Application';
+use CGI::Application::Plugin::Authentication;
+use CGI::Application::Plugin::Session;
+use CGI::Application::Plugin::ValidateRM qw(check_rm);
+use CGI::Upload;
+use Data::FormValidator::Constraints qw(:closures);
+use HTTP::BrowserDetect;
+use MIME::Lite;
+
+require ArrayExpress::AutoSubmission::DB::User;
+require ArrayExpress::AutoSubmission::DB::Experiment;
+require ArrayExpress::AutoSubmission::DB::Design;
+require ArrayExpress::AutoSubmission::DB::Material;
+require ArrayExpress::AutoSubmission::DB::Organism;
+require ArrayExpress::AutoSubmission::DB::Role;
+require ArrayExpress::AutoSubmission::DB::RoleUser;
+require ArrayExpress::AutoSubmission::DB::DataFile;
+require ArrayExpress::AutoSubmission::DB::Spreadsheet;
+
+require ArrayExpress::AutoSubmission::Spreadsheet;
+use ArrayExpress::Curator::Common qw(mage_date date_now untaint);
+use ArrayExpress::Curator::Config qw($CONFIG);
+use File::Spec;
+use File::Path;
+use File::Copy;
+use File::Basename;
+use IO::File;
+use Readonly;
+use Carp;
+
+# The DevPopup plugin can be commented out in production.
+#use CGI::Application::Plugin::DevPopup;
+#use CGI::Application::Plugin::HtmlTidy;
+
+#################
+# Runmode names #
+#################
+
+# N.B. these terms are used in the web interface.
+Readonly my $RM_INTRO => 'Introduction';
+Readonly my $RM_SIGNUP => 'New submitter';
+Readonly my $RM_CREATEUSER => 'Create user';
+Readonly my $RM_LOGIN => 'Log in';
+Readonly my $RM_LOGOUT => 'Log out';
+Readonly my $RM_CHOOSE => 'Experiment list';
+Readonly my $RM_CREATE => 'Create new';
+Readonly my $RM_INSERT => 'Save experiment';
+Readonly my $RM_EDIT => 'Edit';
+Readonly my $RM_UPDATE => 'Update';
+Readonly my $RM_DELETE => 'Delete';
+Readonly my $RM_TEMPLATE => 'Generate template';
+Readonly my $RM_UPLOAD => 'Upload files';
+Readonly my $RM_FILE_UPLOAD => 'Upload';
+Readonly my $RM_SUBMIT => 'Submit experiment';
+Readonly my $RM_ERROR => 'Error';
+
+Readonly my $ERROR_FORMAT => q{<span style="color:red;font-weight:bold" class="dfv_errors">- %s</span>};
+
+#########################
+# Package configuration #
+#########################
+
+__PACKAGE__->authen->config(
+ DRIVER => [
+ 'CDBI',
+ CLASS => 'ArrayExpress::AutoSubmission::DB::User',
+
+ # Use MD5_hex:password for greater security (this would also
+ # need fixing in the ruby code).
+ FIELD_METHODS => [qw(login password)],
+ ],
+ CREDENTIALS => [qw(auth_username auth_password)],
+ LOGIN_RUNMODE => $RM_LOGIN,
+ POST_LOGIN_RUNMODE => $RM_CHOOSE,
+ POST_LOGIN_CALLBACK => \&update_user_access,
+ LOGOUT_RUNMODE => $RM_LOGOUT,
+ LOGIN_SESSION_TIMEOUT => {
+ IDLE_FOR => '60m',
+ EVERY => '1d'
+ },
+);
+
+__PACKAGE__->authen->protected_runmodes(
+ qw(
+ choose
+ create
+ insert
+ edit
+ update
+ delete_expt
+ template
+ upload
+ file_upload
+ )
+);
+
+sub setup {
+ my $self = shift;
+ $self->start_mode($RM_INTRO);
+ $self->error_mode('error');
+ $self->run_modes(
+ $RM_INTRO => 'intro',
+ $RM_SIGNUP => 'signup',
+ $RM_CREATEUSER => 'create_user',
+ $RM_LOGIN => 'login',
+ $RM_LOGOUT => 'logout',
+ $RM_CHOOSE => 'choose',
+ $RM_CREATE => 'create',
+ $RM_INSERT => 'insert',
+ $RM_EDIT => 'edit',
+ $RM_UPDATE => 'update',
+ $RM_DELETE => 'delete_expt',
+ $RM_TEMPLATE => 'spreadsheet_template',
+ $RM_UPLOAD => 'upload',
+ $RM_FILE_UPLOAD => 'file_upload',
+ $RM_SUBMIT => 'submit',
+ $RM_ERROR => 'error',
+ );
+ $self->mode_param('rm');
+
+ # Templates are kept in a subdirectory within the package, but can
+ # be overridden using the 'template_path' param..
+ my @module_dir_array = File::Spec->splitpath(__FILE__);
+ my $template_path =
+ File::Spec->catpath( @module_dir_array[ 0, 1 ], 'templates' );
+ $self->tmpl_path($self->param('template_path') || $template_path);
+
+ return;
+}
+
+##################
+# Shared methods #
+##################
+
+sub cgiapp_init {
+
+ my $self = shift;
+
+ # Data::FormValidator defaults.
+ $self->param('dfv_defaults') ||
+ $self->param('dfv_defaults', {
+ msgs => {
+ any_errors => 'some_errors',
+ prefix => 'err_',
+ invalid_separator => ' <br/> ',
+ missing => q{Missing value(s)},
+ invalid => q{Invalid value(s)},
+ format => $ERROR_FORMAT
+ }
+ });
+
+ return;
+}
+
+sub cgiapp_postrun {
+
+ my ($self, $contentref) = @_;
+
+# Uncomment this for production code.
+# $self->htmltidy_clean($contentref);
+
+ return;
+}
+
+sub _shared_html_params {
+ my $self = shift;
+
+ # Set the HTML::Template params sent to all runmodes.
+ return {
+ CGI_BASE => $self->param('cgi_base') || '/tab2mage.cgi',
+ STYLESHEET => $self->param('stylesheet') || undef,
+ SIDEBAR_ICONS => $self->param('sidebar_icons'),
+ SIDEBAR_LINKS => $self->param('sidebar_links'),
+ RM => $self->mode_param(),
+ AUTHEN => $self->authen->is_authenticated() || undef,
+ EXPT_TYPE => $self->param('experiment_type') || undef,
+ LOGOUT => $RM_LOGOUT,
+ CURATOR_EMAIL => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+ };
+}
+
+sub _load_my_tmpl {
+ my ($self, $tmpl_file, $errors, $params) = @_;
+
+ # Convenience method for generating templates.
+
+ # Filter to allow for dynamic TMPL_INCLUDEs.
+ my $header = $self->param('header_html_template') || '_head.html';
+ my $footer = $self->param('footer_html_template') || '_foot.html';
+ my $filter = sub {
+ my $text_ref = shift;
+ $$text_ref =~ s{<(!--)? *TMPL_INCLUDE *HEADER *(--)?>}
+ {<!-- TMPL_INCLUDE NAME="$header" -->}i;
+ $$text_ref =~ s{<(!--)? *TMPL_INCLUDE *FOOTER *(--)?>}
+ {<!-- TMPL_INCLUDE NAME="$footer" -->}i;
+ };
+
+ # Create the actual HTML::Template object.
+ my $template = $self->load_tmpl(
+ $tmpl_file,
+ filter => $filter,
+ die_on_bad_params => 1,
+ );
+ $template->param($errors) if $errors;
+ $template->param(
+ %{$self->_shared_html_params()},
+ %{$params},
+ );
+
+ return $template;
+}
+
+sub _annotation_labels {
+ my ($self, $experiment) = @_;
+
+ # Get the design, material and organism info from the database.
+
+ # Designs:
+ my %selected_design = $experiment
+ ? map { $_->id() => 1 } $experiment->designs()
+ : ();
+ my @biol_design_labels = map {
+ { name => $_->display_label(),
+ id => $_->id(),
+ selected => $selected_design{$_->id()}}
+ } ArrayExpress::AutoSubmission::DB::Design->search(
+ design_type => 'biological',
+ is_deleted => 0,
+ {order_by => 'display_label'},
+ );
+ my @meth_design_labels = map {
+ { name => $_->display_label(),
+ id => $_->id(),
+ selected => $selected_design{$_->id()}}
+ } ArrayExpress::AutoSubmission::DB::Design->search(
+ design_type => 'methodological',
+ is_deleted => 0,
+ {order_by => 'display_label'},
+ );
+ my @tech_design_labels = map {
+ { name => $_->display_label(),
+ id => $_->id(),
+ selected => $selected_design{$_->id()}}
+ } ArrayExpress::AutoSubmission::DB::Design->search(
+ design_type => 'technological',
+ is_deleted => 0,
+ {order_by => 'display_label'},
+ );
+
+ # Materials:
+ my %selected_material = $experiment
+ ? map { $_->id() => 1 } $experiment->materials()
+ : ();
+ my @material_labels = map {
+ { name => $_->display_label(),
+ id => $_->id(),
+ selected => $selected_material{$_->id()}}
+ } ArrayExpress::AutoSubmission::DB::Material->search_like(
+ id => '%',
+ is_deleted => 0,
+ {order_by => 'display_label'},
+ );
+
+ # Organisms (only list those with taxon ids):
+ my %selected_organism = $experiment
+ ? map { $_->id() => 1 } $experiment->organisms()
+ : ();
+ my @organism_labels = map {
+ { name => $_->scientific_name(),
+ id => $_->id(),
+ selected => $selected_organism{$_->id()}}
+ } ArrayExpress::AutoSubmission::DB::Organism->search_like(
+ id => '%',
+ taxon_id => '_%',
+ is_deleted => 0,
+ {order_by => 'scientific_name'},
+ );
+
+ return {
+ BIOL_DESIGNS => \@biol_design_labels,
+ METH_DESIGNS => \@meth_design_labels,
+ TECH_DESIGNS => \@tech_design_labels,
+ MATERIALS => \@material_labels,
+ ORGANISMS => \@organism_labels,
+ };
+}
+
+sub _is_not_duplicate_filename {
+ my ($name, $experiment_id) = @_;
+
+ # Ensure name is stringified - this sub is passed CGI upload
+ # params; they do not stringify correctly with all versions of the
+ # underlying modules.
+ $name = sprintf("%s", $name);
+
+ # Check the database for entries, return false if found.
+ my $spreadsheet
+ = ArrayExpress::AutoSubmission::DB::Spreadsheet->retrieve(
+ name => $name,
+ experiment_id => $experiment_id,
+ is_deleted => 0,
+ );
+ my $data_file
+ = ArrayExpress::AutoSubmission::DB::DataFile->retrieve(
+ name => $name,
+ experiment_id => $experiment_id,
+ is_deleted => 0,
+ );
+ if ($spreadsheet || $data_file) {
+ return 0;
+ }
+
+ # Check the filesystem, return true if found, false otherwise.
+ my $experiment
+ = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+ id => $experiment_id,
+ );
+ my $subs_dir = $experiment->filesystem_directory();
+ my $upload_file = untaint(basename( $name ));
+ my $filesys_test = -e File::Spec->catfile($subs_dir, $upload_file);
+
+ return $filesys_test ? 0 : 1;
+}
+
+################################
+# Data::FormValidater profiles #
+################################
+
+sub _user_profile {
+ my $self = shift;
+
+ return {
+ required => [qw(username password realname email)],
+ optional => [qw()],
+ dependency_groups => {password_group => [qw/password password2/]},
+ filters => ['trim'],
+ constraints => {
+ email => 'email',
+ username => [
+ {
+ constraint => qr/^\w{6,10}$/,
+ },
+ {
+ name => 'duplicate_username',
+ constraint => sub {
+ my $user
+ = ArrayExpress::AutoSubmission::DB::User->retrieve(
+ login => shift,
+ );
+ return $user ? 0 : 1;
+ },
+ },
+ ],
+ password => [
+ {
+ constraint => qr/^\w{6,10}$/,
+ },
+ {
+ name => 'password_mismatch',
+ constraint => sub {
+ my ($password, $password2) = @_;
+ return ($password eq $password2);
+ },
+ params => [qw(password password2)],
+ },
+ ],
+ },
+ msgs => {
+ constraints => {
+ 'duplicate_username'
+ => q{Username is already taken},
+ 'password_mismatch'
+ => q{Passwords do not match},
+ },
+ },
+ };
+}
+
+sub _choose_profile {
+ my $self = shift;
+
+ return {
+ required => [qw(experiment)],
+ msgs => {
+ missing => q{No experiment selected.},
+ }
+ };
+}
+
+sub _delete_profile {
+ my $self = shift;
+
+ return {
+ required => [qw(experiment)],
+ constraints => {
+ experiment => [
+ {
+ name => 'files_exist',
+ constraint => sub {
+ my $experiment
+ = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(shift);
+ my $files = scalar($experiment->data_files(is_deleted => 0))
+ + scalar($experiment->spreadsheets(is_deleted => 0));
+ return $files ? 0 : 1;
+ },
+ },
+ ],
+ },
+ msgs => {
+ constraints => {
+ 'files_exist'
+ => q{Submission includes uploaded files and cannot be deleted.},
+ },
+ missing => q{No experiment selected.},
+ },
+ };
+}
+
+sub _edit_profile {
+ my $self = shift;
+
+ return {
+ required => [qw(experiment_name)],
+ filters => ['trim'],
+ dependency_groups => {annotation_group => [qw/biol_designs meth_designs tech_designs materials organisms/]},
+ constraints => {
+ experiment_name => [
+ {
+ name => 'duplicate_submission',
+ constraint => sub {
+ my ($name, $id) = @_;
+ my $experiment
+ = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+ name => $name,
+ is_deleted => 0,
+ );
+ # No experiment found, or experiment has our
+ # id (i.e. this is an update).
+ return (!$experiment || $experiment->id() == $id);
+ },
+ params => [qw(experiment_name experiment)],
+ },
+ ],
+ },
+ msgs => {
+ constraints => {
+ 'duplicate_submission'
+ => q{Experiment name is already taken},
+ },
+ },
+ };
+}
+
+sub _upload_profile {
+ my $self = shift;
+
+ return {
+ require_some => {
+ upload_files => [ 1, qw(data_file spreadsheet) ],
+ },
+ };
+}
+
+sub _submit_profile {
+
+ return {
+ required => [qw(experiment)],
+ constraints => {
+ experiment => [
+ {
+ name => 'missing_uploads',
+ constraint => sub {
+ my $id = shift;
+ my $experiment
+ = ArrayExpress::AutoSubmission::DB::Experiment->retrieve($id)
+ or croak("Internal error: No experiment found in database");
+ my $allfiles = scalar($experiment->data_files(is_deleted => 0))
+ && scalar($experiment->spreadsheets(is_deleted => 0));
+ return $allfiles ? 1 : 0;
+ },
+ params => [qw(experiment)],
+ },
+ ],
+ },
+ msgs => {
+ constraints => {
+ 'missing_uploads' => q{Some file(s) have not been uploaded yet},
+ },
+ missing => q{Internal error: No experiment parameter defined},
+ },
+ };
+}
+
+#############################
+# Other convenience methods #
+#############################
+
+sub update_user_access {
+ my $self = shift;
+
+ return unless ($self->authen->is_authenticated());
+
+ my $user
+ = ArrayExpress::AutoSubmission::DB::User->retrieve(
+ login => $self->authen->username(),
+ );
+
+ $user->set(
+ access => mage_date($self->authen->last_access()),
+ );
+
+ $user->update();
+
+ return;
+}
+
+sub get_secure_experiment {
+ my ($self, $id) = @_;
+
+ my $experiment =
+ ArrayExpress::AutoSubmission::DB::Experiment->retrieve($id);
+
+ # Double-check that the experiment does actually belong to the user.
+ unless ( $experiment->user_id->login() eq $self->authen->username() ) {
+ return (undef, $self->error('Attempt to access forbidden page.'));
+ }
+
+ return $experiment;
+};
+
+sub email_confirm_signup {
+
+ # $user is a Class::DBI object.
+ my ($self, $user) = @_;
+
+ # No point trying to send to a null address.
+ return unless $user->email();
+
+ my $template = $self->load_tmpl('email_signup_confirmation.txt');
+ $template->param(
+ TYPE => $self->param('experiment_type'),
+ USERNAME => $user->login(),
+ PASSWORD => $user->password(),
+ REALNAME => $user->name(),
+ URL => $self->query()->url(),
+ );
+
+ my $mail = MIME::Lite->new(
+ From => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+ To => $user->email(),
+ Subject => 'Welcome to ArrayExpress '
+ . $self->param('experiment_type') . ' Submissions',
+ Encoding => 'quoted-printable',
+ Data => $template->output,
+ Type => 'text/plain',
+ );
+
+ # Must use SMTP if running under CGI::Application::Server
+ if ( my $server = $CONFIG->get_AUTOSUBS_SMTP_SERVER() ) {
+ $mail->send('smtp', $server)
+ or die("Error sending signup email: $!");
+ }
+ else {
+ $mail->send()
+ or die("Error sending signup email: $!");
+ }
+
+ return;
+}
+
+sub email_confirm_submission {
+ my ($self, $experiment) = @_;
+
+ # No point trying to send to a null address.
+ return unless $experiment->user_id()->email();
+
+ # $experiment is a Class::DBI object.
+ my $template = $self->load_tmpl('email_subs_confirmation.txt');
+ $template->param(
+ TYPE => $self->param('experiment_type'),
+ EXPTNAME => $experiment->name(),
+ REALNAME => $experiment->user_id()->name(),
+ SUBSDATE => $experiment->date_submitted(),
+ CURATOR_EMAIL => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+ );
+
+ my $submitter_mail = MIME::Lite->new(
+ From => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+ To => $experiment->user_id()->email(),
+ Subject => 'Confirming '
+ . $self->param('experiment_type')
+ . ' submission: '
+ . $experiment->name(),
+ Encoding => 'quoted-printable',
+ Data => $template->output,
+ Type => 'text/plain',
+ );
+
+ my $curator_tmpl = $self->load_tmpl('subs_notify_curator.txt');
+ $curator_tmpl->param(
+ TYPE => $self->param('experiment_type'),
+ EXPTNAME => $experiment->name(),
+ EXPTDIR => $experiment->filesystem_directory(),
+ REALNAME => $experiment->user_id()->name(),
+ LOGIN => $experiment->user_id()->login(),
+ SUBSDATE => $experiment->date_submitted(),
+ SUBMTR_EMAIL => $experiment->user_id()->email(),
+ );
+
+ # Check whether this is a resubmission, and set the subject line
+ # appropriately.
+ my $subject;
+ if ( $experiment->num_submissions() ) {
+ $subject = sprintf(
+ "%s RESUBMISSION: %s",
+ $self->param('experiment_type'),
+ $experiment->name(),
+ );
+ }
+ else {
+ $subject = sprintf(
+ "New %s submission: %s",
+ $self->param('experiment_type'),
+ $experiment->name(),
+ );
+ }
+ my $curator_mail = MIME::Lite->new(
+ From => $CONFIG->get_AUTOSUBS_ADMIN(),
+ To => $CONFIG->get_AUTOSUBS_CURATOR_EMAIL(),
+ Subject => $subject,
+ Encoding => 'quoted-printable',
+ Data => $curator_tmpl->output,
+ Type => 'text/plain',
+ );
+
+ # Must use SMTP if running under CGI::Application::Server
+ if ( my $server = $CONFIG->get_AUTOSUBS_SMTP_SERVER() ) {
+ foreach my $missive ($submitter_mail, $curator_mail) {
+ $missive->send('smtp', $server)
+ or die("Error sending signup email: $!");
+ }
+ }
+ else {
+ foreach my $missive ($submitter_mail, $curator_mail) {
+ $missive->send()
+ or die("Error sending signup email: $!");
+ }
+ }
+
+ return;
+}
+
+###################
+# Runmode methods #
+###################
+
+sub intro {
+ my ($self, $errors) = @_;
+
+ my $template = $self->_load_my_tmpl(
+ $self->param('intro_html_template') || 'intro.html',
+ $errors,
+ {
+ TITLE => $self->param('experiment_type')
+ . ' ArrayExpress submissions',
+ LOGIN => $RM_LOGIN,
+ SIGNUP => $RM_SIGNUP,
+ },
+ );
+
+ return $template->output();
+}
+
+sub signup {
+ my ($self, $errors) = @_;
+
+ my $template = $self->_load_my_tmpl(
+ $self->param('signup_html_template') || 'signup.html',
+ $errors,
+ {
+ TITLE => 'Create new account',
+ USERNAME => 'username',
+ PASSWORD => 'password',
+ PASSWORD2 => 'password2',
+ REALNAME => 'realname',
+ EMAIL => 'email',
+ CREATEUSER => $RM_CREATEUSER,
+ },
+ );
+
+ return $template->output();
+}
+
+sub create_user {
+ my ($self, $errors) = @_;
+
+ # Validate the input.
+ my ($results, $err_page) =
+ $self->check_rm('signup', '_user_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object.
+ my $q = $self->query();
+
+ # FIXME we also need a confirmation email sent to the user. FIXME
+ # also strip surrounding whitespace.
+ my $user
+ = ArrayExpress::AutoSubmission::DB::User->insert({
+ login => $q->param('username'),
+ password => $q->param('password'),
+ name => $q->param('realname'),
+ email => $q->param('email'),
+ created_at => date_now(),
+ is_deleted => 0,
+ });
+
+ # All users signing up here are just submitters.
+ my $role
+ = ArrayExpress::AutoSubmission::DB::Role->retrieve(
+ name => 'submitter',
+ );
+ ArrayExpress::AutoSubmission::DB::RoleUser->insert({
+ role_id => $role,
+ user_id => $user,
+ });
+
+ $self->email_confirm_signup($user);
+
+ my $template = $self->_load_my_tmpl(
+ $self->param('create_user_html_template') || 'create_user.html',
+ $errors,
+ {
+ TITLE => 'User successfully created',
+ USERNAME => 'auth_username',
+ PASSWORD => 'auth_password',
+ USER => $user->login(),
+ PASS => $user->password(),
+ REAL => $user->name(),
+ EMAIL => $user->email(),
+ CHOOSE => $RM_CHOOSE,
+ },
+ );
+
+ return $template->output();
+}
+
+sub login {
+ my ($self, $errors) = @_;
+
+ # Get CGI query object
+ my $q = $self->query();
+
+ # N.B. CGI 'login' param should only be set if the previous login
+ # failed.
+ my $template = $self->_load_my_tmpl(
+ $self->param('login_html_template') || 'login.html',
+ $errors,
+ {
+ TITLE => 'Log in to '
+ . $self->param('experiment_type')
+ . ' submissions',
+ LOGIN => $RM_LOGIN,
+ SIGNUP => $RM_SIGNUP,
+ USERNAME => 'auth_username',
+ PASSWORD => 'auth_password',
+ FAILED => $q->param('login') || 0,
+ },
+ );
+
+ return $template->output();
+}
+
+sub logout {
+ my ($self, $errors) = @_;
+
+ # Log the user out. Check for success FIXME.
+ $self->authen->logout();
+
+ my $template = $self->_load_my_tmpl(
+ $self->param('logout_html_template') || 'logout.html',
+ $errors,
+ {
+ LOGIN => $RM_LOGIN,
+ },
+ );
+
+ return $template->output();
+}
+
+sub choose {
+ my ( $self, $errors ) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ my $user =
+ ArrayExpress::AutoSubmission::DB::User->retrieve(
+ login => $self->authen->username(),
+ );
+ my @experiments = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ user_id => $user->id(),
+ experiment_type => $self->param('experiment_type'),
+ in_curation => 0,
+ is_deleted => 0,
+ );
+ my @submitted = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ user_id => $user->id(),
+ experiment_type => $self->param('experiment_type'),
+ in_curation => 1,
+ is_deleted => 0,
+ );
+
+ my $template = $self->_load_my_tmpl(
+ $self->param('choose_html_template') || 'choose.html',
+ $errors,
+ {
+ EXPERIMENTS =>
+ [ map { { name => $_->name(), id => $_->id() } } @experiments ],
+ SUBMITTED => [
+ map { { name => $_->name(), date => $_->date_submitted() } }
+ @submitted
+ ],
+ TITLE => 'Your ' . $self->param('experiment_type') . ' submissions',
+ CREATE => $RM_CREATE,
+ UPLOAD => $RM_UPLOAD,
+ EDIT => $RM_EDIT,
+ DELETE => $RM_DELETE,
+ },
+ );
+
+ return $template->output();
+}
+
+sub create {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Get CGI query object.
+ my $q = $self->query();
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('create_html_template') || 'create.html',
+ $errors,
+ {
+ %{$self->_annotation_labels()},
+ EXPT_NAME => undef,
+ EXPT_NAME_PARAM => 'experiment_name',
+ AFFYMETRIX => undef,
+ AFFYMETRIX_PARAM => 'is_affymetrix',
+ TITLE => 'Create experiment',
+ INSERT => $RM_INSERT,
+ },
+ );
+
+ return $template->output();
+}
+
+sub insert {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Validate the input.
+ my ($results, $err_page) =
+ $self->check_rm('create', '_edit_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object
+ my $q = $self->query();
+
+ my $user = ArrayExpress::AutoSubmission::DB::User->retrieve(
+ login => $self->authen->username()
+ );
+ my @designs = map {
+ ArrayExpress::AutoSubmission::DB::Design->retrieve($_)
+ } $q->param('biol_designs'),
+ $q->param('meth_designs'),
+ $q->param('tech_designs');
+ my @materials = map {
+ ArrayExpress::AutoSubmission::DB::Material->retrieve($_)
+ } $q->param('materials');
+ my @organisms = map {
+ ArrayExpress::AutoSubmission::DB::Organism->retrieve($_)
+ } $q->param('organisms');
+
+ my $experiment =
+ ArrayExpress::AutoSubmission::DB::Experiment->insert({
+ name => $q->param('experiment_name'),
+ user_id => $user->id(),
+ experiment_type => $self->param('experiment_type'),
+ is_affymetrix => $q->param('is_affymetrix') ? 1 : 0,
+ date_last_edited => date_now(),
+ in_curation => 0,
+ num_submissions => 0,
+ is_deleted => 0,
+ });
+ foreach my $design (@designs) {
+ $experiment->add_to_design_instances({
+ experiment_id => $experiment,
+ design_id => $design,
+ });
+ }
+ foreach my $material (@materials) {
+ $experiment->add_to_material_instances({
+ experiment_id => $experiment,
+ material_id => $material,
+ });
+ }
+ foreach my $organism (@organisms) {
+ $experiment->add_to_organism_instances({
+ experiment_id => $experiment,
+ organism_id => $organism,
+ });
+ }
+
+ my $is_annotated = scalar(@designs)
+ && scalar(@materials)
+ && scalar(@organisms);
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('insert_html_template') || 'insert.html',
+ $errors,
+ {
+ EXPT_ID => $experiment->id(),
+ EXPT_NAME => $experiment->name(),
+ TITLE => 'Experiment created',
+ TEMPLATE => $RM_TEMPLATE,
+ ANNOTATED => $is_annotated,
+ UPLOAD => $RM_UPLOAD,
+ SUBMIT => $RM_SUBMIT,
+ CHOOSE => $RM_CHOOSE,
+ AFFYMETRIX => $q->param('is_affymetrix') ? 1 : 0,
+ DESIGNS => [map { {name => $_->display_label()} } @designs],
+ MATERIALS => [map { {name => $_->display_label()} } @materials],
+ ORGANISMS => [map { {name => $_->scientific_name()} } @organisms],
+ },
+ );
+
+ return $template->output();
+}
+
+sub edit {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Validate the input.
+ my ($results, $err_page) =
+ $self->check_rm('choose', '_choose_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object.
+ my $q = $self->query();
+
+ # Get the experiment, checking that the user is the owner.
+ my ($experiment, $illegal_access) = $self->get_secure_experiment(
+ $q->param('experiment'),
+ );
+ return $illegal_access unless $experiment;
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('edit_html_template') || 'edit.html',
+ $errors,
+ {
+ %{$self->_annotation_labels($experiment)},
+ EXPT_ID => $experiment->id(),
+ EXPT_NAME => $experiment->name(),
+ EXPT_NAME_PARAM => 'experiment_name',
+ AFFYMETRIX => $experiment->is_affymetrix(),
+ AFFYMETRIX_PARAM => 'is_affymetrix',
+ TITLE => 'Edit submission',
+ UPDATE => $RM_UPDATE,
+ },
+ );
+
+ return $template->output();
+}
+
+sub update {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Validate the input.
+ my ($results, $err_page) =
+ $self->check_rm('edit', '_edit_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object
+ my $q = $self->query();
+
+ # Get the experiment, checking that the user is the owner.
+ my ($experiment, $illegal_access) = $self->get_secure_experiment(
+ $q->param('experiment'),
+ );
+ return $illegal_access unless $experiment;
+
+ $experiment->set(
+ name => $q->param('experiment_name'),
+ is_affymetrix => $q->param('is_affymetrix') ? 1 : 0,
+ date_last_edited => date_now(),
+ );
+ $experiment->update();
+
+ my @designs = map {
+ ArrayExpress::AutoSubmission::DB::Design->retrieve($_)
+ } $q->param('biol_designs'),
+ $q->param('meth_designs'),
+ $q->param('tech_designs');
+ $experiment->update_designs(\@designs);
+
+ my @materials = map {
+ ArrayExpress::AutoSubmission::DB::Material->retrieve($_)
+ } $q->param('materials');
+ $experiment->update_materials(\@materials);
+
+ my @organisms = map {
+ ArrayExpress::AutoSubmission::DB::Organism->retrieve($_)
+ } $q->param('organisms');
+ $experiment->update_organisms(\@organisms);
+
+ my $is_annotated = scalar(@designs)
+ && scalar(@materials)
+ && scalar(@organisms);
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('update_html_template') || 'update.html',
+ $errors,
+ {
+ EXPT_ID => $experiment->id(),
+ EXPT_NAME => $experiment->name(),
+ AFFYMETRIX => $q->param('is_affymetrix') ? 1 : 0,
+ TITLE => 'Experiment updated',
+ TEMPLATE => $RM_TEMPLATE,
+ ANNOTATED => $is_annotated,
+ UPLOAD => $RM_UPLOAD,
+ SUBMIT => $RM_SUBMIT,
+ CHOOSE => $RM_CHOOSE,
+ DESIGNS => [map { {name => $_->display_label()} } @designs],
+ MATERIALS => [map { {name => $_->display_label()} } @materials],
+ ORGANISMS => [map { {name => $_->scientific_name()} } @organisms],
+ },
+ );
+
+ return $template->output();
+}
+
+sub delete_expt {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Validate the input. This checks if files have been uploaded, and
+ # forbids deletion if so.
+ my ($results, $err_page) =
+ $self->check_rm('choose', '_delete_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object.
+ my $q = $self->query();
+
+ # Get the experiment, checking that the user is the owner.
+ my ($experiment, $illegal_access) = $self->get_secure_experiment(
+ $q->param('experiment'),
+ );
+ return $illegal_access unless $experiment;
+
+ # Delete the object. We just mark it as is_deleted, rather than
+ # removing it from the database.
+ my $experiment_name = $experiment->name();
+ $experiment->set(is_deleted => 1);
+ $experiment->update();
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('delete_html_template') || 'delete.html',
+ $errors,
+ {
+ EXPT_NAME => $experiment_name,
+ TITLE => 'Experiment deleted',
+ CHOOSE => $RM_CHOOSE,
+ },
+ );
+
+ return $template->output();
+}
+
+sub spreadsheet_template {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Get CGI query object
+ my $q = $self->query();
+
+ # Get the experiment, checking that the user is the owner.
+ my ($experiment, $illegal_access) = $self->get_secure_experiment(
+ $q->param('experiment'),
+ );
+ return $illegal_access unless $experiment;
+
+ # Generate the spreadsheet and return it.
+ my $spreadsheet = ArrayExpress::AutoSubmission::Spreadsheet->new({
+ experiment => $experiment,
+ url => $self->query()->url(),
+ date => date_now(),
+ });
+
+ my $basename = sprintf(
+ "%s_%s_%s_%s.txt",
+ untaint($experiment->user_id()->login()),
+ $self->param('experiment_type'),
+ untaint($experiment->name()),
+ $spreadsheet->get_date(),
+ );
+
+ # Strip some unwanted characters from the filename.
+ $basename =~ s/[ :\\\/]/_/g;
+
+ my $filename = File::Spec->catfile($CONFIG->get_WEBFORM_TEMPLATE_DIR(), $basename);
+ my $fileurl = $CONFIG->get_WEBFORM_TEMPLATE_URL();
+ $fileurl =~ s/[\/]?$/\/$basename/;
+
+ my $ss_fh = IO::File->new($filename, q{>})
+ or croak("Error: unable to open spreadsheet template file $filename for writing: $!");
+ my $ss_content;
+ if ( $self->param('spreadsheet_type') =~ m/\A mage-?tab \z/ixms ) {
+ $ss_content = $spreadsheet->magetab();
+ }
+ else {
+ $ss_content = $spreadsheet->tab2mage();
+ }
+
+ print $ss_fh $ss_content
+ or croak("Error: unable to write output to spreadsheet template file $filename: $!");
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('template_html_template') || 'template.html',
+ $errors,
+ {
+ EXPT_ID => $experiment->id(),
+ EXPT_NAME => $experiment->name(),
+ TITLE => 'Template download',
+ SS_LINK => $fileurl,
+ UPLOAD => $RM_UPLOAD,
+ CHOOSE => $RM_CHOOSE,
+ },
+ );
+
+ return $template->output();
+}
+
+sub upload {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Validate the input.
+ my ($results, $err_page) =
+ $self->check_rm('choose', '_choose_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object
+ my $q = $self->query();
+
+ # Get the experiment, checking that the user is the owner.
+ my ($experiment, $illegal_access) = $self->get_secure_experiment(
+ $q->param('experiment'),
+ );
+ return $illegal_access unless $experiment;
+
+ my @spreadsheets = map {
+ { name => $_->name(), }
+ } $experiment->spreadsheets(is_deleted => 0);
+
+ my @data_files = map {
+ { name => $_->name(), }
+ } $experiment->data_files(is_deleted => 0);
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('upload_html_template') || 'upload.html',
+ $errors,
+ {
+ EXPT_NAME => $experiment->name(),
+ EXPT_ID => $experiment->id(),
+ TITLE => 'File upload',
+ CHOOSE => $RM_CHOOSE,
+ FILE_UPLOAD => $RM_FILE_UPLOAD,
+ SUBMIT => $RM_SUBMIT,
+ DATA_FILE => 'data_file',
+ SPREADSHEET => 'spreadsheet',
+ SHEETS_LOADED => \@spreadsheets,
+ FILES_LOADED => \@data_files,
+ },
+ );
+
+ return $template->output();
+}
+
+sub detect_platform {
+
+ # Return a string that fileparse_set_fstype can understand. Note
+ # that only MSWin32 and MacOS are really understood, and the rest
+ # are assumed to be flavours of Unix.
+ my $self = shift;
+
+ # Attempt to detect non-unix platforms. Windows is the main one at
+ # the moment - MacOSX conforms to unix path structures.
+ my $ua = $self->query()->user_agent();
+ my $browser = HTTP::BrowserDetect->new($ua);
+
+ if ( $browser->windows() ) {
+ return 'MSWin32';
+ }
+ elsif ( $browser->mac() ) {
+ if ( $browser->macosx() ) {
+ return 'MacOSX';
+ }
+ else {
+ return 'MacOS';
+ }
+ }
+ else {
+
+ # Standardise on a default. I happen to be partial to linux...
+ return 'linux';
+ }
+
+}
+
+sub file_upload {
+
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Set the platform before attempting validation.
+ fileparse_set_fstype( $self->detect_platform() );
+
+ # Validate the input.
+ my ($results, $err_page) =
+ $self->check_rm('upload', '_upload_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object
+ my $q = $self->query();
+
+ # Get the experiment, checking that the user is the owner.
+ my ($experiment, $illegal_access) = $self->get_secure_experiment(
+ $q->param('experiment'),
+ );
+ return $illegal_access unless $experiment;
+
+ # Uploads are particularly fraught.
+ if ( my $error = $q->cgi_error() ) {
+ return $self->error('CGI error triggered: ', $error);
+ }
+
+ # Create the submissions directory.
+ my $submissions_dir = $experiment->filesystem_directory();
+
+ # Upload the files. Spreadsheet first:
+ if ( my $upload_path = $q->param('spreadsheet') ){
+
+ # Trim off any directories (Win, Mac or Unix).
+ my $upload_file = untaint(basename( $upload_path ));
+
+ # Input file.
+ my $upload = CGI::Upload->new;
+ my $upload_fh = $upload->file_handle('spreadsheet');
+ $upload_fh->binmode();
+
+ unless ($upload->mime_type('spreadsheet') eq 'text/plain') {
+ return $self->upload({
+ 'err_spreadsheet' =>
+ sprintf (
+ $ERROR_FORMAT,
+ q{Error: the spreadsheet file must be in}
+ . q{ plain ASCII tab-delimited text format.},
+ ),
+ },
+ );
+ }
+
+ # Output file.
+ my $spreadsheet = File::Spec->catfile($submissions_dir, $upload_file);
+ my $out_fh = IO::File->new($spreadsheet, '>')
+ or die("Error: unable to upload file: $!");
+ $out_fh->binmode();
+
+ # Backup spreadsheet.
+ my $backup = File::Spec->catfile(
+ $submissions_dir,
+ "$upload_file.backup." . untaint(date_now())
+ );
+ my $backup_fh = IO::File->new($backup, '>')
+ or die("Error: unable to upload file: $!");
+ $backup_fh->binmode();
+
+ # Copy the file somewhere useful.
+ my ($buffer, $bytes_read);
+ while (my $bytes = read($upload_fh, $buffer, 1024)){
+ $bytes_read += $bytes;
+ print $out_fh $buffer
+ or die("Error writing spreadsheet file to filesystem");
+
+ # Do we really need to croak on error here FIXME?
+ print $backup_fh $buffer
+ or die("Error writing backup spreadsheet file to filesystem");
+ }
+
+ # We don't set permissions on the backup file as curators
+ # should not need to change it. Note that setting permissions
+ # fails if a curator owns the spreadsheet, so we can't afford
+ # to croak indiscriminately. In such cases it's up to the
+ # curator to fix the problem, we only handle the files we own.
+ if ( -o $spreadsheet ) {
+ chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $spreadsheet)
+ or croak("Error changing permissions on $spreadsheet: $!");
+ }
+
+ # Save to database. Note that we leave any previous upload in
+ # place; we may want to revisit that at some point FIXME.
+ my $record = ArrayExpress::AutoSubmission::DB::Spreadsheet->find_or_create(
+ experiment_id => $experiment->id(),
+ is_deleted => 0,
+ );
+ $record->set(name => $upload_file);
+ $record->update();
+ $experiment->set(
+ date_last_edited => date_now(),
+ );
+ $experiment->update();
+ }
+
+ # Now upload the data file:
+ if ( my $upload_path = $q->param('data_file') ){
+
+ # Trim off any directories (Win, Mac or Unix).
+ my $upload_file = untaint(basename( $upload_path ));
+
+ # Input file.
+ my $upload = CGI::Upload->new;
+ my $upload_fh = $upload->file_handle('data_file');
+ $upload_fh->binmode();
+
+ # Output file.
+ my $data_file = File::Spec->catfile($submissions_dir, $upload_file);
+ my $out_fh = IO::File->new($data_file, '>')
+ or die("Error: unable to upload file: $!");
+ $out_fh->binmode();
+
+ # Copy the file somewhere useful.
+ my ($buffer, $bytes_read);
+ while (my $bytes = read($upload_fh, $buffer, 1024)){
+ $bytes_read += $bytes;
+ print $out_fh $buffer
+ or die("Error writing data archive file to filesystem");
+ }
+
+ if ( -o $data_file ) {
+ chmod(oct($CONFIG->get_FILE_PERMISSIONS()), $data_file)
+ or croak("Error changing permissions on $data_file: $!");
+ }
+
+ # Save to database, overwriting any uploaded files with the
+ # same name.
+ my $record = ArrayExpress::AutoSubmission::DB::DataFile->find_or_create(
+ name => $upload_file,
+ experiment_id => $experiment->id(),
+ is_deleted => 0,
+ );
+ $record->set(is_unpacked => 0);
+ $record->update();
+ $experiment->set(
+ date_last_edited => date_now(),
+ );
+ $experiment->update();
+ }
+
+ my @spreadsheets = map {
+ { name => $_->name(), }
+ } $experiment->spreadsheets(is_deleted => 0);
+
+ my @data_files = map {
+ { name => $_->name(), }
+ } $experiment->data_files(is_deleted => 0);
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('file_upload_html_template') || 'file_upload.html',
+ $errors,
+ {
+ EXPT_NAME => $experiment->name(),
+ EXPT_ID => $experiment->id(),
+ TITLE => 'Uploads complete',
+ UPLOAD => $RM_UPLOAD,
+ SUBMIT => $RM_SUBMIT,
+ CHOOSE => $RM_CHOOSE,
+ SPREADSHEET => 'spreadsheet',
+ DATA_FILE => 'data_file',
+ SHEETS_LOADED => \@spreadsheets,
+ FILES_LOADED => \@data_files,
+ SHEETS_OK => scalar @spreadsheets || undef,
+ FILES_OK => scalar @data_files || undef,
+ },
+ );
+
+ return $template->output();
+}
+
+sub submit {
+ my ($self, $errors) = @_;
+ $self->authen->is_authenticated() or return $self->login();
+
+ # Validate the input.
+ my ($results, $err_page) =
+ $self->check_rm('upload', '_submit_profile');
+ return $err_page if $err_page;
+
+ # Get CGI query object
+ my $q = $self->query();
+
+ # Get the experiment, checking that the user is the owner.
+ my ($experiment, $illegal_access) = $self->get_secure_experiment(
+ $q->param('experiment'),
+ );
+ return $illegal_access unless $experiment;
+
+ # in_curation determines web interface visibility; status is used
+ # only by the back-end processing.
+ $experiment->set(
+ in_curation => 1,
+ status => $CONFIG->get_STATUS_PENDING(),
+ date_submitted => date_now(),
+ );
+ $experiment->update();
+
+ # Email the user to confirm submission (but not if it's from the
+ # test user).
+ unless ($experiment->user_id()->login() eq 'test') {
+ $self->email_confirm_submission($experiment);
+ }
+
+ # Increment the number of submissions (this must happen after the
+ # confirmation emails are sent).
+ $experiment->set(
+ num_submissions => ( $experiment->num_submissions() + 1 ),
+ );
+ $experiment->update();
+
+ # Build the page and return it.
+ my $template = $self->_load_my_tmpl(
+ $self->param('submitted_html_template') || 'submitted.html',
+ $errors,
+ {
+ EXPT_NAME => $experiment->name(),
+ EXPT_ID => $experiment->id(),
+ TITLE => 'Experiment submitted',
+ CHOOSE => $RM_CHOOSE,
+ },
+ );
+
+ return $template->output();
+}
+
+sub error {
+
+ # Generate a generic error page that doesn't look too bad.
+ my ( $self, @messages ) = @_;
+
+ my $template = $self->_load_my_tmpl(
+ 'error.html',
+ undef,
+ {
+ MESSAGE => (join(q{}, @messages)
+ || 'Unknown application crash.'),
+ RETURN => $self->authen->is_authenticated()
+ ? $RM_CHOOSE
+ : $RM_INTRO,
+ },
+ );
+
+ return $template->output();
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: WebForm.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::AutoSubmission::WebForm - A CGI::Application web form
+for Tab2MAGE/MAGE-TAB data submissions.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::AutoSubmission::WebForm;
+ my $webapp = ArrayExpress::AutoSubmission::WebForm->new(
+ PARAMS => {
+ experiment_type => 'MAGE-TAB',
+ spreadsheet_type => 'MAGE-TAB',
+ cgi_base => '/cgi-bin/microarray/magetab.cgi',
+ stylesheet => '/microarray/tab2mage.css',
+ intro_html_template => 'magetab_intro.html',
+ upload_html_template => 'magetab_upload.html',
+ template_html_template => 'magetab_template.html',
+ sidebar_icons => [
+ {
+ image => '/microarray/aelogo.png',
+ destination => 'http://www.ebi.ac.uk/arrayexpress/',
+ alt => 'ArrayExpress',
+ },
+ ],
+ sidebar_links => [
+ {
+ text => 'Microarray group',
+ destination => 'http://www.ebi.ac.uk/microarray/',
+ },
+ ],
+ }
+ );
+
+ # Start the CGI script.
+ $webapp->run();
+
+=head1 DESCRIPTION
+
+This module is the core of the web application used in the Tab2MAGE
+and MAGE-TAB data submission web forms. Objects are instantiated with
+a PARAMS attribute which can be used to customise various aspects of
+the web form behaviour.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new>
+
+Object constructor. See the CGI::Application documentation for details.
+
+=item C<run>
+
+The method used to start the web application.
+
+=back
+
+=head1 TODO
+
+Documentation of the options which can be customised using the
+PARAMS attribute.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_annotate.html b/lib/ArrayExpress/AutoSubmission/templates/_annotate.html
new file mode 100644
index 0000000..84a5ec2
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_annotate.html
@@ -0,0 +1,183 @@
+ <!-- TMPL_IF NAME=SOME_ERRORS -->
+ <p>There were some errors:</p>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_EXPERIMENT_NAME -->
+ <!-- TMPL_VAR NAME=ERR_EXPERIMENT_NAME --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_BIOL_DESIGNS -->
+ <!-- TMPL_VAR NAME=ERR_BIOL_DESIGNS --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_METH_DESIGNS -->
+ <!-- TMPL_VAR NAME=ERR_METH_DESIGNS --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_TECH_DESIGNS -->
+ <!-- TMPL_VAR NAME=ERR_TECH_DESIGNS --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_MATERIALS -->
+ <!-- TMPL_VAR NAME=ERR_MATERIALS --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_ORGANISMS -->
+ <!-- TMPL_VAR NAME=ERR_ORGANISMS --><br/>
+ <!-- /TMPL_IF -->
+
+ <table summary="experiment top-level info">
+ <tr>
+ <td>Name:</td>
+ <td<!-- TMPL_IF NAME=ERR_EXPERIMENT_NAME --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+ <input type="text"
+ name="<!-- TMPL_VAR NAME=EXPT_NAME_PARAM -->"
+ size="40"
+ value="<!-- TMPL_VAR NAME=EXPT_NAME -->"/></td>
+ </tr>
+ <tr>
+ <td>Affymetrix experiment?</td>
+ <td>
+ <input type="checkbox"
+ name="<!-- TMPL_VAR NAME=AFFYMETRIX_PARAM -->"
+ <!-- TMPL_IF NAME=AFFYMETRIX -->
+ checked="checked"
+ <!-- /TMPL_IF --> />
+ </td>
+ </tr>
+ </table>
+
+ <p class="text">If you wish to have a spreadsheet template to be
+ constructed for you, please select one or more items from each of
+ the following lists. Please note that you can omit this step if
+ you have already created your own spreadsheet. Once you have
+ entered the appropriate information, please click the button at
+ the bottom of the page.</p>
+
+ <div class="text">
+ <div align="center">
+ <table summary="experiment design annotation">
+ <tr>
+ <th>
+ <!-- TMPL_IF NAME=ERR_BIOL_DESIGNS -->
+ <span style="font-size: 150%; color: red;">*</span>
+ <!-- /TMPL_IF -->
+ Biological design:
+ </th>
+ <th>
+ <!-- TMPL_IF NAME=ERR_METH_DESIGNS -->
+ <span style="font-size: 150%; color: red;">*</span>
+ <!-- /TMPL_IF -->
+ Methodology:
+ </th>
+ <th>
+ <!-- TMPL_IF NAME=ERR_TECH_DESIGNS -->
+ <span style="font-size: 150%; color: red;">*</span>
+ <!-- /TMPL_IF -->
+ Technology used:
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <select name="biol_designs" size="10" multiple="multiple">
+ <!-- TMPL_LOOP NAME=BIOL_DESIGNS -->
+ <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+ value="<!-- TMPL_VAR NAME=ID -->">
+ <!-- TMPL_VAR NAME=NAME -->
+ </option>
+ <!-- /TMPL_LOOP -->
+ </select>
+ </td><td>
+ <select name="meth_designs" size="10" multiple="multiple">
+ <!-- TMPL_LOOP NAME=METH_DESIGNS -->
+ <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+ value="<!-- TMPL_VAR NAME=ID -->">
+ <!-- TMPL_VAR NAME=NAME -->
+ </option>
+ <!-- /TMPL_LOOP -->
+ </select>
+ </td><td>
+ <select name="tech_designs" size="10" multiple="multiple">
+ <!-- TMPL_LOOP NAME=TECH_DESIGNS -->
+ <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+ value="<!-- TMPL_VAR NAME=ID -->">
+ <!-- TMPL_VAR NAME=NAME -->
+ </option>
+ <!-- /TMPL_LOOP -->
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td align="center">
+ <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#BiologicalProperty"
+ target="content">MGED Ontology link</a>
+ </td>
+ <td align="center">
+ <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MethodologicalDesign"
+ target="content">MGED Ontology link</a>
+ </td>
+ <td align="center">
+ <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#TechnologicalDesign"
+ target="content">MGED Ontology link</a>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div align="center">
+ <table summary="materials and species annotation">
+ <tr>
+ <th>
+ <!-- TMPL_IF NAME=ERR_MATERIALS -->
+ <span style="font-size: 150%; color: red;">*</span>
+ <!-- /TMPL_IF -->
+ Materials used:
+ </th>
+ <th>
+ <!-- TMPL_IF NAME=ERR_ORGANISMS -->
+ <span style="font-size: 150%; color: red;">*</span>
+ <!-- /TMPL_IF -->
+ Organisms used:
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <select name="materials" size="10" multiple="multiple">
+ <!-- TMPL_LOOP NAME=MATERIALS -->
+ <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+ value="<!-- TMPL_VAR NAME=ID -->">
+ <!-- TMPL_VAR NAME=NAME -->
+ </option>
+ <!-- /TMPL_LOOP -->
+ </select>
+ </td>
+ <td>
+ <select name="organisms" size="10" multiple="multiple">
+ <!-- TMPL_LOOP NAME=ORGANISMS -->
+ <option <!-- TMPL_IF NAME=SELECTED -->selected="selected"<!-- /TMPL_IF -->
+ value="<!-- TMPL_VAR NAME=ID -->">
+ <!-- TMPL_VAR NAME=NAME -->
+ </option>
+ <!-- /TMPL_LOOP -->
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td align="center">
+ <a href="http://mged.sourceforge.net/ontologies/MGEDontology.php#MaterialType"
+ target="content">MGED Ontology link</a>
+ </td>
+ <td align="center">
+ <a href="http://www.ncbi.nlm.nih.gov/Taxonomy/taxonomyhome.html/"
+ target="content">NCBI Taxonomy link</a>
+ </td>
+ </tr>
+ </table>
+
+ <p class="text">If your organism is not listed, please <a
+ href="mailto:<!-- TMPL_VAR NAME=CURATOR_EMAIL -->?subject=Add New Species (<!-- TMPL_VAR NAME=EXPT_TYPE -->)"> contact us</a>.
+ Note that you may still upload
+ files without using this template generation tool, by leaving
+ all the selection boxes blank.</p>
+
+ </div>
+ </div>
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_display.html b/lib/ArrayExpress/AutoSubmission/templates/_display.html
new file mode 100644
index 0000000..7e865b0
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_display.html
@@ -0,0 +1,48 @@
+ <!-- TMPL_IF NAME=ANNOTATED -->
+ <!-- TMPL_INCLUDE NAME="_display_annotation.html" -->
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ANNOTATED -->
+
+ <p class="text">If you wish to use the automatically generated
+ spreadsheet template, please click on the button below to open the
+ template in a new window. You will then be able to save the template
+ to your hard drive.</p>
+
+ <div class="navbar">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=TEMPLATE -->"/>
+ </form>
+ </div>
+ <!-- TMPL_ELSE -->
+
+ <p class="text">Optional spreadsheet template construction
+ requires design, organism and material to be entered.</p>
+
+ <!-- /TMPL_IF -->
+
+ <p class="text">Please use the following links to upload your data
+ files and completed spreadsheet.</p>
+
+ <div class="navbar">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+ value="<!-- TMPL_VAR NAME=SUBMIT -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"/>
+ </form>
+ </div>
\ No newline at end of file
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html b/lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html
new file mode 100644
index 0000000..b334585
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_display_annotation.html
@@ -0,0 +1,28 @@
+ <!-- TMPL_IF NAME=AFFYMETRIX --><p>This is an Affymetrix experiment.</p><!-- /TMPL_IF -->
+
+ <table summary="annotation list">
+ <tr>
+ <td>Experiment designs:</td>
+ <td>
+ <!-- TMPL_LOOP NAME=DESIGNS -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ <tr>
+ <td>Materials used:</td>
+ <td>
+ <!-- TMPL_LOOP NAME=MATERIALS -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ <tr>
+ <td>Species studied:</td>
+ <td>
+ <!-- TMPL_LOOP NAME=ORGANISMS -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ </table>
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_foot.html b/lib/ArrayExpress/AutoSubmission/templates/_foot.html
new file mode 100644
index 0000000..4463d8e
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_foot.html
@@ -0,0 +1,3 @@
+ </div>
+ </body>
+</html>
\ No newline at end of file
diff --git a/lib/ArrayExpress/AutoSubmission/templates/_head.html b/lib/ArrayExpress/AutoSubmission/templates/_head.html
new file mode 100644
index 0000000..bcc3470
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/_head.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
+ <head>
+ <title><!-- TMPL_VAR NAME=TITLE --></title>
+ <link rel="stylesheet" type="text/css" href="<!-- TMPL_VAR NAME=STYLESHEET -->" />
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ </head>
+ <body>
+ <div class="link-list" id="sidebar">
+
+ <div align="center">
+ <p class="sidebar-title"><!-- TMPL_VAR NAME=EXPT_TYPE --> submissions</p>
+
+ <div align="left">
+ <ul>
+ <!-- TMPL_LOOP NAME=SIDEBAR_LINKS -->
+ <li>
+ <a href="<!-- TMPL_VAR NAME=DESTINATION -->">
+ <!-- TMPL_VAR NAME=TEXT -->
+ </a>
+ </li>
+ <!-- /TMPL_LOOP -->
+ </ul>
+ </div>
+
+ <!-- TMPL_LOOP NAME=SIDEBAR_ICONS -->
+ <p>
+ <a href="<!-- TMPL_VAR NAME=DESTINATION -->">
+ <img src="<!-- TMPL_VAR NAME=IMAGE -->"
+ border="0"
+ <!-- TMPL_IF NAME=WIDTH -->
+ width="<!-- TMPL_VAR NAME=WIDTH -->"
+ <!-- /TMPL_IF -->
+ alt="<!-- TMPL_VAR NAME=ALT -->"/>
+ </a>
+ </p>
+ <!-- /TMPL_LOOP -->
+ </div>
+
+ <!-- TMPL_IF NAME=AUTHEN -->
+ <div align="center">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=LOGOUT -->"/>
+ </form>
+ </div>
+ <!-- /TMPL_IF -->
+
+ <div align="left">
+ <ul>
+ <li>
+ <a href="mailto:<!-- TMPL_VAR NAME=CURATOR_EMAIL
+ -->?subject=<!-- TMPL_VAR NAME=EXPT_TYPE --> Submissions Help">Contact us</a>
+ </li>
+ </ul>
+ </div>
+
+ </div>
+ <div id="main">
diff --git a/lib/ArrayExpress/AutoSubmission/templates/choose.html b/lib/ArrayExpress/AutoSubmission/templates/choose.html
new file mode 100644
index 0000000..d819143
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/choose.html
@@ -0,0 +1,49 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Please select the experiment to edit from the
+ following list, or create a new submission:</p>
+
+ <!-- TMPL_IF NAME=SOME_ERRORS -->
+ <!-- /TMPL_IF -->
+ <!-- TMPL_IF NAME=ERR_EXPERIMENT -->
+ <p class="text"><!-- TMPL_VAR NAME=ERR_EXPERIMENT --></p>
+ <!-- /TMPL_IF -->
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <div>
+ <select name="experiment" size="5">
+ <!-- TMPL_LOOP NAME=EXPERIMENTS -->
+ <option value="<!-- TMPL_VAR NAME=ID -->"><!-- TMPL_VAR NAME=NAME --></option>
+ <!-- /TMPL_LOOP -->
+ </select>
+ </div>
+ <br/>
+ <div class="navbar">
+ <input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CREATE -->"/>
+ <input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=EDIT -->"/>
+ <input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+ <input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=DELETE -->"/>
+ </div>
+ <div>
+ <p>Previously submitted experiments:</p>
+ <table summary="previous experiments list">
+ <tr>
+ <th>Experiment name</th>
+ <th>Submission date</th>
+ </tr>
+ <!-- TMPL_LOOP NAME=SUBMITTED -->
+ <tr>
+ <td><!-- TMPL_VAR NAME=NAME --></td>
+ <td><!-- TMPL_VAR NAME=DATE --></td>
+ </tr>
+ <!-- /TMPL_LOOP -->
+ </table>
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/create.html b/lib/ArrayExpress/AutoSubmission/templates/create.html
new file mode 100644
index 0000000..70ed97f
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/create.html
@@ -0,0 +1,16 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Please enter some summary information for your
+ experiment here.</p>
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <!-- TMPL_INCLUDE NAME="_annotate.html" -->
+ <div class="navbar">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=INSERT -->"/>
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/create_user.html b/lib/ArrayExpress/AutoSubmission/templates/create_user.html
new file mode 100644
index 0000000..6c43fe1
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/create_user.html
@@ -0,0 +1,29 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Your account has been successfully created:</p>
+
+ <table summary="user details">
+ <tr><td>Real name:</td><td><!-- TMPL_VAR NAME=REAL --></td></tr>
+ <tr><td>Username:</td><td><!-- TMPL_VAR NAME=USER --></td></tr>
+ <tr><td>Password:</td><td><!-- TMPL_VAR NAME=PASS --></td></tr>
+ <tr><td>Email:</td><td><!-- TMPL_VAR NAME=EMAIL --></td></tr>
+ </table>
+
+ <p class="text">Please now continue to the experiment creation page:</p>
+
+ <div class="navbar">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="hidden"
+ name="<!-- TMPL_VAR NAME=USERNAME -->"
+ value="<!-- TMPL_VAR NAME=USER -->"/>
+ <input type="hidden"
+ name="<!-- TMPL_VAR NAME=PASSWORD -->"
+ value="<!-- TMPL_VAR NAME=PASS -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"
+ class="button" />
+ </form>
+ </div>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/delete.html b/lib/ArrayExpress/AutoSubmission/templates/delete.html
new file mode 100644
index 0000000..cbb6d95
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/delete.html
@@ -0,0 +1,14 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Successfully deleted experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+ <div class="navbar">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"
+ class="button" />
+ </form>
+ </div>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/edit.html b/lib/ArrayExpress/AutoSubmission/templates/edit.html
new file mode 100644
index 0000000..84a310a
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/edit.html
@@ -0,0 +1,15 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Editing experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="hidden" name="experiment" value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <!-- TMPL_INCLUDE NAME="_annotate.html" -->
+ <div class="navbar">
+ <input type="submit" name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=UPDATE -->"/>
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt b/lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt
new file mode 100644
index 0000000..9dd48d4
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/email_signup_confirmation.txt
@@ -0,0 +1,16 @@
+Dear <TMPL_VAR NAME=REALNAME>,
+
+Welcome to the ArrayExpress submissions system for <TMPL_VAR NAME=TYPE> experiments.
+
+The following account has been created at your request:
+
+ username: <TMPL_VAR NAME=USERNAME>
+ password: <TMPL_VAR NAME=PASSWORD>
+
+You may now submit your experimental data using the following web page:
+
+ <TMPL_VAR NAME=URL>
+
+ Best regards,
+
+ The ArrayExpress team
diff --git a/lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt b/lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt
new file mode 100644
index 0000000..24d11e3
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/email_subs_confirmation.txt
@@ -0,0 +1,14 @@
+Dear <TMPL_VAR NAME=REALNAME>,
+
+Thank you for your <TMPL_VAR NAME=TYPE> submission to ArrayExpress.
+Our curation team will contact you shortly regarding your
+experiment "<TMPL_VAR NAME=EXPTNAME>".
+
+In the meantime, please contact us at <TMPL_VAR NAME=CURATOR_EMAIL>
+with any questions.
+
+ Best regards,
+
+ The ArrayExpress team
+
+Submission date: <TMPL_VAR NAME=SUBSDATE>
diff --git a/lib/ArrayExpress/AutoSubmission/templates/error.html b/lib/ArrayExpress/AutoSubmission/templates/error.html
new file mode 100644
index 0000000..57c8420
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/error.html
@@ -0,0 +1,22 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1>An internal error has occurred:</h1>
+
+ <p class="text"><!-- TMPL_VAR NAME=MESSAGE --></p>
+
+ <p class="text">Please report this error to <!-- TMPL_VAR
+ NAME=CURATOR_EMAIL -->. You may use the button below to return to
+ the submission forms:</p>
+
+ <form method="post"
+ action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+ enctype="multipart/form-data">
+
+ <div class="navbar">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=RETURN -->"
+ class="button" />
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/file_upload.html b/lib/ArrayExpress/AutoSubmission/templates/file_upload.html
new file mode 100644
index 0000000..17055ca
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/file_upload.html
@@ -0,0 +1,63 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">The following files have been successfully
+ uploaded for your experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+ <!-- TMPL_IF NAME=SOME_ERRORS -->
+ <p>There were some errors:</p>
+ <!-- /TMPL_IF -->
+
+ <form method="post"
+ action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+ enctype="multipart/form-data">
+ <table summary="file upload summary">
+ <tr>
+ <th>
+ Spreadsheet
+ </th>
+ <td>
+ <!-- TMPL_LOOP NAME=SHEETS_LOADED -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ <tr>
+ <th>
+ Data file archives
+ </th>
+ <td>
+ <!-- TMPL_LOOP NAME=FILES_LOADED -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ </table>
+
+ <div class="navbar">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="hidden"
+ name="<!-- TMPL_VAR NAME=DATA_FILE -->"
+ value="<!-- TMPL_VAR NAME=FILES_OK -->"/>
+ <input type="hidden"
+ name="<!-- TMPL_VAR NAME=SPREADSHEET -->"
+ value="<!-- TMPL_VAR NAME=SHEETS_OK -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=UPLOAD -->"
+ class="button" />
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=SUBMIT -->"
+ onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+ class="button" />
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"
+ class="button" />
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/insert.html b/lib/ArrayExpress/AutoSubmission/templates/insert.html
new file mode 100644
index 0000000..b9bdde3
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/insert.html
@@ -0,0 +1,8 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Successfully created experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+ <!-- TMPL_INCLUDE NAME="_display.html" -->
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/intro.html b/lib/ArrayExpress/AutoSubmission/templates/intro.html
new file mode 100644
index 0000000..57fd703
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/intro.html
@@ -0,0 +1,65 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Welcome to the <!-- TMPL_VAR NAME=EXPT_TYPE -->
+ submissions system. This web page will allow you to submit your
+ data files with experimental annotation entered in a
+ spreadsheet. If you wish, you may have a template spreadsheet
+ automatically generated for you. Alternatively, if you are an
+ experienced user of Tab2MAGE, you may create your own spreadsheet
+ using the <a
+ href="http://tab2mage.sourceforge.net/docs/index.html">Tab2MAGE
+ documentation</a>.</p>
+
+ <p class="text">Note that if you wish to submit your data using
+ the more flexible <a
+ href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+ format</a>, you should use our <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/magetab.cgi">MAGE-TAB
+ submissions web page</a>. MAGE-TAB is particularly recommended
+ in the following circumstances:</p>
+
+ <ul>
+
+ <li>Submission of high-throughput sequencing data (e.g., Solexa,
+ 454).</li>
+
+ <li>Submission of other non array-based data (e.g.,
+ metabolomics, proteomics).</li>
+
+ <li>Cases in which you wish to precisely specify annotation
+ terms and their source <br/> (e.g., disease state terms, and their
+ accession numbers, from a disease ontology).</li>
+
+ </ul>
+
+ <p class="text">Please see <a
+ href="http://tab2mage.sourceforge.net/docs/magetab_vs_tab2mage.html">this
+ comparison page</a> for more detail. ArrayExpress will continue to
+ support both Tab2MAGE and MAGE-TAB submissions; if, however, you
+ wish to convert your Tab2MAGE spreadsheet into a MAGE-TAB
+ document, a <a
+ href="http://tab2mage.sourceforge.net/index.html#tabconverter">Tab2MAGE
+ to MAGE-TAB converter tool</a> has been created and made <a
+ href="http://sourceforge.net/project/showfiles.php?group_id=120325&package_id=226041">available
+ for download here</a>.</p>
+
+ <p class="text">Login to start a submission and generate a
+ template. Please use the "New submitter" link on this page if you
+ need to create a new account.</p>
+
+ <p class="text">Full help documentation is available using the links to
+ the side of this page.</p>
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+
+ <div class="navbar">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=LOGIN -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=SIGNUP -->"/>
+ </div>
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/login.html b/lib/ArrayExpress/AutoSubmission/templates/login.html
new file mode 100644
index 0000000..bab6ba5
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/login.html
@@ -0,0 +1,65 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <p class="text">Please enter your username and password in the fields below:</p>
+
+ <!-- TMPL_IF NAME=FAILED -->
+ <p class="error">Error: login failed. Please check your username and password.</p>
+ <!-- /TMPL_IF -->
+
+ <table summary="login form">
+ <tr>
+ <td>Username</td>
+ <td>
+ <input id="authen_loginfield"
+ tabindex="1"
+ type="text"
+ name="<!-- TMPL_VAR NAME=USERNAME -->"
+ size="30"
+ value="" />
+ </td>
+ </tr>
+ <tr>
+ <td>Password</td>
+ <td>
+ <input id="authen_passwordfield"
+ tabindex="2"
+ type="password"
+ name="<!-- TMPL_VAR NAME=PASSWORD -->"
+ size="30" />
+ </td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td>
+ <div class="buttons">
+ <input type="hidden"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=LOGIN -->" />
+ <input type="submit"
+ tabindex="3"
+ name="login"
+ value="Log in"
+ class="button" />
+ <input type="reset"
+ tabindex="4"
+ name="resetlogin"
+ value="Reset"
+ class="button" />
+ </div>
+ </td>
+ </tr>
+ </table>
+ </form>
+ <p class="text">If you do not yet have a username and password, please sign up using this link:</p>
+ <div class="navbar" align="center">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="submit"
+ tabindex="5"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=SIGNUP -->"
+ class="button" />
+ </form>
+ </div>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/logout.html b/lib/ArrayExpress/AutoSubmission/templates/logout.html
new file mode 100644
index 0000000..f5014a8
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/logout.html
@@ -0,0 +1,13 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h2>You have successfully logged out.</h2>
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+
+ <div class="navbar">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=LOGIN -->"/>
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html b/lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html
new file mode 100644
index 0000000..f5cf8f2
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/magetab_intro.html
@@ -0,0 +1,42 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Welcome to the <!-- TMPL_VAR NAME=EXPT_TYPE -->
+ submissions system. This web page will allow you to submit your
+ data files with experimental annotation entered in a MAGE-TAB
+ document. Please see our <a
+ href="http://tab2mage.sourceforge.net/docs/magetab_subs.html">MAGE-TAB
+ submission help notes</a> to get you started. If you wish, you may
+ use this web page to generate a template document which you can
+ download and complete on your computer. Alternatively, if you are
+ an experienced user of MAGE-TAB, you may create your own document
+ using the <a
+ href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+ documentation</a>.</p>
+
+ <p class="text">Note that if you wish to submit your data using
+ our simpler <a
+ href="http://tab2mage.sourceforge.net/docs/spreadsheet.html">Tab2MAGE
+ format</a>, you should use our <a
+ href="http://www.ebi.ac.uk/cgi-bin/microarray/tab2mage.cgi">Tab2MAGE
+ submissions web page</a>.</p>
+
+ <p class="text">Login to start a submission and generate a
+ template. Please use the "New submitter" link on this page if you
+ need to create a new account.</p>
+
+ <p class="text">Full help documentation is available using the links to
+ the side of this page.</p>
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+
+ <div class="navbar">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=LOGIN -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=SIGNUP -->"/>
+ </div>
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/magetab_template.html b/lib/ArrayExpress/AutoSubmission/templates/magetab_template.html
new file mode 100644
index 0000000..417b811
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/magetab_template.html
@@ -0,0 +1,37 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Template successfully generated for experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+ <p class="text">Please download your spreadsheet template file
+ here (right-click or control-click to save directly to disk):</p>
+
+ <h3 class="text">
+ <a href="<!-- TMPL_VAR NAME=SS_LINK -->" target="<!-- TMPL_VAR NAME=SS_LINK -->">Spreadsheet template</a>
+ </h3>
+
+ <p class="text">Note that template files are only stored on this server for 24 hours.</p>
+
+ <p class="text">See the online <a
+ href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+ documentation</a> for help on filling out your template
+ spreadsheet. Please also see these <a
+ href="http://tab2mage.sourceforge.net/docs/magetab_subs.html#realexamples">real-world
+ example spreadsheets</a> for tips on specific
+ experiment types.</p>
+
+ <div class="navbar">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"/>
+ </form>
+ </div>
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html b/lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html
new file mode 100644
index 0000000..724da37
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/magetab_upload.html
@@ -0,0 +1,152 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Uploading files for your experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+ <p class="text">Please upload the following:</p>
+
+ <ul class="text">
+ <li>One completed plain-text spreadsheet file containing your
+ experiment annotation (IDF and SDRF)</li>
+
+ <li>One or more zipped (.zip) or tar-gzipped (.tar.gz, .tgz)
+ archives containing all your data files.</li>
+ </ul>
+
+ <p class="text">Note: if you wish to upload your IDF and SDRF
+ files as separate documents, please upload your SDRF in the same
+ package as your data files.</p>
+
+ <p class="text">See the online <a
+ href="http://tab2mage.sourceforge.net/docs/magetab_docs.html">MAGE-TAB
+ documentation</a> for help on filling out your spreadsheet. Please
+ also see these <a
+ href="http://tab2mage.sourceforge.net/examples/magetab/real/">real-world
+ example spreadsheets</a> for tips on specific
+ experiment types.</p>
+
+ <p class="text">Supported data file formats are described in the <a
+ href="http://tab2mage.sourceforge.net/docs/datafiles.html">Tab2MAGE
+ help notes</a>.</p>
+
+ <p class="text">Please note that uploading a file with the same
+ name as a previously uploaded file will overwrite the old file
+ with the new one.</p>
+
+ <p class="text">To replace a previously uploaded spreadsheet,
+ simply upload a new one using the form below. This will substitute
+ your new spreadsheet for the old one.</p>
+
+ <p class="text">You may upload your data files in as many separate
+ compressed archives as are needed; the form below adds new data
+ file uploads without replacing the old files (unless they are
+ named the same).</p>
+
+ <p class="text">Once you have uploaded these files you will be
+ able to finalize your submission.</p>
+
+ <!-- TMPL_IF NAME=SOME_ERRORS -->
+ <p>There were some errors:</p>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_EXPERIMENT -->
+ <!-- TMPL_VAR NAME=ERR_EXPERIMENT --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_UPLOAD_FILES -->
+ <!-- TMPL_VAR NAME=ERR_UPLOAD_FILES --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_SPREADSHEET -->
+ <!-- TMPL_VAR NAME=ERR_SPREADSHEET --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_DATA_FILE -->
+ <!-- TMPL_VAR NAME=ERR_DATA_FILE --><br/>
+ <!-- /TMPL_IF -->
+
+ <form method="post"
+ name="upload"
+ action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+ enctype="multipart/form-data">
+ <table summary="file upload form">
+ <tr>
+ <th>
+ Upload type
+ </th>
+ <th>
+ Select file
+ </th>
+ <th>
+ Files already uploaded
+ </th>
+ </tr>
+ <tr>
+ <td>
+ Upload/replace spreadsheet or IDF:
+ </td>
+ <td>
+ <input type="file"
+ name="<!-- TMPL_VAR NAME=SPREADSHEET -->" />
+ </td>
+ <td>
+ <!-- TMPL_LOOP NAME=SHEETS_LOADED -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Upload/add Data file archive (with SDRF if used):
+ </td>
+ <td>
+ <input type="file"
+ name="<!-- TMPL_VAR NAME=DATA_FILE -->" />
+ </td>
+ <td>
+ <!-- TMPL_LOOP NAME=FILES_LOADED -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ <tr>
+ <td>
+
+ </td>
+ <td>Click here to upload the selected files:
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=FILE_UPLOAD -->"
+ class="button"
+ <!-- TMPL_IF NAME=SHEETS_LOADED -->
+ onclick="if (document.upload.<!-- TMPL_VAR NAME=SPREADSHEET -->.value) {return confirm('Really overwrite previously uploaded spreadsheet?');}"
+ <!-- /TMPL_IF -->
+ />
+ </td>
+ <td>
+
+ </td>
+ </tr>
+ </table>
+
+ <p class="text">If you are not yet ready to upload files,
+ please use the link below to return to the experiment
+ listing.</p>
+
+ <div class="navbar">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"
+ class="button" />
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=SUBMIT -->"
+ onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+ class="button" />
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/signup.html b/lib/ArrayExpress/AutoSubmission/templates/signup.html
new file mode 100644
index 0000000..1846599
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/signup.html
@@ -0,0 +1,82 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <p class="text">Please complete all the following fields:</p>
+
+ <!-- TMPL_IF NAME=SOME_ERRORS -->
+ <p>There were some errors:</p>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_USERNAME -->
+ <!-- TMPL_VAR NAME=ERR_USERNAME --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_PASSWORD -->
+ <!-- TMPL_VAR NAME=ERR_PASSWORD --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_REALNAME -->
+ <!-- TMPL_VAR NAME=ERR_REALNAME --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_EMAIL -->
+ <!-- TMPL_VAR NAME=ERR_EMAIL --><br/>
+ <!-- /TMPL_IF -->
+
+ <p class="text">N.B. both username and password must be 6 to 10
+ characters long.</p>
+
+ <table summary="user details entry">
+ <tr>
+ <td>Name</td>
+ <td<!-- TMPL_IF NAME=ERR_REALNAME --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+ <input id="authen_namefield"
+ type="text"
+ name="<!-- TMPL_VAR NAME=REALNAME -->"
+ size="30"
+ value=""/>
+ </td>
+ </tr>
+ <tr>
+ <td>Desired username</td>
+ <td<!-- TMPL_IF NAME=ERR_USERNAME --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+ <input id="authen_loginfield"
+ type="text"
+ name="<!-- TMPL_VAR NAME=USERNAME -->"
+ size="30"
+ value=""/></td>
+ </tr>
+ <tr>
+ <td>Password</td>
+ <td<!-- TMPL_IF NAME=ERR_PASSWORD --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+ <input id="authen_passwordfield"
+ type="password"
+ name="<!-- TMPL_VAR NAME=PASSWORD -->"
+ size="30"/></td>
+ </tr>
+ <tr>
+ <td>Confirm password</td>
+ <td<!-- TMPL_IF NAME=ERR_PASSWORD --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+ <input id="authen_passwordfield2"
+ type="password"
+ name="<!-- TMPL_VAR NAME=PASSWORD2 -->"
+ size="30"/></td>
+ </tr>
+ <tr>
+ <td>Email address</td>
+ <td<!-- TMPL_IF NAME=ERR_EMAIL --> class="fieldWithErrors"<!-- /TMPL_IF -->>
+ <input id="authen_email"
+ type="text"
+ name="<!-- TMPL_VAR NAME=EMAIL -->"
+ size="30"/></td>
+ </tr>
+ </table>
+ <div class="navbar">
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CREATEUSER -->"
+ class="button"/>
+ </div>
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/submitted.html b/lib/ArrayExpress/AutoSubmission/templates/submitted.html
new file mode 100644
index 0000000..0d3bcc4
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/submitted.html
@@ -0,0 +1,32 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <!-- TMPL_IF NAME=SOME_ERRORS -->
+ <p>There were some errors:</p>
+
+ <!-- TMPL_ELSE -->
+
+ <p class="text">The experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"
+ was successfully submitted. The ArrayExpress curators will contact
+ you shortly regarding your submission. In the meantime, please
+ contact <a href="mailto:<!-- TMPL_VAR NAME=CURATOR_EMAIL -->">
+ <!-- TMPL_VAR NAME=CURATOR_EMAIL --></a> with any questions.</p>
+
+ <!-- /TMPL_IF -->
+
+ <form method="post"
+ action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+ enctype="multipart/form-data">
+
+ <div class="navbar">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"
+ class="button" />
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt b/lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt
new file mode 100644
index 0000000..03e55dd
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/subs_notify_curator.txt
@@ -0,0 +1,15 @@
+Dear Curators,
+
+A new <TMPL_VAR NAME=TYPE> experiment has just been submitted:
+
+Name: <TMPL_VAR NAME=EXPTNAME>
+Directory: <TMPL_VAR NAME=EXPTDIR>
+User: <TMPL_VAR NAME=REALNAME>
+Login: <TMPL_VAR NAME=LOGIN>
+Email: <TMPL_VAR NAME=SUBMTR_EMAIL>
+
+Submission date: <TMPL_VAR NAME=SUBSDATE>
+
+Best regards,
+
+your friendly neighborhood submissions system.
diff --git a/lib/ArrayExpress/AutoSubmission/templates/template.html b/lib/ArrayExpress/AutoSubmission/templates/template.html
new file mode 100644
index 0000000..e0d021b
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/template.html
@@ -0,0 +1,37 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Template successfully generated for experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+ <p class="text">Please download your spreadsheet template file
+ here (right-click or control-click to save directly to disk):</p>
+
+ <h3 class="text">
+ <a href="<!-- TMPL_VAR NAME=SS_LINK -->" target="<!-- TMPL_VAR NAME=SS_LINK -->">Spreadsheet template</a>
+ </h3>
+
+ <p class="text">Note that template files are only stored on this server for 24 hours.</p>
+
+ <p class="text">See the online <a
+ href="http://tab2mage.sourceforge.net/docs/spreadsheet.html">Tab2MAGE
+ documentation</a> for help on filling out your template
+ spreadsheet. Please also see these <a
+ href="http://tab2mage.sourceforge.net/docs/spreadsheet.html#realexamples">real-world
+ example spreadsheets</a> for tips on specific
+ experiment types.</p>
+
+ <div class="navbar">
+ <form method="post" action="<!-- TMPL_VAR NAME=CGI_BASE -->" enctype="multipart/form-data">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=UPLOAD -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"/>
+ </form>
+ </div>
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/update.html b/lib/ArrayExpress/AutoSubmission/templates/update.html
new file mode 100644
index 0000000..f6cce62
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/update.html
@@ -0,0 +1,8 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Successfully updated experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->".</p>
+
+ <!-- TMPL_INCLUDE NAME="_display.html" -->
+
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/AutoSubmission/templates/upload.html b/lib/ArrayExpress/AutoSubmission/templates/upload.html
new file mode 100644
index 0000000..7306b3d
--- /dev/null
+++ b/lib/ArrayExpress/AutoSubmission/templates/upload.html
@@ -0,0 +1,148 @@
+<!-- TMPL_INCLUDE HEADER -->
+ <h1><!-- TMPL_VAR NAME=TITLE --></h1>
+
+ <p class="text">Uploading files for your experiment "<!-- TMPL_VAR NAME=EXPT_NAME -->"</p>
+
+ <p class="text">Please upload the following:</p>
+
+ <ul class="text">
+ <li>One completed plain-text spreadsheet file containing your
+ experiment annotation</li>
+
+ <li>One or more zipped (.zip) or tar-gzipped (.tar.gz, .tgz)
+ archives containing all your data files.</li>
+ </ul>
+
+ <p class="text">See the online <a
+ href="http://tab2mage.sourceforge.net/docs/spreadsheet.html">Tab2MAGE
+ documentation</a> for help on filling out your spreadsheet. Please
+ also see these <a
+ href="http://tab2mage.sourceforge.net/docs/spreadsheet.html#realexamples">real-world
+ example spreadsheets</a> for tips on specific
+ experiment types.</p>
+
+ <p class="text">Supported data file formats are described in the <a
+ href="http://tab2mage.sourceforge.net/docs/datafiles.html">Tab2MAGE
+ help notes</a>.</p>
+
+ <p class="text">Please note that uploading a file with the same
+ name as a previously uploaded file will overwrite the old file
+ with the new one.</p>
+
+ <p class="text">To replace a previously uploaded spreadsheet,
+ simply upload a new one using the form below. This will substitute
+ your new spreadsheet for the old one.</p>
+
+ <p class="text">You may upload your data files in as many separate
+ compressed archives as are needed; the form below adds new data
+ file uploads without replacing the old files (unless they are
+ named the same).</p>
+
+ <p class="text">Once you have uploaded these files you will be
+ able to finalize your submission.</p>
+
+ <!-- TMPL_IF NAME=SOME_ERRORS -->
+ <p>There were some errors:</p>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_EXPERIMENT -->
+ <!-- TMPL_VAR NAME=ERR_EXPERIMENT --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_UPLOAD_FILES -->
+ <!-- TMPL_VAR NAME=ERR_UPLOAD_FILES --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_SPREADSHEET -->
+ <!-- TMPL_VAR NAME=ERR_SPREADSHEET --><br/>
+ <!-- /TMPL_IF -->
+
+ <!-- TMPL_IF NAME=ERR_DATA_FILE -->
+ <!-- TMPL_VAR NAME=ERR_DATA_FILE --><br/>
+ <!-- /TMPL_IF -->
+
+ <form method="post"
+ name="upload"
+ action="<!-- TMPL_VAR NAME=CGI_BASE -->"
+ enctype="multipart/form-data">
+ <table summary="file upload form">
+ <tr>
+ <th>
+ Upload type
+ </th>
+ <th>
+ Select file
+ </th>
+ <th>
+ Files already uploaded
+ </th>
+ </tr>
+ <tr>
+ <td>
+ Upload/replace Spreadsheet:
+ </td>
+ <td>
+ <input type="file"
+ name="<!-- TMPL_VAR NAME=SPREADSHEET -->" />
+ </td>
+ <td>
+ <!-- TMPL_LOOP NAME=SHEETS_LOADED -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Upload/add Data file archive:
+ </td>
+ <td>
+ <input type="file"
+ name="<!-- TMPL_VAR NAME=DATA_FILE -->" />
+ </td>
+ <td>
+ <!-- TMPL_LOOP NAME=FILES_LOADED -->
+ <!-- TMPL_VAR NAME=NAME --><br/>
+ <!-- /TMPL_LOOP -->
+ </td>
+ </tr>
+ <tr>
+ <td>
+
+ </td>
+ <td>Click here to upload the selected files:
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=FILE_UPLOAD -->"
+ class="button"
+ <!-- TMPL_IF NAME=SHEETS_LOADED -->
+ onclick="if (document.upload.<!-- TMPL_VAR NAME=SPREADSHEET -->.value) {return confirm('Really overwrite previously uploaded spreadsheet?');}"
+ <!-- /TMPL_IF -->
+ />
+ </td>
+ <td>
+
+ </td>
+ </tr>
+ </table>
+
+ <p class="text">If you are not yet ready to upload files,
+ please use the link below to return to the experiment
+ listing.</p>
+
+ <div class="navbar">
+ <input type="hidden"
+ name="experiment"
+ value="<!-- TMPL_VAR NAME=EXPT_ID -->"/>
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=CHOOSE -->"
+ class="button" />
+ <input type="submit"
+ name="<!-- TMPL_VAR NAME=RM -->"
+ value="<!-- TMPL_VAR NAME=SUBMIT -->"
+ onclick="return confirm('Are you sure? You will be unable to make further changes.');"
+ class="button" />
+ </div>
+
+ </form>
+<!-- TMPL_INCLUDE FOOTER -->
diff --git a/lib/ArrayExpress/Curator/Common.pm b/lib/ArrayExpress/Curator/Common.pm
new file mode 100644
index 0000000..3a70c1a
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Common.pm
@@ -0,0 +1,620 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the other ArrayExpress::Curator
+# modules.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Common.pm 2013 2008-03-31 17:23:14Z tfrayner $
+#
+
+package ArrayExpress::Curator::Common;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../index.html">
+ <img src="../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Common.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Common.pm - a module providing some simple
+utility functions and regexps.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Common qw(date_now $RE_EMPTY_STRING);
+
+ if ( $test =~ $RE_EMPTY_STRING ) {
+ print date_now();
+ }
+
+=head1 DESCRIPTION
+
+This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<strip_discards( $indexcols, $line_array )>
+
+Sub to strip values out of an arrayref ($line_array) based on an
+arrayref of unwanted array indices ($indexcols). Does not modify the
+input lists, but instead returns a suitably stripped arrayref.
+
+=item C<round( $number, $precision )>
+
+A simple rounding function that returns $number rounded to $precision
+decimal places.
+
+=item C<get_indexcol( $list, $name )>
+
+Function to return the first index within @$list matching the string
+or regexp passed as $name. Returns -1 on failure.
+
+=item C<check_linebreaks( $path )>
+
+Takes a filename as an argument, checks for Mac, Unix or DOS line
+endings by reading the whole file in chunks, and regexp matching the
+various linebreak types. Returns the appropriate linebreak for
+acceptable line breaks (line breaks must be unanimous), undef if a
+consensus is unreachable.
+
+=item C<clean_hash( $hashref )>
+
+Strip out undef or empty string values from $hashref.
+
+=item C<date_now()>
+
+Return current date and time as a MAGE best practice date-time string
+(uses mage_date(), below).
+
+=item C<mage_date( $time )>
+
+When passed the return value from e.g. perl's time() built-in, returns
+a MAGE best practice date-time string. This string will always be from
+the UTC/GMT time zone.
+
+=item C<untaint( $string )>
+
+A convenient data untainting function which replaces any run of
+non-whitelisted characters with a single underscore.
+
+=item C<find_cdf( $name, $dir )>
+
+Given a filename and an optional directory, returns an actual filename
+found in a case-insensitive fashion. Preferentially checks the main
+Affy library directory if set in $CONFIG (see
+L<ArrayExpress::Curator::Config>).
+
+=item C<decamelize( $string )>
+
+Function to convert CamelCase strings to underscore_delimited.
+
+=item C<get_filepath_from_uri( $string, $dir )>
+
+Given a string and an optional directory argument, this function
+determines which URI scheme is in use (default is "file://"), and
+downloads "http://" and "ftp://" URIs to either the indicated
+filesystem directory or the current working directory. Returns the
+filesystem path to the file.
+
+=back
+
+=head1 REGEXPS
+
+=over 2
+
+=item C<$RE_EMPTY_STRING>
+
+Matches an empty string (whitespace ignored).
+
+=item C<$RE_COMMENTED_STRING>
+
+Matches a string beginning with #.
+
+=item C<$RE_SURROUNDED_BY_WHITESPACE>
+
+Matches a string with whitespace on either side; $1 contains the
+string minus the whitespace.
+
+=item C<$RE_WITHIN_PARENTHESES>
+
+Matches a string with parentheses on either side; $1 contains the
+string minus the parentheses.
+
+=item C<$RE_WITHIN_BRACKETS>
+
+Matches a string with brackets on either side; $1 contains the
+string minus the brackets.
+
+=item C<$RE_SQUARE_BRACKETS>
+
+Matches either [ or ]; $1 contains the matched character.
+
+=item C<$RE_LINE_BREAK>
+
+Matches a linebreak (DOS, Unix or MacOS).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Carp;
+use charnames qw( :full );
+
+use Scalar::Util qw( openhandle );
+use File::Spec;
+use IO::File;
+use IO::Handle;
+use Readonly;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ strip_discards
+ round
+ get_indexcol
+ check_linebreaks
+ clean_hash
+ date_now
+ mage_date
+ untaint
+ find_cdf
+ decamelize
+ get_filepath_from_uri
+ mx2tab
+
+ $RE_EMPTY_STRING
+ $RE_COMMENTED_STRING
+ $RE_SURROUNDED_BY_WHITESPACE
+ $RE_WITHIN_PARENTHESES
+ $RE_WITHIN_BRACKETS
+ $RE_SQUARE_BRACKETS
+ $RE_LINE_BREAK
+);
+
+# Define some standard regexps:
+Readonly our $RE_EMPTY_STRING => qr{\A \s* \z}xms;
+Readonly our $RE_COMMENTED_STRING => qr{\A [\"\s]* \#}xms;
+Readonly our $RE_SURROUNDED_BY_WHITESPACE => qr{\A [\"\s]* (.*?) [\"\s]* \z}xms;
+Readonly our $RE_WITHIN_PARENTHESES => qr{\( \s* (.*?) \s* \)}xms;
+Readonly our $RE_WITHIN_BRACKETS => qr{\[ \s* (.*?) \s* \]}xms;
+Readonly our $RE_SQUARE_BRACKETS => qr{( [\[\]] )}xms;
+Readonly our $RE_LINE_BREAK => qr{[\r\n]* \z}xms;
+
+##################################################################
+# Given a list and a value in that list, returns the value index #
+##################################################################
+
+sub get_indexcol {
+ my ( $columnlist, $columnname ) = @_;
+
+ ref $columnlist eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ defined($columnname) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # For efficiency; N.B. $columnname may contain whitepace, so no /x
+ # modifier here. Similarly we rely on the caller to specify /i or not.
+ my $re;
+
+ # Empty string is a special case (won't match \b)
+ if ( $columnname eq q{} ) {
+ $re = qr/\A\s*$columnname\s*\z/ms;
+ }
+ else{
+ $re = qr/\A\s*\b$columnname\b\s*\z/ms;
+ }
+ my $num_cols = scalar @{$columnlist};
+
+ for ( my $i = 0; $i < $num_cols; $i++ ) {
+ return $i if $columnlist->[$i] =~ $re;
+ }
+ return -1;
+}
+
+sub strip_discards {
+
+ # Sub to strip values out of an array (@line_array) based on a list
+ # of unwanted array indices (@indexcols). Does not modify the input
+ # lists.
+
+ my ( $indexcols, $line_array ) = @_;
+
+ ref $indexcols eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $line_array eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my %to_be_discarded = map { $_ => 1 } @$indexcols;
+
+ my @new_line;
+ my $last_index = $#$line_array; # Calculate this just once
+ foreach my $i ( 0 .. $last_index ) {
+ push( @new_line, $line_array->[$i] )
+ unless ( $to_be_discarded{$i} );
+ }
+
+ return \@new_line;
+
+}
+
+##############################################################
+# Round function to deal with floating-point rounding errors #
+##############################################################
+
+# stolen shamelessly from www.perlmonks.org
+
+sub round {
+ my ( $number, $precision ) = @_;
+ my $sign = ( $number > 0 ) ? 1 : -1; # store the sign for later
+
+ $precision ||= 0; # $precision should not be undefined
+
+ $number *= 10**$precision; # move the decimal place
+ # $precision places to the
+ # right
+
+ $number = int( $number + .5 * $sign ); # add 0.5, correct the sign and
+ # truncate the number after the
+ # decimal point, thereby
+ # rounding it
+
+ return ( $number / 10**$precision ); # move the decimal place back again
+}
+
+sub clean_hash {
+
+ # Strip out undef or empty string values
+
+ my ($hashref) = @_;
+
+ ref $hashref eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my %cleaned;
+ while ( my ( $key, $value ) = each %$hashref ) {
+ $cleaned{$key} = $value if ( defined($value) && $value ne q{} );
+ }
+
+ return \%cleaned;
+
+}
+
+sub check_linebreaks {
+
+ # Takes a filename as an argument, checks for Mac, Unix or Dos line
+ # endings by reading the whole file in chunks, and regexp
+ # matching the various linebreak types. Returns the appropriate
+ # linebreak for acceptable line breaks (line breaks must be
+ # unanimous FIXME), undef for not.
+
+ my $path = shift;
+ my $fh = IO::File->new( $path, '<' )
+ or croak(
+ "Error: Failed to open file $path for linebreak checking: $!\n");
+
+ my $bytelength = -s $path;
+
+ # Count all the line endings. This can get memory intensive
+ # (implicit list generation, can be over 1,000,000 entries for
+ # Affy CEL). We read the file in defined chunks to address this.
+ my ( $unix_count, $mac_count, $dos_count );
+ my $chunk_size = 3_000_000; # ~10 chunks to a big CEL file.
+ my $previous_final_char = q{};
+ for ( my $offset = 0; $offset < $bytelength; $offset += $chunk_size ) {
+
+ my $chunk;
+
+ my $bytes_read = read( $fh, $chunk, $chunk_size );
+
+ unless ( defined($bytes_read) ) {
+ croak("Error reading file chunk at offset $offset ($path): $!\n");
+ }
+
+ # Lists generated implicitly here.
+ $unix_count += () = ( $chunk =~ m{\N{LINE FEED}}g );
+ $mac_count += () = ( $chunk =~ m{\N{CARRIAGE RETURN}}g );
+ $dos_count += () = ( $chunk =~ m{\N{CARRIAGE RETURN}\N{LINE FEED}}g );
+
+ # DOS line endings could conceivably be split between chunks.
+ if ( $bytes_read ) { # Skip if at end of file.
+ if ( ( substr( $chunk, 0, 1 ) eq "\N{LINE FEED}" )
+ && ( $previous_final_char eq "\N{CARRIAGE RETURN}" ) ) {
+ $dos_count++;
+ }
+ $previous_final_char = substr( $chunk, -1, 1 );
+ }
+ }
+
+ close($fh)
+ or croak("Error closing file $path in sub check_linebreaks: $!\n");
+
+ my $dos = $dos_count;
+ my $mac = $mac_count - $dos_count;
+ my $unix = $unix_count - $dos_count;
+
+ # Set to undef on failure.
+ my $line_ending = undef;
+
+ # Determine the file line endings format, return the "standard" line
+ # ending to use
+ if ( $unix && !$mac && !$dos ) { # Unix
+ $line_ending = "\N{LINE FEED}";
+ }
+ elsif ( $mac && !$unix && !$dos ) { # Mac
+ $line_ending = "\N{CARRIAGE RETURN}";
+ }
+ elsif ( $dos && !$mac && !$unix ) { # DOS
+ $line_ending = "\N{CARRIAGE RETURN}\N{LINE FEED}";
+ }
+
+ # Calling in scalar context just gives $line_ending.
+ my $counts = {
+ unix => $unix,
+ dos => $dos,
+ mac => $mac,
+ };
+ return wantarray ? ( $counts, $line_ending ) : $line_ending;
+
+}
+
+sub date_now {
+
+ # Return current date and time as a MAGE best practice date-time
+ # string.
+
+ return mage_date(time);
+}
+
+sub mage_date {
+
+ my $datenum = shift;
+
+ # Return a MAGE best practice date-time string. Note that the
+ # following "Z" implies UTC, hence we use gmtime here.
+
+ my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime($datenum);
+ $mon++; # localtime starts months from 0
+ $year += 1900; # localtime starts years from 100
+
+ return sprintf( "%04d-%02d-%02dT%02d:%02d:%02dZ",
+ $year, $mon, $mday, $hour, $min, $sec );
+
+}
+
+sub untaint {
+
+ # Untaint the input (runs with perl -T switch)
+
+ my $string = shift;
+ my @nameparts = $string =~ m/[-a-zA-Z0-9\.@\-\+\_]+/g;
+ $string = join( '_', @nameparts );
+ return $string;
+}
+
+sub find_cdf {
+
+ # Given a filename and an optional directory, returns an actual
+ # filename found in a case-insensitive fashion. Preferentially
+ # checks the main Affy library directory if set in $CONFIG.
+ my ( $name, $dir ) = @_;
+
+ # Check Affy library dir or source dir or working dir.
+ my $querydir = $CONFIG->get_AFFYMETRIX_LIBRARYPATH()
+ || $dir
+ || q{.};
+
+ opendir (my $dirhandle, $querydir)
+ or croak("Error: Can't open directory $querydir");
+
+ my $actualname;
+ while ( my $candidate = readdir($dirhandle) ) {
+ if ( $name =~ /\A $candidate \z/ixms ) {
+ $actualname = $candidate;
+ last;
+ }
+ }
+
+ my $path = File::Spec->catfile( $querydir, $actualname );
+
+ # Beware calling in list context.
+ return $actualname ? ($actualname, $path)
+ : wantarray ? ()
+ : undef;
+}
+
+sub decamelize {
+
+ # Function to convert CamelCase strings to underscore_delimited.
+ my ( $camel ) = @_;
+
+ # Underscore separates internal capitals
+ $camel =~ s/([a-z])([A-Z])/$1\_$2/g;
+
+ # substitute spaces
+ $camel =~ s/\s+/\_/g;
+
+ # and then lowercase
+ $camel = lc($camel);
+
+ return $camel;
+}
+
+sub get_filepath_from_uri {
+
+ my ( $uri_string, $dir ) = @_;
+
+ require URI;
+
+ # N.B. URI module doesn't seem to like the file://my_filename.txt
+ # URI form, and confuses the path with the authority. URI module
+ # behaviour is probably correct, but the MAGE-TAB spec asks for
+ # this (invalid?) URI form. We fix that here:
+ $uri_string =~ s/\A file:\/\/ //ixms;
+
+ my $uri = URI->new( $uri_string );
+
+ # Assume file as default URI scheme.
+ my $path;
+ if ( ! $uri->scheme() || $uri->scheme() eq 'file' ) {
+
+ $uri->scheme('file');
+
+ # URI::File specific, this avoids quoting e.g. spaces in filenames.
+ my $uri_path = $uri->file();
+
+ if ( $dir ) {
+ $path = File::Spec->file_name_is_absolute( $uri_path )
+ ? $uri_path
+ : File::Spec->catfile( $dir, $uri_path );
+ }
+ else {
+ $path = File::Spec->rel2abs( $uri_path );
+ }
+ }
+ # Add the common network URI schemes.
+ elsif ( $uri->scheme() eq 'http' || $uri->scheme() eq 'ftp' ) {
+ $path = cache_network_file( $uri, $dir );
+ }
+ else {
+ croak(sprintf(
+ "ERROR: Unsupported URI scheme: %s\n", $uri->scheme(),
+ ));
+ }
+
+ return $path;
+}
+
+sub cache_network_file {
+
+ my ( $uri, $dir ) = @_;
+
+ require LWP::UserAgent;
+
+ # N.B. we don't handle URI fragments, just the path.
+ my ( $basename ) = ( $uri->path() =~ m!/([^/]+) \z!xms );
+
+ my $target;
+ if ( $dir ) {
+ $target = File::Spec->catfile( $dir, $basename );
+ }
+ else {
+ $target = $basename;
+ }
+
+ # Only download the file once.
+ unless ( -f $target ) {
+
+ printf STDOUT (
+ qq{Downloading network file "%s"...\n},
+ $uri->as_string(),
+ );
+
+ # Download the $uri->as_string()
+ my $ua = LWP::UserAgent->new();
+
+ my $response = $ua->get(
+ $uri->as_string(),
+ ':content_file' => $target,
+ );
+
+ unless ( $response->is_success() ) {
+ croak(sprintf(
+ qq{Error downloading network file "%s" : %s\n},
+ $uri->as_string(),
+ $response->status_line(),
+ ));
+ }
+ }
+
+ return $target;
+}
+
+sub mx2tab {
+
+ # Temporary method to wrap calls to mx_to_mtab until better error
+ # reporting is included in that call.
+
+ my ( $submission ) = @_;
+
+ eval {
+ require ExperimentConvert;
+ ExperimentConvert->import('mx_to_mtab');
+ };
+ if ($EVAL_ERROR) {
+ # FIXME not really an error; module may just not be
+ # available. This can probably be safely removed once
+ # ExperimentConvert is included in tab2mage core.
+ }
+ else {
+
+ eval {
+ mx_to_mtab($submission);
+ };
+
+ if ($EVAL_ERROR) {
+ # FIXME do something here (note that status has already
+ # been set). This will be more valuable once the module
+ # dies correctly on failure. N.B. may be better handled in
+ # the caller.
+ }
+ else {
+
+ # This is the best we can do at the moment to detect
+ # failure.
+ if ( $submission->status() =~ /\b fail(?:ed|ure) \b/ixms ) {
+ die("Error: mx_to_mtab process failed.");
+ }
+
+ # Submission should have been set to status='Waiting',
+ # experiment_type='MAGE-TAB' at this point. We also have
+ # to set the in_curation flag here:
+ $submission->set( in_curation => 1 );
+ $submission->update();
+ }
+ }
+
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Config.pm b/lib/ArrayExpress/Curator/Config.pm
new file mode 100644
index 0000000..4bc9189
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Config.pm
@@ -0,0 +1,589 @@
+#!/usr/bin/env perl
+#
+# Module to provide constant values for a local installation of the
+# ArrayExpress::Curator modules.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Config.pm 2092 2008-06-26 15:42:22Z tfrayner $
+#
+
+package ArrayExpress::Curator::Config;
+
+use strict;
+use warnings;
+
+use Config::YAML;
+use Tie::IxHash;
+use Readonly;
+use File::Spec;
+
+use base 'Exporter';
+our @EXPORT_OK = qw($CONFIG);
+
+my $module_path = File::Spec->rel2abs(__FILE__);
+my @module_dir_array = File::Spec->splitpath($module_path);
+
+# Always at least set these defaults.
+my $moduleconf = $ENV{PAR_TEMP} # running under PAR
+ ? File::Spec->catfile( $ENV{PAR_TEMP},
+ qw( inc lib ArrayExpress Curator Config.yml ) )
+ : File::Spec->catpath( @module_dir_array[ 0, 1 ], 'Config.yml' );
+
+# Add your site config path in the next line. Alternatively, create a
+# config file ~/.tab2mage.conf in your home directory.
+my $siteconf = q{};
+
+my $userconf = File::Spec->catpath( undef, $ENV{HOME}, '.tab2mage.conf' );
+
+our $CONFIG = Config::YAML->new(
+ config => $moduleconf,
+ output => $userconf,
+);
+
+if ($siteconf) {
+ $CONFIG->read($siteconf)
+ or croak("Error: Site config file $siteconf not found: $!\n");
+}
+
+# NOTE that order is critical in the arrays referenced below:
+# Acceptable columns for T2M raw and normalized data. These can also
+# be specified as qr// quoted strings, e.g. if case-insensitive
+# matching is desirable.
+tie my %datafile_indices, 'Tie::IxHash', (
+
+ # Preferential treatment given to Generic file format; this is
+ # because if we miss these and fixate on e.g. GenePix headers in
+ # the same file, we will then create duplicate MetaRow/MetaColumn
+ # headings, which is messy.
+ Generic => [qr/MetaColumn/i, qr/MetaRow/i, qr/Column/i, qr/Row/i],
+
+ GenePix => [qw(Block Column Row X Y)],
+ ArrayVision => [qw(Primary Secondary)],
+ Agilent => [qw(Row Col PositionX PositionY)],
+ Scanalyze => [qw(GRID COL ROW LEFT TOP RIGHT BOT)],
+ ScanArray => [
+ 'Array Column',
+ 'Array Row',
+ 'Spot Column',
+ 'Spot Row',
+ 'X',
+ 'Y'
+ ],
+ QuantArray => [ 'Array Column', 'Array Row', 'Column', 'Row' ],
+ Spotfinder => [qw(MC MR SC SR C R)],
+ MEV => [qw(MC MR C R UID)],
+ CodeLink => [qw(Logical_row Logical_col Center_X Center_Y)],
+ BlueFuse => [qw(COL ROW SUBGRIDCOL SUBGRIDROW)],
+ UCSFSpot => [qw(Arr-colx Arr-rowy Spot-colx Spot-rowy)],
+ NimbleScanFeature => [qw(X Y PROBE_ID X_PIXEL Y_PIXEL)],
+ NimblegenNASA => [qw(X_BC Y_BC Feature_ID ProbID_BC)],
+ ImaGene => ['Meta Column', 'Meta Row', 'Column', 'Row', 'Field', 'Gene ID', q{}],
+ ImaGene3 => [qw(Meta_col Meta_row Sub_col Sub_row Name Selected)],
+ ImaGene7 => [qw(Block Column Row), 'Ch1 XCoord', 'Ch1 YCoord', 'Ch2 XCoord', 'Ch2 YCoord'],
+ CSIRO_Spot => [qw(grid_c grid_r spot_c spot_r indexs)],
+
+ # N.B. the FGEM and FGEM_CS indices are assumed to reside here
+ # by other classes. Also AffyNorm and GEO.
+ FGEM => [qr/Reporter ?Identifier/i],
+ FGEM_CS => [qr/Composite ?Sequence ?Identifier/i],
+ GEO => [qw(ID_REF)],
+ AffyNorm => ['Probe ?Set ?(Name|ID)'],
+
+ # Lower specificity column headings.
+ NimbleScanNorm => [qw(X Y PROBE_ID)],
+ AppliedBiosystems => [qw(Probe_ID Gene_ID)],
+ ArrayVision_lg2 => ['Spot labels'],
+
+ # Very non-specific column headings are left till the end:
+ Illumina => [qw(PROBE_ID)],
+);
+
+# Some content is not appropriate for the config file, we handle it
+# internally here. This overwrites any siteconf changes, but can still
+# be overridden by userconf settings.
+my %default = (
+ MX_DBPARAMS => { PrintError => 0, RaiseError => 1 },
+ AE_DBPARAMS => { PrintError => 0, RaiseError => 1 },
+ AUTOSUBS_DBPARAMS => { PrintError => 0, RaiseError => 1 },
+
+ # Acceptable columns form tab2mage data. See above - this is a
+ # tied hash to retain order.
+ T2M_INDICES => \%datafile_indices,
+
+ # Ignored QTs for general QT checks on non-data matrix files.
+ IGNORED_QTS => [
+ qr/MetaColumn/i,
+ qr/MetaRow/i,
+ qr/Column/i,
+ qr/Row/i,
+ qr/Reporter ?(Name|Identifier)/i,
+ qr/Composite ?Sequence ?(Name|Identifier)/i,
+ qr/ID_REF/,
+ qr/X/,
+ qr/Y/,
+ qr/CellHeader=X/,
+ qr/Block/,
+ qr/Name/,
+ qr/ID/,
+ ],
+
+ # Order here is important - see Tab2MAGE.pm
+ T2M_FILE_TYPES => [qw(raw normalized)],
+ FGEM_FILE_TYPE => 'transformed',
+ RAW_DM_FILE_TYPE => 'measured_data_matrix',
+
+ # N.B. at least one value (1,2,4,8...) should be kept back so that
+ # we can always determine if the process has simply died (error
+ # code 255). Note also that the use of these codes will probably
+ # need to be modulated depending on whether we've got a tab2mage
+ # or MX submission.
+
+ # Innocent errors which may be ignored.
+ ERROR_INNOCENT => 2,
+
+ # Missing MIAME information.
+ ERROR_MIAME => 8,
+
+ # Parsing may fail or give erroneous results.
+ ERROR_PARSEBAD => 32,
+
+ # Parsing _will_ fail.
+ ERROR_PARSEFAIL => 128,
+
+ # Checking crashed (in a recoverable way).
+ ERROR_CHECKERCRASH => 512,
+
+ # Values used in tracking MIAME checklist compliance. This list
+ # will probably grow.
+ MIAME_RAWDATA => 1,
+ MIAME_NORMDATA => 2,
+ MIAME_FACTORVALUES => 4,
+ MIAME_NORMPROTOCOL => 8,
+ MIAME_ARRAYSEQ => 16,
+
+ # Values used in tracking AEDW suitability. Again, will probably grow.
+ AEDW_DESIGNTYPE => 1,
+ AEDW_HYBNUMBER => 2,
+ AEDW_ARRAYLOADED => 4,
+ AEDW_FACTORVALUES => 8,
+ AEDW_GOODDATA => 16,
+
+ ERROR_MESSAGE_ARGS => 'Bad parameters passed to method',
+ ERROR_MESSAGE_PRIVATE => 'Attempt to access a private method',
+
+ STATUS_PENDING => 'Waiting',
+ STATUS_DB_RETRIEVAL => 'Retrieving info from MX',
+ STATUS_CHECKING => 'Checking in progress',
+ STATUS_CRASHED => '** CHECKER CRASH **',
+ STATUS_PASSED => 'Checking passed',
+ STATUS_FAILED => 'Checking failed',
+ STATUS_EXPORT => 'MAGE-ML export',
+ STATUS_EXPORT_POSTPONED => 'Export postponed',
+ STATUS_EXPORT_ERROR => 'Export failed',
+ STATUS_COMPLETE => 'Complete',
+
+ EXPTCHECK_PROGNAME => 'expt_check.pl',
+ EXPTCHECK_VERSION => '3.2.2',
+
+ TAB2MAGE_PROGNAME => 'Tab2MAGE',
+ TAB2MAGE_VERSION => '2.2.2',
+
+ # This is now set in QT_list.pm
+ DEFAULT_QT_FILENAME => q{},
+ DEFAULT_ENTREZ_FILENAME => $ENV{PAR_TEMP} # running under PAR
+ ? File::Spec->catfile( $ENV{PAR_TEMP},
+ qw( inc lib ArrayExpress Curator Entrez_list.txt ) )
+ : File::Spec->catpath(
+ @module_dir_array[ 0, 1 ], 'Entrez_list.txt' ),
+
+ # N.B. these are processed using oct()
+ FILE_PERMISSIONS => '0666',
+ DIR_PERMISSIONS => '0777',
+);
+
+$CONFIG->fold( \%default );
+
+# Incorporate user config, if present.
+if ( $ENV{HOME} ) {
+ if ( -f $userconf ) {
+ warn("Reading user config file $userconf\n");
+ $CONFIG->read($userconf);
+ }
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Config.pm - a module defining general config options.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Config qw($CONFIG);
+
+=head1 DESCRIPTION
+
+This module provides definition of some configuration options used
+throughout the code. These options may be changed by editing the
+ArrayExpress/Curator/Config.yml file. If you wish to point your
+Tab2MAGE installation to an alternate site config YAML file, please
+edit the $siteconf variable in the ArrayExpress/Curator/Config.pm
+file. For user-specific configuration, this module will also read any
+.tab2mage.conf file located in your home directory (i.e.,
+$HOME/.tab2mage.conf).
+
+See L<ArrayExpress::Curator::MAGE::Definitions> for other
+MAGE-specific constants.
+
+=head2 Configuration options
+
+=over 2
+
+=item MX_DSN
+
+MIAMExpress data source name or DSN, e.g., "DBI:mysql:db_name:host:port".
+
+=item MX_USERNAME
+
+The username used to connect to the MIAMExpress database.
+
+=item MX_PASSWORD
+
+The password used to connect to the MIAMExpress database.
+
+=item AE_DSN
+
+For local ArrayExpress installations only. This is the ArrayExpress
+data source name or DSN, e.g., "DBI:Oracle:db_name:host:port" used in
+direct connection to the database. Note that the appropriate
+DBD::Oracle module must be installed for this to work.
+
+=item AE_USERNAME
+
+The username used to connect to the ArrayExpress database.
+
+=item AE_PASSWORD
+
+The password used to connect to the ArrayExpress database.
+
+=item AEDW_DSN
+
+For checking the data warehouse readiness of an experiment
+submission. This database connection is used in checking that any
+required array designs have been loaded into the data warehouse. This
+config value should be the ArrayExpress DW data source name or DSN,
+e.g., "DBI:Oracle:db_name:host:port" used in direct connection to the
+data warehouse. Note that the appropriate DBD::Oracle module must be
+installed for this to work.
+
+=item AEDW_USERNAME
+
+The username used to connect to the AEDW database.
+
+=item AEDW_PASSWORD
+
+The password used to connect to the AEDW database.
+
+=item AE_ARRAYDESIGN_LIST
+
+Remote ArrayExpress array design list web page. Used by Tab2MAGE,
+MIAMExpress and the experiment checker. This is currently accessible
+from outside EBI, and so the setting can be left as it is.
+
+=item AE_RETRIEVE_FEATURELIST
+
+The ArrayExpress web page root for retrieving array feature lists. In
+use, the accession number for the PhysicalArrayDesign is appended to
+AE_RETRIEVE_FEATURELIST before it is used in an HTTP GET.
+
+=item AE_RETRIEVE_ADF
+
+The ArrayExpress web page root for retrieving ADFs. In use, the
+database identifier for the PhysicalArrayDesign is appended to
+AE_RETRIEVE_ADF before it is used in an HTTP GET.
+
+=item AFFYMETRIX_LIBRARYPATH
+
+The path to a directory containing CDF files needed for parsing
+Affymetrix CHP files. The default value is an empty string, which
+indicates to the scripts that the CDF files are in the current working
+directory.
+
+=item T2M_PROTOCOL_PREFIX
+
+The prefix used to autogenerate reassigned protocol
+accessions. Default value is "P-TABM-". Please note that you should
+change this to prevent conflicts if you intend to use protocol
+accession reassignment and submit the resulting MAGE-ML to
+ArrayExpress.
+
+=item T2M_EXPERIMENT_PREFIX
+
+The prefix used in creating experiment accessions. This is used to
+check that a valid experiment accession number has been used in
+conjunction with the protocol reassignment mechanism.
+
+=item MAX_LWP_DOWNLOAD
+
+The maximum size of LWP::UserAgent downloads. This applies to
+ArrayExpress ADF and feature list downloads. Currently set to 40MB.
+
+=item MAX_DATAFILE_SIZE
+
+The maximum size of data file which these scripts will attempt to
+parse. Currently set to 100MB.
+
+=item VISUALIZE_FONT
+
+The name of the default font to use in visualization graph
+creation. This gets passed to the "dot" program. Currently set to
+"Courier".
+
+=item AUTOSUBS_PIDFILE
+
+Full path to the file used by autosubs_checkd.pl to track running
+instances of itself.
+
+=item AUTOSUBS_ADMIN
+
+Email address of the administrator responsible for managing the
+checker and exporter daemon processes. Emails will be sent to this
+address on abnormal termination of the process (e.g. on crashes).
+
+=item AUTOSUBS_ADMIN_USERNAME
+
+Login for the administrator responsible for managing the checker and
+exporter daemon processes. Other users are restricted from launching
+these daemons to aid in process management.
+
+=item AUTOSUBS_DOMAIN
+
+The default domain used in creating MAGE-ML identifiers when exporting
+experiments using the autosubmissions system. This is set by default
+to 'ebi.ac.uk'. Note that this does not affect submissions exported
+from a MIAMExpress database.
+
+=item AUTOSUBS_CURATOR_EMAIL
+
+The email to which enquiries will be directed from the autosubmissions
+web form.
+
+=item AUTOSUBS_SMTP_SERVER
+
+The SMTP server used to send notification email from the
+autosubmissions system.
+
+=item AUTOSUBS_DSN
+
+The DSN to use when connecting to the autosubmissions database
+system. Typically this will be of the form
+"DBI:mysql:dbname:host:port".
+
+=item AUTOSUBS_USERNAME
+
+The username to use when connecting to the autosubmissions database.
+
+=item AUTOSUBS_PASSWORD
+
+The password to use when connecting to the autosubmissions database.
+
+=item AUTOSUBMISSIONS_FILEBASE
+
+The filesystem path to the top-level directory where the
+autosubmissions system should store uploaded spreadsheets and data
+files.
+
+=item AUTOSUBMISSIONS_TARGET
+
+The filesystem directory into which new submissions are exported as
+MAGE-ML. A new directory, named using the automatically assigned
+experiment accession, will be created and populated.
+
+=item WEBFORM_TEMPLATE_DIR
+
+The filesystem directory where the web submissions form may write
+temporary files. This is principally used for temporary storage of
+spreadsheet template files. It is recommended that you run a regular
+script from e.g. your crontab to delete old files.
+
+=item WEBFORM_TEMPLATE_URL
+
+The URL pointing to the WEBFORM_TEMPLATE_DIR, used to create links to
+the temporary template files from the web submissions form.
+
+=item MX_AUTOSUBS_PIDFILE
+
+Full path to the file used by mx_autocheck_daemon.pl to track running
+instances of itself.
+
+=item MX_AUTOSUBS_ADMIN
+
+Email address of the administrator responsible for managing the
+mx_autocheck_daemon.pl process. See notes for AUTOSUBS_ADMIN.
+
+=item MX_MAGEML_EXPORT_COMMAND
+
+Full command (minus arguments) used to export MAGE-ML from a local
+MIAMExpress database.
+
+=item AEDW_DESIGN_TYPES
+
+A list of MO ExperimentDesignTypes that indicate an experiment is
+suitable for the ArrayExpress Data Warehouse.
+
+=item AEDW_UNWANTED_DESIGN_TYPES
+
+A list of MO ExperimentDesignTypes that indicate an experiment is not
+suitable for the ArrayExpress Data Warehouse. This is used to
+differentiate between experiments known to be of the wrong type from
+those which are merely under-annotated.
+
+=item AEDW_MINIMUM_HYBS
+
+The minimum number of hybridizations required in an experiment for it
+to be considered for the AE Data Warehouse.
+
+=item MIAME_COMPLIANT_ARRAY_PIPELINES
+
+A list of array design accession prefixes (e.g. "A-AFFY-") where every
+design can be assumed to be MIAME compliant, for the purposes of
+experiment MIAME checking.
+
+=back
+
+=head1 Private options
+
+These are options we recommend you don't change, unless you know what
+you're doing.
+
+=over 2
+
+=item T2M_INDICES
+
+A hashref, with keys representing data file format type and values as
+arrayrefs listing the coordinate index columns to be used for parsing
+those formats. See also L<ArrayExpress::Datafile>. Current format
+types are: Generic, GenePix, Affymetrix, ArrayVision, Agilent,
+Scanalyze, ScanArray, QuantArray, Spotfinder, BlueFuse, UCSF Spot,
+Illumina, CodeLink, Applied Biosystems, NimbleScan.
+
+=item T2M_FILE_TYPES
+
+Supported data file types for per-hyb parsing and MAGE-ML
+creation. These tags can appear in the Tab2MAGE spreadsheet as part of
+a File[] column heading. Currently supported: "raw" and "normalized".
+
+=item FGEM_FILE_TYPE
+
+Supported data file type for FGEM parsing and MAGE-ML creation. These
+tags can appear in the Tab2MAGE spreadsheet as part of a File[] column
+heading. Currently supported: "transformed".
+
+=item IGNORED_QTS
+
+A list of regular expression which match QTs which are omitted
+from analyses or MAGE-ML output from non-data matrix files.
+
+=item ERROR_INNOCENT, ERROR_MIAME, ERROR_PARSEBAD, ERROR_PARSEFAIL
+
+Errors returned to the shell by each of the scripts are represented by
+8-bit integers; here we map them to the constants used.
+
+=item ERROR_CHECKERCRASH
+
+Similar to the errors above, this error indicates that the checking
+process crashed for some reason.
+
+=item ERROR_MESSAGE_ARGS, ERROR_MESSAGE_PRIVATE
+
+A selection of internal error message texts.
+
+=item MX_EXTENDED_REPORT
+
+Controls whether extended reporting of sample annotation and factor
+values is available (only supported for generic MIAMExpress
+installations).
+
+=item DEFAULT_QT_FILENAME
+
+Name of the file to use as the default source of QT information. This
+is used to point to a file installed alongside the perl modules, and
+should not be changed unless you know what you're doing.
+
+=item DEFAULT_ENTREZ_FILENAME
+
+Name of the file containing a list of Entrez-approved publication
+abbreviations. Again, this value should not be changed unless strictly
+necessary.
+
+=item FILE_PERMISSIONS
+
+Octal number indicating the default permissions to use when creating
+files using the autosubmission system. This is useful if, for example,
+your webserver process is in a different group from that of your
+users. The default is 0555.
+
+=item DIR_PERMISSIONS
+
+Octal number indicating the default directory permissions for the
+autosubmission system. The default is 0777.
+
+=item MX_DBPARAMS
+
+A hashref of parameters used in the MIAMExpress database connection.
+
+=item AE_DBPARAMS
+
+A hashref of parameters used in the ArrayExpress database connection.
+
+=item AEDW_DBPARAMS
+
+A hashref of parameters used in the AEDW database connection.
+
+=item AUTOSUBS_DBPARAMS
+
+A hashref of parameters used in the autosubmissions database connection.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/Config.yml b/lib/ArrayExpress/Curator/Config.yml
new file mode 100644
index 0000000..9c1deab
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Config.yml
@@ -0,0 +1,89 @@
+---
+
+# See 'perldoc ArrayExpress::Curator::Config' for a full description
+# of these configuration options.
+
+# MIAMExpress connection settings.
+MX_DSN: ''
+MX_PASSWORD: ''
+MX_USERNAME: ''
+MX_EXTENDED_REPORT: 1
+
+# ArrayExpress database connection settings.
+AE_DSN: ''
+AE_PASSWORD: ''
+AE_USERNAME: ''
+
+# ArrayExpress warehouse connection settings.
+AEDW_DSN: ''
+AEDW_PASSWORD: ''
+AEDW_USERNAME: ''
+AEDW_DESIGN_TYPES:
+ - co-expression_design
+AEDW_UNWANTED_DESIGN_TYPES:
+ - comparative_genome_hybridization_design
+ - binding_site_identification_design
+ - genotyping_design
+ - tiling_path_design
+ - platform_comparison_design
+ - array_platform_variation_design
+ - hardware_variation_design
+ - software_variation_design
+ - operator_variation_design
+ - quality_control_testing_design
+ - optimization_design
+ - normalization_testing_design
+ - self_vs_self_design
+AEDW_MINIMUM_HYBS: 6
+
+# A list of array pipelines assumed to be MIAME compliant.
+MIAME_COMPLIANT_ARRAY_PIPELINES:
+ - A-AFFY-
+ - A-AGIL-
+ - A-BUGS-
+ - A-WMIT-
+ - A-GEHB-
+
+# ArrayExpress repository webpages.
+AE_RETRIEVE_ADF: http://www.ebi.ac.uk/aerep/lob?name=adss&id=
+AE_RETRIEVE_FEATURELIST: http://www.ebi.ac.uk/aerep/report?cmd=featurelist&array=
+AE_ARRAYDESIGN_LIST: http://www.ebi.ac.uk/aerep/report?cmd=arraydesignlist
+
+# Path to directory containing CDF files.
+AFFYMETRIX_LIBRARYPATH: ''
+
+# Autosubmission system settings.
+AUTOSUBS_ADMIN: ''
+AUTOSUBS_ADMIN_USERNAME: ''
+AUTOSUBS_PIDFILE: ''
+AUTOSUBS_DOMAIN: ebi.ac.uk
+
+AUTOSUBS_DSN: ''
+AUTOSUBS_USERNAME: ''
+AUTOSUBS_PASSWORD: ''
+
+AUTOSUBMISSIONS_FILEBASE: ''
+AUTOSUBMISSIONS_TARGET: ''
+
+AUTOSUBS_CURATOR_EMAIL: ''
+AUTOSUBS_SMTP_SERVER: ''
+
+WEBFORM_TEMPLATE_DIR: ''
+WEBFORM_TEMPLATE_URL: ''
+
+MX_AUTOSUBS_ADMIN: ''
+MX_AUTOSUBS_PIDFILE: ''
+MX_MAGEML_EXPORT_COMMAND: ''
+
+# Download size constraints. Note that $MAX_LWP_DOWNLOAD needs to be
+# at least as big as the downloaded arraydesignlist, otherwise the
+# script won't be able to find all the array design database ids.
+MAX_DATAFILE_SIZE: 104857600
+MAX_LWP_DOWNLOAD: 62914560
+
+# Settings for protocol accession reassignment.
+T2M_EXPERIMENT_PREFIX: 'E-[A-Z]{4}-'
+T2M_PROTOCOL_PREFIX: P-TABM-
+
+# The font used in GraphViz graph PNG generation.
+VISUALIZE_FONT: Courier
diff --git a/lib/ArrayExpress/Curator/Database.pm b/lib/ArrayExpress/Curator/Database.pm
new file mode 100644
index 0000000..602831b
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Database.pm
@@ -0,0 +1,672 @@
+#!/usr/bin/env perl
+#
+# Database.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2004 ArrayExpress team, EBI
+#
+# $Id: Database.pm 2027 2008-04-16 10:25:26Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../index.html">
+ <img src="../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Database.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Database - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Database qw(retrieve_AE_adf parse_adf);
+
+ my $db_id = 123456789;
+ my ( $fh, $accession ) = retrieve_AE_adf( $db_id, undef, \*STDERR, 0 );
+ my $array_design = parse_adf( $fh, $accession );
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to interact with the ArrayExpress databases. Queries may
+be redirected to local ArrayExpress instances by editing values in
+the L<ArrayExpress::Curator::Config> package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<get_ae_dbh()>
+
+Returns a singleton database handle for the currently-configured
+ArrayExpress repository database instance.
+
+=item C<get_aedw_dbh()>
+
+Returns a singleton database handle for the currently-configured
+ArrayExpress warehouse database instance.
+
+=item C<retrieve_AE_adf( $db_id, $accession, $error_fh, $skip_adf_download )>
+
+Given either AE database table row identifier or AE accession, an open
+filehandle for error reporting and a flag indicating whether the ADF
+should be downloaded, check for the existence of said ADF (by database
+id or accession), download it and return an opened filehandle and the
+accession number (the latter is useful when querying by database id).
+
+=item C<retrieve_AE_featurelist( $accession )>
+
+Given an array accession number, return a hashref where the keys are
+q{.}-delimited lists of MetaColumn.MetaRow.Column.Row feature
+coordinates, and the values are Reporter identifiers.
+
+=item C<parse_adf( $adf_fh, $accno, $reporter_prefix, $compseq_prefix )>
+
+Passed an ADF filehandle and an accession number (see
+retrieve_AE_adf()), and optional Reporter or Composite Sequence
+identifier prefixes, parse the ADF and return an ArrayDesign object
+(see L<ArrayExpress::Datafile::ArrayDesign>).
+
+=item C<arrayaccession_in_aedw( $accession )>
+
+Given an array design accession number, return true if the design is
+loaded into the currently-configured AE data warehouse, false
+otherwise.
+
+=item C<map_affy_accno_to_name( $accession )>
+
+Given an array design accession number, attempt to match it to an
+Affymetrix design name. This relies on the names of loaded Affy array
+designs including the design name (e.g. HG-U133A) in square brackets
+(e.g. "Affymetrix GeneChip Human Genome HG-U133A [HG-U133A]").
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Database;
+
+use strict;
+use warnings;
+
+use LWP::UserAgent;
+use List::Util qw( min );
+use Scalar::Util qw( openhandle );
+
+use Carp;
+use IO::File;
+
+use ArrayExpress::Curator::Common qw(
+ get_indexcol
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::Datafile::ArrayDesign;
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ get_ae_dbh
+ get_aedw_dbh
+ retrieve_AE_adf
+ retrieve_AE_featurelist
+ parse_adf
+ arrayaccession_in_aedw
+ map_affy_accno_to_name
+ get_ae_array_details
+);
+
+# Globals to hold AE and AEDW database handles, if needed.
+my ($AE_DBH, $AEDW_DBH);
+
+# Global to store retrieved AE array accession number table; we only
+# want to retrieve it once.
+my @AE_ARRAY_ACCNO_TABLE;
+
+sub _connect_to_oracle {
+
+ my ($dsn, $username, $password, $params) = @_;
+
+ eval {
+ require DBI;
+ require DBD::Oracle;
+ };
+ if ($@) {
+ croak(<<END_ERROR);
+
+Error: Connection to a local ArrayExpress database instance requires
+both the DBI and the DBD::Oracle modules to be installed.
+The following error was encountered:
+
+$@
+
+END_ERROR
+ }
+
+ my $dbh = DBI->connect( $dsn, $username, $password, $params )
+ or croak( "Error: Cannot connect to local ArrayExpress"
+ . " database instance: $DBI::errstr\n" );
+
+ return $dbh;
+}
+
+sub get_ae_dbh {
+
+ # If we're not yet connected, and we know how to, do so.
+ if ( ! $AE_DBH ) {
+ if ( $CONFIG->get_AE_DSN() ) {
+ print STDOUT ("Connecting to ArrayExpress database...\n");
+ $AE_DBH = _connect_to_oracle(
+ $CONFIG->get_AE_DSN(), $CONFIG->get_AE_USERNAME(),
+ $CONFIG->get_AE_PASSWORD(), $CONFIG->get_AE_DBPARAMS()
+ );
+ }
+ }
+
+ # Check that we're still connected, and reconnect if
+ # necessary. N.B. This still doesn't fix the problem of sharing
+ # DBH between forked processes, which is generally frowned upon
+ # anyway.
+ elsif ( ! $AE_DBH->ping() ) {
+ print STDOUT ("Reconnecting to ArrayExpress database...\n");
+ $AE_DBH = _connect_to_oracle(
+ $CONFIG->get_AE_DSN(), $CONFIG->get_AE_USERNAME(),
+ $CONFIG->get_AE_PASSWORD(), $CONFIG->get_AE_DBPARAMS()
+ );
+ }
+
+ return $AE_DBH;
+}
+
+sub get_aedw_dbh {
+
+ # If we're not yet connected, and we know how to, do so.
+ if ( !$AEDW_DBH && $CONFIG->get_AEDW_DSN() ) {
+ print STDOUT ("Connecting to ArrayExpress Data Warehouse...\n");
+ $AEDW_DBH = _connect_to_oracle(
+ $CONFIG->get_AEDW_DSN(), $CONFIG->get_AEDW_USERNAME(),
+ $CONFIG->get_AEDW_PASSWORD(), $CONFIG->get_AEDW_DBPARAMS()
+ );
+ }
+
+ return $AEDW_DBH;
+}
+
+sub map_affy_accno_to_name {
+
+ my ( $accno ) = @_;
+
+ unless ( scalar @AE_ARRAY_ACCNO_TABLE ) {
+ retrieve_accno_table();
+ }
+
+ my $affyname = q{};
+
+ AE_ACCNO_LINE:
+ foreach my $line ( @AE_ARRAY_ACCNO_TABLE ) {
+ next AE_ACCNO_LINE if ( $line =~ m/^\s*$/ ); # skip empty lines
+
+ my @line_array = split /\t/, $line;
+ if ( $line_array[1] eq $accno ) {
+ ($affyname) = ($line_array[2] =~ m/\[ ([^\]]+) \]/xms);
+ last AE_ACCNO_LINE;
+ }
+ }
+
+ return $affyname;
+}
+
+sub parse_adf { # Accepts filehandles, not filenames. This is to allow
+ # the use of temporary files via IO::File when getting
+ # the ADF from ArrayExpress.
+
+ my ( $adf_filehandle, $accno, $reporter_prefix, $compseq_prefix ) = @_;
+
+ openhandle($adf_filehandle)
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $feature_indices = [];
+ my $reporter_indices = [];
+ my $compseq_indices = [];
+
+ # Special case of Affymetrix identifiers. This is tied to the
+ # ArrayExpress accession number format.
+ if ( $accno && $accno =~ m/\A A-AFFY-\w+ \z/xms ) {
+
+ # Use the AE-derived name (in brackets) where available,
+ # otherwise fall back to a simple default.
+ my $affyname = map_affy_accno_to_name($accno) || '[^:]*';
+
+ # Don't override user-supplied prefixes.
+ unless (defined $reporter_prefix) {
+ $reporter_prefix = "Affymetrix:Reporter:$affyname:";
+ }
+ unless (defined $compseq_prefix) {
+ $compseq_prefix = "Affymetrix:CompositeSequence:$affyname:";
+ }
+ }
+
+ # First, scan down through whatever header there is.
+ ADF_HEADER_LINE:
+ while ( my $line = <$adf_filehandle> ) {
+
+ chomp $line;
+
+ # Start parsing the ADF on the first line containing both
+ # Identifier and Name columns (either Reporter or
+ # CompositeSequence). This replaces a parsing start based on
+ # the number of columns in the ADF, which doesn't work for too
+ # many crappy ADFs in AE.
+ my $identifier_re = qr/(Reporter|Composite [ ]* Sequence) [ ]* Identifier/ixms;
+ my $name_re = qr/(Reporter|Composite [ ]* Sequence) [ ]* Name/ixms;
+ if ( $line =~ m/(\A|\t) [ ]* $identifier_re [ ]* (\t|\z)/xms
+ && $line =~ m/(\A|\t) [ ]* $name_re [ ]* (\t|\z)/xms ) {
+
+ # Scan through the column headings for identifiers and coord headings.
+ my @line_array = split /\t/, $line;
+
+ # Feature coordinates.
+ foreach my $indexname qw(MetaColumn MetaRow Column Row) {
+ my $colnum = get_indexcol( \@line_array, qr/$indexname/i );
+ push( @$feature_indices, $colnum ) unless ( $colnum < 0 );
+ }
+
+ # 'Reporter ?Identifier'
+ foreach my $indexname (
+ @{ $CONFIG->get_T2M_INDICES()->{FGEM} } ) {
+ my $colnum = get_indexcol( \@line_array, qr/$indexname/i );
+ push( @$reporter_indices, $colnum ) unless ( $colnum < 0 );
+ }
+
+ # 'Composite ?Sequence ?Identifier'
+ foreach my $indexname (
+ @{ $CONFIG->get_T2M_INDICES()->{FGEM_CS} } ) {
+ my $colnum = get_indexcol( \@line_array, qr/$indexname/i );
+ push( @$compseq_indices, $colnum ) unless ( $colnum < 0 );
+ }
+
+ last ADF_HEADER_LINE
+ if ( ( $#$feature_indices == 3 )
+ || @$reporter_indices
+ || @$compseq_indices ); # Require one of these
+ }
+ }
+
+ unless ( @$reporter_indices || @$compseq_indices ) {
+ print STDOUT (
+ "Error: ADF does not contain reporter or composite sequence identifiers! ",
+ "Combined data matrix file checking will not work properly.\n"
+ );
+ }
+
+ # Set up our identifier prefix regexps. Either use a supplied
+ # prefix, or default to using MIAMExpress-style prefixes.
+ my $reporter_prefix_regexp
+ = defined($reporter_prefix) ? qr/\A $reporter_prefix/xms
+ : defined($accno) ? qr/\A (ebi.ac.uk:MIAMExpress:Reporter:$accno\.|R:$accno:)/xms
+ : undef;
+ my $compseq_prefix_regexp
+ = defined($compseq_prefix) ? qr/\A $compseq_prefix/xms
+ : defined($accno) ? qr/\A (ebi.ac.uk:MIAMExpress:CompositeSequence:$accno\.|CS:$accno:)/xms
+ : undef;
+
+ # Then process the remainder of the adf lines. %design_elements
+ # is a hash of (potentially) three sub-hashes, themselves with
+ # keys composed of feature coords, reporter ids or compseq
+ # ids.
+ my (%design_elements);
+ while ( my $line = <$adf_filehandle> ) {
+ chomp $line;
+
+ # split is faster than a regexp.
+ my @line_array = split /\t/, $line, -1;
+
+ # Features
+ if (@$feature_indices) {
+
+ my @coords;
+ foreach my $index (@$feature_indices) {
+ push( @coords, $line_array[$index] );
+ }
+ my $key = join( ".", @coords );
+ $design_elements{adf_features}{$key}++;
+ }
+
+ # Reporters (one column only)
+ if ( @$reporter_indices == 1
+ && $line_array[ $reporter_indices->[0] ] ) {
+
+ my $key = $line_array[ $reporter_indices->[0] ];
+
+ if ( defined($reporter_prefix_regexp) ) {
+ $key =~ s/$reporter_prefix_regexp//;
+ }
+
+ $design_elements{adf_reporters}{$key}++;
+ }
+
+ # Composite Sequences (one column only)
+ if ( @$compseq_indices == 1
+ && $line_array[ $compseq_indices->[0] ] ) {
+
+ my $key = $line_array[ $compseq_indices->[0] ];
+
+ if ( defined($compseq_prefix_regexp) ) {
+ $key =~ s/$compseq_prefix_regexp//;
+ }
+
+ $design_elements{adf_compseqs}{$key}++;
+ }
+ }
+
+ my $array = ArrayExpress::Datafile::ArrayDesign->new({
+ accession => $accno,
+ });
+
+ $array->set_features( $design_elements{adf_features} || {} );
+ $array->set_reporters( $design_elements{adf_reporters} || {} );
+ $array->set_compseqs( $design_elements{adf_compseqs} || {} );
+
+ return ( $array );
+}
+
+sub retrieve_accno_table {
+
+ if ( my $dbh = get_ae_dbh() ) {
+
+ # If possible, connect via DBI to avoid public/private array
+ # design issues.
+ print STDOUT (
+ "\nConnecting to ArrayExpress database...retrieving accession number table.\n"
+ );
+
+ # Select just the fully-loaded array designs (having an entry
+ # in PL_LABEL).
+ my $sth = $dbh->prepare(<<'QUERY');
+select i.id, i.identifier, i.name
+from tt_identifiable i,
+ tt_physicalarraydesign a,
+ pl_label l
+where i.id=a.id
+and i.id=l.mainobj_id
+QUERY
+
+ $sth->execute()
+ or croak("Error retrieving array list from database: " . $sth->errstr);
+
+ while ( my $result = $sth->fetchrow_arrayref() ) {
+ push @AE_ARRAY_ACCNO_TABLE, join("\t", map { $_ || q{} } @{ $result } );
+ }
+ }
+ else {
+
+ # Fall back to web access. Note that private arrays are
+ # unavailable here.
+ print STDOUT (
+ "\nConnecting to ArrayExpress web site...retrieving accession number table.\n"
+ );
+
+ my $ua = LWP::UserAgent->new();
+ $ua->max_size( $CONFIG->get_MAX_LWP_DOWNLOAD() );
+
+ my $response = $ua->get( $CONFIG->get_AE_ARRAYDESIGN_LIST() );
+
+ unless ( $response->is_success ) {
+ croak( "Error connecting to ArrayExpress: "
+ . $response->status_line
+ . "\n" );
+ }
+
+ # If the database goes down, ArrayExpress can return an error
+ # page. We trap this as HTML.
+ if ( $response->content =~ m/^\s*<html>/i ) {
+ croak(
+ "Error connecting to ArrayExpress (web query returned HTML, not text).\n"
+ );
+ }
+
+ @AE_ARRAY_ACCNO_TABLE = split /\n/, $response->content; # split into rows
+ }
+
+ return;
+}
+
+sub get_ae_array_details {
+
+ my ( $args ) = @_;
+
+ my $accession = $args->{'accession'};
+ my $db_id = $args->{'database_id'};
+ my $array_name;
+
+ unless ( $accession || $db_id ) {
+ croak("Error: Neither accession nor database ID given for array design.");
+ }
+
+ # Only get this once; we use the @AE_ARRAY_ACCNO_TABLE global to
+ # store the array design id -> accno -> name mapping.
+ unless ( scalar @AE_ARRAY_ACCNO_TABLE ) { # Should contain at least one row!
+ retrieve_accno_table();
+ }
+
+ AE_ACCNO_LINE:
+ foreach my $line ( @AE_ARRAY_ACCNO_TABLE ) {
+ next AE_ACCNO_LINE if ( $line =~ m/^\s*$/ ); # skip empty lines
+
+ my @line_array = split /\t/, $line;
+ if ( $db_id && $line_array[0] == $db_id ) {
+ $accession = $line_array[1];
+ $array_name = $line_array[2];
+ last AE_ACCNO_LINE;
+ }
+ if ( $accession && $line_array[1] eq $accession ) {
+ $db_id = $line_array[0];
+ $array_name = $line_array[2];
+ last AE_ACCNO_LINE;
+ }
+ }
+
+ {
+ # Hide the exceptions from our sig handler (but set $@).
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ unless ( $accession && $db_id ) {
+ if ( $db_id ) {
+ die(
+ q{Error: Could not find array accession number }
+ . qq{for database identifier "$db_id" in ArrayExpress.\n}
+ );
+ }
+ if ( $accession ) {
+ die(
+ q{Error: Could not find array database identifier }
+ . qq{for accession number "$accession" in ArrayExpress.\n}
+ );
+ }
+ die(
+ "Error: Neither accession number nor database identifier "
+ . "for array found in ArrayExpress.\n"
+ );
+ }
+ $SIG{__DIE__} = $sighandler;
+ }
+
+ return wantarray ?
+ ($accession, $db_id, $array_name) : $accession;
+}
+
+sub retrieve_AE_adf {
+
+ my ( $db_id, $accession, $error_fh, $skip_adf_download ) = @_;
+
+ unless ( $accession || $db_id ) {
+ carp(
+ "ERROR: Neither array accession nor AE database identifier available "
+ . "for at least one array.\n" );
+ return;
+ }
+
+ openhandle($error_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $array_name;
+
+ ( $accession, $db_id, $array_name )
+ = get_ae_array_details({ accession => $accession,
+ database_id => $db_id, });
+
+ if ( $array_name && $array_name =~ m/Agilent/i ) {
+ print $error_fh (
+ "Warning: Agilent array design used: $array_name. "
+ . "The orientation of the array may be incorrect.\n"
+ );
+ }
+
+ # Get a tab-delimited ADF from ArrayExpress, unless we've been
+ # told not to (e.g. we're skipping the data file checks):
+ my $temporary_adf_filehandle;
+ if ( $skip_adf_download ) {
+ print STDOUT (
+ "Skipping ArrayExpress ADF download.\n"
+ );
+ }
+ else {
+ print STDOUT (
+ "Connecting to ArrayExpress..."
+ . "retrieving ADF for Accession Number $accession.\n"
+ );
+
+ my $ua = LWP::UserAgent->new();
+ $ua->max_size( $CONFIG->get_MAX_LWP_DOWNLOAD() );
+
+ my $response = $ua->get( $CONFIG->get_AE_RETRIEVE_ADF() . $db_id );
+
+ unless ( $response->is_success ) {
+ croak( "Error retrieving ADF from ArrayExpress: "
+ . $response->status_line
+ . "\n" );
+ }
+
+ # put it into a temporary filehandle, unless it was too big to download
+ $temporary_adf_filehandle = IO::File->new_tmpfile;
+ if ( $response->headers()->header('Client-Aborted') ) {
+ warn(
+ "Warning: ADF size exceeds download limit. Feature parsing will be skipped.\n"
+ );
+ }
+ else {
+ print $temporary_adf_filehandle ( $response->content );
+ seek $temporary_adf_filehandle, 0,
+ 0; # reset the filehandle for reading
+ }
+ }
+
+ return wantarray
+ ? ( $temporary_adf_filehandle, $accession )
+ : $temporary_adf_filehandle;
+
+}
+
+sub retrieve_AE_featurelist {
+
+ my ($accession) = @_;
+
+ $accession or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Get feature IDs from ArrayExpress:
+ print STDOUT (
+ "Attempting to connect to ArrayExpress...retrieving feature identifiers for Array accession $accession.\n"
+ );
+
+ my $ua = LWP::UserAgent->new();
+ $ua->max_size( $CONFIG->get_MAX_LWP_DOWNLOAD() );
+
+ my $response
+ = $ua->get( $CONFIG->get_AE_RETRIEVE_FEATURELIST() . $accession );
+
+ unless ( $response->is_success ) {
+ croak( "Error retrieving feature list from ArrayExpress: "
+ . $response->status_line
+ . "\n" );
+ }
+
+ my $coords_to_identifiers; # undef has a meaning here
+ if ( $response->headers()->header('Client-Aborted') ) {
+ warn("Warning: Feature table size exceeds download limit.\n");
+ }
+
+ # HTML or empty string signifies an error.
+ elsif ( $response->content && ( $response->content !~ m/<html>/i ) ) {
+ %$coords_to_identifiers = map {
+ my ( @coords, $identifier );
+ ( @coords[ 0 .. 3 ], $identifier ) = ( split /\t/ );
+ join( '.', @coords ) => $identifier;
+ } split /[\r\n]+/, $response->content;
+ }
+
+ return $coords_to_identifiers;
+
+}
+
+sub arrayaccession_in_aedw {
+
+ # Given an array accession, query the DW and return the number of
+ # rows that match (i.e. zero if not loaded).
+
+ my ($accession) = @_;
+
+ my $dbh = get_aedw_dbh();
+
+ unless ($dbh) {
+ warn("AEDW database handle not available; skipping array check.\n");
+ return;
+ }
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct * from AE2__ARRAYDESIGN
+where ARRAYDESIGN_ACCESSION=?
+QUERY
+
+ $sth->execute($accession);
+
+ my $count = scalar @{ $sth->fetchall_arrayref() };
+
+ $sth->finish();
+
+ return $count;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Entrez_list.pm b/lib/ArrayExpress/Curator/Entrez_list.pm
new file mode 100644
index 0000000..e034bd9
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Entrez_list.pm
@@ -0,0 +1,121 @@
+#!/usr/bin/env perl
+#
+# Entrez_list.pm
+#
+# $Id: Entrez_list.pm 1941 2008-02-11 12:27:15Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../index.html">
+ <img src="../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Entrez_list.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Entrez_list - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Entrez_list qw(parse_entrez_names);
+
+ my $is_recognized = parse_entrez_names();
+ die("Error: Unknown journal: $pub") unless $is_recognized->{ $pub };
+
+=head1 DESCRIPTION
+
+This module provides a very basic interface to the listing of approved
+Entrez publication names included with the Tab2MAGE package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<parse_entrez_names( $fh )>
+
+Parses the contents of either the optional passed filehandle, or the
+internally-defined Entrez_list.txt filehandle, to generate a hashref
+of publication name keys and true values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Entrez_list;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw($RE_LINE_BREAK);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(parse_entrez_names);
+
+sub parse_entrez_names {
+
+ # Optional filehandle argument, default is to read the standard
+ # list ($CONFIG->get_DEFAULT_ENTREZ_FILENAME()).
+ my $passed_fh = shift;
+
+ # Get the Entrez list into memory
+ my %entrez_approved;
+ my $entrez_fh = $passed_fh || _entrez_list_fh();
+
+ while ( my $line = <$entrez_fh> ) {
+ $line =~ s/$RE_LINE_BREAK//xms;
+ $entrez_approved{$line}++;
+ }
+
+ return \%entrez_approved;
+}
+
+sub _entrez_list_fh {
+
+ my $filehandle
+ = IO::File->new( $CONFIG->get_DEFAULT_ENTREZ_FILENAME(), '<' )
+ or croak( "Error opening Entrez publications list file "
+ . $CONFIG->get_DEFAULT_ENTREZ_FILENAME()
+ . ": $!\n" );
+
+ return ($filehandle);
+
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Entrez_list.txt b/lib/ArrayExpress/Curator/Entrez_list.txt
new file mode 100644
index 0000000..e7701b9
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Entrez_list.txt
@@ -0,0 +1,19188 @@
+1199 News
+20 Century Br Hist
+21st Century Sci Technol
+A & M
+A A G Bijdr
+A M A Arch Ind Hyg Occup Med
+AACN Clin Issues
+AACN Clin Issues Crit Care Nurs
+AADE Ed J
+AAHS J
+AANA J
+AANNT J
+AAOHN J
+AAPPO J
+AAPS J
+AAPS PharmSci
+AAPS PharmSciTech
+AARC Times
+AARN News Lett
+AB Bookm Wkly
+ABA J
+ABC Decor
+ABNF J
+ACE Q
+ACES Bull
+ACM Queue
+ACOG Comm Opin
+ACOG Educ Bull
+ACOG Tech Bull
+ACP J Club
+ACRH Rep
+AD Nurse
+ADA News
+ADM
+AFL CIO Am Fed
+AFOSR TN United States Air Force Off Sci Res
+AFOSR TR United States Air Force Off Sci Res
+AGSO J Aust Geol Geophys
+AHA Hosp Technol Ser
+AHIP Cover
+AHME J
+AHP J
+AIA J
+AIAA Stud J
+AICC Econ Rev
+AIChE J
+AID Eval News
+AIDS
+AIDS Action
+AIDS Action Policy Brief
+AIDS Action Update
+AIDS Alert
+AIDS Anal Afr
+AIDS Anal Asia
+AIDS Asia
+AIDS Behav
+AIDS Care
+AIDS Clin Care
+AIDS Clin Rev
+AIDS Educ Prev
+AIDS Forsch
+AIDS Health Promot Exch
+AIDS Illus
+AIDS Inf Exch
+AIDS Inst Newsl
+AIDS Patient Care
+AIDS Patient Care STDS
+AIDS Policy Law
+AIDS Public Policy J
+AIDS Read
+AIDS Res
+AIDS Res Hum Retroviruses
+AIDS Res Ther
+AIDS Rev
+AIDS STD Health Promot Exch
+AIDS Soc
+AIDS Treat News
+AIDS Updates
+AIDS Watch
+AIDS Wkly
+AIDS Wkly Plus
+AIDSED Newsl
+AIDSlink
+AIHA J (Fairfax, Va)
+AIHAJ
+AIPLA Q J
+AJNR Am J Neuroradiol
+AJR Am J Roentgenol
+AJS
+AJS Rev
+ALAFO
+ALTEX
+AM Rep
+AMA Am J Dis Child
+AMA Arch Derm
+AMA Arch Derm Syphilol
+AMA Arch Ind Health
+AMA Arch Intern Med
+AMA Arch Neurol
+AMA Arch Neurol Psychiatry
+AMA Arch Ophthalmol
+AMA Arch Otolaryngol
+AMA Arch Pathol
+AMA Arch Surg
+AMA J Dis Child
+AMB Rev Assoc Med Bras
+AMD TR Rep
+AMDI Boll
+AMFAR Rep
+AMHC Forum
+AMIA Annu Symp Proc
+AMRL TR
+AMRO
+AN Ark Nurse
+ANA Clin Conf
+ANA Clin Sess
+ANA Publ
+ANEC
+ANL Rep
+ANNA J
+ANPHI Pap
+ANQ
+ANS Adv Nurs Sci
+ANU Hist J
+ANZ J Surg
+AOHA
+AOHA Today
+AORN J
+APA Newsl Philos Med
+APDI Newsl
+APIC
+APMIS
+APMIS Suppl
+AQ
+ARAG Natl Newsl
+ARN J
+ARTnews
+ASA Newsl
+ASAIO J
+ASAIO Trans
+ASDA News
+ASDC J Dent Child
+ASEAN Econ Bull
+ASGSB Bull
+ASHA
+ASHA Monogr
+ASHA Suppl
+ASM News
+ASNA Rep
+ASTM Stand News
+ATLA Abstr
+AUAA J
+AVISO
+AVS News
+AVSC News
+AWHONN Lifelines
+AWHONN Voice
+AWHONNS Clin Issues Perinat Womens Health Nurs
+AWR Bull
+Aachen Kunstbl
+Aastaraam Eesti Tead Seltsi Rootsis
+Abbottempo
+Abdom Imaging
+Abdom Surg
+Aberdeen Univ Rev
+Abh Ber Dtsch Mus
+Abh Ber Staatl Mus Voelkerkd Dres
+Abh Gesch Med Naturwiss
+Aborig Isl Health Work J
+Abort Res Notes
+Abortion Rev
+Abr Nahain
+Abstr Gen Meet Am Soc Microbiol
+Abstr Soc Neurosci
+Acad Bookman
+Acad Emerg Med
+Acad Manage J
+Acad Manage Rev
+Acad Med
+Acad News
+Acad Nurse
+Acad Peru Cir
+Acad Psychiatry
+Acad Radiol
+Acad Rev Calif Acad Periodontol
+Academic Psychol Bull
+Acadiensis
+Acarologia
+Acc Chem Res
+Accad Bibl Ital
+Accad Medica
+Accad Naz Lincei Rome
+Accid Anal Prev
+Accid Emerg Nurs
+Account Res
+Acog Nurse Bull
+Acoust Res Lett Online
+Acquir Immunodefic Syndr (AIDS) Wkly Surveill Rep
+Acquis Med Recent
+Across Board (NY)
+Act Hepato
+Act Nerv Super (Praha)
+Acta Acad Med Wuhan
+Acta Acad Sci Pol
+Acta Agron
+Acta Allergol
+Acta Allergol Suppl (Copenh)
+Acta Anaesthesiol
+Acta Anaesthesiol Belg
+Acta Anaesthesiol Ital
+Acta Anaesthesiol Scand
+Acta Anaesthesiol Scand Suppl
+Acta Anaesthesiol Sin
+Acta Anaesthesiol Taiwan
+Acta Anat (Basel)
+Acta Anat Suppl (Basel)
+Acta Anthropogenet
+Acta Antiq Hung
+Acta Appl Math
+Acta Argent Fisiol Fisiopatol
+Acta Astronaut
+Acta Balt
+Acta Belg Arte Med Pharm Mil
+Acta Belg Hist Med
+Acta Belg Med Phys
+Acta Biochim Biophys Acad Sci Hung
+Acta Biochim Biophys Hung
+Acta Biochim Biophys Sin (Shanghai)
+Acta Biochim Pol
+Acta Biol Acad Sci Hung
+Acta Biol Exp (Warsz)
+Acta Biol Hung
+Acta Biol Med (Gdansk)
+Acta Biol Med Ger
+Acta Biomed Ateneo Parmense
+Acta Biotechnol
+Acta Biotheor
+Acta Bot Gallica
+Acta Brevia Neerl Physiol Pharmacol Microbiol E A
+Acta Cancerol (Lima)
+Acta Cardiol
+Acta Cardiol Suppl
+Acta Chem Scand
+Acta Chem Scand A
+Acta Chem Scand B
+Acta Chir Acad Sci Hung
+Acta Chir Belg
+Acta Chir Hung
+Acta Chir Ital
+Acta Chir Iugosl
+Acta Chir Orthop Traumatol Cech
+Acta Chir Patav
+Acta Chir Plast
+Acta Chir Scand
+Acta Chir Scand Suppl
+Acta Cient Venez
+Acta Cir Bras
+Acta Cl
+Acta Clin Belg
+Acta Clin Belg Suppl
+Acta Clin Odontol
+Acta Crystallogr
+Acta Crystallogr A
+Acta Crystallogr B
+Acta Crystallogr C
+Acta Crystallogr D Biol Crystallogr
+Acta Crystallograph Sect F Struct Biol Cryst Commun
+Acta Cytol
+Acta Demogr
+Acta Derm Venereol
+Acta Derm Venereol Suppl (Stockh)
+Acta Dermatol Kyoto Engl Ed
+Acta Dermatovenerol Alp Panonica Adriat
+Acta Dermatovenerol Croat
+Acta Diabetol
+Acta Diabetol Lat
+Acta Embryol Exp (Palermo)
+Acta Embryol Morphol Exp
+Acta Endocrinol (Copenh)
+Acta Endocrinol Iber
+Acta Endocrinol Panam
+Acta Endocrinol Suppl (Copenh)
+Acta Endoscopica
+Acta Ethnol Linguist
+Acta Eur Fertil
+Acta Gastroenterol Belg
+Acta Gastroenterol Latinoam
+Acta Genet Med Gemellol (Roma)
+Acta Genet Stat Med
+Acta Geogr
+Acta Geogr Croat
+Acta Geogr Sin
+Acta Gerontol (Milano)
+Acta Ginecol (Madr)
+Acta Gynaecol Obstet Hisp Lusit
+Acta Haematol
+Acta Haematol Pol
+Acta Hepatogastroenterol (Stuttg)
+Acta Hepatosplenol
+Acta Hist Leopoldina
+Acta Hist Med Pharm Vet (Beograd)
+Acta Hist Med Vallisoletana Monogr
+Acta Hist Rerum Nat Tech
+Acta Hist Sci Nat Med
+Acta Histochem
+Acta Histochem Suppl
+Acta Hortic
+Acta Hosp
+Acta Iber Radiol Cancerol
+Acta Isot (Padova)
+Acta Leiden
+Acta Leprol
+Acta Manila Ser A
+Acta Med Acad Sci Hung
+Acta Med Austriaca
+Acta Med Austriaca Suppl
+Acta Med Auxol (Milano)
+Acta Med Biol (Niigata)
+Acta Med Cathol
+Acta Med Colomb
+Acta Med Costarric
+Acta Med Croatica
+Acta Med Dom
+Acta Med Hidalguense
+Acta Med Hisp
+Acta Med Hist Patav
+Acta Med Hung
+Acta Med Indones
+Acta Med Iran
+Acta Med Ital Mal Infett Parassit
+Acta Med Ital Med Trop Subtrop Gastroenterol
+Acta Med Iugosl
+Acta Med Leg Soc (Liege)
+Acta Med Nagasaki
+Acta Med Okayama
+Acta Med Orient
+Acta Med Patav
+Acta Med Patavina Suppl
+Acta Med Philipp
+Acta Med Pol
+Acta Med Port
+Acta Med Rom
+Acta Med Scand
+Acta Med Scand Suppl
+Acta Med Tenerife
+Acta Med Valle
+Acta Med Vet (Napoli)
+Acta Med Vietnam
+Acta Medica (Hradec Kralove)
+Acta Medica (Hradec Kralove) Suppl
+Acta Medica Cordoba
+Acta Microbiol Acad Sci Hung
+Acta Microbiol Bulg
+Acta Microbiol Hung
+Acta Microbiol Immunol Hung
+Acta Microbiol Pol
+Acta Microbiol Pol A
+Acta Microbiol Pol B
+Acta Microbiol Sin
+Acta Microbiol Virol Immunol (Sofiia)
+Acta Morphol Acad Sci Hung
+Acta Morphol Hung
+Acta Morphol Neerl Scand
+Acta Mus Napocensis
+Acta Myol
+Acta Neurobiol Exp (Wars)
+Acta Neurochir (Wien)
+Acta Neurochir Suppl
+Acta Neurochir Suppl (Wien)
+Acta Neurol (Napoli)
+Acta Neurol Belg
+Acta Neurol Latinoam
+Acta Neurol Psychiatr Belg
+Acta Neurol Scand
+Acta Neurol Scand Suppl
+Acta Neurol Taiwan
+Acta Neurol [Quad] (Napoli)
+Acta Neuropathol (Berl)
+Acta Neuropathol Suppl (Berl)
+Acta Neuroveg (Wien)
+Acta Neuroveg Suppl
+Acta Numis
+Acta Obstet Ginecol Hisp Lusit
+Acta Obstet Ginecol Hisp Lusit Suppl
+Acta Obstet Gynaecol Jpn
+Acta Obstet Gynecol Scand
+Acta Obstet Gynecol Scand Suppl
+Acta Odontol Latinoam
+Acta Odontol Pediatr
+Acta Odontol Scand
+Acta Odontol Scand Suppl
+Acta Odontol Venez
+Acta Oncol
+Acta Oncol (Madr)
+Acta Ophthalmol (Copenh)
+Acta Ophthalmol Scand
+Acta Ophthalmol Scand Suppl
+Acta Ophthalmol Suppl
+Acta Orthop
+Acta Orthop Belg
+Acta Orthop Scand
+Acta Orthop Scand Suppl
+Acta Orthop Suppl
+Acta Orthop Traumatol Turc
+Acta Otolaryngol
+Acta Otolaryngol Suppl
+Acta Otorhinolaryngol Belg
+Acta Otorhinolaryngol Ital
+Acta Otorinolaryngol Iber Am
+Acta Otorrinolaringol Esp
+Acta Paediatr
+Acta Paediatr Acad Sci Hung
+Acta Paediatr Belg
+Acta Paediatr Hung
+Acta Paediatr Jpn
+Acta Paediatr Lat
+Acta Paediatr Scand
+Acta Paediatr Scand Suppl
+Acta Paediatr Suppl
+Acta Paediatr Taiwan
+Acta Paedolog
+Acta Paedopsychiatr
+Acta Palaeontol Pol
+Acta Pathol Jpn
+Acta Pathol Microbiol Immunol Scand Suppl
+Acta Pathol Microbiol Immunol Scand [A]
+Acta Pathol Microbiol Immunol Scand [B]
+Acta Pathol Microbiol Immunol Scand [C]
+Acta Pathol Microbiol Scand
+Acta Pathol Microbiol Scand Suppl
+Acta Pathol Microbiol Scand [A]
+Acta Pathol Microbiol Scand [B]
+Acta Pathol Microbiol Scand [B] Microbiol Immunol
+Acta Pathol Microbiol Scand [C]
+Acta Pediatr Esp
+Acta Pharm
+Acta Pharm Hung
+Acta Pharm Int
+Acta Pharm Jugosl
+Acta Pharm Nord
+Acta Pharm Suec
+Acta Pharmacol Sin
+Acta Pharmacol Toxicol (Copenh)
+Acta Physiol (Oxf)
+Acta Physiol Acad Sci Hung
+Acta Physiol Hung
+Acta Physiol Lat Am
+Acta Physiol Pharmacol Bulg
+Acta Physiol Pharmacol Latinoam
+Acta Physiol Pharmacol Neerl
+Acta Physiol Pharmacol Ther Latinoam
+Acta Physiol Plant
+Acta Physiol Pol
+Acta Physiol Scand
+Acta Physiol Scand Suppl
+Acta Physiother Rheumatol Belg
+Acta Phytophysiol Sinica
+Acta Pol Hist
+Acta Pol Pharm
+Acta Protozool
+Acta Psiquiatr Psicol Am Lat
+Acta Psychiatr Belg
+Acta Psychiatr Neurol Scand
+Acta Psychiatr Neurol Scand Suppl
+Acta Psychiatr Scand
+Acta Psychiatr Scand Suppl
+Acta Psychol (Amst)
+Acta Psychother Psychosom Orthopaedagog
+Acta Radiol
+Acta Radiol Cancerol
+Acta Radiol Diagn (Stockh)
+Acta Radiol Oncol
+Acta Radiol Oncol Radiat Phys Biol
+Acta Radiol Suppl
+Acta Radiol Ther Phys Biol
+Acta Reprod Turc
+Acta Rheumatol Scand
+Acta Rhumatol
+Acta Rhumatol Belg
+Acta Salmant Hist Med
+Acta Salmanticensia Ser Filos Letra
+Acta Sch Med Univ Kioto
+Acta Sci Sin
+Acta Soc Med Ups
+Acta Soc Med Ups Suppl
+Acta Sociol
+Acta Sociomed Scand
+Acta Sociomed Scand Suppl
+Acta Stomatol Belg
+Acta Stomatol Croat
+Acta Stomatol Int
+Acta Ther
+Acta Theriol (Warsz)
+Acta Trop
+Acta Trop Suppl
+Acta Tuberc Belg
+Acta Tuberc Jpn
+Acta Tuberc Pneumol Belg
+Acta Tuberc Pneumol Scand
+Acta Tuberc Scand
+Acta Tuberc Scand Suppl
+Acta Unio Int Contra Cancrum
+Acta Univ Agric Fac Agron
+Acta Univ Carol Geogr
+Acta Univ Carol Med Monogr
+Acta Univ Carol Philos Hist
+Acta Univ Carol [Med] (Praha)
+Acta Univ Lund
+Acta Univ Palacki Olomuc Fac Med
+Acta Univ Szeged Attila Jozsef Nominatae
+Acta Univ Ups
+Acta Urol Belg
+Acta Vet (Beogr)
+Acta Vet Acad Sci Hung
+Acta Vet Brno
+Acta Vet Hung
+Acta Vet Scand
+Acta Vet Scand Suppl
+Acta Virol
+Acta Vitaminol
+Acta Vitaminol Enzymol
+Acta Zool
+Acta Zool Pathol Antverp
+Actas Dermatol Dermatopatol
+Actas Dermosifiliogr
+Actas Esp Psiquiatr
+Actas Luso Esp Neurol Psiquiatr
+Actas Luso Esp Neurol Psiquiatr Cienc Afines
+Actas Reun Cient Cuerfo Fac Inst Policlin
+Actas Urol Esp
+Actes Rech Sci Soc
+Action Child
+Action Contre SIDA
+Action Natl
+Actionaid Disabil News
+Acts Legis Prov Manit Manit
+Acts Parliam Commonw Aust Pass Sess Aust
+Actual Anatpathol
+Actual Biol
+Actual Cardiol Angeiol Int (Paris)
+Actual Chim
+Actual Econ
+Actual Endocrinol (Paris)
+Actual Hepatogastroenterol (Paris)
+Actual Jurid Aranzadi
+Actual Medica
+Actual Neurophysiol (Paris)
+Actual Odontostomatol (Paris)
+Actual Pediatr (Granada)
+Actual Pharm
+Actual Pharmacol (Paris)
+Actual Physiol Pathol (Paris)
+Actual Psychiatr
+Actuel Urol
+Acupunct Electrother Res
+Acupunct Med
+Acute Care
+Ad Astra
+Addict Behav
+Addict Biol
+Addict Dis
+Addiction
+Additamenta Folia Med Neerl
+Adiyat Halab
+Adler
+Adler Mus Bull
+Adm Change
+Adm Dev
+Adm Law J
+Adm Law Rev
+Adm Manage
+Adm Ment Health
+Adm Policy Ment Health
+Adm Radiol
+Adm Radiol J
+Adm Sci Q
+Adm Soc
+Adm Soc Work
+Admit Manage J
+Adolesc Educ Newsl
+Adolesc Med
+Adolesc Med Clin
+Adolesc Pediatr Gynecol
+Adolesc Psychiatry
+Adolescence
+Adult Educ
+Adv Abort Care
+Adv Adolesc Mental Health
+Adv Alcohol Subst Abuse
+Adv Anat Embryol Cell Biol
+Adv Anat Pathol
+Adv Appl Microbiol
+Adv Behav Biol
+Adv Biochem Eng Biotechnol
+Adv Biochem Psychopharmacol
+Adv Bioeth
+Adv Biol Med Phys
+Adv Biol Skin
+Adv Biomed Eng Med Phys
+Adv Biophys
+Adv Biosci
+Adv Biotechnol Processes
+Adv Bot Res
+Adv Cancer Res
+Adv Carbohydr Chem
+Adv Carbohydr Chem Biochem
+Adv Card Surg
+Adv Cardiol
+Adv Cardiopulm Dis
+Adv Cell Biol
+Adv Chem Ser
+Adv Chemother
+Adv Child Dev Behav
+Adv Chromatogr
+Adv Chronic Kidney Dis
+Adv Clin Care
+Adv Clin Chem
+Adv Clin Path
+Adv Clin Pharmacol
+Adv Clin Rehabil
+Adv Colloid Interface Sci
+Adv Comp Physiol Biochem
+Adv Consum Res
+Adv Contracept
+Adv Contracept Deliv Syst
+Adv Cryog Eng
+Adv Cyclic Nucleotide Protein Phosphorylation Res
+Adv Cyclic Nucleotide Res
+Adv Cytopharmacol
+Adv Data
+Adv Dent Res
+Adv Dermatol
+Adv Drug Deliv Rev
+Adv Drug Res
+Adv Ecol Rs
+Adv Endocrinol Metab
+Adv Enzyme Regul
+Adv Enzymol Relat Areas Mol Biol
+Adv Enzymol Relat Subj Biochem
+Adv Exp Med Biol
+Adv Fertil Control
+Adv Fluorine Res
+Adv Food Nutr Res
+Adv Food Res
+Adv Food Res Suppl
+Adv Genet
+Adv Gerontol
+Adv Gerontol Res
+Adv Health Econ Health Serv Res
+Adv Health Econ Health Serv Res Suppl
+Adv Health Sci Educ Theory Pract
+Adv Heterocycl Chem
+Adv Hum Genet
+Adv Imaging
+Adv Immun Cancer Ther
+Adv Immunol
+Adv Inorg Biochem
+Adv Intern Med
+Adv Lipid Res
+Adv Mar Biol
+Adv Mass Spectrom
+Adv Med Sociol
+Adv Metab Disord
+Adv Microb Ecol
+Adv Microb Physiol
+Adv Mind Body Med
+Adv Morphog
+Adv Myocardiol
+Adv Myochem
+Adv Neonatal Care
+Adv Nephrol Necker Hosp
+Adv Neural Inf Process Syst
+Adv Neuroimmunol
+Adv Neurol
+Adv Nurse Pract
+Adv Nutr Res
+Adv Ophthalmic Plast Reconstr Surg
+Adv Ophthalmol
+Adv Oral Biol
+Adv Otorhinolaryngol
+Adv Pain Res Ther
+Adv Parasitol
+Adv Pathobiol
+Adv Pediatr
+Adv Pediatr Infect Dis
+Adv Perit Dial
+Adv Pest Control Res
+Adv Pharm Sci
+Adv Pharmacol
+Adv Pharmacol Chemother
+Adv Physiol Educ
+Adv Plan Parent
+Adv Popul
+Adv Pract Nurs Q
+Adv Prostaglandin Thromboxane Leukot Res
+Adv Prostaglandin Thromboxane Res
+Adv Protein Chem
+Adv Psychobiol
+Adv Psychol Res
+Adv Psychosom Med
+Adv Radiat Biol
+Adv Ren Replace Ther
+Adv Reprod Physiol
+Adv Robot
+Adv Sci
+Adv Second Messenger Phosphoprotein Res
+Adv Sex Horm Res
+Adv Shock Res
+Adv Skin Wound Care
+Adv Sociodent Res
+Adv Space Biol Med
+Adv Space Res
+Adv Steroid Biochem Pharmacol
+Adv Surg
+Adv Tech Stand Neurosurg
+Adv Ther
+Adv Tracer Methodol
+Adv Tuberc Res
+Adv Vet Med
+Adv Vet Sci
+Adv Vet Sci Comp Med
+Adv Virus Res
+Adv Wound Care
+Advent Herit
+Adverse Drug React Acute Poisoning Rev
+Adverse Drug React Bull
+Adverse Drug React Toxicol Rev
+Advert Age
+Advocacy Now
+Advocate
+Aegyptus
+Aere Perennius
+Aeromed Acta
+Aeromed Rev
+Aeronaut Astronaut
+Aerosol Sci Technol
+Aerosp Am
+Aerosp Eng
+Aerosp Hist
+Aerosp Med
+Aesthetic Plast Surg
+Aevum
+Aff Soc Int
+Affilia
+Afghan Med J
+Afghanistan
+Afr Aff (Lond)
+Afr Alternat
+Afr Anal
+Afr Asie Mod
+Afr Concord
+Afr Contemp
+Afr Demogr
+Afr Dent J
+Afr Dev
+Afr Dev Rev
+Afr Doc
+Afr Econ Hist
+Afr Environ
+Afr Farmer
+Afr Francaise Chir
+Afr Health
+Afr Health Sci
+Afr Hist Stud
+Afr Insight
+Afr J Fertil Sexual Reprod Heal
+Afr J Health Sci
+Afr J Med Med Sci
+Afr J Med Pract
+Afr J Med Sci
+Afr J Psychiatry
+Afr J Psychol Study Soc Issues
+Afr J Reprod Health
+Afr J Sex Transmi Dis
+Afr J Sociol
+Afr Link
+Afr Litt Artist
+Afr Marburgen
+Afr Med
+Afr Media Rev
+Afr Notes
+Afr Notes News
+Afr Popul Dev Bull
+Afr Popul Newsl
+Afr Q
+Afr Recovery
+Afr Rep
+Afr Res Bull
+Afr Rev
+Afr Soc Res
+Afr South
+Afr Stud
+Afr Stud Rev
+Afr Study Monogr
+Afr Today
+Afr Uebersee
+Afr Urban Notes B
+Afr Urban Q
+Afr Urban Stud
+Afr Woman
+Afr Women Health
+Africa
+Africa (Lond)
+Africasia
+Africom
+Afrique 2000
+Afro Am N Y Life Hist
+Afro Tech Pap
+Afya
+Agapit
+Age
+Age (Omaha)
+Age Ageing
+Aged Care Serv Rev
+Ageing Int
+Ageing Res Rev
+Ageing Soc
+Agenda
+Agenda Salud
+Agents Actions
+Agents Actions Suppl
+Aggiorn Clinico Ter
+Aggiorn Fisiol
+Aggiorn Ostet Ginecol
+Aggiorn Pediatr
+Aggiorn Sulle Mal Infez
+Aggiorn Ter Oftalmol
+Aggress Behav
+Aggress Violent Behav
+Aging
+Aging (Milano)
+Aging Cell
+Aging Clin Exp Res
+Aging Leis Living
+Aging Male
+Aging Ment Health
+Aging Trends
+Aging Work
+Agnes Karll Schwest Krankenpfleger
+Agratort Szle
+Agressologie
+Agri
+Agric Biol Chem
+Agric Econ Res
+Agric Hist
+Agric Hist Rev
+Agric Human Values
+Agric Inf Dev Bull
+Agric Syst
+Agron J
+Aguiaine
+Ahfad J
+Ahot Beyisrael
+Ai Zheng
+Aichi Gakuin Daigaku Shigakkai Shi
+Aichi Gakuin Dent Sci
+Aidos News
+Aidscaptions
+Ain Shams Med J
+Air Force Law Rev
+Air Line Pilot
+Air Med J
+Air Space
+Air Univ Rev (US ed)
+Air Waste
+Air Water Pollut
+Aisa Found News
+Akad Dzive
+Akad Iatr
+Akron Bus Econ Rev
+Akron Law Rev
+Aktuel Endokrinol Stoffwechsel
+Aktuel Ernahrungsmed
+Aktuel Probl Phoniatr Logop
+Aktuelle Derm
+Aktuelle Gerontol
+Aktuelle Otorhinolaryngol
+Aktuelle Probl Chir
+Aktuelle Probl Chir Orthop
+Aktuelle Radiol
+Aktuelle Traumatol
+Aktuelle Urol
+Akush Ginekol (Mosk)
+Akush Ginekol (Sofiia)
+Akwesasne Notes
+Al Azhar Med J
+Al Darat
+Al Mihan Al Tibbiyah
+Al Qantara
+Al Raida
+Al Usrah Wa Al Umran Al Bashari
+Al-Arabi
+Ala Dent Rev
+Ala Herit
+Ala Hist Q
+Ala J Med Sci
+Ala Law Rev
+Ala Med
+Ala Nurse
+Ala Rev
+Alaska J
+Alaska Med
+Alaska Nurse
+Albany Law J Sci Technol
+Albany Law Rev
+Alberta Hist
+Alberta Med Bull
+Albertus Magnus Blaett
+Albion
+Albrecht Von Graefes Arch Klin Exp Ophthalmol
+Albrecht Von Graefes Arch Ophthalmol
+Albuq Tribune
+Alcohol
+Alcohol Alcohol
+Alcohol Alcohol Suppl
+Alcohol Clin Exp Res
+Alcohol Drug Res
+Alcohol Drugs Driving
+Alcohol Health Res World
+Alcohol Res Health
+Alcohol Treat Q
+Aldarah
+Alergia
+Alex Dent J
+Alex J Pharm Sci
+Alexandria Med J
+Alger Medicale
+Aliment Pharmacol Ther
+Aliment Vie
+Aliso
+Alkaloids Chem Biol
+All Engl Law Rep
+All Stat Arch
+Allem Aujourdhui
+Allerg Asthma (Leipz)
+Allerg Asthmaforsch
+Allerg Immunol (Leipz)
+Allerg Immunol (Paris)
+Allerg Pointe Claire
+Allergo J
+Allergol Immunopathol (Madr)
+Allergy
+Allergy Asthma Proc
+Allergy Proc
+Allg Homoopath Ztg
+Allied Health Behav Sci
+Alma Mater (Baltimore)
+Alma Mater Philipp
+Almanac
+Almustaqbal Alarabi
+Alpha Omega Fr
+Alpha Omegan
+Alsk Hist
+Alsk Statut 1962 Alsk
+Alt Thuring
+Alta Law Rev
+Alta RN
+Altamura
+Altern Entwickl Aging Dev
+Altern Lab Anim
+Altern Med Rev
+Altern Ther Health Med
+Alternative Lifestyles
+Altertum
+Altnurnb Landsch
+Altsprachi Unterr
+Alumnae Mag
+Alumnae Mag Columbia Univ Presbyt Hosp Sch Nurs Alumnae Assoc
+Alumni Bull Sch Dent Indiana Univ
+Alumni Bull Univ Mich Sch Dent
+Alumni Mag
+Alumni Mag Columbia Univ Presbyt Hosp Sch Nurs Alumni Assoc
+Alzheimer Dis Assoc Disord
+Am Ann Deaf
+Am Anthropol
+Am Antiq
+Am Arch
+Am Arch Rehabil Ther
+Am Art J
+Am Asian Rev
+Am Assoc Ind Nurses J
+Am Assoc Pet Geol Bull
+Am Baby
+Am Bar Assoc J
+Am Bar Found Res J
+Am Behav Sci
+Am Benedictine Rev
+Am Biol Teach
+Am Biotechnol Lab
+Am Clin Lab
+Am Coll Cardiol Extended Learn Suppl Tape
+Am Coll Physicians Obs
+Am Correct Ther J
+Am Crim Law Rev
+Am Demogr
+Am Eccles Rev
+Am Econ
+Am Econ Rev
+Am Educ
+Am Educ Res J
+Am Enterp
+Am Fam Physician
+Am Fam Physician GP
+Am Health
+Am Heart Hosp J
+Am Heart J
+Am Herit
+Am Herit Invent Technol
+Am Hist Illus
+Am Hist Rev
+Am Horseman
+Am Imago
+Am Ind Hyg Assoc J
+Am Ind Hyg Assoc Q
+Am Indian Alsk Native Ment Health Res
+Am Indian Alsk Native Ment Health Res Monogr Ser
+Am Indian Art Mag
+Am Indian Cult Res J
+Am Indian Q
+Am Indig
+Am J Acupunct
+Am J Addict
+Am J Agric Econ
+Am J Alzheimers Dis Other Demen
+Am J Anat
+Am J Anesthesiol
+Am J Art Ther
+Am J Audiol
+Am J Bioeth
+Am J Bot
+Am J Cancer
+Am J Card Imaging
+Am J Cardiol
+Am J Cardiovasc Drugs
+Am J Cardiovasc Pathol
+Am J Chin Med
+Am J Chin Med (Gard City N Y)
+Am J Clin Dermatol
+Am J Clin Hypn
+Am J Clin Nutr
+Am J Clin Oncol
+Am J Clin Pathol
+Am J Community Psychol
+Am J Comp Law
+Am J Contact Dermat
+Am J Correct
+Am J Crim Law
+Am J Crit Care
+Am J Dent
+Am J Dermatopathol
+Am J Dig Dis
+Am J Dis Child
+Am J Drug Alcohol Abuse
+Am J EEG Technol
+Am J Econ Sociol
+Am J Electroneurodiagnostic Technol
+Am J Emerg Med
+Am J Epidemiol
+Am J Ethics Med
+Am J Fam Law
+Am J Fam Ther
+Am J Forensic Med Pathol
+Am J Forensic Psychiatry
+Am J Forensic Psychol
+Am J Gastroenterol
+Am J Geriatr Cardiol
+Am J Geriatr Pharmacother
+Am J Geriatr Psychiatry
+Am J Gynecol Health
+Am J Health Behav
+Am J Health Plann
+Am J Health Promot
+Am J Health Syst Pharm
+Am J Hematol
+Am J Hosp Care
+Am J Hosp Palliat Care
+Am J Hosp Pharm
+Am J Hum Biol
+Am J Hum Genet
+Am J Hyg
+Am J Hypertens
+Am J Ind Med
+Am J Infect Control
+Am J Int Law
+Am J Jurisprud
+Am J Kidney Dis
+Am J Knee Surg
+Am J Law Med
+Am J Leg Hist
+Am J Manag Care
+Am J Med
+Am J Med Electron
+Am J Med Genet
+Am J Med Genet A
+Am J Med Genet B Neuropsychiatr Genet
+Am J Med Genet C Semin Med Genet
+Am J Med Genet Suppl
+Am J Med Qual
+Am J Med Sci
+Am J Med Technol
+Am J Ment Defic
+Am J Ment Retard
+Am J Nephrol
+Am J Nurs
+Am J Obstet Dis Women Child
+Am J Obstet Gynecol
+Am J Occup Ther
+Am J Ophthalmol
+Am J Optom Arch Am Acad Optom
+Am J Optom Physiol Opt
+Am J Orthod
+Am J Orthod Dentofacial Orthop
+Am J Orthop
+Am J Orthop Surg
+Am J Orthopsychiatry
+Am J Otol
+Am J Otolaryngol
+Am J Pathol
+Am J Pediatr Hematol Oncol
+Am J Perinatol
+Am J Pharm
+Am J Pharm Educ
+Am J Pharm Sci Support Public Health
+Am J Pharmacogenomics
+Am J Philol
+Am J Phys
+Am J Phys Anthropol
+Am J Phys Med
+Am J Phys Med Rehabil
+Am J Physiol
+Am J Physiol Cell Physiol
+Am J Physiol Endocrinol Metab
+Am J Physiol Gastrointest Liver Physiol
+Am J Physiol Heart Circ Physiol
+Am J Physiol Imaging
+Am J Physiol Lung Cell Mol Physiol
+Am J Physiol Regul Integr Comp Physiol
+Am J Physiol Renal Physiol
+Am J Pol Sci
+Am J Pract Nurs
+Am J Prev Med
+Am J Primatol
+Am J Proctol
+Am J Proctol Gastroenterol Colon Rectal Surg
+Am J Psychiatry
+Am J Psychoanal
+Am J Psychol
+Am J Psychother
+Am J Public Health
+Am J Public Health Nations Health
+Am J Reprod Immunol
+Am J Reprod Immunol Microbiol
+Am J Respir Cell Mol Biol
+Am J Respir Crit Care Med
+Am J Respir Med
+Am J Rhinol
+Am J Roentgenol Radium Ther Nucl Med
+Am J Sci
+Am J Soc Psychiatry
+Am J Sociol
+Am J Speech Lang Pathol
+Am J Sports Med
+Am J Surg
+Am J Surg Pathol
+Am J Syph Gonorrhea Vener Dis
+Am J Ther
+Am J Transplant
+Am J Trial Advocacy
+Am J Trop Med Hyg
+Am J Vet Med
+Am J Vet Res
+Am Jew
+Am Jew Arch
+Am Jew Hist
+Am Jew Hist Q
+Am Lab
+Am Lat
+Am Laund Dig
+Am Law Rep ALR 3rd Cases Annot
+Am Libr
+Am Lit
+Am Lung Assoc Bull
+Am Math Mon
+Am Med News
+Am Mus Novit
+Am Nat
+Am Neptune
+Am Nurse
+Am Orthopt J
+Am Pharm
+Am Philat
+Am Philos Q
+Am Polit Q
+Am Polit Sci Rev
+Am Potato J
+Am Pract Dig Treat
+Am Presbyt
+Am Prospect
+Am Psychol
+Am Q
+Am Rehabil
+Am Rev Can Stud
+Am Rev Respir Dis
+Am Rev Tuberc
+Am Sch Board J
+Am Scholar
+Am Sci
+Am Soc Legion Honor Mag
+Am Sociol
+Am Sociol Rev
+Am Spect
+Am Speech
+Am Stat
+Am Stud (Lawrence)
+Am Surg
+Am Univ Field Staff Rep Afr
+Am Univ Field Staff Rep Asia
+Am Univ Field Staff Rep North Am
+Am Univ Field Staff Rep South Am
+Am Univ J Gend Soc Policy Law
+Am Univ J Int Law Policy
+Am Univ Law Rev
+Am Zool
+Amatus Lusit
+Ambio
+Ambix
+Ambul Care
+Ambul Outreach
+Ambul Pediatr
+Amerasia J
+America (NY)
+Americas (Engl ed)
+Amerikastudien
+Amic J
+Amicus
+Amif
+Amino Acids
+Amphib Reptile Conserv
+Amstelodamum
+Amyloid
+Amyotroph Lateral Scler Other Motor Neuron Disord
+An Acad Bras Cienc
+An Acad Geogr Hist Guatem
+An Antropol
+An Bras Derm Sifilogr
+An Bras Dermatol
+An Bras Ginecol
+An Casa Salud Valdecilla
+An Chil Hist Med
+An Cir (Rosario)
+An Esc Nacl Saude Publica Med Trop (Lisb)
+An Esp Odontoestomatol
+An Esp Pediatr
+An Fac Farm Porto
+An Fac Med Lima
+An Fac Med Montev
+An Fac Med Porto Alegre
+An Fac Med Recife
+An Fac Med Univ Repub Montev Urug
+An Fac Med Univ Sao Paulo
+An Fac Odontol
+An Fac Odontol Univ Fed Pernambuco
+An Fac Quim Farm (Santiago)
+An Farm Hosp (Madr)
+An Gazi Husrev Begove Bibl
+An Hist
+An Hist Antigua Mediev
+An Hist Contemp
+An Hosp St Cruz San Pablo
+An Ilustre Col Of Med
+An Inst Barraquer
+An Inst Estud Madr
+An Inst Hig Med Trop (Lisb)
+An Inst Invest Odontol (Maracaibo)
+An Inst Invest Vet
+An Inst Med Trop (Lisb)
+An Inst Nac Antropol Hist
+An Klin Bol Dr M Stojanovic
+An Matern Sao Paulo
+An Med (Barc)
+An Med (Lima)
+An Med Cir
+An Med Concepc
+An Med Espec
+An Med Interna
+An Med Med
+An Med Publica
+An Microbiol (Rio J)
+An Nestle (B Aires)
+An Nestle (Rio De Janeiro)
+An Otorrinolaringol Ibero Am
+An Paul Med Cir
+An Pediatr (Barc)
+An Programa Acad Med (Lima)
+An R Acad Nac Med (Madr)
+An Real Acad Farm
+An Sist Sanit Navar
+An Soc Geogr Hist
+An Soc Mex Hist Cienc Tec
+An Stiint Univ AI I Cuza Iasi Sect III C Stiint Econ
+An Univ Bucur Istor
+Anadolu Kardiyol Derg
+Anaerobe
+Anaesth Intensive Care
+Anaesth Resusc Intensive Ther
+Anaesthesia
+Anaesthesiol Reanim
+Anaesthesist
+Anal Bioanal Chem
+Anal Biochem
+Anal Cell Pathol
+Anal Chem
+Anal Chim Acta
+Anal Lett
+Anal Polit
+Anal Psychol
+Anal Quant Cytol
+Anal Quant Cytol Histol
+Anal Sci
+Anal Soc
+Analecta Sacra Tarracon
+Analog Sci Fict Sci Fact
+Analysis
+Analyst
+Analyst (Lond)
+Analytica
+Anasth Intensivther Notfallmed
+Anasthesiol Intensivmed Notfallmed Schmerzther
+Anasthesiol Intensivmed Prax
+Anat Anz
+Anat Chir
+Anat Clin
+Anat Embryol (Berl)
+Anat Histol Embryol
+Anat Pathol
+Anat Rec
+Anat Rec A Discov Mol Cell Evol Biol
+Anat Rec B New Anat
+Anat Rec Suppl
+Anat Sci Int
+Anc Philos
+Andover Newton Q
+Andrologia
+Andrologie
+Anest Rianim
+Anesteziol Reanimatol
+Anesth Anal
+Anesth Analg
+Anesth Analg (Paris)
+Anesth Pain Control Dent
+Anesth Prog
+Anesthesiol Clin North America
+Anesthesiol Rev
+Anesthesiology
+Angeiol Ann Soc Fr Angeiol Histopathol
+Angeiologie
+Angelicum
+Angew Chem Int Ed Engl
+Angew Chem Weinheim Bergstr Ger
+Angew Parasitol
+Angiogenesis
+Angiol Sosud Khir
+Angiologia
+Angiologica
+Angiology
+Angle Orthod
+Anglia
+Anglican Theol Rev
+Anglo Am Law Rev
+Anglo Ger Med Rev
+Anglo Sax Engl
+Anim Agenda
+Anim Behav
+Anim Biol Leiden Neth
+Anim Biotechnol
+Anim Blood Groups Biochem Genet
+Anim Blood Groups Biochem Genet Suppl
+Anim Cogn
+Anim Genet
+Anim Health Res Rev
+Anim Learn Behav
+Anim Prod
+Anim Reprod Sci
+Anim Technol
+Anim Welf
+Ankara Univ Hekim Fak Derg
+Ann Acad Med Singapore
+Ann Acad Med Stetin
+Ann Acad Med Stetin Supl
+Ann Acad Sci Fenn A
+Ann Acad Sci Fenn A 3 Geol Geogr
+Ann Acad Sci Fenn [Biol]
+Ann Agric Environ Med
+Ann Allergy
+Ann Allergy Asthma Immunol
+Ann Am Acad Pol Soc Sci
+Ann Anat
+Ann Anat Pathol (Paris)
+Ann Anesthesiol Fr
+Ann Appl Biol
+Ann Archeol Syrie
+Ann Arid Zone
+Ann Assoc Am Geogr
+Ann Aust Coll Dent Surg
+Ann Behav Med
+Ann Belg Med Mil
+Ann Biochem Exp Med
+Ann Biol
+Ann Biol Anim Biochim Biophys
+Ann Biol Clin (Paris)
+Ann Biomed Eng
+Ann Bot (Lond)
+Ann Bourgogne
+Ann Bretagne Payes Ouest
+Ann Cardiol Angeiol (Paris)
+Ann Cerc Roy Hist Archeol Ath
+Ann Chim
+Ann Chin Hist Soc Pac Northwest
+Ann Chir
+Ann Chir Gynaecol
+Ann Chir Gynaecol Fenn
+Ann Chir Gynaecol Fenn Suppl
+Ann Chir Gynaecol Suppl
+Ann Chir Infant
+Ann Chir Main
+Ann Chir Main Memb Super
+Ann Chir Plast
+Ann Chir Plast Esthet
+Ann Chir Thorac Cardiovasc
+Ann Cisalp Hist Soc
+Ann Clin Biochem
+Ann Clin Lab Sci
+Ann Clin Microbiol Antimicrob
+Ann Clin Psychiatry
+Ann Clin Res
+Ann Demogr Hist (Paris)
+Ann Dent
+Ann Dermatol Syphiligr (Paris)
+Ann Dermatol Venereol
+Ann Diagn Pathol
+Ann Droit Int Med
+Ann Dyslexia
+Ann Econ Soc Civilis
+Ann Econ Stat
+Ann Emerg Med
+Ann Endocrinol (Paris)
+Ann Entomol Soc Am
+Ann Epidemiol
+Ann Est
+Ann Eugen
+Ann Fac Econ Commer
+Ann Fac Lett Sci Hum
+Ann Fac Lett Sci Hum Nice
+Ann Fac Med Chir Univ Studi Perugia
+Ann Fam Med
+Ann Fond Luigi Einaudi
+Ann Fr Anesth Reanim
+Ann Gastroenterol Hepatol (Paris)
+Ann Gen Hosp Psychiatry
+Ann Gen Psychiatry
+Ann Genet
+Ann Genet Sel Anim
+Ann Georgr
+Ann Health Law
+Ann Hematol
+Ann Hepatol
+Ann Hist Comput
+Ann Hist Mantois
+Ann Hist Revolut Fr
+Ann Histochim
+Ann Homeopath Fr
+Ann Hum Biol
+Ann Hum Genet
+Ann Hyg
+Ann Hyg Med Colon
+Ann ICRP
+Ann IFORD
+Ann Ig
+Ann Ig (Roma)
+Ann Immunol
+Ann Immunol (Paris)
+Ann Immunol Hung
+Ann Insee
+Ann Inst Archeol Luxemb
+Ann Inst Pasteur (Paris)
+Ann Inst Pasteur Immunol
+Ann Inst Pasteur Lille
+Ann Inst Pasteur Microbiol
+Ann Inst Pasteur Virol
+Ann Intern Med
+Ann Iowa
+Ann Isnardi Auxol Norm Patol
+Ann Ist Carlo Forlanini
+Ann Ist Giangiacomo Feltrinelli
+Ann Ist Mus Stor Sci Fir
+Ann Ist Stor Italo Ger Trento
+Ann Ist Super Sanita
+Ann Ital Chir
+Ann Ital Dermatol Sifilogr
+Ann Ital Med Int
+Ann Ital Pediatr
+Ann Laringol Otol Rinol Faringol
+Ann Limnol
+Ann Med
+Ann Med Exp Biol Fenn
+Ann Med Hist
+Ann Med Intern Fenn
+Ann Med Interne (Paris)
+Ann Med Leg Criminol Police Sci Toxicol
+Ann Med Nancy
+Ann Med Nav (Roma)
+Ann Med Nav Trop
+Ann Med Phys (Lille)
+Ann Med Psychol (Paris)
+Ann Med Sect Pol Acad Sci
+Ann Med Sondalo
+Ann Med Univ Bialyst Pol
+Ann Microbiol
+Ann Microbiol (Paris)
+Ann Microbiol Enzimol
+Ann Midi
+Ann N Y Acad Sci
+Ann Natl Acad Med Sci
+Ann Natl Assoc Geogr
+Ann Nestle [Eng]
+Ann Nestle [Fr]
+Ann Nestle [Ger]
+Ann Neurol
+Ann Neuropsichiatr Psicoanal
+Ann Noninvasive Electrocardiol
+Ann Normandie
+Ann Nucl Med
+Ann Nutr Aliment
+Ann Nutr Metab
+Ann Occup Hyg
+Ann Ocul (Paris)
+Ann Odontostomatol (Lyon)
+Ann Oncol
+Ann Ophthalmol
+Ann Osp Maria Vittoria Torino
+Ann Ostet Ginecol
+Ann Ostet Ginecol Med Perinat
+Ann Otol Rhinol Laryngol
+Ann Otol Rhinol Laryngol Suppl
+Ann Otolaryngol
+Ann Otolaryngol Chir Cervicofac
+Ann Ottalmol Clin Ocul
+Ann Paediatr
+Ann Paediatr Fenn
+Ann Parasitol Hum Comp
+Ann Pathol
+Ann Pediatr (Paris)
+Ann Periodontol
+Ann Pharm Fr
+Ann Pharmacother
+Ann Phys (Paris)
+Ann Phys Med
+Ann Physiol Anthropol
+Ann Plast Surg
+Ann R Australas Coll Dent Surg
+Ann R Coll Physicians Surg Can
+Ann R Coll Surg Engl
+Ann Radiol (Paris)
+Ann Radiol Diagn (Bologna)
+Ann Readapt Med Phys
+Ann Rech Vet
+Ann Reg Sci
+Ann Rheum Dis
+Ann Sanita Pubblica
+Ann Saudi Med
+Ann Sc Norm Super Pisa
+Ann Sci
+Ann Sci Univ Besancon Med
+Ann Sclavo
+Ann Sclavo Collana Monogr
+Ann Serv Antiq Egypte
+Ann Soc Angeiol Histopathologie
+Ann Soc Belg Hist Hop
+Ann Soc Belg Med Trop
+Ann Soc Entomol Fr
+Ann Soc R Archeol Brux
+Ann Soc R Sci Med Nat Brux
+Ann Soc Sci Counc Niger
+Ann Stomatol (Roma)
+Ann Surg
+Ann Surg Oncol
+Ann Thorac Cardiovasc Surg
+Ann Thorac Surg
+Ann Transplant
+Ann Trop Med Parasitol
+Ann Trop Paediatr
+Ann Tuberc
+Ann Univ Abidjan Ser G
+Ann Univ Ankara
+Ann Univ Mariae Curie Sklodowska [Med]
+Ann Univ Sarav [Med]
+Ann Univ Sci Budap Rolando Eotvos Nom Sect Geogr
+Ann Univ Sci Budap Rolando Eotvos Nominatae [Biol]
+Ann Urol (Paris)
+Ann Vasc Surg
+Ann West Med Surg
+Ann Wyo
+Ann soc Emul Bruges
+Annal Droit
+Annee Biol
+Annee Endocrinol
+Annee Psychol
+Annee Soc
+Annee Sociol
+Annee Ther
+Annee Ther Clin Ophtalmol
+Annot Code Public Gen Laws Md Md
+Annot Laws Mass Mass
+Annot Zool Jpn
+Annotation
+Annu Afr Nord
+Annu Conf Res Med Educ
+Annu Mediaev
+Annu Meet Am Inst Oral Biol
+Annu Pays Ocean Indien
+Annu Proc Assoc Adv Automot Med
+Annu Prog Reprod Med
+Annu Rep Div Biol Med Res Argonne Natl Lab
+Annu Rep Med Chem
+Annu Rep R Soc Promot Health
+Annu Rep Res Inst Environ Med Nagoya Univ
+Annu Rev Anthropol
+Annu Rev Biochem
+Annu Rev Biomed Eng
+Annu Rev Biophys Bioeng
+Annu Rev Biophys Biomol Struct
+Annu Rev Biophys Biophys Chem
+Annu Rev Cell Biol
+Annu Rev Cell Dev Biol
+Annu Rev Earth Planet Sci
+Annu Rev Ecol Syst
+Annu Rev Entomol
+Annu Rev Fluid Mech
+Annu Rev Genet
+Annu Rev Genomics Hum Genet
+Annu Rev Gerontol Geriatr
+Annu Rev Immunol
+Annu Rev Med
+Annu Rev Microbiol
+Annu Rev Neurosci
+Annu Rev Nucl Sci
+Annu Rev Nurs Res
+Annu Rev Nutr
+Annu Rev Pharmacol
+Annu Rev Pharmacol Toxicol
+Annu Rev Phys Chem
+Annu Rev Physiol
+Annu Rev Phytopathol
+Annu Rev Plant Biol
+Annu Rev Plant Physiol
+Annu Rev Plant Physiol Plant Mol Biol
+Annu Rev Popul Law
+Annu Rev Psychol
+Annu Rev Public Health
+Annu Rev Rehabil
+Annu Rev Sex Res
+Annu Rev Sociol
+Annu Soc Christ Ethics
+Annu Stat Assist Previd Soc
+Annu Stat Suppl Soc Secur Bull
+Annu Surv Am Law
+Annu Symp Nurs Fac Pract
+Anot Pediatr
+Antaios
+Antarct J US
+Antarct Sci
+Anthropol Anz
+Anthropol Archeol Eurasia
+Anthropol Educ Q
+Anthropol Kozl
+Anthropol Pap Am Mus Nat Hist
+Anthropol Q
+Anthropol Soc
+Anthropol Today
+Anthropol UCLA
+Anthropol Work Rev
+Anthropologia
+Anthropologica
+Anthropotes
+Anthroquest
+Anthrozoos
+Antibiot Annu
+Antibiot Chemother
+Antibiot Khimioter
+Antibiot Med Biotekhnol
+Antibiotic Med Clin Ther
+Antibiotica
+Antibiotiki
+Anticancer Agents Med Chem
+Anticancer Drug Des
+Anticancer Drugs
+Anticancer Res
+Antichthon
+Anticipation
+Antiek
+Antike Welt
+Antimicrob Agents Chemother
+Antimicrobial Agents Chemother (Bethesda)
+Antioch Rev
+Antioquia Med
+Antioxid Redox Signal
+Antipode
+Antiq Class
+Antiqueweek
+Antiquity
+Antisense Nucleic Acid Drug Dev
+Antisense Res Dev
+Antiseptic
+Antivir Chem Chemother
+Antivir Ther
+Antiviral Res
+Antonie Van Leeuwenhoek
+Anu Bras Odontol
+Anu Estud Am
+Anu Estud Atl
+Anu Estud Centroam
+Anu Estud Mediev
+Anu Indig
+Anu Isnt Istor Arheol Xenopol
+Anxiety
+Anz Altertwiss
+Anz Philol Hist Kl Osterr Akad Wiss
+Anzhes J
+Apeiron
+Apex
+Aphasiology
+Apollonia (Malmo)
+Apollonia (Sydney)
+Apoptosis
+Apoth Kunst
+Appalach J
+Appetite
+Appl Anal
+Appl Anim Behav Sci
+Appl Behav Sci Rev
+Appl Biochem Biotechnol
+Appl Biochem Microbiol
+Appl Bioinformatics
+Appl Cardiol
+Appl Cardiopulm Pathophysiol
+Appl Catal
+Appl Cogn Psychol
+Appl Demor
+Appl Econ
+Appl Econ Discuss Pap Ser
+Appl Econ Lett
+Appl Eng Agric
+Appl Entomol Zool (Jpn)
+Appl Environ Microbiol
+Appl Ergon
+Appl Geogr
+Appl Geogr Dev
+Appl Health Econ Health Policy
+Appl Human Sci
+Appl Immunohistochem Mol Morphol
+Appl Magn Reson
+Appl Math Model
+Appl Microbiol
+Appl Microbiol Biotechnol
+Appl Micrograv Technol
+Appl Neurophysiol
+Appl Neuropsychol
+Appl Nurs Res
+Appl Occup Environ Hyg
+Appl Opt
+Appl Parasitol
+Appl Pathol
+Appl Philos
+Appl Phys B
+Appl Physiol Nutr Metab
+Appl Polym Symp
+Appl Prev Psychol
+Appl Psychol
+Appl Psychophysiol Biofeedback
+Appl Radiat Isot
+Appl Radiol
+Appl Res Ment Retard
+Appl Spectrosc
+Appl Stat
+Appl Theor Electrophor
+Appl Ther
+Appraisal J
+Apres Demain
+Aptechn Delo
+Aquaculture
+Aquat Toxicol
+Arab Aff
+Arab Econ
+Arab J Soc Sci
+Arab Stud Q
+Arabia
+Arabica
+Aramco World Mag
+Arb Gesch Med Giessen
+Arb Paul Ehrlich Inst Bundesamt Sera Impfstoffe Frankf A M
+Arb Paul Ehrlich Inst Georg Speyer Haus Ferdinand Blum Inst Frankf A M
+Arb Soz
+Arbeitsmed Sozialmed Praventivmed
+Arbeitsphysiologie
+Arbetarrorel Arsb
+Arbor
+Arc Cercle
+Arch AIDS Res
+Arch Anat Cytol Pathol
+Arch Anat Histol Embryol
+Arch Anat Microsc Morphol Exp
+Arch Anat Pathol (Paris)
+Arch Androl
+Arch Anim Nutr
+Arch Argent Dermatol
+Arch Argent Pediatr
+Arch Argent Tisiol
+Arch Argent Tisiol Neumonol
+Arch Belg
+Arch Belg Dermatol
+Arch Belg Dermatol Syphiligr
+Arch Belg Med Soc
+Arch Biochem
+Arch Biochem Biophys
+Arch Biol (Liege)
+Arch Biol Andina
+Arch Biol Med Exp (Santiago)
+Arch Bronconeumol
+Arch Cardiol Mex
+Arch Cas
+Arch Child Health
+Arch Chir Neerl
+Arch Chir Torac Cardiovasc
+Arch Clin Neuropsychol
+Arch Col Med El Salv
+Arch Cuba Cancerol
+Arch De Vecchi Anat Patol
+Arch Derm Syphilol
+Arch Dermatol
+Arch Dermatol Forsch
+Arch Dermatol Res
+Arch Dis Child
+Arch Dis Child Fetal Neonatal Ed
+Arch Domin Pediatr
+Arch Emerg Med
+Arch Enferm Coraz Vasos
+Arch Environ Contam Toxicol
+Arch Environ Health
+Arch Esp Morfol
+Arch Esp Urol
+Arch Eur Sociol
+Arch Exp Veterinarmed
+Arch Facial Plast Surg
+Arch Fam Med
+Arch Farmacol Sper Sci Affin
+Arch Farmacol Toxicol
+Arch Fisiol
+Arch Fr Mal App Dig
+Arch Fr Pediatr
+Arch Franciscanum Hist
+Arch Fund Roux Ocefa
+Arch Gen Psychiatry
+Arch Genet (Zur)
+Arch Gerontol Geriatr
+Arch Gerontol Geriatr Suppl
+Arch Gesamte Psychol
+Arch Gesamte Virusforsch
+Arch Gesch Buchwes
+Arch Gesch Naturwiss
+Arch Gesch Oberfranken
+Arch Geschwulstforsch
+Arch Gewerbepathol Gewerbehyg
+Arch Gynakol
+Arch Gynecol
+Arch Gynecol Obstet
+Arch Hisp
+Arch Hist Exact Sci
+Arch Hist Filoz Med
+Arch Hist Med (Warsz)
+Arch Hist Med Port
+Arch Histol Cytol
+Arch Histol Jpn
+Arch Histol Norm Patol
+Arch Hosp (Paris)
+Arch Hosp Univ
+Arch Hydrobiol Suppl Algol Stud
+Arch Hyg Bakteriol
+Arch Iatr Epistem
+Arch Iatr Hetaireon
+Arch Ibero Am
+Arch Immunol Ther Exp (Warsz)
+Arch Ind Hyg Occup Med
+Arch Insect Biochem Physiol
+Arch Inst Biol Andina
+Arch Inst Cardiol Mex
+Arch Inst Farmacol Exp (Madr)
+Arch Inst Pasteur Alger
+Arch Inst Pasteur Hell
+Arch Inst Pasteur Madagascar
+Arch Inst Pasteur Martinique
+Arch Inst Pasteur Tunis
+Arch Int Claude Bernard
+Arch Int Hist Idees
+Arch Int Hist Sci (Paris)
+Arch Int Neurol
+Arch Int Pharmacodyn Ther
+Arch Int Physiol
+Arch Int Physiol Biochim
+Arch Int Physiol Biochim Biophys
+Arch Interam Rheumatol
+Arch Intern Med
+Arch Internazionale Studi Neurol
+Arch Invest Med (Mex)
+Arch Iran Med
+Arch Ital Anat Embriol
+Arch Ital Anat Istol Patol
+Arch Ital Biol
+Arch Ital Chir
+Arch Ital Dermatol Sifilogr Venereol
+Arch Ital Dermatol Venereol Sessuol
+Arch Ital Laringol
+Arch Ital Mal Appar Dig
+Arch Ital Otol Rinol Laringol
+Arch Ital Otol Rinol Laringol Patol Cervicofacc
+Arch Ital Otol Rinol Laringol Patol Cervicofacc Suppl
+Arch Ital Otol Rinol Laringol Suppl
+Arch Ital Patol Clin Tumori
+Arch Ital Pediatr Pueric
+Arch Ital Sci Farmacol
+Arch Ital Sci Med Trop Parassitol
+Arch Ital Urol
+Arch Ital Urol Androl
+Arch Ital Urol Nefrol
+Arch Ital Urol Nefrol Androl
+Arch Julius Klaus Stift Vererbungsforsch Sozialanthropol Rassenhyg
+Arch Kinderheilkd
+Arch Kinderheilkd Suppl
+Arch Klin Exp Dermatol
+Arch Klin Exp Ohren Nasen Kehlkopfheilkd
+Arch Klin Med
+Arch Kreislaufforsch
+Arch Kriminol
+Arch Kulturgesch
+Arch Latinoam Nutr
+Arch Lebensmittelhyg
+Arch Mal Appar Dig Mal Nutr
+Arch Mal Coeur Vaiss
+Arch Mal Prof
+Arch Maragliano Patol Clin
+Arch Maragliano Patolog Clin Collana Monogr
+Arch Med Cuba
+Arch Med Exp
+Arch Med Gen Trop
+Arch Med Infant
+Arch Med Intern
+Arch Med Interna
+Arch Med Mutual
+Arch Med Ouest
+Arch Med Panamenos
+Arch Med Res
+Arch Med Sadowej Kryminol
+Arch Med Soc
+Arch Medicos Mex
+Arch Mediterr Med
+Arch Meteorol Geophys Bioklimatol [B]
+Arch Microbiol
+Arch Middx Hosp
+Arch Mikrobiol
+Arch Monaldi
+Arch Monaldi Mal Torace
+Arch Nat Hist
+Arch Neurobiol (Madr)
+Arch Neurol
+Arch Neurol Psychiatry
+Arch Odonto Estomatol
+Arch Oftalmol B Aires
+Arch Ohren Nasen Kehlkopfheilkd
+Arch Ophtalmol (Paris)
+Arch Ophtalmol Rev Gen Ophtalmol
+Arch Ophthal
+Arch Ophthalmol
+Arch Oral Biol
+Arch Orient
+Arch Orientforsch
+Arch Orthop Trauma Surg
+Arch Orthop Unfallchir
+Arch Ortop
+Arch Osp Mare
+Arch Ostet Ginecol
+Arch Otolaryngol
+Arch Otolaryngol Head Neck Surg
+Arch Otorhinolaryngol
+Arch Otorhinolaryngol Suppl
+Arch Ottamol
+Arch Papyrusforsch
+Arch Pathol
+Arch Pathol (Chic)
+Arch Pathol Lab Med
+Arch Patol Clin Med
+Arch Pediatr
+Arch Pediatr (Barc)
+Arch Pediatr Adolesc Med
+Arch Pediatr Urug
+Arch Pharm
+Arch Pharm (Weinheim)
+Arch Pharm Ber Dtsch Pharm Ges
+Arch Pharm Chem (Kbh)
+Arch Pharm Res
+Arch Philos
+Arch Phys Med
+Arch Phys Med Rehabil
+Arch Phys Ther
+Arch Phys Ther (Leipz)
+Arch Physiol Biochem
+Arch Protistenkd
+Arch Psicol Neurol Psichiatr
+Arch Psychiatr Nervenkr
+Arch Psychiatr Nervenkr Z Gesamte Neurol Psychiatr
+Arch Psychiatr Nurs
+Arch Psychol (Frankf)
+Arch Putti Chir Organi Mov
+Arch Radiol
+Arch Reformation Hist
+Arch Report
+Arch Roum Pathol Exp Microbiol
+Arch STD HIV Res
+Arch Sci
+Arch Sci Biol (Bologna)
+Arch Sci Compte Rendu Seances Soc
+Arch Sci Med (Torino)
+Arch Sci Physiol (Paris)
+Arch Sci Soc Relig
+Arch Sex Behav
+Arch Soc Cir Chile
+Arch Soc Esp Oftalmol
+Arch Soc Rom Stor Patria
+Arch Sozialgesch
+Arch Stomatol
+Arch Stomatol (Napoli)
+Arch Stor Ital
+Arch Stor Lomb
+Arch Stor Prov Napol
+Arch Stor Sicil
+Arch Suicide Res
+Arch Surg
+Arch Tierernahr
+Arch Tisiol
+Arch Tisiol Mal Appar Respir
+Arch Toxicol
+Arch Toxicol Suppl
+Arch Toxikol
+Arch Urug Med Cir Espec
+Arch Veneto
+Arch Venez Med Trop Parasitol Med
+Arch Venez Pueric Pediatr
+Arch Vet Ital
+Arch Vet Pol
+Arch Virol
+Arch Virol Suppl
+Arch Womens Ment Health
+Arch Z
+Archaea
+Archaeol Aeliana
+Archaeol Anz
+Archaeol Cantiana
+Archaeology
+Archeologia
+Archief Meded k Zeeuwsch Genoot Wet
+Archifacts
+Archiginnasio
+Archit Aujourdhui
+Archit Rec
+Architecture
+Archivar
+Archivaria
+Archivio Chir Torace
+Archivist
+Archivmitteilungen
+Archivos Soc Oftalmol Hisp Am
+Arcisp S Anna Ferrara
+Arctic Anthropol
+Arctic Med Res
+Arerugi
+Arethusa
+Argent Repub Laws Statut
+Argos
+Argument
+Arh Farm (Belgr)
+Arh Hig Rada
+Arh Hig Rada Toksikol
+Arh Zast
+Ariel (Jerus)
+Ariz Dent J
+Ariz J Int Comp Law
+Ariz Law Rev
+Ariz Med
+Ariz Nurse
+Ariz Revis Statut Annot Ariz
+Ariz State Law J
+Ariz West
+Ark Archeol
+Ark Dent
+Ark Dent J
+Ark Hist Q
+Ark Law Rev
+Ark Light Newsl
+Ark Statut 1947 Ark
+Arkh Anat Gistol Embriol
+Arkh Patol
+Armed Forces Med J India
+Armed Forces Soc
+Armen Rev
+Army Lawyer
+Army Q Def J
+Arq Bras Cardiol
+Arq Bras Endocrinol Metabol
+Arq Bras Med
+Arq Bras Med Nav
+Arq Bras Oftalmol
+Arq Bras Psicol
+Arq Cent Cult Port
+Arq Cent Estud Curso Odontol
+Arq Cent Estud Fac Odontol UFMG (Belo Horiz)
+Arq Cir Clin Exp
+Arq Clin (Rio De J)
+Arq Fac Hig Saude Publica Univ Sao Paulo
+Arq Gastroenterol
+Arq Hig Saude Publica
+Arq Inst Biol (Sao Paulo)
+Arq Inst Bras Invest Tubers
+Arq Inst Farm
+Arq Med Cirur Pernamb
+Arq Min Leprol
+Arq Neuropsiquiatr
+Arq Oncol
+Arq Patol
+Arq Pediatr
+Arq Port Bioquim
+Arq Port Oftalmol
+Arquivo
+Arrows Change
+Ars Curandi Odontol
+Arsb Goteb Tandlak Sallsk
+Arsb Odontol Samf Finl
+Arsberet Kbh Univ Med Hist Inst Mus
+Arsberet Kobenhavns Univ Med Hist Mus
+Art Am
+Art Bull
+Art Hist
+Art Med
+Arteres Veines
+Arterioscler Thromb
+Arterioscler Thromb Vasc Biol
+Arteriosclerosis
+Artery
+Artes Mex
+Artha Vijnana
+Arthritis Care Res
+Arthritis Res
+Arthritis Res Ther
+Arthritis Rheum
+Arthritis Rheum (Munch)
+Arthroscopy
+Articulator (Columb)
+Articulator (Syd)
+Artif Cells Blood Substit Immobil Biotechnol
+Artif Intell Med
+Artif Life
+Artif Limbs
+Artif Organs
+Arukoru Kenkyuto Yakubutsu Ison
+Arv
+Arzneimittelforschung
+Arztebl Baden Wurtt
+Arztebl Rheinl Pfalz
+Arztl Dienst
+Arztl Forsch
+Arztl Fortbild
+Arztl Jugendkd
+Arztl Kosmetol
+Arztl Prax
+Arztl Sammelbl
+Arztl Wochensch
+Asbury Theol J
+Ascania
+Asclepio
+Asepsis
+Asia
+Asia Mag
+Asia Major
+Asia Oceania J Obstet Gynaecol
+Asia Pac Econ Lit
+Asia Pac J Clin Nutr
+Asia Pac J Public Health
+Asia Pac J Rural Dev
+Asia Pac J Soc Work
+Asia Pac Obs
+Asia Pac POPIN Bull
+Asia Pac Pop Policy
+Asia Pac Popul J
+Asia Pac Popul Res Abstr
+Asia Pac Viewp
+Asia Q
+Asia-Pac POPIN Newsl
+Asian Afr Stud
+Asian Am Pac Isl J Health
+Asian Cardiovasc Thorac Ann
+Asian Dev Rev
+Asian Econ
+Asian Econ Rev
+Asian Folkl Stud
+Asian Forum Newsl
+Asian Geogr
+Asian J Aesthet Dent
+Asian J Androl
+Asian J Commun
+Asian J Control
+Asian J Econ
+Asian J Econ Soc Stud
+Asian J Infect Dis
+Asian J Surg
+Asian Med J
+Asian Migr
+Asian Pac Cens Forum
+Asian Pac Census Newsl
+Asian Pac J Allergy Immunol
+Asian Pac J Cancer Prev
+Asian Pac Migr J
+Asian Pac Popul Forum
+Asian Pac Popul Programme News
+Asian Perspect
+Asian Popul Programme News
+Asian Popul Stud Ser
+Asian Profile
+Asian Stud
+Asian Surv
+Asian Thought Soc
+Asiat Forsch
+Asiaweek
+Asien Afr Lateinam
+Ask Sihhiye Derg
+Asklepii
+Aslib Proc
+Aspen Emphysema Conf
+Aspens Advis Nurse Exec
+Assay Drug Dev Technol
+Assertive Nurse
+Assessment
+Assia Jew Med Ethics
+Assignment Child
+Assist Inferm Ric
+Assist Reprod Rev
+Assist Technol
+Assoc Manage
+Assoc Soc Manager
+Asthet Med (Berl)
+Astragale
+Astrameddelande
+Astrobiology
+Astron Astrophys
+Astron Astrophys Suppl Ser
+Astron J
+Astronomy
+Astrophys J
+Astrophys J Suppl Ser
+Astrophys Lett Commun
+Astrophys Space Sci
+At Energy Law J
+At Energy Rev
+Aten Primaria
+Atene Roma
+Atenea
+Ateneo Parmense Acta Biomed
+Ateneo Parmense Acta Nat
+Ateneo Parmense [1]
+Ateneo Veneto
+Athena
+Atheroscler Suppl
+Atherosclerosis
+Atl Econ J
+Atl Mon
+Atl Report
+Atlanta
+Atlanta Hist
+Atlanta Hist J.
+Atlanta Med
+Atlanta Wkly
+Atlantic
+Atlantida
+Atlantis (Montr)
+Atlantis (Wolfv)
+Atlas Oral Maxillofac Surg Clin North Am
+Atlas Radiol Clin Presse Med
+Atlas World Press Rev
+Atmos Environ
+Atoll Res Bull
+Atompraxis
+Attach Hum Dev
+Atti Accad Fisiocrit Siena
+Atti Accad Fisiocrit Siena [Med Fis]
+Atti Accad Med Lomb
+Atti Accad Naz Lincei
+Atti Accad Sci Ist Bologna Classe Sci Fis Rend
+Atti Clin Otorinolaringoitr Univ Palermo
+Atti Congr Naz Soc Ital Med Leg
+Atti Mem Accad Ital Stor Farm
+Atti Mem Accad Naz Sci Lett Arti Modena
+Atti Mem Accad Stor Arte Sanit
+Atti Mem Accad Toscana Sci Lett Colombaria
+Atti Mem Deput Stor Patria Antiche Prov Modenesi
+Atti Mem R Accad Petrarca Lett Arti Sci
+Atti Soc Ital Cardiol
+Attual Dent
+Attual Diet
+Attual Ematol
+Attual Ostet Ginecol
+Auckl Univ Law Rev
+Audiol Neurootol
+Audiology
+Audit Unit News
+Audubon
+Auris Nasus Larynx
+Ausgrab Funde
+Aussenwirtschaft
+Aust Agder Arv
+Aust Bull Labour
+Aust Clin Rev
+Aust Coll Midwives Inc J
+Aust Crit Care
+Aust Cult Hist
+Aust Dent J
+Aust Dent Pract
+Aust Econ Hist Rev
+Aust Econ Pap
+Aust Econ Rev
+Aust Endod J
+Aust Fam Physician
+Aust Geogr
+Aust Geogr Stud
+Aust Health Rev
+Aust Hist Stud
+Aust Hosp
+Aust Intellect Prop J
+Aust J Adv Nurs
+Aust J Ageing
+Aust J Agric Res
+Aust J Agric Resour Econ
+Aust J Anthropol
+Aust J Biol Sci
+Aust J Biotechnol
+Aust J Chem
+Aust J Chin Aff
+Aust J Clin Hypnother
+Aust J Dermatol
+Aust J Early Child
+Aust J Exp Biol Med Sci
+Aust J Fam Law
+Aust J Forensic Sci
+Aust J Fr Stud
+Aust J Geod Photogramm Surv
+Aust J Holist Nurs
+Aust J Marriage Fam
+Aust J Ment Retard
+Aust J Midwifery
+Aust J Ophthalmol
+Aust J Optom
+Aust J Physiother
+Aust J Plant Physiol
+Aust J Polit Hist
+Aust J Polit Sci
+Aust J Prof Appl Ethics
+Aust J Psychol
+Aust J Public Health
+Aust J Rural Health
+Aust J Sci Med Sport
+Aust J Sci Res (B)
+Aust J Sex Marriage And Fam
+Aust J Soc Issues
+Aust J Stat
+Aust J Zool
+Aust Law J
+Aust Libr J
+Aust Med Rec J
+Aust N Z Gen Pract
+Aust N Z J Med
+Aust N Z J Ment Health Nurs
+Aust N Z J Obstet Gynaecol
+Aust N Z J Obstet Gynaecol Suppl
+Aust N Z J Ophthalmol
+Aust N Z J Psychiatry
+Aust N Z J Public Health
+Aust N Z J Sociol
+Aust N Z J Surg
+Aust New Zealand Health Policy
+Aust Nurs J
+Aust Nurses J
+Aust Orthod J
+Aust Outlook
+Aust Paediatr J
+Aust Prosthodont J
+Aust Prosthodont Soc Bull
+Aust Psychol
+Aust Q
+Aust Soc
+Aust Soc Prosthodontists Bull
+Aust Vet J
+Australas Ann Med
+Australas Biotechnol
+Australas Cathol Rec
+Australas J Ageing
+Australas J Dermatol
+Australas J Philos
+Australas Nurses J
+Australas Phys Eng Sci Med
+Australas Psychiatry
+Australas Radiol
+Austriaca
+Autism
+Autoimmun Rev
+Autoimmunity
+Auton Agent Multi Agent Syst
+Auton Autacoid Pharmacol
+Auton Neurosci
+Auton Robots
+Auxiliaire
+Av Commun Rev
+Av Odontoestomatol
+Av Periodoncia
+Av Piscol Clin Latinonot
+Avenir Med
+Aviakosm Ekolog Med
+Avian Dis
+Avian Pathol
+Aviat Space Environ Med
+Aviat Week Space Technol
+Axone
+Azerbaidzhanskii Meditsinskii Zhurnal
+Aziia Afr Segodnia
+Aztlan
+B C Med J
+B C Stud
+B-ENT
+BDD
+BERC Bull
+BETA
+BIRPERHT Publ
+BJOG
+BJR Suppl
+BJU Int
+BL Gesz
+BL Heimatkd
+BL Wueritemb Kirchengesch
+BMC Anesthesiol
+BMC Biochem
+BMC Bioinformatics
+BMC Biol
+BMC Biotechnol
+BMC Blood Disord
+BMC Cancer
+BMC Cardiovasc Disord
+BMC Cell Biol
+BMC Chem Biol
+BMC Clin Pathol
+BMC Clin Pharmacol
+BMC Complement Altern Med
+BMC Dermatol
+BMC Dev Biol
+BMC Ear Nose Throat Disord
+BMC Ecol
+BMC Emerg Med
+BMC Endocr Disord
+BMC Evol Biol
+BMC Fam Pract
+BMC Gastroenterol
+BMC Genet
+BMC Genomics
+BMC Geriatr
+BMC Health Serv Res
+BMC Immunol
+BMC Infect Dis
+BMC Int Health Hum Rights
+BMC Med
+BMC Med Educ
+BMC Med Ethics
+BMC Med Genet
+BMC Med Imaging
+BMC Med Inform Decis Mak
+BMC Med Res Methodol
+BMC Microbiol
+BMC Mol Biol
+BMC Musculoskelet Disord
+BMC Nephrol
+BMC Neurol
+BMC Neurosci
+BMC Nucl Med
+BMC Nurs
+BMC Ophthalmol
+BMC Oral Health
+BMC Palliat Care
+BMC Pediatr
+BMC Pharmacol
+BMC Physiol
+BMC Plant Biol
+BMC Pregnancy Childbirth
+BMC Psychiatry
+BMC Public Health
+BMC Pulm Med
+BMC Struct Biol
+BMC Surg
+BMC Urol
+BMC Vet Res
+BMC Womens Health
+BME
+BMJ
+BMQ
+BNI Q
+BNWL Rep
+BSCS Pam
+BTTA Rev
+BZB Bayer Zahnarztebl
+Backgr Notes Ser
+Backgrounder
+Bacteriol Rev
+Bacteriol Virusol Parazitol Epidemiol
+Bacteriol Virusol Parazitol Epidemiol (Bucur)
+Baillieres Best Pract Res Clin Endocrinol Metab
+Baillieres Best Pract Res Clin Gastroenterol
+Baillieres Best Pract Res Clin Haematol
+Baillieres Best Pract Res Clin Obstet Gynaecol
+Baillieres Best Pract Res Clin Rheumatol
+Baillieres Clin Anaesthesiol
+Baillieres Clin Endocrinol Metab
+Baillieres Clin Gastroenterol
+Baillieres Clin Haematol
+Baillieres Clin Immunol Allergy
+Baillieres Clin Neurol
+Baillieres Clin Obstet Gynaecol
+Baillieres Clin Oncol
+Baillieres Clin Rheumatol
+Baker Cederberg Noteb
+Balance
+Balcanica
+Baldwins Ky Revis Statut Annot Ky
+Baltim Mag
+Bangkok Post
+Banglad J Microbiol
+Bangladesh Dev Stud
+Bangladesh J Sci Res
+Bangladesh Med J
+Bangladesh Med Res Counc Bull
+Banque
+Baptist Hist Herit
+Baptist Q
+Barbados Nurs J
+Barc Quir
+Barrister
+Barrons
+Bartonia
+Basal Facts
+Basic Appl Histochem
+Basic Appl Myol
+Basic Appl Soc Psych
+Basic Clin Pharmacol Toxicol
+Basic Life Sci
+Basic Res Cardiol
+Basl Z Gesch Altertumskd
+Bassini
+Baxter Health Policy Rev
+Bayl Law Rev
+Baylor Dent J
+Baylor Nurs Educ
+Beaver
+Bedside Nurse
+Begg J Orthod Theory Treat
+Beginnings
+Behav Anal
+Behav Assess
+Behav Biol
+Behav Brain Funct
+Behav Brain Res
+Behav Brain Sci
+Behav Change
+Behav Cogn Neurosci Rev
+Behav Ecol
+Behav Ecol Sociobiol
+Behav Eng
+Behav Genet
+Behav Healthc
+Behav Healthc Tomorrow
+Behav Inf Technol
+Behav Med
+Behav Med Update
+Behav Modif
+Behav Neural Biol
+Behav Neurol
+Behav Neuropsychiatry
+Behav Neurosci
+Behav Pharmacol
+Behav Processes
+Behav Res Methods
+Behav Res Methods Instrum Comput
+Behav Res Ther
+Behav Sci
+Behav Sci Law
+Behav Sci Res
+Behav Sleep Med
+Behav Soc Action J
+Behav Soc Sci Librar
+Behav Ther
+Behaviorism
+Behaviormetrika
+Behaviour
+Behring Inst Mitt
+Beijing Da Xue Xue Bao
+Beilstein J Org Chem
+Beitr Dtsch Philol
+Beitr Gerichtl Med
+Beitr Gesch Arb
+Beitr Gesch Med Nebengeb
+Beitr Gesch Pharm Ihrer Nachbargeb
+Beitr Hochsch Wissenschaftsgesch Erfurts
+Beitr Hyg Epidemiol
+Beitr Infusionsther
+Beitr Infusionsther Transfusionsmed
+Beitr Infusionther Klin Ernahr
+Beitr Klin Chir
+Beitr Klin Erforsch Tuberk Lungenkr
+Beitr Klin Neurol Psychiatr
+Beitr Klin Tuberk Spezif Tuberkuloseforsch
+Beitr Krebsforsch
+Beitr Neurochir
+Beitr Orthop Traumatol
+Beitr Pathol
+Beitr Pathol Anat
+Beitr Rheumatol
+Beitr Sexualforsch
+Beitr Silikoseforsch
+Beitr Silikoseforsch Pneumokoniose
+Beitr Trop Landwirtsch Veterinarmed
+Beitr Wurttemb Apothekengesch
+Belcast J Belizean Aff
+Beleid Maatsch
+Belfagor
+Belg Tijdschr Geneesk
+Belg Tijdschr Nieuwste Geschied
+Belizean Stud
+Belleten
+Benders Health Care Law Mon
+Benefits Q
+Benelux
+Bengal Past Present
+Ber Bonn Univ Poliklin Mund Zahn Kieferkr
+Ber Dtsch Bot Ges
+Ber Dtsch Landeskd
+Ber Naturforsch Ges Freibg Br
+Ber Phys Med Ges Wurzbg
+Ber Raumforsch Raumplan
+Ber Wissenschaftsgesch
+Ber Zusammenkunft Dtsch Ophthalmol Ges
+Berita Jururawat
+Berkala Ilmu Kedokteran
+Berkeley J Sociol
+Berkeley Munic Code 1976 Berkeley Calif
+Berkeley Technol Law J
+Berkeley Womens Law J
+Berl Geogr Stud
+Berl Hist Stud
+Berl J Soziol
+Berl Med
+Berl Munch Tierarztl Wochenschr
+Berl Stat
+Berl Stat Mon Schr
+Berl Tierarztl Wochenschr
+Berufsdermatosen
+Berufsdermatosen (Monogr)
+Berytus
+Best Pract Benchmarking Healthc
+Best Pract Res Clin Anaesthesiol
+Best Pract Res Clin Endocrinol Metab
+Best Pract Res Clin Gastroenterol
+Best Pract Res Clin Haematol
+Best Pract Res Clin Obstet Gynaecol
+Best Pract Res Clin Rheumatol
+Bests Rev Life Health Insur Ed
+Better Homes Gard
+Between Species
+Bevolking Gezin
+Beyond Relief
+Bib Mitt
+Bibl Anat
+Bibl Cardiol
+Bibl Ec Chartes
+Bibl Gastroenterol
+Bibl Gynaecol
+Bibl Haematol
+Bibl Hig Instituta NR Srb
+Bibl Humanisme Renaiss
+Bibl Laeger
+Bibl Microbiol
+Bibl Nutr Dieta
+Bibl Ophthalmol
+Bibl Paediatr
+Bibl Psychiatr
+Bibl Psychiatr Neurol
+Bibl Radiol
+Bibl Stor Sci
+Bibl Tuberc
+Bibl Wiss
+Bibliofilia
+Bibliogr Int Demogr Hist
+Bibliogr Ital Stor Sci
+Bibliographr Reprod
+Bibliotheck
+Biblos
+Bien Naitre
+Bijdr Geschied
+Bijdr Meded
+Bijdr Meded Geschied Ned
+Bijdragen
+Biken J
+Bild Wiss
+Bildgebung
+Bilt Hematol Transfuz
+Bilt Mednar Fed Zob Teh
+Bilt Udruz Ortodonata Jugosl
+Binocul Vis Strabismus Q
+BioDrugs
+Biocell
+Biochem Arch
+Biochem Biophys Res Commun
+Biochem Cell Biol
+Biochem Clin
+Biochem Exp Biol
+Biochem Genet
+Biochem Int
+Biochem J
+Biochem Med
+Biochem Med Metab Biol
+Biochem Mol Biol Int
+Biochem Mol Med
+Biochem Pharmacol
+Biochem Physiol Pflanz
+Biochem Soc Symp
+Biochem Soc Trans
+Biochem Syst Ecol
+Biochem Z
+Biochemistry
+Biochemistry (Mosc)
+Biochim Biophys Acta
+Biochim Clin
+Biochimie
+Bioconjug Chem
+Biocontrol Sci
+Biocycle
+Biodegradation
+Biodynamica
+Bioelectrochem Bioenerg
+Bioelectrochemistry
+Bioelectromagnetics
+Bioessays
+Bioeth Bull
+Bioeth Exam
+Bioeth News
+Bioeth Res Notes
+Bioethics
+Bioethics Dig
+Bioethics Forum
+Bioethics Northwest
+Bioethics Q
+Biofactors
+Biofeedback Self Regul
+Biofilms
+Biofizika
+Biofouling
+Biog Amines
+Biogerontology
+Biogr Mem Fellows R Soc
+Biogr Mem Natl Acad Sci
+Biography
+Bioinformatics
+Bioinorg Chem
+Biokhimiia
+Biol Blood Marrow Transplant
+Biol Bull
+Biol Bull Acad Sci USSR
+Biol Cell
+Biol Chem
+Biol Chem Hoppe Seyler
+Biol Conserv
+Biol Cybern
+Biol Direct
+Biol Gastroenterol (Paris)
+Biol Hum Aff
+Biol J Linn Soc Lond
+Biol Lat
+Biol Listy
+Biol Mass Spectrom
+Biol Med (Paris)
+Biol Mem
+Biol Membr
+Biol Met
+Biol Neonat
+Biol Neonate
+Biol Pharm Bull
+Biol Plant
+Biol Proced Online
+Biol Psychiatry
+Biol Psychol
+Biol Psychol Bull
+Biol Reprod
+Biol Reprod Suppl
+Biol Res
+Biol Res Nurs
+Biol Res Pregnancy Perinatol
+Biol Rev Camb Philos Soc
+Biol Rhythm Res
+Biol Sci Space
+Biol Signals
+Biol Signals Recept
+Biol Soc
+Biol Sport
+Biol Struct Morphog
+Biol Trace Elem Res
+Biol Zent Bl
+Biologia
+Biologia (Bratisl)
+Biologica (Santiago)
+Biologicals
+Biologist
+Biologist (London)
+Biom Hum
+Biom J
+Biom Z
+Biomacromolecules
+Biomagn Res Technol
+Biomarkers
+Biomater Artif Cells Artif Organs
+Biomater Artif Cells Immobilization Biotechnol
+Biomater Med Devices Artif Organs
+Biomaterials
+Biomech Model Mechanobiol
+Biomed Biochim Acta
+Biomed Bull
+Biomed Chromatogr
+Biomed Commun
+Biomed Digit Libr
+Biomed Eng
+Biomed Eng (NY)
+Biomed Eng Online
+Biomed Environ Mass Spectrom
+Biomed Environ Sci
+Biomed Ethics
+Biomed Instrum
+Biomed Instrum Technol
+Biomed Khim
+Biomed Lib Bull
+Biomed Mass Spectrom
+Biomed Mater Eng
+Biomed Microdevices
+Biomed News
+Biomed Pap Med Fac Univ Palacky Olomouc Czech Repub
+Biomed Pept Proteins Nucleic Acids
+Biomed Pharmacother
+Biomed Purv
+Biomed Res
+Biomed Sci
+Biomed Sci Instrum
+Biomed Sci Technol
+Biomed Tech (Berl)
+Biomedica
+Biomedicine
+Biomembranes
+Biometals
+Biometrics
+Biometrika
+Biomimetics
+Biomol Eng
+Bioorg Chem
+Bioorg Khim
+Bioorg Med Chem
+Bioorg Med Chem Lett
+Biopharm Drug Dispos
+Biophys Chem
+Biophys J
+Biophys Struct Mech
+Biophysik
+Biopolim Kletka
+Biopolymers
+Bioprocess Biosyst Eng
+Bioprocess Technol
+Bioresour Technol
+Biorheology
+Biorheology Suppl
+Bios
+Biosci Biotechnol Biochem
+Biosci Rep
+Bioscience
+Biosecur Bioterror
+Biosens Bioelectron
+Biosensors
+Bioseparation
+Biospectroscopy
+Biostatistics
+Biosystems
+Biotech Histochem
+Biotechniques
+Biotechnol Adv
+Biotechnol Annu Rev
+Biotechnol Appl Biochem
+Biotechnol Bioeng
+Biotechnol Bioeng Symp
+Biotechnol Genet Eng Rev
+Biotechnol Lett
+Biotechnol Prog
+Biotechnol Ther
+Biotechnology
+Biotechnology (N Y)
+Biotelem Patient Monit
+Biotelemetry
+Biotherapy
+Biotronics
+Biotypologie
+Bipolar Disord
+Birth
+Birth Defects Orig Artic Ser
+Birth Defects Res A Clin Mol Teratol
+Birth Defects Res B Dev Reprod Toxicol
+Birth Defects Res C Embryo Today
+Birth Fam J
+Birth Gaz
+Birth Psychol Bull
+Birthright
+Biul Bibl Jagiellon
+Biul IGS
+Biul Inst Med Morsk Gdansk
+Biul Panstw Inst Med Morsk Trop J W Gdansku
+Biul Zydowskiego Inst Hist Pol
+Biull Eksp Biol Med
+Biull Mosk Ova Ispyt Prir (Biol)
+Biull Vsesoiuznogo Kardiol Nauchn Tsentra AMN SSSR
+Biulleten Russ SFSR Minist Zdravookhraneniia Uchenyi Meditsinskii sov
+Bl Dtsch Int Polit
+Bl Zahnheilkd
+Black Aging
+Black Law J
+Black Scholar
+Blake Stud
+Blood
+Blood Cells
+Blood Cells Mol Dis
+Blood Coagul Fibrinolysis
+Blood Press
+Blood Press Monit
+Blood Press Suppl
+Blood Purif
+Blood Rev
+Blood Vessels
+Bloomsbury Geogr
+Blue Cross Assoc Res Ser
+Blue Sheet
+Blut
+Blutalkohol
+Boardr Rep
+Bodleian Libr Rec
+Body Forum
+Body Posit
+Boei Eisei
+Boei Ika Daigakko Zasshi
+Bogens Verden
+Bogeon sahoe nonjib
+Bohemia Jahrb Coll Carolinum
+Bol Acad Nac Hist (Argent)
+Bol Acad Nac Hist (Caracas)
+Bol Acad Nac Med
+Bol Acad Peru Cir
+Bol Agrup Odontol Zona Cent Cap Fed B Aires
+Bol Anal Demogr
+Bol Antropol
+Bol Antropol Am
+Bol Asoc Argent Odontol Ninos
+Bol Asoc Chil Prot Fam
+Bol Asoc Demogr Hist
+Bol Asoc Med P R
+Bol Asoc Medica Nac Repub Panama
+Bol Asoc Venez Enferm Prof
+Bol B Aires Univ Nac Inst Clin Quir
+Bol Bibl Menendez Pelayo
+Bol Bibliogr Secr Hacienda Credito Publico
+Bol Cent Biol Reprod
+Bol Cent Estud Hosp Servidores Estado
+Bol Cent Estud Siglo XVIII
+Bol Chil Parasitol
+Bol Circ Argent Odontol
+Bol Clin Hosp Civis Lisb
+Bol Col Prof Enferm P R
+Bol Cons Nac Poblac
+Bol Cult Bibliogr
+Bol Cult Inf Cons Gen Col Med Esp
+Bol Demogr
+Bol Dent Oper
+Bol Dent Urug
+Bol Dir Gen Odontol (Santa Fe)
+Bol Div Nac Dermatol Sanit
+Bol Ed Col Mex
+Bol Epidemiol AIDS
+Bol Epidemiol Chile
+Bol Equipe Odontol Sanit
+Bol Estud Latinoam Caribe
+Bol Estud Med Biol
+Bol Fac Farm Odontol Ribeirao Preto
+Bol Fac Odontol Piracicaba
+Bol Geogr
+Bol Geogr Teor
+Bol Hist Antig
+Bol Hosp Mil Cuba Ejercito Hosp Mil Dr Carlos J Finlay
+Bol Hosp Oftalmol
+Bol Hosp San Juan Dios
+Bol Indic Coyunt
+Bol Inf Cent Doc Pesqui
+Bol Inf Col Odontol Estomatol (Barc)
+Bol Inf Dent (Madr)
+Bol Inf Parasit Chil
+Bol Inf Socio Econ
+Bol Inst Angola
+Bol Inst Estud Asturianos
+Bol Inst Estud Med Biol Univ Nac Auton Mex
+Bol Inst Hist Argent Am Dr Emilio Ravignani
+Bol Inst Interam Nino
+Bol Inst Invest Bibliogr (Mexico)
+Bol Inst Pueric
+Bol Inst Pueric Martagao Gesteira
+Bol Inst Sancho Sabio
+Bol Liga Contra Cancer Havana
+Bol Mat Dent
+Bol Med Hosp Infant Mex
+Bol Mens Estad DANE
+Bol Mex Derecho Comp
+Bol Odontol (B Aires)
+Bol Odontol (Bogota)
+Bol Odontol Mex
+Bol Of Estado Gac Madr Spain
+Bol Of Sanit Panam (Engl)
+Bol Oficina Sanit Panam
+Bol PROLAP
+Bol Poblac
+Bol Popul Emprego Renda Nordeste
+Bol Protes
+Bol Psicol
+Bol R Soc Esp Hist Nat Secc Biol
+Bol SIDEMA
+Bol Sanat Sao Lucas
+Bol Sanid Mil
+Bol Serv Odontol Sanit (Porto Alegre)
+Bol Soc Castell Cult
+Bol Soc Catalana Pediatr
+Bol Soc Chil Obstet Ginecol
+Bol Soc Cir Rosario
+Bol Soc Cir Urug
+Bol Soc Cubana Dermatol Sifilogr
+Bol Soc Dent Guatem
+Bol Soc Esp Hidrol Med
+Bol Soc Esp Hist Farm
+Bol Soc Esp Hist Med
+Bol Soc Estomatol Argent
+Bol Soc Geogr Lisb
+Bol Soc Mex Hist Filos Med
+Bol Soc Quim Peru
+Bol Soc Valencia Pediatr
+Bol Socioecon
+Bol Tr Soc Argent Cir
+Bol Trab Soc Cir B Aires
+Bol Univ Granada
+Bold
+Boletin
+Boll Cent Int Beltrame Stor Spazio Tempo
+Boll Chim Farm
+Boll Demogr Stor
+Boll Inf Consoc Naz (Rome)
+Boll Ist Sieroter Milan
+Boll Mal Orecch Gola Naso
+Boll Mem Soc Piemont Chir
+Boll Mem Soc Tosco Umbra Chir
+Boll Metallogr
+Boll Ocul
+Boll Osp Varese
+Boll Soc Ital Biol Sper
+Boll Soc Ital Cardiol
+Boll Soc Med Chir Bresciana
+Boll Soc Med Chir Cremona
+Boll Soc Med Chir Pisa
+Boll Stor Bibliogr Subalp
+Bologna Incontri
+Bologna Medica
+Bombay Hosp J
+Bone
+Bone Marrow Transplant
+Bone Miner
+Bonn Geschichtsbl
+Book Collect
+Book Suppl J Child Psychol Psychiatr
+Bookman
+Bord Chir
+Bord Med
+Bordens Rev Nutr Res
+Borgyogy Venerol Sz
+Borsuye
+Bosn J Basic Med Sci
+Boston
+Boston Bar J
+Boston Coll Environ Aff Law Rev
+Boston Coll Ind Commer Law Rev
+Boston Coll Law Rev
+Boston Coll Third World Law J
+Boston College Int Comp Law Rev
+Boston Globe
+Boston Univ Int Law J
+Boston Univ J
+Boston Univ Law Rev
+Boston Univ Public Interest Law J
+Bostonia
+Bot Acta
+Bot Bull Acad Sinica (Taiwan)
+Bot Gaz
+Bot J Linn Soc
+Bot Mus Lealf Harv Univ
+Bot Rev
+Botsw Natl Health Bull
+Botsw Notes Rec
+Br Book News
+Br Dent J
+Br Dent Nurs J
+Br Dent Surg Assist
+Br Heart J
+Br Herit
+Br Hist Illus
+Br Homeopath J
+Br J 18th Cent Stud
+Br J Addict
+Br J Addict Alcohol Other Drugs
+Br J Anaesth
+Br J Audiol
+Br J Audiol Suppl
+Br J Biomed Sci
+Br J Cancer
+Br J Cancer Suppl
+Br J Clin Pharmacol
+Br J Clin Pract
+Br J Clin Pract Suppl
+Br J Clin Psychol
+Br J Community Nurs
+Br J Criminol
+Br J Dermatol
+Br J Dis Chest
+Br J Disord Commun
+Br J Educ Psychol
+Br J Educ Technol
+Br J Exp Pathol
+Br J Fam Plann
+Br J Gen Pract
+Br J Guid Counc
+Br J Haematol
+Br J Health Psychol
+Br J Hist Sci
+Br J Hosp Med
+Br J Hosp Med (Lond)
+Br J Ind Med
+Br J Int Stud
+Br J Law Soc
+Br J Math Stat Psychol
+Br J Med Educ
+Br J Med Hypn
+Br J Med Psychol
+Br J Neurosurg
+Br J Nurs
+Br J Nutr
+Br J Obstet Gynaecol
+Br J Ophthalmol
+Br J Oral Maxillofac Surg
+Br J Oral Surg
+Br J Orthod
+Br J Perioper Nurs
+Br J Pharmacol
+Br J Pharmacol Chemother
+Br J Philos Sci
+Br J Phys Med
+Br J Physiol Opt
+Br J Plast Surg
+Br J Polit Sci
+Br J Prev Soc Med
+Br J Psychiatry
+Br J Psychiatry Suppl
+Br J Psychol
+Br J Radiol
+Br J Radiol Suppl
+Br J Rheumatol
+Br J Sex Med
+Br J Soc Clin Psychol
+Br J Soc Med
+Br J Soc Psychol
+Br J Soc Work
+Br J Sociol
+Br J Sociol Educ
+Br J Sports Hist
+Br J Sports Med
+Br J Surg
+Br J Theatre Nurs
+Br J Tuberc Dis Chest
+Br J Urol
+Br J Vener Dis
+Br Libr J
+Br Med Bull
+Br Med J
+Br Med J (Clin Res Ed)
+Br Poult Sci
+Br Rev Econ Issues
+Br Sci News
+Br Stud Monit
+Br Vet J
+Br Yearb Sci
+Brachytherapy
+Brain
+Brain Behav Evol
+Brain Behav Immun
+Brain Cogn
+Brain Dev
+Brain Inj
+Brain Lang
+Brain Pathol
+Brain Res
+Brain Res Brain Res Protoc
+Brain Res Brain Res Rev
+Brain Res Bull
+Brain Res Cogn Brain Res
+Brain Res Dev Brain Res
+Brain Res Gene Expr Patterns
+Brain Res Mol Brain Res
+Brain Topogr
+Brain Tumor Pathol
+Brandeis J Fam Law
+Brandeis Law J
+Brandeis Rev
+Bras Med
+Bratisl Lek Listy
+Braunschw Veroff Gesch Pharm Naturwiss
+Braz Dent J
+Braz Econ Stud
+Braz J Biol
+Braz J Infect Dis
+Braz J Med Biol Res
+Braz J Popul Stud
+Breast
+Breast Cancer
+Breast Cancer Res
+Breast Cancer Res Treat
+Breast Dis
+Breast J
+Breastfeed Rev
+Brethr Life Thought
+Brevia (Rome)
+Bridg Wash D C
+Brief Bioinform
+Brief Funct Genomic Proteomic
+Brief Med Ethics
+Briefs
+Brigh Young Univ Law Rev
+Brigham Young Univ Stud
+Bristol Med Chir J
+Britannia
+Bromley Local Hist
+Bronches
+Bronchopneumologie
+Bronte Soc Trans
+Brookhaven Symp Biol
+Brookings Bull
+Brookings Pap Econ Act
+Brookings Rev
+Brooklyn Hosp J
+Brooklyn J Int Law
+Brooklyn Law Rev
+Broward Rev
+Bruns Beitr Klin Chir
+Bruns Beitr Klinischen Chir
+Brux Med
+Bryologist
+Buch Augenarzt
+Buffalo Law Rev
+Build Oper Manage
+Build Syst Des
+Built Environ
+Bul Keluarga
+Bul Pol Acad Sci Chem
+Bul Stiint Sect Stiint Medicale Acad Republicii Pop Romane
+Bul Univ Shtet Tiranes Ser Shk Mjekesore
+Bull Acad Chir Dent (Paris)
+Bull Acad Dent (Paris)
+Bull Acad Dent Handicap
+Bull Acad Gen Dent
+Bull Acad Med N J
+Bull Acad Natl Chir Dent
+Bull Acad Natl Med
+Bull Acad Pol Sci Biol
+Bull Acad R Med Belg
+Bull Acad Serbe Sci Arts
+Bull Acad Soc Lorraines Sci
+Bull Acad Vet Fr
+Bull Akron City Hosp
+Bull Akron Dent Soc
+Bull Alameda Cty Dent Soc
+Bull Alexandria Fac
+Bull Am Acad Arts Sci
+Bull Am Acad Psychiatry Law
+Bull Am Assoc Dent Ed
+Bull Am Assoc Hist Nurs
+Bull Am Assoc Hosp Dent
+Bull Am Coll Nurse Midwifery
+Bull Am Coll Nurse Midwives
+Bull Am Coll Physicians
+Bull Am Coll Surg
+Bull Am Protestant Hosp Assoc
+Bull Am Schools Orient Res
+Bull Am Soc Inf Sci
+Bull Anesth Hist
+Bull Anim Health Prod Afr
+Bull Assoc Anat (Nancy)
+Bull Assoc Diplomes Microbiol Fac Pharm Nancy
+Bull Assoc Fr Etud Cancer
+Bull Assoc Geogr Fr
+Bull Assoc Guillaume Bude
+Bull Assoc Med Haiti
+Bull Assoc Tunis Plan Fam
+Bull At Sci
+Bull Atl Cape May Cty Dent Soc
+Bull Audiophonol
+Bull Bergen Cty Dent Soc
+Bull Bibl Nat
+Bull Bibl R Albert
+Bull Bibliogr
+Bull Biol Fr Belg
+Bull Br Assoc Orient
+Bull Br Mus
+Bull Br Psychol Soc
+Bull Br Soc Middle East Stud
+Bull Bronx Cty Dent Soc
+Bull Brooklyn
+Bull Calcutta Sch Trop Med
+Bull Can Hist Med
+Bull Cancer
+Bull Cancer Inst Okayama Univ Med Sch
+Bull Cancer Radiother
+Bull Cent Hist Econ Soc Reg Lyon
+Bull Cercle Benelux Hist Pharm
+Bull Cerp
+Bull Chem Soc Jpn
+Bull Chest Dis Res Inst Kyoto Univ
+Bull Chic Med Soc
+Bull Cincinnati Dent Soc
+Bull Cl Lett Sci Morales Polit
+Bull Cleve Dent Soc
+Bull Clevel Med Libr
+Bull Clin Neurosci
+Bull Coll Lib Arts
+Bull Concern Asian Sch
+Bull Contra Costa Dent Soc
+Bull Cosm Club
+Bull Czechoslovak Law
+Bull Dayton Dent Soc
+Bull Dent Guid Counc Cereb Palsy
+Bull Ec Fr Extr Orient
+Bull Econ Res
+Bull Econ Soc Maroc
+Bull Eighth Dist Dent Soc
+Bull Eleventh Dist Dent Soc
+Bull Endem Dis (Baghdad)
+Bull Entomol Res
+Bull Environ Contam Toxicol
+Bull Epizoot Dis Afr
+Bull Essex Cty Dent Soc
+Bull Etud Orient
+Bull Etud Port Bres
+Bull Etud Valeryennes
+Bull Eugen Soc
+Bull Eur Physiopathol Respir
+Bull Exp Biol Med
+Bull Fed Soc Gynecol Obstet Lang Fr
+Bull Fifth Dist Dent Soc (Fresno)
+Bull Fifth Dist Dent Soc State N Y
+Bull Francoph Afr
+Bull Ga Acad Sci
+Bull Gandhigram Inst Rural Health Fam Welf Trust
+Bull Gastrointest Endosc
+Bull Geisinger
+Bull Georgetown Univ Med Cent
+Bull Ghana Geogr Assoc
+Bull Gloucester Cty Hist Soc
+Bull Group Eur Rech Sci Stomatol Odontol
+Bull Group Int Rech Sci Stomatol
+Bull Group Int Rech Sci Stomatol Odontol
+Bull Haffkine
+Bull High Inst Public Health
+Bull His Sci Auvergne
+Bull Hisp
+Bull Hist Dent
+Bull Hist Electr
+Bull Hist Med
+Bull Hist Med Suppl
+Bull Hist Polit
+Bull Hosp Joint Dis
+Bull Hosp Jt Dis
+Bull Hosp Jt Dis Orthop Inst
+Bull Hosp Spec Surg
+Bull Hudson Cty Dent Soc
+Bull Hunt Inst Bot Doc
+Bull Hyg
+Bull Hyg (Lond)
+Bull Ill Dent Hyg Assoc
+Bull Indian Inst Hist Med Hyderabad
+Bull Indones Econ Stud
+Bull Infirm Cathol Can
+Bull Inform Fed Pharm Mediterr
+Bull Inst Chem Res Kyoto Univ
+Bull Inst Class Stud Univ Lond
+Bull Inst Egypte
+Bull Inst Fond Afr Noire Ser B
+Bull Inst Fr Archeol Orient
+Bull Inst Hist Med Hyderabad
+Bull Inst Hist Res
+Bull Inst Marit Trop Med Gdynia
+Bull Inst Med Res Kuala Lumpur
+Bull Inst Med Res Univ Madr
+Bull Inst Mod Hist Acad Sin
+Bull Inst Natl Sante Rech Med
+Bull Int Acad Pol Sci Let Cl Med
+Bull Int Comm Urgent Anthropol Ethnol Res
+Bull Int Pediatr Assoc
+Bull Int Serv Sante Armees Terre Mer Air
+Bull Int Union Tuberc
+Bull Int Union Tuberc Lung Dis
+Bull Islam Med
+Bull J N Y State Archeol Assoc
+Bull J Rylands Univ Libr Manchester
+Bull Jersey City Margaret Hague Mat Hosp
+Bull Johns Hopkins Hosp
+Bull Kanagawa Dent Coll
+Bull Kresge Eye Inst
+Bull Lat Am Res
+Bull Leo Baeck Inst
+Bull Liaison Demogr Afr
+Bull Los Angel Neuro Soc
+Bull Los Angeles Dent Soc
+Bull Los Angeles Neurol Soc
+Bull Louis A Weiss Mem Hosp
+Bull Mason Clin
+Bull Mass Nurses Assoc
+Bull Math Biol
+Bull Math Biophys
+Bull McGuire Clin St Lukes Hosp Richmond St Lukes Hosp McGuire Clin
+Bull Med
+Bull Med Ethics
+Bull Med Leg Toxicol
+Bull Med Leg Urgence Med Cent Antipoisons
+Bull Med Libr Assoc
+Bull Mem Acad R Med Belg
+Bull Mem Fac Mixte Med Pharm Dakar
+Bull Mem Soc Anthropol Paris
+Bull Mem Soc Chir Paris
+Bull Mem Soc Fr Ophtalmol
+Bull Mem Soc Med Hop Paris
+Bull Menninger Clin
+Bull Mens Soc Med Mil Fr
+Bull Mens Soc Vet Prat Fr
+Bull Methodol Sociol
+Bull Mich Dent Hyg Assoc
+Bull Micr Appl
+Bull Midtown Dent Soc
+Bull Millard Fillmore Hosp Buffalo
+Bull Mo Hist Soc
+Bull Monmouth Ocean Cty Dent Soc
+Bull Montg Bucks Dent Soc
+Bull Moore White Med Found Los Angel
+Bull Mt Desert Isl Biol Lab Salisb Cove Maine
+Bull Munic Off Ville Mars
+Bull Mus Hong B Arts
+Bull Mus Rov Beaux Arts Belg
+Bull N J Coll Med Dent
+Bull N J Soc Dent Child
+Bull N Y Acad Med
+Bull N Y State Dent Soc Anesthesiol
+Bull N Y State Soc Dent Child
+Bull N Z Soc Periodontol
+Bull Narc
+Bull Nat Fam Plan Counc Vic
+Bull Natl Clgh Poison Control Cent
+Bull Natl Guild Cathol Psychiatr
+Bull Natl Inst Health
+Bull Natl Med Dent Assoc Natl Advocates Soc
+Bull Natl Tuberc Assoc
+Bull Natl Tuberc Respir Dis Assoc
+Bull New Engl Med Cent
+Bull Newark Dent Club
+Bull Ninth Dist Dent Soc
+Bull North Dist Dent Soc
+Bull Off Chambre Synd Med Seine
+Bull Off Int Epizoot
+Bull Ophthalmol Soc Egypt
+Bull Osaka Med Coll
+Bull Osaka Med Sch
+Bull Osaka Med Sch Suppl
+Bull Pac Coast Soc Orthod
+Bull Pan Am Health Organ
+Bull Parenter Drug Assoc
+Bull Park Ridge Cent
+Bull Passaic Cty Dent Soc
+Bull Peace Propos
+Bull Pharm Res Inst
+Bull Phila Assoc Psychoanal
+Bull Phila Cty Dent Soc
+Bull Phila Pa Hosp Ayer Clin Lab
+Bull Philol Hist
+Bull Philos Mediev
+Bull Physiopathol Respir (Nancy)
+Bull Plainfield Dent Soc
+Bull Popul Dev Stud Cent
+Bull Postgrad Comm Med Univ Syd
+Bull Postgrad Inst Med Educ Res Chandigarh
+Bull Prosthet Res
+Bull Psychon Soc
+Bull Publ Health Soc (Kuala Lumpur)
+Bull Res Counc Isr
+Bull Res Counc Isr Sect E Exp Med
+Bull Res Humanit
+Bull Rheum Dis
+Bull San Diego Cty Dent Soc
+Bull San Mateo Cty Dent Soc
+Bull Sch Med
+Bull Sch Med Univ Md
+Bull Sch Orient Afr Stud
+Bull Schweiz Akad Med Wiss
+Bull Sci Med (Bologna)
+Bull Sci Technol Soc
+Bull Seances Acad R Sci Outre Mer
+Bull Second Dist Dent Soc
+Bull Sect Hist Mod Contemp
+Bull Sinai Hosp Detroit
+Bull Sloane Hosp Women Columbia Presbyt Med
+Bull Soc Agric Sci Arts Sarthe
+Bull Soc Amis Sci (Med) (Poznan)
+Bull Soc Amis Vieux Toulon
+Bull Soc Archeol Finistere
+Bull Soc Archeol Hist Artist Vieux Pap
+Bull Soc Archeol Hist Litt Sci Gers
+Bull Soc Belg Etud Geogr
+Bull Soc Belge Etud Napoleon
+Bull Soc Belge Ophtalmol
+Bull Soc Borda
+Bull Soc Chim Biol (Paris)
+Bull Soc Chim Fr
+Bull Soc Chir Paris
+Bull Soc Etud His Alpes
+Bull Soc Etud Oceaniennes
+Bull Soc Fr Dermatol Syphiligr
+Bull Soc Fr Hist Hop
+Bull Soc Hist Archeol Perigord
+Bull Soc Hist Mod
+Bull Soc Hist Protestant Fr
+Bull Soc Ind Mulhouse
+Bull Soc Int Chir
+Bull Soc Ital Med Ig Trop Sez Eritrea
+Bull Soc Languedoc Geogr
+Bull Soc Lett Sci Arts Correze
+Bull Soc Liban Hist Med
+Bull Soc Med Afr Noire Lang Fr
+Bull Soc Med Hyg Alger
+Bull Soc Nat Antiq Fr
+Bull Soc Ophtalmol Fr
+Bull Soc Pathol Exot
+Bull Soc Pathol Exot Filiales
+Bull Soc Pharm Bord
+Bull Soc Pharm Strasb
+Bull Soc R Belge Gynecol Obstet
+Bull Soc Ramond
+Bull Soc Sci Lettr Arts Pau
+Bull Soc Sci Med Grand Duche Luxemb
+Bull Soc Univ Cartogr
+Bull St Louis Park Med Cen
+Bull Statec
+Bull Suffolk Cty Dent Soc
+Bull Tenn Nurses Assoc
+Bull Tenth Dist Dent Soc (Rockville Centre)
+Bull Tex Nurses Assoc
+Bull Tokyo Dent Coll
+Bull Tokyo Med Dent Univ
+Bull Toledo Dent Soc
+Bull Train
+Bull Tri Cty Dent Soc
+Bull Trimest Inst Actuaires Francais
+Bull Trimest Plan Fam
+Bull Trimest Soc Mycol Fr
+Bull Tufts N Engl Med Cent
+Bull Tulane Univ Med Fac
+Bull U S Army Med Dep
+Bull Unesco Reg Off Educ Asia Pac
+Bull Union Cty Dent Soc
+Bull Val Dent Soc
+Bull Vanc Med Assoc
+Bull World Health Org Suppl
+Bull World Health Organ
+Bull World Med Assoc
+Bull Yamaguchi Med Sch
+Bulletin NY Med Coll
+Bundesgesundheitsblatt
+Bundesgesundheitsblatt Gesundheitsforschung Gesundheitsschutz
+Bur
+Bur Justice Stat Spec Rep
+Burgenl Heimatbl
+Burgense
+Burlingt Mag
+Burma Med J
+Burns
+Burns Incl Therm Inj
+Bus Econ
+Bus Econ Rev
+Bus Ethics
+Bus Ethics Q
+Bus Health
+Bus Hist
+Bus Hist Rev
+Bus Horiz
+Bus Insur
+Bus Prof Ethics
+Bus Prof Ethics J
+Bus Rev
+Bus Thail
+Bus Week
+BusinessIndia
+Butll Soc Amics Hist Cienc Farm Catalana
+By Bygd
+Bygone Kent
+Byzantinische Forsch
+Byzantinische Z
+Byzantinoslavica
+Byzantion
+C R Acad Sci Gen
+C R Acad Sci Hebd Seances Acad Sci D
+C R Acad Sci II
+C R Acad Sci III
+C R Assoc Anat
+C R Biol
+C R Hebd Seances Acad Sci
+C R Seances Acad Sci D
+C R Seances Acad Sci III
+C R Seances Soc Biol Fil
+C R Soc Fr Gyncol
+C R Ther Pharmacol Clin
+C R Trav Lab Carlsberg
+C R Trav Lab Carlsberg [Chim]
+CA Cancer J Clin
+CAFS News
+CAL
+CANA
+CANNT J
+CAP Today
+CARE Briefs Develop Isssues
+CBE Views
+CCAR J
+CCICA Annual
+CCL Family Found
+CCL News
+CCQ
+CDA J
+CDC AIDS Wkly
+CDE Work Pap
+CDR (Lond Engl Rev)
+CDR (Lond Engl Wkly)
+CDS Rev
+CDT Dig
+CE Focus
+CEDPA Netw
+CEDPA World Wide
+CEPAL Rev
+CES Odontol
+CEX Rep Civ Eff Exerc
+CHA Insight
+CHAC Rev
+CIBA Symp
+CICIAMS Nouv
+CIN Plus
+CIRDAP Dev Dig
+CIRES Cah Ivoir Rech Econ Soc
+CLAO J
+CMAJ
+CNA Bull
+CNS Drug Rev
+CNS Drugs
+CNS Neurol Disord Drug Targets
+CNS Spectr
+COBASHECA
+COMBROAD
+COO Rep
+CPC Res Rev
+CPJ
+CRC Crit Rev Biochem
+CRC Crit Rev Bioeng
+CRC Crit Rev Clin Lab Sci
+CRC Crit Rev Clin Neurobiol
+CRC Crit Rev Clin Radiol Nucl Med
+CRC Crit Rev Diagn Imaging
+CRC Crit Rev Food Sci Nutr
+CRC Crit Rev Immunol
+CRC Crit Rev Microbiol
+CRC Crit Rev Plant Sci
+CRC Crit Rev Radiol Sci
+CRC Crit Rev Toxicol
+CRHCS news
+CRNA
+CVI Forum
+CVI Newswatch
+CVP
+Cabo
+Cad Casa Oswaldo Cruz
+Cad Hist Saude
+Cad Pesqui
+Cad Saude Publica
+Cad Ter Labor
+Caduceus
+Caementum
+Cah Acad Bretagne
+Cah Afr Adm Publique
+Cah Alex
+Cah All Isr Univers
+Cah Alsac Archeol Art Hist
+Cah Am Lat
+Cah Am Lat Ser Sci Homme
+Cah Anal Donnees
+Cah Anesthesiol
+Cah Ann Normandie
+Cah Anthropol
+Cah Archeol Hist Berry
+Cah Brux
+Cah Cent Rech Etud Paris Lle de France
+Cah Civilis Alp
+Cah Clio
+Cah Coll Med Hop Paris
+Cah Confront
+Cah Dix
+Cah Econ Brux
+Cah Econ Soc
+Cah Etud Afr
+Cah Etud Anc
+Cah Etud Mediev
+Cah Fontenay
+Cah Geogr Que
+Cah Geogr Rouen
+Cah Geol
+Cah Haut Marnais
+Cah Hist
+Cah Hist Mond
+Cah Hist Second Guerr Mond
+Cah Int Hist Econ Soc
+Cah Iroise
+Cah Laennec
+Cah Leopold Delisle
+Cah Lorrains
+Cah Med
+Cah Med Inter Prof
+Cah Med Lyon
+Cah Med Vet
+Cah Medicaux Union Fr
+Cah Mediterr Orient Monde Turco Iran
+Cah Monde Russe Sov
+Cah Nurs
+Cah O M
+Cah Odontostomatol Touraine
+Cah Orient
+Cah Orstom (Sci Hum)
+Cah Prothese
+Cah Psychiatr
+Cah Que Demogr
+Cah Ration
+Cah Rech Archit
+Cah Rech Sociol
+Cah Relig Afr
+Cah Rmf Bull Med Guide Pract Rev
+Cah Sci Hum
+Cah Semin Philos
+Cah Sexol Clin
+Cah Sociol Demogr Med
+Cah Tech AFRO
+Cah Tunis
+Cah Victor Edouardiens
+Cah Vilfredo Pareto
+Cairo
+Cairo Today
+Calcif Tissue Int
+Calcif Tissue Res
+Calcutta Med J
+Calif Anthropol
+Calif Clin
+Calif Code Regul
+Calif Counts
+Calif Health
+Calif Hist Q
+Calif Hist Soc Q
+Calif Hosp
+Calif J
+Calif Law Rev
+Calif Mag
+Calif Manage Rev
+Calif Med
+Calif Nurse
+Calif Sociol
+Calif State Bar J
+Calif West Int Law J
+Calif West Law Rev
+Californians
+Calyx
+Camb Anthropol
+Camb Law J
+Camb Munic Code 1988 Camb Mass
+Camb Q Healthc Ethics
+Cambodge Soir
+Cambridge J Econ
+Campbell Law Rev
+Can AIDS News
+Can Aeronaut Space J
+Can Am Slav Stud
+Can Anaesth Soc J
+Can Assoc Radiol J
+Can Bar J
+Can Bar Rev
+Can Bull Cardiovasc Nurs
+Can Bull Med Hist
+Can Commun Dis Rep
+Can Crit Care Nurs J
+Can Dent Hyg
+Can Dimens
+Can Dis Wkly Rep
+Can Doct
+Can Entom
+Can Ethn Stud
+Can Fam Law Q
+Can Fam Physician
+Can Forces Dent Serv Bull
+Can Forces Dent Serv Q
+Can Forum
+Can Geogr
+Can HIV AIDS Policy Law Newsl
+Can HIV AIDS Policy Law Rev
+Can Hist Rev
+Can Hosp
+Can Hum Rights Advocate
+Can Hum Rights Yearb
+Can Int Educ
+Can J Afr Stud
+Can J Aging
+Can J Anaesth
+Can J Appl Physiol
+Can J Appl Sport Sci
+Can J Behav Sci
+Can J Biochem
+Can J Biochem Cell Biol
+Can J Biochem Physiol
+Can J Bot
+Can J Cardiol
+Can J Cardiovasc Nurs
+Can J Chem
+Can J Clin Pharmacol
+Can J Commun Ment Health
+Can J Community Dent
+Can J Comp Med
+Can J Comp Med Vet Sci
+Can J Diet Pract Res
+Can J Earth Sci
+Can J Econ
+Can J Exp Psychol
+Can J Fam Law
+Can J For Res
+Can J Gastroenterol
+Can J Genet Cytol
+Can J Hist
+Can J Hist Sport
+Can J Hist Sport Phys Educ
+Can J Hosp Pharm
+Can J Hum Sex
+Can J Infect Control
+Can J Ital Stud
+Can J Lat Am Caribb Stud
+Can J Law Soc
+Can J Med Radiat Technol
+Can J Med Sci
+Can J Med Technol
+Can J Microbiol
+Can J Native Stud
+Can J Neurol Sci
+Can J Nurs Adm
+Can J Nurs Leadersh
+Can J Nurs Res
+Can J Ob Gyn Womens Health Care
+Can J Occup Ther
+Can J Oncol
+Can J Ophthalmol
+Can J Otolaryngol
+Can J Otolaryngol Suppl
+Can J Philos
+Can J Phys
+Can J Physiol Pharmacol
+Can J Polit Sci
+Can J Psychiatr Nurs
+Can J Psychiatry
+Can J Psychol
+Can J Public Health
+Can J Radiogr Radiother Nucl Med
+Can J Reg Sci
+Can J Res
+Can J Rural Med
+Can J Sociol
+Can J Sport Sci
+Can J Stat
+Can J Surg
+Can J Urban Res
+Can J Urol
+Can J Vet Res
+Can J Women Law
+Can J Womens Health Care Phys Addressing Womens Health Issues
+Can J Zool
+Can J. Criminol
+Can Labour
+Can Med Assoc J
+Can Ment Health
+Can Mineral
+Can Nurse
+Can Oncol Nurs J
+Can Oper Room Nurs J
+Can Pharm J
+Can Psychiatr Assoc J
+Can Psychol
+Can Psychol Rev
+Can Public Adm
+Can Public Policy
+Can Respir J
+Can Rev Am Stud
+Can Rev Sociol Anthropol
+Can Serv Med J
+Can Slavon Pap
+Can Soc Trends
+Can Soc Work Rev
+Can Stat Rev
+Can Stud Popul
+Can Supreme Court Rep Can Supreme Court
+Can Vet J
+Can Welf
+Can Womens Stud
+Can Yearb Int Law
+Canadian
+Cancer
+Cancer Biochem Biophys
+Cancer Biol Ther
+Cancer Biother
+Cancer Biother Radiopharm
+Cancer Bull
+Cancer Causes Control
+Cancer Cell
+Cancer Cell Int
+Cancer Cells
+Cancer Chemother Biol Response Modif
+Cancer Chemother Pharmacol
+Cancer Chemother Rep
+Cancer Chemother Rep 2
+Cancer Chemother Rep 3
+Cancer Clin Trials
+Cancer Commun
+Cancer Control
+Cancer Cytol
+Cancer Detect Prev
+Cancer Detect Prev Suppl
+Cancer Drug Deliv
+Cancer Epidemiol Biomarkers Prev
+Cancer Gene Ther
+Cancer Genet Cytogenet
+Cancer Imaging
+Cancer Immun
+Cancer Immunol Immunother
+Cancer Invest
+Cancer J
+Cancer J Sci Am
+Cancer Lett
+Cancer Metastasis Rev
+Cancer Nurs
+Cancer Pract
+Cancer Prev Control
+Cancer Prog
+Cancer Radiother
+Cancer Res
+Cancer Sci
+Cancer Surv
+Cancer Ther
+Cancer Treat Rep
+Cancer Treat Res
+Cancer Treat Rev
+Cancro
+Canine Pract
+Cantium
+Cap Chem
+Cap Nurs
+Cap Univ Law Rev
+Cape Breton Post
+Capitation Manag Rep
+Capitation Rates Data
+Carbohydr Lett
+Carbohydr Res
+Carcinog Compr Surv
+Carcinogenesis
+Card Electrophysiol Rev
+Cardiol Clin
+Cardiol Manage
+Cardiol Prat
+Cardiol Rev
+Cardiol Young
+Cardiologia
+Cardiology
+Cardioscience
+Cardiovasc Clin
+Cardiovasc Diabetol
+Cardiovasc Dis
+Cardiovasc Drug Rev
+Cardiovasc Drugs Ther
+Cardiovasc Hematol Agents Med Chem
+Cardiovasc Intervent Radiol
+Cardiovasc J S Afr
+Cardiovasc Nurs
+Cardiovasc Pathol
+Cardiovasc Radiat Med
+Cardiovasc Radiol
+Cardiovasc Res
+Cardiovasc Res Cent Bull
+Cardiovasc Rev Rep
+Cardiovasc Revasc Med
+Cardiovasc Surg
+Cardiovasc Toxicol
+Cardiovasc Ultrasound
+Cardozo Law Rev
+Care Giver
+Care Manag J
+Career Dev Q
+Caribb Aff
+Caribb Health
+Caribb Med J
+Caribb Rev
+Caribb Stud
+Caribe Contemp
+Caridad Cienc Arte
+Caries Res
+Caring
+Carinthia I
+Caritas
+Carlsberg Res Commun
+Carmarthen Antiq
+Carmarthensh Hist
+Carnegie Q
+Carnets Enfance
+Carney Hosp J
+Carol J Pharm
+Carol Plann
+Carrefour Afr
+Carta Econ Reg
+Carta Inf
+Cartogr J
+Cas Cesk Lek
+Cas Cesk Vet
+Cas Lek Cesk
+Cas Matice Moravske
+Cas Nar Muz
+Cas Zgodovino Narodop
+Casa Mujer
+Casana
+Case Comment
+Case Manager
+Case Stud Health Adm
+Case West Reserve J Int Law
+Case West Reserve Law Rev
+Catena Suppl
+Cater Health
+Cathet Cardiovasc Diagn
+Catheter Cardiovasc Interv
+Cathol Hist Rev
+Cathol Hosp
+Cathol Lawyer
+Cathol Med Q
+Cathol Mind
+Cathol Nurse
+Cathol Nurse (Wallsend)
+Cathol Stand
+Cathol Univers Law Rev
+Cathol Update
+Cathol Woman
+Cathol World
+Cato J
+Cell
+Cell Adhes Commun
+Cell Biochem Biophys
+Cell Biochem Funct
+Cell Biol Educ
+Cell Biol Int
+Cell Biol Int Rep
+Cell Biol Rev
+Cell Biol Toxicol
+Cell Biophys
+Cell Calcium
+Cell Chromosome
+Cell Commun Adhes
+Cell Commun Signal
+Cell Cycle
+Cell Death Differ
+Cell Differ
+Cell Differ Dev
+Cell Growth Differ
+Cell Immunol
+Cell Metab
+Cell Microbiol
+Cell Mol Biol
+Cell Mol Biol (Noisy-le-grand)
+Cell Mol Biol Incl Cyto Enzymol
+Cell Mol Biol Lett
+Cell Mol Biol Res
+Cell Mol Immunol
+Cell Mol Life Sci
+Cell Mol Neurobiol
+Cell Motil
+Cell Motil Cytoskeleton
+Cell Muscle Motil
+Cell Oncol
+Cell Physiol Biochem
+Cell Prolif
+Cell Regul
+Cell Res
+Cell Signal
+Cell Stress Chaperones
+Cell Struct Funct
+Cell Tissue Bank
+Cell Tissue Kinet
+Cell Tissue Res
+Cell Transplant
+Cell Vis
+Cells Tissues Organs
+Cellule
+Cent Afr J Med
+Cent Asia Monit
+Cent Call
+Cent Est Med
+Cent Estud Recur Odontol Nino
+Cent Eur Hist
+Cent Eur J Public Health
+Cent Home Care Policy Res Policy Briefs
+Cent Issues Anthropol
+Cent Mag
+Cent Med
+Cent Nerv Syst Trauma
+Cent States Speech J
+Cent View
+Centaurus
+Centen Rev
+Centerpoint
+Centerviews
+Centr Asian Surv
+Cephalalgia
+Cereb Cortex
+Cereb Palsy Bull
+Cereb Palsy J
+Cereb Palsy Rev
+Cerebellum
+Cerebrospinal Fluid Res
+Cerebrovasc Brain Metab Rev
+Cerebrovasc Dis
+Cerebrum
+Ceres
+Cerrahpasa Tip Fak Derg
+Certif Dent Tech
+Cervello G Nevrol
+Cervix Low Female Genital Tract
+Cesk Cas Hist
+Cesk Dermatol
+Cesk Epidemiol Mikrobiol Imunol
+Cesk Farm
+Cesk Fysiol
+Cesk Gastroenterol Vyz
+Cesk Gynekol
+Cesk Hyg
+Cesk Morfol
+Cesk Neurol
+Cesk Neurol Neurochir
+Cesk Oftalmol
+Cesk Onkol
+Cesk Otolaryngol
+Cesk Patol
+Cesk Pediatr
+Cesk Psychiatr
+Cesk Psychol
+Cesk Radiol
+Cesk Rentgenol
+Cesk Slov Oftalmol
+Cesk Stomatol
+Cesk Zdrav
+Ceska Gynekol
+Ceska Slov Farm
+Ceska Slov Psychiatr
+Ceskoslov Biol
+Ceskoslov Nemocnice
+Cesra Saule
+Ceylon Dent J
+Ceylon Med J
+Chain Drug Rev
+Chang Gung Med J
+Chang Men
+Change
+Changes
+Changgeng Yi Xue Za Zhi
+Changing Times
+Channels
+Chaos
+Chaos Solitons Fractals
+Chart
+Chatelaine
+Chaucer Rev
+Chekh Fiziol
+Chekhoslovatskaia Biol
+Chem Analityczna
+Chem Ber
+Chem Biol
+Chem Biol Drug Des
+Chem Biol Interact
+Chem Br
+Chem Can
+Chem Commun (Camb)
+Chem Depend
+Chem Drug
+Chem Eng News
+Chem Erde
+Chem Geol
+Chem Herit
+Chem Immunol
+Chem Immunol Allergy
+Chem Ind
+Chem Lett (Jpn)
+Chem Mater
+Chem Pharm Bull (Tokyo)
+Chem Phys
+Chem Phys Lett
+Chem Phys Lipids
+Chem Rec
+Chem Res Toxicol
+Chem Rev
+Chem Scr
+Chem Senses
+Chem Soc Rev
+Chem Week
+Chem Weekbl
+Chem Zvesti
+Chembiochem
+Chemioterapia
+Chemistry
+Chemistry (Easton)
+Chemosphere
+Chemotherapia (Basel)
+Chemotherapy
+Chemphyschem
+Chemtech
+Chesopiean
+Chest
+Chest Surg Clin N Am
+Chesterton Rev
+Chic Hist
+Chic J Int Law
+Chic Kent Law Rev
+Chic Med
+Chic Med Rec
+Chic Med Sch Q
+Chic Pol Rev
+Chic Stud
+Chic Today
+Chicago Bar Rec
+Chicano Law Rev
+Chief Inf Off J
+Chieh Fang Chun I Hsueh Tsa Chih
+Chikwa Kijae Hakhoe Chi
+Child Abuse Negl
+Child Adolesc Psychiatr Clin N Am
+Child Adolesc Social Work J
+Child Care Health Dev
+Child Care Q
+Child Dev
+Child Fam
+Child Health Alert
+Child Health Care
+Child Health Dialogue
+Child Leg Rights J
+Child Maltreat
+Child Nephrol Urol
+Child Neuropsychol
+Child Psychiatry Hum Dev
+Child Psychiatry Q
+Child Surviv Action News
+Child Today
+Child Trop
+Child Welfare
+Child Worldw
+Child Youth Serv Rev
+Children
+Childs Brain
+Childs Nerv Syst
+Chim Ther
+Chimia (Aarau)
+Chin Cult
+Chin Econ Stud
+Chin Environ Dev
+Chin Geogr Sci
+Chin J Biotechnol
+Chin J Dent Res
+Chin J Dig Dis
+Chin J Integr Med
+Chin J Physiol
+Chin J Popul Sci
+Chin J Traumatol
+Chin Med J
+Chin Med J (Engl)
+Chin Med Sci J
+Chin Sci
+Chin Sociol Anthropol
+China Aktuell
+China Bus Rev
+China J
+China Law Pract
+China Newsl
+China Popul Headl
+China Popul Newsl
+China Popul Res Leads
+China Popul Res Newsl
+China Popul Today
+China Q
+China Reconstr
+China Rep
+Chinas Med
+Chir Dent Fr
+Chir Forum Exp Klin Forsch
+Chir Ital
+Chir Main
+Chir Maxillofac Plast
+Chir Narzadow Ruchu Ortop Pol
+Chir Organi Mov
+Chir Patol Sper
+Chir Pediatr
+Chir Urol
+Chirality
+Chirigaku Hyoron
+Chiropr Hist
+Chiropr Osteopat
+Chirurg
+Chirurgia (Bucur)
+Chirurgie
+Chiryo
+Chittys Law J
+Chnia Geogr
+Choice (Middletown)
+Choices
+Choices Respir Manage
+Chonnam Med J
+Chosen Ibo
+Choson Uihak
+Chot Mai Het Kan Phayaban
+Christ Bioeth
+Christ Century
+Christ Crisis
+Christ Med Dent Soc J
+Christ Med Soc J
+Christ Nurse (Mysore)
+Christ Nurse Int
+Christ Sch Rev
+Christ Sci Monitor
+Christ Sci Monitor (East Ed)
+Christ Today
+Christ World
+Christiana Albertina Kiel Univ Z
+Chromatogr Rev
+Chromosoma
+Chromosome Res
+Chron Actual SEDEIS
+Chron CEPED
+Chron Egypte
+Chron High Educ
+Chron Horticult
+Chron Okla
+Chron R Coll Physicians Edinb
+Chron Respir Dis
+Chronic Dis Can
+Chronicle
+Chronicle (Phila)
+Chronobiol Int
+Chronobiologia
+Chudoku Kenkyu
+Chung Ang Ui Dai Chi
+Chung Hua Chien Yen I Hsueh Tsa Chih
+Chung Hua Chuan Jan Ping Tsa Chih
+Chung Hua Wei Sheng Wu Hsueh Ho Mieh I Hsueh Tsa Chih
+Chung Kuo Yao Hsueh Tsa Chih
+Chungang Uihak
+Church Hist
+Church Soc
+Ciba Found Study Group
+Ciba Found Symp
+Ciba Symp
+Ciba Z (Basel)
+Ciel Terre
+Cienc Cult
+Cienc Desarro
+Cienc Invest
+Cienc Soc
+Cinci Hist Soc Bull
+Cinci J Med
+Cincinnati Dent Soc Bull
+Cir Bucal
+Cir Cir
+Cir Esp
+Cir Ginecol Urol
+Cir Pediatr
+Circ Farm
+Circ J
+Circ Odontol San Martin Tres Febr
+Circ Res
+Circ Shock
+Circ Shock Suppl
+Circulation
+Cities
+City J
+Cityscape
+Civ Cattol
+Civ Code Annot State Calif Calif
+Civ Liberties Rev
+Civ War Hist
+Civ War Times Illus
+Civil Lib
+Civilisations
+Cladistics
+Class Antiq
+Class J
+Class Outlook
+Class Philol
+Class Q
+Class World
+Classical Rev
+Clay Miner
+Clays Clay Miner
+Clearing House
+Cleft Palate Craniofac J
+Cleft Palate J
+Clergy Rev
+Cleve Clin J Med
+Cleve Clin Q
+Clevel State Law Rev
+Clgh Rev
+Clim Change
+Climacteric
+Clin Adv Hematol Oncol
+Clin Allergy
+Clin Allergy Immunol
+Clin Anat
+Clin Anesth
+Clin Appl Thromb Hemost
+Clin Auton Res
+Clin Biochem
+Clin Biochem Rev
+Clin Biomech (Bristol, Avon)
+Clin Breast Cancer
+Clin Bull
+Clin Calcium
+Clin Cancer Res
+Clin Cardiol
+Clin Chem
+Clin Chem Lab Med
+Clin Chest Med
+Clin Child Fam Psychol Rev
+Clin Chim Acta
+Clin Colorectal Cancer
+Clin Commun Disord
+Clin Cornerstone
+Clin Dermatol
+Clin Dev Immunol
+Clin Diagn Lab Immunol
+Clin Diagn Ultrasound
+Clin Diagn Virol
+Clin Dysmorphol
+Clin EEG Neurosci
+Clin Electroencephalogr
+Clin Endocrinol (Oxf)
+Clin Endocrinol Metab
+Clin Eng
+Clin Eng Inf Serv
+Clin Eng News
+Clin Ethics Rep
+Clin Eur
+Clin Evid
+Clin Excell Nurse Pract
+Clin Excerpts J Devoted Ther
+Clin Exp Allergy
+Clin Exp Dermatol
+Clin Exp Dial Apheresis
+Clin Exp Hypertens
+Clin Exp Hypertens A
+Clin Exp Hypertens B
+Clin Exp Immunol
+Clin Exp Med
+Clin Exp Metastasis
+Clin Exp Nephrol
+Clin Exp Neurol
+Clin Exp Obstet Gynecol
+Clin Exp Optom
+Clin Exp Pathol
+Clin Exp Pharmacol Physiol
+Clin Exp Pharmacol Physiol Suppl
+Clin Exp Rheumatol
+Clin Experiment Ophthalmol
+Clin Gastroenterol
+Clin Gastroenterol Hepatol
+Clin Genet
+Clin Genitourin Cancer
+Clin Geriatr Med
+Clin Gerontol
+Clin Ginecol
+Clin Haematol
+Clin Hemorheol
+Clin Hemorheol Microcirc
+Clin Imaging
+Clin Immunol
+Clin Immunol Immunopathol
+Clin Immunol Rev
+Clin Implant Dent Relat Res
+Clin Infect Dis
+Clin Intensive Care
+Clin Invest Ginecol Obstet
+Clin Invest Med
+Clin Investig
+Clin J Oncol Nurs
+Clin J Pain
+Clin J Sport Med
+Clin Lab
+Clin Lab (Zaragoza)
+Clin Lab Haematol
+Clin Lab Manage Rev
+Clin Lab Med
+Clin Lab Sci
+Clin Laser Mon
+Clin Lat
+Clin Leadersh Manag Rev
+Clin Linguist Phon
+Clin Liver Dis
+Clin Lung Cancer
+Clin Lymphoma
+Clin Lymphoma Myeloma
+Clin Mater
+Clin Med
+Clin Med (Northfield Il)
+Clin Med Res
+Clin Microbiol Infect
+Clin Microbiol Newsl
+Clin Microbiol Rev
+Clin Mol Allergy
+Clin Mol Pathol
+Clin Nephrol
+Clin Neurol Neurosurg
+Clin Neuropathol
+Clin Neuropharmacol
+Clin Neurophysiol
+Clin Neuropsychol
+Clin Neurosci
+Clin Neurosurg
+Clin Notes Respir Dis
+Clin Nucl Med
+Clin Nuova Rass Prog Med Internazionale
+Clin Nurs Pract Epilepsy
+Clin Nurs Res
+Clin Nurse Spec
+Clin Nutr
+Clin Obstet Gynaecol
+Clin Obstet Gynecol
+Clin Occup Environ Med
+Clin Odontol
+Clin Oncol
+Clin Oncol (R Coll Radiol)
+Clin Oral Implants Res
+Clin Oral Investig
+Clin Orthod Res
+Clin Orthop
+Clin Orthop Relat Res
+Clin Ortop
+Clin Ostet Ginecol
+Clin Otolaryngol
+Clin Otolaryngol Allied Sci
+Clin Otorinolaringoiatr
+Clin Pediatr (Bologna)
+Clin Pediatr (Phila)
+Clin Perform Qual Health Care
+Clin Perinatol
+Clin Pharm
+Clin Pharmacokinet
+Clin Pharmacol Ther
+Clin Phys Physiol Meas
+Clin Physiol
+Clin Physiol Biochem
+Clin Physiol Funct Imaging
+Clin Plast Surg
+Clin Podiatr Med Surg
+Clin Podiatr Med Surg North Am
+Clin Podiatry
+Clin Positron Imaging
+Clin Pract Epidemol Ment Health
+Clin Pract Guidel Quick Ref Guide Clin
+Clin Prev Dent
+Clin Privil White Pap
+Clin Proc Child Hosp Dist Columbia
+Clin Proc Child Hosp Natl Med Cent
+Clin Prostate Cancer
+Clin Psychol
+Clin Psychol Rev
+Clin Radiol
+Clin Rehabil
+Clin Rep
+Clin Reprod Fertil
+Clin Res
+Clin Res Cardiol
+Clin Res Pr Drug Regul Aff
+Clin Resour Manag
+Clin Rev Allergy
+Clin Rev Allergy Immunol
+Clin Rheum Dis
+Clin Rheumatol
+Clin Sci
+Clin Sci (Lond)
+Clin Sci Mol Med
+Clin Sci Mol Med Suppl
+Clin Soc Work J
+Clin Sports Med
+Clin Symp
+Clin Tech Small Anim Pract
+Clin Ter
+Clin Term
+Clin Ther
+Clin Torax
+Clin Toxicol
+Clin Toxicol (Phila)
+Clin Transl Oncol
+Clin Transpl
+Clin Transplant
+Clin Trials
+Clin Trials J
+Clin Trials Metaanal
+Clin Vaccine Immunol
+Clin Vet (Milano)
+Clinic (Paris)
+Clinica
+Clinician (Goa)
+Clinics
+Clinique (Paris)
+Clio
+Clio Med
+Cloning
+Cloning Stem Cells
+Cmd
+Coast Manage
+Cochrane Database Syst Rev
+Code Ala 1975 Ala
+Code Fed Regul Public Welfare
+Code Fed Regul Shipping
+Code Ga Annot Ga
+Code Laws S C 1976 Annot S C
+Code Va 1950 Va
+Coeur
+Coeur Med Interne
+Coevol Q
+Coexistence
+Cogn Affect Behav Neurosci
+Cogn Behav Neurol
+Cogn Behav Pract
+Cogn Behav Ther
+Cogn Neuropsychol
+Cogn Process
+Cognit Neuropsychiatry
+Cognit Psychol
+Cognit Ther Res
+Cognition
+Coimbra Med
+Coin World
+Colby Libr Q
+Cold Spring Harb Symp Quant Biol
+Colecc Estud CIEPLAN
+Colecc Hist Cienc Salud
+Coll Antropol
+Coll Relat Res
+Coll Rev
+Coll Stud J
+Coll Works Cardiopulm Dis
+Collana Monogr Gazz Med Sicil
+Collect N S Hist Soc
+Collections
+Collegian
+Colloids Surf B Biointerfaces
+Colloq Int CNRS
+Colloq Natx Cent Natl Rech Sci
+Colo Herit
+Colo J Int Environ Law Policy
+Colo Lawyer
+Colo Mag
+Colo Med
+Colo Nurse
+Colo Q
+Colo Revis Statut 1973 Colo
+Colomb Med
+Color Res Appl
+Colorectal Dis
+Columbia Forum
+Columbia Human Rights Law Rev
+Columbia J Gend Law
+Columbia J Law Soc Probl
+Columbia J Rev
+Columbia J World Bus
+Columbia Law Rev
+Columbia Sci Technol Law Rev
+Columbus Dent Soc Bull
+Comb Chem High Throughput Screen
+Commentary
+Commer Law Eur
+Commer Law J
+Commitment
+Common Factor
+Common Law World Rev
+Common Place
+Commonweal
+Commun ACM
+Commun Agric Appl Biol Sci
+Commun Behav Biol
+Commun Dis Intell
+Commun Dis Public Health
+Commun Dis Rep CDR Rev
+Commun Dis Rep CDR Suppl
+Commun Dis Rep CDR Wkly
+Commun Hist Artis Med Suppl
+Commun Monogr
+Commun Newsl
+Commun Nurs Res
+Commun Psychopharmacol
+Commun Soil Sci Plant Anal
+Commun Theory
+Communic Res
+Communio
+Communique
+Communique (Wash DC)
+Communist Econ
+Communist Econ Econ Transform
+Communist Post-Communist Stud
+Community Based Public Health Policy Pract
+Community Dent Health
+Community Dent Oral Epidemiol
+Community Dev J
+Community Eye Health
+Community Genet
+Community Health (Bristol)
+Community Health Stud
+Community Jr Coll J
+Community Med
+Community Ment Health J
+Community Ment Health Rev
+Community Nurs
+Community Nurse
+Community Outlook
+Community Pract
+Comp Biochem Physiol
+Comp Biochem Physiol A
+Comp Biochem Physiol A Mol Integr Physiol
+Comp Biochem Physiol A Physiol
+Comp Biochem Physiol B
+Comp Biochem Physiol B Biochem Mol Biol
+Comp Biochem Physiol Biochem Mol Biol
+Comp Biochem Physiol C
+Comp Biochem Physiol C Pharmacol Toxicol Endocrinol
+Comp Biochem Physiol C Toxicol Pharmacol
+Comp Biochem Physiol Comp Physiol
+Comp Biochem Physiol Pharmacol Toxicol Endocrinol
+Comp Biochem Physiol Physiol
+Comp Econ Stud
+Comp Educ Rev
+Comp Gen Pharmacol
+Comp Hepatol
+Comp Immunol Microbiol Infect Dis
+Comp Int Law J South Afr
+Comp Labor Law J
+Comp Med
+Comp Med East West
+Comp Polit
+Comp Polit Stud
+Comp Soc Res
+Comp Stud Soc Hist
+Comp Urban Res
+Compare
+Compend Contin Educ Dent
+Compend Contin Educ Dent Suppl
+Compend Contin Educ Gen Dent
+Compend Suppl
+Compendium
+Compens Benefits Rev
+Compens Rev
+Complement
+Complement Inflamm
+Complement Ther Clin Pract
+Complement Ther Med
+Complement Ther Nurs Midwifery
+Complexity
+Complicat Card Patient
+Compost Sci Util
+Compr Gerontol [A]
+Compr Gerontol [B]
+Compr Gerontol [C]
+Compr Immunol
+Compr Ophthalmol Update
+Compr Psychiatry
+Compr Ther
+Comput Aided Surg
+Comput Appl Biosci
+Comput Biol Chem
+Comput Biol Med
+Comput Biomed Res
+Comput Cardiol
+Comput Chem
+Comput Environ Urban Syst
+Comput Geosci
+Comput Graph
+Comput Graph (ACM)
+Comput Healthc
+Comput Hosp
+Comput Hum
+Comput Human Behav
+Comput Inform Nurs
+Comput Law J
+Comput Math Appl
+Comput Med Imaging Graph
+Comput Methods Biomech Biomed Engin
+Comput Methods Programs Biomed
+Comput Nurs
+Comput Phys Commun
+Comput Programs Biomed
+Comput Radiol
+Comput Support Coop Work
+Comput Tomogr
+Comput Vis Image Underst
+Computertomographie
+Computerworld
+Comunita
+Concepts Immunopathol
+Concern
+Concern (Anaheim)
+Concern Care Aging
+Concern Dying
+Concerned Demogr
+Concienc Latinoam
+Concordia Hist Inst Q
+Concordia J
+Concours Med
+Cond Reflex
+Conditio Jud
+Condorcet Stud
+Conector
+Conf Board Rec
+Conf Estud Hist Organ Cienc
+Conf Hist Med
+Conf Lyon Ophtalmol
+Confed Aust Crit Care Nurses J
+Configurations
+Confin Neurol
+Confin Psychiatr
+Congenit Anom (Kyoto)
+Congest Heart Fail
+Congr Int Stomatol
+Congr Q Wkly Rep
+Congr Rec (Dly Ed)
+Congr. Mon
+Conn Dent Stud J
+Conn Gen Statut Annot Conn
+Conn Health Bull
+Conn Hist Soc Bull
+Conn J Int Law
+Conn Law Rev
+Conn Med
+Conn Nurs News
+Conn Probate Law J
+Connaiss Arts
+Connect Tissue Res
+Conscience
+Conscious Cogn
+Consens Dev Conf Summ Natl Inst Health
+Consens Statement
+Conserv Biol
+Conserv Jud
+Consommation
+Const Comment
+Const Law J
+Constellations
+Constitution
+Consult Pharm
+Consult Specif Eng
+Consultant
+Consum Health Perspect
+Consum Mark Abroad
+Consum Rep
+Consum Rep Health
+Cont Lens Anterior Eye
+Contact
+Contact Dermatitis
+Contact Intraocul Lens Med J
+Contact Point
+Contactologia
+Contam Control
+Contam Control Biomed Environ
+Contemp Adm
+Contemp Adm Long Term Care
+Contemp Anesth Pract
+Contemp Clin Trials
+Contemp Crisis
+Contemp Drug Probl
+Contemp Econ Policy
+Contemp Educ Psychol
+Contemp Eur Hist
+Contemp Fr Civiliz
+Contemp Intern Med
+Contemp Issues Clin Biochem
+Contemp Jew
+Contemp Longterm Care
+Contemp Marx
+Contemp Neurol Ser
+Contemp Nurse
+Contemp Ob Gyn
+Contemp Oncol
+Contemp Orthop
+Contemp Pac
+Contemp Pediatr
+Contemp Pharm Pract
+Contemp Philos
+Contemp Policy Issues
+Contemp Psychoanal
+Contemp Rev
+Contemp Rev Obstet Gynaecol
+Contemp Sociol
+Contemp Southeast Asia
+Contemp Surg
+Contemp Ther
+Contemp Top Immunobiol
+Contemp Top Lab Anim Sci
+Contemp Top Mol Immunol
+Contemp Urol
+Contin Care
+Contin Chang
+Continuity
+Continuum
+Continuum (N Y)
+Continuum Soc Soc Work Leadersh Health Care
+Contracept Deliv Syst
+Contracept Fertil Sex
+Contracept Fertil Sex (Paris)
+Contracept Rep
+Contracept Technol
+Contracept Technol Update
+Contraception
+Contract
+Contract Des
+Contract Healthc
+Contract Inter
+Contradictions
+Contrastes
+Contrat Soc
+Contree
+Contrepoint
+Contrib Asian Stud
+Contrib Biol Geol
+Contrib Embryol
+Contrib Gynecol Obstet
+Contrib Hist Econ Soc
+Contrib Indian Sociol
+Contrib Med Stud
+Contrib Microbiol
+Contrib Microbiol Immunol
+Contrib Mineral Petrol
+Contrib Nepalese Stud
+Contrib Nephrol
+Contrib Polit Econ
+Contrib Primatol
+Contrib Psychol
+Contrib Sens Physiol
+Contributi
+Control Clin Trials
+Control Eng Pract
+Convuls Ther
+Cooley Law Rev
+Coop Confl
+Coop Dent (B Aires)
+Coord Noteb
+Copeia
+Cor Vasa
+Core J Pediatr
+Cornea
+Cornell Hotel Restaur Adm Q
+Cornell Int Law J
+Cornell J Law Public Policy
+Cornell J Soc Relat
+Cornell Law Rev
+Cornell Mag
+Cornell Vet
+Coron Artery Dis
+Corp Comment
+Corps Med (Ettelbruck)
+Corr Farm
+Correct Soc Psych J Behav Tech Methods Ther
+Correctcare
+Correo Poblac Salud
+Cortex
+Cosmetologica
+Cost Containment
+Cost Eff Resour Alloc
+Cost Qual
+Cost Qual Q J
+Cough
+Couns Psychol
+Couns Psychol Q
+Couns Values
+Countdown Istanb
+Courier
+Courr Pays Est
+Courr Unesco
+Courrier
+Courts Health Sci Law
+Covertaction Q
+Coyunt Econ
+Cpr Popul Res
+Cr Trav Lab Carlsberg Ser Physiol
+Cranio
+Cranio Clin Int
+Creat Nurs
+Creces
+Creighton Law Rev
+Crianca Port
+Crim Behav Ment Health
+Crim Justice Behav
+Crim Justice Ethics
+Crim Justice Hist
+Crim Justice J
+Crim Law Bull
+Crim Law Forum
+Crim Law J
+Crim Law Rev
+Crim Rep Can New Ser
+Crime Delinq
+Crime Law Soc Change
+Crime Soc Justice
+Crisis
+Crit Anthropol
+Crit Care
+Crit Care Clin
+Crit Care Med
+Crit Care Nurs Clin North Am
+Crit Care Nurs Q
+Crit Care Nurse
+Crit Care Resusc
+Crit Care Update
+Crit Inq
+Crit Path AIDS Proj
+Crit Rev Biochem Mol Biol
+Crit Rev Bioeng
+Crit Rev Biomed Eng
+Crit Rev Biotechnol
+Crit Rev Clin Lab Sci
+Crit Rev Comput Tomogr
+Crit Rev Diagn Imaging
+Crit Rev Eukaryot Gene Expr
+Crit Rev Food Sci Nutr
+Crit Rev Immunol
+Crit Rev Med Inform
+Crit Rev Microbiol
+Crit Rev Neurobiol
+Crit Rev Oncog
+Crit Rev Oncol Hematol
+Crit Rev Oral Biol Med
+Crit Rev Ther Drug Carrier Syst
+Crit Rev Toxicol
+Crit Soc Res
+Crit Sociol
+Crit Stor
+Crit Stud Mass Commun
+Criterion
+Critica
+Critique
+Croat Med J
+Cron IDI
+Cron Pompeiane
+Crop Sci
+Cross Curr
+Cross Curr (Ann Arbor MI)
+Crossref Hum Resour Manage
+Crossreference
+Crown Agents Rev
+Crucible
+Crux
+Cryo Letters
+Cryobiology
+Ctry Demogr Profiles
+Ctry Life
+Cuad Am
+Cuad Arquit Urban
+Cuad CENDES
+Cuad CLAEH
+Cuad Complut Hist Med Cienc
+Cuad Comun AMIDEP
+Cuad Econ
+Cuad Econ Soc
+Cuad Estud Gallegos
+Cuad Filol Cl
+Cuad Hisp Hist Med Cienc
+Cuad Hispanoam
+Cuad Hist Esp
+Cuad Hist Med Esp
+Cuad Hist Salud Publica
+Cuad Invest Hist
+Cuad Med Soc
+Cuad Nutr
+Cuad Valencia Hist Med Cienc
+Cuban Stud
+Cult Anthropol
+Cult Confl
+Cult Dev
+Cult Divers Ment Health
+Cult Health Sex
+Cult Med
+Cult Med Psychiatry
+Cult Sc
+Cult Surv Q
+Cult Tech
+Culta Bononia
+Cultur Divers Ethnic Minor Psychol
+Culture
+Cumberl Cty Hist
+Cumberland Law Rev
+Cumberland Samford Law Rev
+Cuore Circ
+Curare
+Curationis
+Curr Aff Bull
+Curr Alcohol
+Curr Allergy Asthma Rep
+Curr Allergy Rep
+Curr Alzheimer Res
+Curr Anaesth Crit Care
+Curr Anthropol
+Curr Atheroscler Rep
+Curr Bibliogr Afr Aff
+Curr Biol
+Curr Cancer Drug Targets
+Curr Cardiol Rep
+Curr Clin Top Infect Dis
+Curr Conc Cerebrovasc Dis Stroke
+Curr Concepts Hosp Pharm Manage
+Curr Concepts Nutr
+Curr Contents Clin Med
+Curr Control Trials Cardiovasc Med
+Curr Dev Psychopharmacol
+Curr Diab Rep
+Curr Dig Post Sov Press
+Curr Dig Sov Press
+Curr Dir Autoimmun
+Curr Drug Deliv
+Curr Drug Discov Technol
+Curr Drug Metab
+Curr Drug Targets
+Curr Drug Targets CNS Neurol Disord
+Curr Drug Targets Cardiovasc Haematol Disord
+Curr Drug Targets Immune Endocr Metabol Disord
+Curr Drug Targets Infect Disord
+Curr Drug Targets Inflamm Allergy
+Curr Eye Res
+Curr Gastroenterol Rep
+Curr Gene Ther
+Curr Genet
+Curr HIV Res
+Curr HIV/AIDS Rep
+Curr Heart Fail Rep
+Curr Hematol Rep
+Curr Hist
+Curr Hypertens Rep
+Curr Infect Dis Rep
+Curr Interv Cardiol Rep
+Curr Issues Intest Microbiol
+Curr Issues Mol Biol
+Curr Issues Public Health
+Curr Law Statut Annot GB
+Curr Leg Probl
+Curr Med Atty
+Curr Med Chem
+Curr Med Chem Anticancer Agents
+Curr Med Chem Cardiovasc Hematol Agents
+Curr Med Drugs
+Curr Med Pract
+Curr Med Res Opin
+Curr Microbiol
+Curr Mod Biol
+Curr Mol Med
+Curr Neurol Neurosci Rep
+Curr Neurovasc Res
+Curr Obstet Gynaecol
+Curr Oncol Rep
+Curr Opin Allergy Clin Immunol
+Curr Opin Anaesthesiol
+Curr Opin Biotechnol
+Curr Opin Cardiol
+Curr Opin Cell Biol
+Curr Opin Chem Biol
+Curr Opin Clin Nutr Metab Care
+Curr Opin Cosmet Dent
+Curr Opin Crit Care
+Curr Opin Dent
+Curr Opin Drug Discov Devel
+Curr Opin Gastroenterol
+Curr Opin Gen Surg
+Curr Opin Genet Dev
+Curr Opin Hematol
+Curr Opin Immunol
+Curr Opin Infect Dis
+Curr Opin Investig Drugs
+Curr Opin Lipidol
+Curr Opin Microbiol
+Curr Opin Mol Ther
+Curr Opin Nephrol Hypertens
+Curr Opin Neurobiol
+Curr Opin Neurol
+Curr Opin Neurol Neurosurg
+Curr Opin Obstet Gynecol
+Curr Opin Oncol
+Curr Opin Ophthalmol
+Curr Opin Orthop
+Curr Opin Otolaryngol Head Neck Surg
+Curr Opin Pediatr
+Curr Opin Periodontol
+Curr Opin Pharmacol
+Curr Opin Plant Biol
+Curr Opin Psychiatry
+Curr Opin Pulm Med
+Curr Opin Radiol
+Curr Opin Rheumatol
+Curr Opin Struct Biol
+Curr Opin Urol
+Curr Orthop
+Curr Osteoporos Rep
+Curr Pain Headache Rep
+Curr Pharm Biotechnol
+Curr Pharm Des
+Curr Popul Rep Consum Income
+Curr Popul Rep Popul Charact
+Curr Popul Rep Popul Estim Proj
+Curr Popul Rep [Spec Censuses]
+Curr Pract Gerontol Nurs
+Curr Pract Obstet Gynecol Nurs
+Curr Pract Orthop Surg
+Curr Pract Pediatr Nurs
+Curr Prescr
+Curr Probl Cancer
+Curr Probl Cardiol
+Curr Probl Clin Biochem
+Curr Probl Dermatol
+Curr Probl Diagn Radiol
+Curr Probl Obstet Gynecol
+Curr Probl Obstet Gynecol Fertil
+Curr Probl Pediatr
+Curr Probl Pediatr Adolesc Health Care
+Curr Probl Sov Med
+Curr Probl Surg
+Curr Protein Pept Sci
+Curr Psychiatr Ther
+Curr Psychiatry Rep
+Curr Psychol
+Curr Psychol Cogn
+Curr Res Anesth Analg
+Curr Res Occup Prof
+Curr Rev Pain
+Curr Rheumatol Rep
+Curr Sci
+Curr Sep
+Curr Sociol
+Curr Sports Med Rep
+Curr Stud Hematol Blood Transfus
+Curr Stud Nat Brain Funct
+Curr Surg
+Curr Theol Mission
+Curr Ther (Seaforth)
+Curr Ther Endocrinol Metab
+Curr Ther Res Clin Exp
+Curr Top Cell Regul
+Curr Top Comp Pathobiol
+Curr Top Dev Biol
+Curr Top Exp Endocrinol
+Curr Top Eye Res
+Curr Top Hematol
+Curr Top Med Chem
+Curr Top Med Mycol
+Curr Top Microbiol Immunol
+Curr Top Mol Endocrinol
+Curr Top Pathol
+Curr Top Radiat Res Q
+Curr Treat Options Cardiovasc Med
+Curr Treat Options Gastroenterol
+Curr Treat Options Neurol
+Curr Treat Options Oncol
+Curr Urol Rep
+Curr Vasc Pharmacol
+Curr Womens Health Rep
+Curr World Lead
+Current
+Curso Int Oftalmol
+Cutis
+Cyberpsychol Behav
+Cylchgrawn Llyfrgell Genedlaethol Cymru
+Cyprus Med J
+Cytobiologie
+Cytobios
+Cytogenet Cell Genet
+Cytogenet Genome Res
+Cytogenetics
+Cytojournal
+Cytokine
+Cytokine Growth Factor Rev
+Cytokines
+Cytokines Cell Mol Ther
+Cytokines Mol Ther
+Cytologia (Tokyo)
+Cytometry
+Cytometry A
+Cytometry B Clin Cytom
+Cytometry Suppl
+Cytopathology
+Cytotechnology
+Cytotherapy
+Czas Geogr
+Czas Praw Hist
+Czas Stomatol
+Czech Econ Dig
+Czech Med
+Czech Sociol Rev
+Czechoslov Sociol Rev
+D Cent Am
+D Yucatan
+DA West Asia Rep
+DAR Mag
+DDZ
+DE J Dent Eng
+DEEP Dev Educ Exch Pap
+DEF
+DGHS Chron
+DHS Dimens
+DICP
+DLR Nachr
+DNA
+DNA Cell Biol
+DNA Repair (Amst)
+DNA Res
+DNA Seq
+DP Rep
+DRG Monit
+Dacca Univ Stud A
+Daedalus
+Dakar Med
+Dalhous Law J
+Dalhousie Dent J
+Dalhousie Rev
+Dalton Trans
+Dan Amtsrad
+Dan Med Bull
+Dan Medicinhist Arbog
+Dan Tidsskr Farm
+Daphnis
+Dapim Refuiim
+Data Asia
+Data Bull (Cent Stud Health Syst Change)
+Data Strateg Benchmarks
+Data User News
+Database
+Dauphin Cty Rep
+De Economia
+De Paul Law Rev
+DePaul J Health Care Law
+Deans List
+Deans Notes
+Death Educ
+Death Stud
+Debat
+Deccan Geogr
+Decisions
+Decubitus
+Deep Sea Res A
+Def Couns J
+Def Law J
+Def Natl
+Def Sci J
+Defenders
+Dejiny Ved Tech
+Del Code Annot Del
+Del Hist
+Del Med J
+Del Nurse
+Delft Prog Rep
+Delt Hell Mikrobiol Hygieinol Hetair
+Delt Paidiatr Klin Panepistem Athenon
+Deltion / Iatrocheirourgikes Hetaireias Athenon
+Dement Geriatr Cogn Disord
+Dementia
+Democracy
+Demogr Afr
+Demogr Econ
+Demogr Hist Bull Inf Soc
+Demogr India
+Demogr Inf
+Demogr Issled
+Demogr Res
+Demogr Sveske
+Demografia
+Demografie
+Demography
+Demohrafichni Doslidzhennia
+Demos
+Demosta
+Dens
+Dens (Curitiba)
+Dens Sapiens
+Dent
+Dent Abstr
+Dent Anaesth Sedat
+Dent Angles
+Dent Assist
+Dent Assist (Waco Tx)
+Dent Assist J
+Dent Cadmos
+Dent Clin North Am
+Dent Concepts
+Dent Delin
+Dent Dialogue
+Dent Dienst
+Dent Dig
+Dent Dimens
+Dent Discourse
+Dent Echo (Heidelb)
+Dent Econ
+Dent Health (London)
+Dent Hist
+Dent Hyg (Chic)
+Dent Hyg (San Franc)
+Dent Images
+Dent Implantol Update
+Dent Items Interest
+Dent J
+Dent J Aust
+Dent J Malays
+Dent J Malaysia Singapore
+Dent J Zamb
+Dent Jpn (Tokyo)
+Dent Lab Bl
+Dent Lab Manage Today
+Dent Lab Rev
+Dent Labor (Munch)
+Dent Mag Oral Top
+Dent Manage
+Dent Mater
+Dent Mater J
+Dent Mirror (Atlanta)
+Dent Mirror (Quezon City)
+Dent News
+Dent News (Lond)
+Dent Off
+Dent Outlook
+Dent Pract
+Dent Pract (Cincinnati)
+Dent Pract (Ewell)
+Dent Pract Dent Rec
+Dent Pract Manage
+Dent Press
+Dent Prog (Chic)
+Dent Radiogr Photogr
+Dent Rec (London)
+Dent Res Grad Study
+Dent Rev
+Dent Rundsch
+Dent Sch Q
+Dent Stud
+Dent Surv
+Dent Team
+Dent Teamwork
+Dent Tech
+Dent Ther Newsl
+Dent Today
+Dent Traumatol
+Dent Update
+Dent World
+Dentago
+Dentalhygienistnews
+Dentalpractice
+Dentessence
+Dentist
+Dentistry
+Dentistry (Loma Linda)
+Dentisuto
+Dentomaxillofac Radiol
+Dentomaxillofac Radiol Suppl
+Dentoral (Istanbul)
+Dentoscope
+Denver J Int Law Policy
+Denver Law J
+Denver Univ Law Rev
+Denver West Roundup
+Dep State Bull
+Depress Anxiety
+Depression
+Derm Beruf Umwelt
+Dermatitis
+Dermatol Clin
+Dermatol Iber Lat Am
+Dermatol Int
+Dermatol Monatsschr
+Dermatol Nurs
+Dermatol Online J
+Dermatol Surg
+Dermatol Ther
+Dermatol Trop Ecol Geogr
+Dermatol Wochenschr
+Dermatologica
+Dermatology
+Dermatovenerologia
+Desarro Base
+Desarro Rural Amer
+Desarro Soc
+Desarrollo Econ
+Deseret News
+Desertif Control
+Desmos
+Detroit Coll Law Rev
+Detroit Dent Bull
+Detroit Perspect
+Dev Biol
+Dev Biol (Basel)
+Dev Biol (N Y 1985)
+Dev Biol Stand
+Dev Brief
+Dev Bull
+Dev Cell
+Dev Change
+Dev Civilis
+Dev Commun Rep
+Dev Comp Immunol
+Dev Dialogue
+Dev Dig
+Dev Dir
+Dev Dyn
+Dev Econ
+Dev Forum
+Dev Gend Brief
+Dev Genes Evol
+Dev Genet
+Dev Growth Differ
+Dev Health Econ Public Policy
+Dev Immunol
+Dev Int
+Dev Med Child Neurol
+Dev Med Child Neurol Suppl
+Dev Neuropsychol
+Dev Neurosci
+Dev Ophthalmol
+Dev Peace
+Dev Pharmacol Ther
+Dev Policy Rev
+Dev Pract
+Dev Psychobiol
+Dev Psychol
+Dev Psychopathol
+Dev Que
+Dev Rev
+Dev Sante
+Dev Sci
+Dev Soc
+Dev South Afr
+Dev Suppl
+Dev Toxicol Environ Sci
+Dev Volta
+Developing World Bioeth
+Development
+Deviant Behav
+Devon Assoc Adv Sci
+Devon Hist
+Dhaka Univ Stud B Biol Stud
+Di Yi Jun Yi Da Xue Xue Bao
+Dia Med
+Diab Vasc Dis Res
+Diabet Med
+Diabete
+Diabete Metab
+Diabetes
+Diabetes Care
+Diabetes Educ
+Diabetes Forecast
+Diabetes Metab
+Diabetes Metab Res Rev
+Diabetes Metab Rev
+Diabetes Nutr Metab
+Diabetes Obes Metab
+Diabetes Res
+Diabetes Res Clin Pract
+Diabetes Res Clin Pract Suppl
+Diabetes Self Manag
+Diabetes Technol Ther
+Diabetol Croat
+Diabetologia
+Diagn Afr
+Diagn Clin Immunol
+Diagn Cytopathol
+Diagn Gynecol Obstet
+Diagn Histopathol
+Diagn Imaging
+Diagn Imaging (San Franc)
+Diagn Imaging Clin Med
+Diagn Immunol
+Diagn Interv Radiol
+Diagn Microbiol Infect Dis
+Diagn Mol Pathol
+Diakonia
+Dial Transplant
+Dialect Anthropol
+Dialect Hum
+Dialog
+Dialog Fairleigh Dickinson Univ Sch Dent
+Dialogue
+Dialogue Can Philos Assoc
+Dialogue Diarrhoea
+Dialogues Clin Neurosci
+Dialogues Contracept
+Dialoog
+Diamond
+Diarrhoea Dialogue
+Diastema
+Dickensian
+Dickinson Law Rev
+Dicle Univ Tip Fakul Derg
+Dif Soc
+Differences
+Differentiation
+Dig Antibiot
+Dig Dis
+Dig Dis Sci
+Dig Liver Dis
+Dig Surg
+Digest Public Gen Bills
+Digestion
+Digitale Bilddiagn
+Dimens Crit Care Nurs
+Dimens Health Serv
+Dimens Oncol Nurs
+Dimension
+Dimensions (Wash)
+Diogenes
+Diotima
+Dir Boards
+Dir Lav
+Dirasat Med Biol Sci
+Dirasat Sukkaniyah
+Director
+Dirim
+Dis Aquat Organ
+Dis Chest
+Dis Colon Rectum
+Dis Esophagus
+Dis Manag
+Dis Manag Advis
+Dis Markers
+Dis Mon
+Dis Nerv Syst
+Disabil Handicap Soc
+Disabil Rehabil
+Disabil Soc
+Disaster Manag Response
+Disasters
+Disch Plann Update
+Discov Innov
+Discover
+Discuss Faraday Soc
+Diskussionsforum Med Ethik
+Displays
+Diss Abstr
+Dissent
+Dist Nurs
+Divulg Cult Odontol
+Dix Huit Siecle
+Dly Mail Guard
+Dly Rep China
+Dly Rep Peoples Repub China
+Dly Stat Can
+Dly Times
+Dly Wash Law Report
+Doc Anal Geogr
+Doc Geigy Acta Psychosom (Dtsch Ausg)
+Doc Geigy Acta Rheumatol
+Doc Hist Vocab Sci
+Doc Med Ethics
+Doc Med Geogr Trop
+Doc Neerl Indones Morbis Trop
+Doc Ophthalmol
+Doc Ophthalmol Proc Ser
+Doc Rheumatol
+Documents
+Dodone
+Dok Hoechst Arch
+Dokl Akad Nauk
+Dokl Akad Nauk SSSR
+Dokl Biochem
+Dokl Biochem Biophys
+Dokl Biol Sci
+Dokl Biophys
+Dokl Bulg Acad Nauk
+Dolentium Hominum
+Dom Law Rep
+Domes
+Domest Anim Endocrinol
+Donauraum
+Dong Wu Xue Bao
+Downs Syndr Res Pract
+Drake Law Rev
+Draper Fund Rep
+Draper World Popul Fund Rep
+Dritte Welt
+Droit Soc
+Drosoph Inf Serv
+Drug Alcohol Depend
+Drug Alcohol Rev
+Drug Chem Toxicol
+Drug Cosmet Ind
+Drug Deliv
+Drug Des Deliv
+Drug Des Discov
+Drug Dev Ind Pharm
+Drug Dev Res
+Drug Discov Today
+Drug Forum
+Drug Inf J
+Drug Intell Clin Pharm
+Drug Merch
+Drug Metab Dispos
+Drug Metab Pharmacokinet
+Drug Metab Rev
+Drug Metabol Drug Interact
+Drug News Perspect
+Drug Nutr Interact
+Drug Res
+Drug Res Rep
+Drug Resist Updat
+Drug Saf
+Drug Stand
+Drug Ther
+Drug Ther (NY)
+Drug Ther Bull
+Drug Topics
+Drug Trade News
+Drugs
+Drugs Aging
+Drugs Exp Clin Res
+Drugs Made Ger
+Drugs R D
+Drugs Soc (New York)
+Drugs Today (Barc)
+Dtsch Apoth
+Dtsch Apoth Ztg
+Dtsch Arch Klin Med
+Dtsch Arztebl
+Dtsch Aussenpolit
+Dtsch Gesundheit
+Dtsch Gesundheitspolit
+Dtsch Gesundheitsw
+Dtsch Jahrb Volkskd
+Dtsch Krankenpflegez
+Dtsch Lebensmitt Rundsch
+Dtsch Med J
+Dtsch Med Wochenschr
+Dtsch Rentenversicher
+Dtsch Schiffahrtsarch
+Dtsch Schwesternztg
+Dtsch Stomatol
+Dtsch Tierarztl Wochenschr
+Dtsch Vierteljahresschr Litt Wiss Geistesgesch
+Dtsch Z Gesamte Gerichtl Med
+Dtsch Z Mund Kiefer Gesichtschir
+Dtsch Z Nervenheilkd
+Dtsch Z Philos
+Dtsch Z Sportmed
+Dtsch Z Verdau Stoffwechselkr
+Dtsch Zahn Mund Kieferheilkd Zentralbl
+Dtsch Zahn Mund Kieferheilkd Zentralbl Gesamte
+Dtsch Zahnarztl Z
+Dtsch Zentralbl Krankenpfl
+Dtschl Arch
+Dubl Univ Law J
+Dublin Hist Rec
+Dubrovnik
+Duke J Gend Law Policy
+Duke Law J
+Duke Law Technol Rev
+Dunel Notes
+Duodecim
+Duodecim Suppl
+Duquesne Law Rev
+Durham Univ J
+Durius
+Dusseld Arb Gesch Med
+Dusseld Arb Gesch Med Beih
+Dusseld Geogr Schr
+Dyn Med
+Dyn Psychiatr
+Dynamics
+Dynamis
+Dyny Ysrl
+Dyslexia
+Dysphagia
+E
+EBRI Issue Brief
+ED Manag
+EDS Mag
+EDTNA ERCA J
+EEG EMG Z Elektroenzephalogr Elektromyogr Verwandte Geb
+EHP Toxicogenomics
+EITV
+EMBO J
+EMBO Rep
+EMT J
+ENLB Emerg Nurse Leg Bull
+ENO FO
+EOS
+EPI Newsl
+ERS Spectr
+ESA Bull
+ESA J
+ESRC Data Arch Bull
+ET J
+EURE
+EURO Rep Stud
+EXS
+Ear Hear
+Ear Nose Throat J
+Early Am Lit
+Early Child Dev Care
+Early Child Res Q
+Early Educ Dev
+Early Hum Dev
+Early Pregnancy
+Early Sci Med
+Earth (Waukesha)
+Earth Miner Sci
+Earth Moon Planets
+Earth Negot Bull
+Earth Planet Sci Lett
+Earth Sci Hist
+Earth Space
+Earth Space Rev
+Earthwatch
+East Afr Econ Rev
+East Afr Geogr Rev
+East Afr J Rural Dev
+East Afr Med J
+East Afr Soc Sci Res Rev
+East Anthropol
+East Cent Eur
+East Econ J
+East Eur Jew Aff
+East Eur Polit Soc
+East Eur Q
+East Europ Econ
+East Horiz
+East Mediterr Health J
+East South Afr Geogr J
+East Tenn Soc Publ
+Eat Behav
+Eat Weight Disord
+Ebony
+Echo Med Cevennes
+Echo Med Nord
+Echocardiography
+Echoes
+Ecol Appl
+Ecol Dis
+Ecol Econ
+Ecol Eng
+Ecol Entomol
+Ecol Food Nutr
+Ecol Law Q
+Ecol Lett
+Ecol Modell
+Ecol Monogr
+Ecologist
+Ecology
+Econ Bot
+Econ Bull Asia Pac
+Econ Bull Ghana
+Econ Comput Econ Cybern Stud Res
+Econ Desarro
+Econ Educ Rev
+Econ Eye
+Econ Geogr
+Econ Geol
+Econ Hist (Sweden)
+Econ Hist Rev
+Econ Hum Biol
+Econ Humanisme
+Econ Inq
+Econ J Nepal
+Econ Lav
+Econ Lett
+Econ Med Anim
+Econ Merid
+Econ Model
+Econ Outlook USA
+Econ Philos
+Econ Polit Wkly
+Econ Prospect Int
+Econ Pubblica
+Econ Rec
+Econ Reun
+Econ Rev
+Econ Soc
+Econ Soc Hist Jaarb
+Econ Soc Rev (Irel)
+Econ Sociol
+Econ Stat
+Econ Stat Ber
+Econ Stor
+Econometrica
+Economia
+Economia Umana Rassegna Medica Internazionale
+Economica
+Economist
+Economist (Leiden)
+Ecotoxicol Environ Saf
+Ecotoxicology
+Ecrits Paris
+Ecum Rev
+Edcentric
+Edinb Dent Hosp Gaz
+Edinburgh Med J
+Editor Res Rep
+Educ Adm Q
+Educ Broadcast Int
+Educ Dent (Ica)
+Educ Dir Dent Aux
+Educ Dir Dent Hyg
+Educ Forum
+Educ Gerontol
+Educ Health (Abingdon)
+Educ Ind Telev
+Educ Leadersh
+Educ Med Salud
+Educ Psychol Meas
+Educ Rec
+Educ Stud
+Educ Theory
+Educ Train Ment Retard
+Educ Update
+Educ Urban Soc
+Educafrica
+Eesti Nsv Tead Akad TOIM Biol
+Eff Clin Pract
+Eff Health Care
+Effluent Water Treat J
+Egeszsegtudomany
+Egypt Dent J
+Egypt J Bilharz
+Egypt J Immunol
+Egypt J Psychiatry
+Egypt Med J
+Egypt Popul Fam Plann Rev
+Egypte Contemp
+Egypte Monde Arabe
+Eichsfelder Heimath
+Eicosanoids
+Eighteenth Century (Lubbock)
+Eighteenth Century Irel
+Eighteenth Century Life
+Eighteenth Century Stud
+Einstein Q J Biol Med
+Eire Irel
+Eisei Dobutsu
+Eisei Shikenjo Hokoku
+Ekistics Probl Sci Hum Settl
+Ekologiia
+Ekon Cas
+Ekon Keuangan Indones
+Ekon Misao
+Ekon Pregl
+Ekon Revija
+Ekon Sov Ukr
+Eksp Khir Anesteziol
+Eksp Khirurgiia
+Eksp Klin Farmakol
+Eksp Klin Gastroenterol
+Eksp Med Morfol
+Eksp Onkol
+Elder Care
+Elder Law J
+Electrochim Acta
+Electrodiagn Ther
+Electroencephalogr Clin Neurophysiol
+Electroencephalogr Clin Neurophysiol Suppl
+Electromagn Biol Med
+Electromyogr Clin Neurophysiol
+Electromyography
+Electron Microsc Rev
+Electrophoresis
+Elektromed Biomed Tech
+Embryologia (Nagoya)
+Eme Eme Estud Domin
+Emerg Dep News
+Emerg Health Serv Q
+Emerg Health Serv Rev
+Emerg Infect Dis
+Emerg Med
+Emerg Med (Fremantle)
+Emerg Med Australas
+Emerg Med Clin North Am
+Emerg Med J
+Emerg Med Rep
+Emerg Med Serv
+Emerg Nurse
+Emerg Plann Dig
+Emerg Radiol
+Emerg Themes Epidemiol
+Emergency
+Emerita
+Emisor Demogr
+Emory Int Law Rev
+Emory Law J
+Emory Mag
+Emotion
+Emp State Rep
+Emphasis Nurs
+Empir Econ
+Empl Benefits J
+Empl Desempl
+Empl Health Fit
+Employ Relat Today
+Employee Benefit Plan Rev
+Employee Relat Law J
+Emporia State Res Stud
+Enantiomer
+Encephale
+Encounter
+Encounter (Lond)
+Endeavour
+Endocr Dev
+Endocr J
+Endocr Metab Immune Disord Drug Targets
+Endocr Pathol
+Endocr Pract
+Endocr Regul
+Endocr Relat Cancer
+Endocr Res
+Endocr Res Commun
+Endocr Rev
+Endocr Terap
+Endocrine
+Endocrinol Exp
+Endocrinol Jpn
+Endocrinol Metab Clin North Am
+Endocrinol Sci Cost
+Endocrinologie
+Endocrinologist
+Endocrinology
+Endod Dent Traumatol
+Endod Prac
+Endod Rep
+Endodoncia
+Endodoncia (Mex)
+Endokrinologie
+Endokrynol Diabetol Chor Przemiany Materii Wieku Rozw
+Endokrynol Pol
+Endosc Surg Allied Technol
+Endoscopy
+Endothelium
+Energy Fuels
+Energy Policy
+Enfance
+Enfant Milieu Trop
+Enferm Infecc Microbiol Clin
+Enferm Intensiva
+Enferm Torax
+Enfermeras
+Enfermeria
+Enfoque
+Enfoque Fem
+Enfoques Aten Prim
+Eng Med
+Eng Sci
+Engage Soc Action
+Engl Hist Rev
+Engl Lang Notes
+Enlace
+Enquetes Mus Vie Wallonne
+Entechnology
+Entomol Exp Appl
+Entomol News
+Entre Nous Cph Den
+Entret Bichat Med Entret Bichat
+Entret Bichat Pitie Salpetriere Ther
+Entscheid Bundesgerichtshofes Zivilsachen
+Env Polit
+Environ Aciton
+Environ Aff
+Environ Afr
+Environ Behav
+Environ Biol Med
+Environ Biosafety Res
+Environ Change Secur Proj Rep
+Environ Conserv
+Environ Educ Inf
+Environ Entomol
+Environ Ethics
+Environ Exp Bot
+Environ Folio
+Environ Geochem Health
+Environ Health
+Environ Health Perspect
+Environ Health Ser [Radiol Health]
+Environ Hist Rev
+Environ Impact Assess Rev
+Environ Int
+Environ Law
+Environ Lett
+Environ Manage
+Environ Med
+Environ Microbiol
+Environ Mol Mutagen
+Environ Monit Assess
+Environ Mutagen
+Environ Physiol Biochem
+Environ Plan A
+Environ Plan D
+Environ Plann B Plann Des
+Environ Plann C Gov Policy
+Environ Pollut
+Environ Qual Annu Rep Counc Environ Qual
+Environ Qual Saf
+Environ Qual Saf Suppl
+Environ Res
+Environ Rev
+Environ Sci
+Environ Sci Pollut Res Int
+Environ Sci Technol
+Environ Technol
+Environ Toxicol
+Environ Toxicol Chem
+Environ Urban
+Environ Values
+Environment
+Environmentalist
+Environments
+Enzyme
+Enzyme Microb Technol
+Enzyme Protein
+Enzymol Biol Clin (Basel)
+Enzymologia
+Eos (Washington DC)
+Epa J
+Epatologia
+Epeirotike Hestia
+Epet Etaireias Stereoelladikon Meleton
+Epheta
+Epidemiol Bull
+Epidemiol Community Health
+Epidemiol Infect
+Epidemiol Mikrobiol Imunol
+Epidemiol Perspect Innov
+Epidemiol Prev
+Epidemiol Psichiatr Soc
+Epidemiol Rev
+Epidemiology
+Epigraph Stud
+Epigraphica
+Epilepsia
+Epilepsy Behav
+Epilepsy Curr
+Epilepsy Res
+Epilepsy Res Suppl
+Epileptic Disord
+Epione
+Episteme
+Epithelial Cell Biol
+Epitheor Koin Ereun
+Epopteia
+Equilib Res
+Equilibrium
+Equine Vet J
+Equine Vet J Suppl
+Equinoxe
+Era Social
+Erde
+Erdkd Wissen
+Erdkunde
+Erfahrungsheilkunde
+Ergeb Allg Pathol Pathol Anat
+Ergeb Anat Entwicklungsgesch
+Ergeb Biol
+Ergeb Chir Orthop
+Ergeb Enzymforsch
+Ergeb Gesamten Tuberkuloseforsch
+Ergeb Hyg Bakteriol Immunitatsforsch Exp Ther
+Ergeb Inn Med Kinderheilkd
+Ergeb Mikrobiol Immunitatsforsch Exp Ther
+Ergeb Physiol
+Ergon Des
+Ergonomics
+Ernahrungsforsch Ber Mitt
+Ernst Schering Res Found Workshop
+Erzieh Unterr
+Esencia Odontol
+Esope
+Espace Geogr
+Espace Popul Soc
+Espaces Soc
+Espiral
+Esprit
+Esquire
+Essays Arts Sci
+Essays Biochem
+Essays Fundam Immunol
+Essays Neurochem Neuropharmacol
+Essays Stud (Lond)
+Essence (Downsview)
+Essent Drugs Monit
+Essent Psychopharmacol
+Essex Inst Hist Collect
+Essex J
+Estad Panamena
+Estad Panamena Situac Demogr Secc 231 Migr Migr Int
+Estad Venez
+Estadastica
+Estampille
+Este Pais
+Estodont Press
+Estomatol Cult
+Estomatologia
+Estud Asia Afr
+Estud CEBRAP
+Estud Cent Am
+Estud Cl
+Estud Cult Nahuatl
+Estud Demogr
+Estud Demogr Urbanos Col Mex
+Estud Dep Hist Mod
+Estud Econ
+Estud Fem
+Estud Geogr
+Estud Hist Doc Arxius Protoc
+Estud Hist Novohisp
+Estud Hist Saude
+Estud Hist Soc
+Estud Hist Soc Econ Am
+Estud Latinoam
+Estud Migr Latinoam
+Estud Parag
+Estud Poblac
+Estud Publicos
+Estud Rom (Barcelona)
+Estud Rurales Latinoam
+Estud Segov
+Estud Soc
+Estud Soc Centroam
+Estud Sociol
+Estudios
+Estudis
+Eta Sigma Gamman
+Etc
+Ethical Currents
+Ethical Hum Psychol Psychiatry
+Ethical Hum Sci Serv
+Ethical Perspect
+Ethical Theory Moral Pract
+Ethics
+Ethics Behav
+Ethics Comm Newsl
+Ethics Inf Technol
+Ethics Int Aff
+Ethics Intellect Disabil
+Ethics Med
+Ethics Medics
+Ethics Sci Med
+Ethiop Herald
+Ethiop J Dev Res
+Ethiop Med J
+Ethiop Obs
+Ethique
+Ethn Dis
+Ethn Forum
+Ethn Health
+Ethn Racial Stud
+Ethnic Groups
+Ethnicity
+Ethnographie.
+Ethnohistory
+Ethnol Eur
+Ethnol Fr
+Ethnol Scand
+Ethnology
+Ethnomedizin
+Ethnos
+Ethol Sociobiol
+Ethology
+Ethos
+Etnogr Pol
+Etnol Stud
+Etud Angl
+Etud Balk
+Etud Can
+Etud Class
+Etud Doc Groupe Demogr Afr
+Etud Expans
+Etud Ger
+Etud Hist Afr
+Etud Int
+Etud Inuit
+Etud Irl
+Etud Limousines
+Etud Napoleon
+Etud Polemol
+Etud Rurales
+Etud Soins Serv Infirm
+Etud Togol Popul
+Etud Vauclus
+Etud Zair
+Etude Popul Afr
+Etudes (Paris)
+Etudes Freud
+Etudes Mali
+Etudes Neonatales
+Etudes Rwandaises
+Etudes Stat Inst Natl Stat
+Eubios J Asian Int Bioeth
+Eugen News
+Eugen Q
+Eugen Rev
+Eugen Soc Symp
+Eukaryot Cell
+Eur Addict Res
+Eur Arch Otorhinolaryngol
+Eur Arch Otorhinolaryngol Suppl
+Eur Arch Psychiatry Clin Neurosci
+Eur Arch Psychiatry Neurol Sci
+Eur Asia Stud
+Eur Biophys J
+Eur Cell Mater
+Eur Child Adolesc Psychiatry
+Eur Cytokine Netw
+Eur Econ Rev
+Eur Ethn
+Eur Foreign Aff Rev
+Eur Heart J
+Eur Heart J Suppl
+Eur Hist Q
+Eur Hochschulschr VII Med B Gesch Med
+Eur Hum Rights Law Rev
+Eur Intellect Prop Rev
+Eur J Anaesthesiol
+Eur J Anaesthesiol Suppl
+Eur J Appl Physiol
+Eur J Appl Physiol Occup Physiol
+Eur J Basic Appl Histochem
+Eur J Biochem
+Eur J Cancer
+Eur J Cancer B Oral Oncol
+Eur J Cancer Care (Engl)
+Eur J Cancer Clin Oncol
+Eur J Cancer Prev
+Eur J Cardiol
+Eur J Cardiothorac Surg
+Eur J Cardiovasc Nurs
+Eur J Cardiovasc Prev Rehabil
+Eur J Cell Biol
+Eur J Cell Biol Suppl
+Eur J Clin Chem Clin Biochem
+Eur J Clin Invest
+Eur J Clin Microbiol
+Eur J Clin Microbiol Infect Dis
+Eur J Clin Nutr
+Eur J Clin Pharmacol
+Eur J Contracept Reprod Health Care
+Eur J Dent Educ
+Eur J Dermatol
+Eur J Disord Commun
+Eur J Drug Metab Pharmacokinet
+Eur J Echocardiogr
+Eur J Emerg Med
+Eur J Endocrinol
+Eur J Epidemiol
+Eur J Gastroenterol Hepatol
+Eur J Gen Pract
+Eur J Genet Soc
+Eur J Gynaecol Oncol
+Eur J Haematol
+Eur J Haematol Suppl
+Eur J Health Econ
+Eur J Health Law
+Eur J Heart Fail
+Eur J Histochem
+Eur J Hum Genet
+Eur J Immunogenet
+Eur J Immunol
+Eur J Int Aff
+Eur J Intensive Care Med
+Eur J Intern Med
+Eur J Mark
+Eur J Mass Spectrom (Chichester, Eng)
+Eur J Med
+Eur J Med Chem
+Eur J Med Genet
+Eur J Med Res
+Eur J Morphol
+Eur J Neurol
+Eur J Neurosci
+Eur J Nucl Med
+Eur J Nucl Med Mol Imaging
+Eur J Nutr
+Eur J Obstet Gynecol
+Eur J Obstet Gynecol Reprod Biol
+Eur J Oncol Nurs
+Eur J Oper Res
+Eur J Ophthalmol
+Eur J Oral Sci
+Eur J Orthod
+Eur J Paediatr Dent
+Eur J Paediatr Neurol
+Eur J Pain
+Eur J Pediatr
+Eur J Pediatr Surg
+Eur J Pharm Biopharm
+Eur J Pharm Sci
+Eur J Pharmacol
+Eur J Polit Res
+Eur J Popul
+Eur J Prosthodont Restor Dent
+Eur J Protistol
+Eur J Public Health
+Eur J Radiol
+Eur J Respir Dis
+Eur J Respir Dis Suppl
+Eur J Rheumatol Inflamm
+Eur J Sex Transm Dis
+Eur J Soc Psychol
+Eur J Surg
+Eur J Surg Oncol
+Eur J Surg Suppl
+Eur J Toxicol
+Eur J Toxicol Environ Hyg
+Eur J Toxicol Hyg Environ
+Eur J Ultrasound
+Eur J Vasc Endovasc Surg
+Eur J Vasc Surg
+Eur Law Rev
+Eur Med (Paris)
+Eur Neurol
+Eur Neuropsychopharmacol
+Eur Om
+Eur Phys J E Soft Matter
+Eur Psychiatry
+Eur Qual Assur Netw Newsl
+Eur Radiol
+Eur Reg
+Eur Respir J
+Eur Respir J Suppl
+Eur Rev
+Eur Rev Appl Psychol
+Eur Rev Med Pharmacol Sci
+Eur Sociol Rev
+Eur Spine J
+Eur Stud Rev
+Eur Surg Res
+Eur Urban Reg Stud
+Eur Urol
+Eur Water Pollut Control
+Eura Medicophys
+Euro Surveill
+Eurobiologiste
+Europ Demogr Inf Bull
+Europa Archiv
+Europace
+Europe
+Europhys Lett
+Euthan Rev
+Euthanasia News
+Eval Health Prof
+Eval Pract
+Eval Program Plann
+Eval Q
+Eval Rev
+Evaluation
+Evening Sun
+Evid Based Cardiovasc Med
+Evid Based Complement Alternat Med
+Evid Based Dent
+Evid Based Ment Health
+Evid Based Nurs
+Evid Rep Technol Assess (Summ)
+Evocations
+Evol Anthropol
+Evol Comput
+Evol Dev
+Evol Hum Behav
+Evol Med
+Evol Psychiatr (Paris)
+Evolution Int J Org Evolution
+Except Child
+Excerpta Med
+Excerpta Med (Dermatol)
+Excerpta Med (Ophthalmol)
+Excerpta Med (Urol)
+Excerpta Medica 15 Chest Dis
+Excerpta Medica 20 Gerontol Geriatr
+Exec Housekeep Today
+Exec Housekeeper
+Exec Intell Rev
+Exec Solut Healthc Manag
+Exerc Immunol Rev
+Exerc Sport Sci Rev
+Exp Aging Res
+Exp Anim
+Exp Appl Acarol
+Exp Biol
+Exp Biol Med
+Exp Biol Med (Maywood)
+Exp Brain Res
+Exp Cell Biol
+Exp Cell Res
+Exp Cell Res Suppl
+Exp Clin Endocrinol
+Exp Clin Endocrinol Diabetes
+Exp Clin Immunogenet
+Exp Clin Psychopharmacol
+Exp Clin Transplant
+Exp Dermatol
+Exp Diabesity Res
+Exp Embryol Teratol
+Exp Eye Res
+Exp Gerontol
+Exp Hematol
+Exp Lung Res
+Exp Med Pathol Klin
+Exp Med Surg
+Exp Mol Med
+Exp Mol Pathol
+Exp Mycol
+Exp Nephrol
+Exp Neurol
+Exp Oncol
+Exp Parasitol
+Exp Pathol
+Exp Pathol (Jena)
+Exp Pathol Suppl
+Exp Physiol
+Exp Psychol
+Exp Toxicol Pathol
+Expedition
+Experientia
+Experientia Suppl
+Expert Opin Biol Ther
+Expert Opin Drug Deliv
+Expert Opin Drug Saf
+Expert Opin Emerg Drugs
+Expert Opin Investig Drugs
+Expert Opin Pharmacother
+Expert Opin Ther Targets
+Expert Rev Anti Infect Ther
+Expert Rev Anticancer Ther
+Expert Rev Cardiovasc Ther
+Expert Rev Med Devices
+Expert Rev Mol Diagn
+Expert Rev Mol Med
+Expert Rev Neurother
+Expert Rev Proteomics
+Expert Rev Vaccines
+Explor Econ Hist
+Explor J
+Explor Knowl
+Explor Renaiss Cult
+Explorer (Hayward)
+Explorer (Kansas City)
+Expo Times
+Expos Annu Biochim Med
+Extremophiles
+Eye
+Eye Contact Lens
+Eye Ear Nose Throat Mon
+FAO Fish Circ
+FAO Food Nutr Pap
+FAO Food Nutr Ser
+FAO Nutr Meet Rep Ser
+FAO Nutr Stud
+FAS Prof Bull
+FAS Public Interest Rep
+FASEB J
+FDA Consum
+FDA Drug Bull
+FDA Med Bull
+FDC Rep Drugs Cosmet
+FDI World
+FEBS J
+FEBS Lett
+FEMNET News
+FEMS Immunol Med Microbiol
+FEMS Microbiol Ecol
+FEMS Microbiol Immunol
+FEMS Microbiol Lett
+FEMS Microbiol Rev
+FEMS Yeast Res
+FLA Bar J
+FNIB
+FNIB Info
+FO
+FPM Bull
+Fa Yi Xue Za Zhi
+Fac Cathol Lille
+Fac Notes (New Orleans La)
+Facial Orthop Temporomandibular Arthrol
+Facial Plast Surg
+Facial Plast Surg Clin North Am
+Fact Sheets Swed
+Factor Odontol
+Factotum
+Facts Infant Feed
+Fag Tidsskr Sykepleien
+Fait Tend
+Faith Philos
+Fam Cancer
+Fam Circle
+Fam Community Health
+Fam Concil Courts Rev
+Fam Consum Sci Res J
+Fam Coord
+Fam Dev
+Fam Econ Rev
+Fam Health
+Fam Law Q
+Fam Law Rep
+Fam Law Rev
+Fam Life Coord
+Fam Life Educ
+Fam Life Matters
+Fam Matters
+Fam Med
+Fam Plan Manag
+Fam Plann
+Fam Plann (Palo Alto)
+Fam Plann Dig
+Fam Plann Inf Serv
+Fam Plann Perspect
+Fam Plann Popul Rep
+Fam Plann Q
+Fam Plann Resume
+Fam Plann Today
+Fam Pract
+Fam Pract Manag
+Fam Pract News
+Fam Pract Res J
+Fam Process
+Fam Relat
+Fam Soc
+Fam Syst Health
+Fam Syst Med
+Fam Ther
+Fam Urol
+Familia
+Family Law
+Family Plan World
+Famplanco News
+Far East Aff
+Far East Econ Rev
+Faraday Discuss
+Faraday Discuss Chem Soc
+Farm Clin
+Farm Glas
+Farm Hosp
+Farm Nueva
+Farm Obz
+Farm Pol
+Farm Tid
+Farm Vestn
+Farm Zh
+Farmacihist Sallsk Ars
+Farmaco
+Farmaco [Prat]
+Farmaco [Sci]
+Farmacognosia
+Farmacoter Actual
+Farmakol Toksikol
+Farmakoterapi
+Farmatsiia
+Farmatsiia (Sofia)
+Fasett
+Fauchard
+Faulkner Grays Med Health
+Fed Bar J
+Fed Bull
+Fed Insur Corp Couns Q
+Fed Insur Couns Q
+Fed Law Rep
+Fed Oper Dent
+Fed Probat
+Fed Proc
+Fed Proc Transl Suppl
+Fed Regist
+Fed Report
+Fed Rules Decis
+Fed Suppl
+Feedback
+Fegato
+Feldsher Akush
+Feline Pract
+Fem Econ
+Fem Issues
+Fem Psychol
+Fem Rev
+Fem Stud
+Female Health Top Diagn Report
+Female patient
+Femina
+Fennia
+Fertil Contracept
+Fertil Contracept Sex
+Fertil Determ Res Notes
+Fertil Orthog
+Fertil Steril
+Fetal Diagn Ther
+Fetal Pediatr Pathol
+Fetal Ther
+Feuill Biol
+Feuill Prat
+Fibrinolysis
+Fides Et Hist
+Field Staff Rep
+Fieldstaff Rep
+Fieldstaff Rep West Eur Ser
+Fiji Med J
+Filaria J
+Film Hist
+Filson Club Hist Q
+Fin Lakaresallsk Handl
+Financ Times
+Finance Dev
+Finanzarchiv
+Find Brief
+Finisterra
+Fire J
+Fire Technol
+First People
+First Things
+Fish Shellfish Immunol
+Fission Prod Inhal Proj
+Fitoterapia
+Fiziol Cheloveka
+Fiziol Norm Patol
+Fiziol Zh
+Fiziol Zh Im I M Sechenova
+Fiziol Zh SSSR Im I M Sechenova
+Fla Dent J
+Fla Entomol
+Fla Hist Q
+Fla J Int Law
+Fla Law Rev
+Fla Nurse
+Fla State Univ Law Rev
+Fla Suppl
+Flambeau
+Flash Inf
+Fletcher Forum
+Fletcher Forum World Aff
+Flight Saf Dig
+Flinders J Hist Polit
+Flintknappers Exch
+Florilegium
+Flying Saf
+Focus
+Focus AACN
+Focus Autism Other Dev Disabl
+Focus Crit Care
+Focus Gend
+Focus MDA
+Focus Ohio Dent
+Focus Tech Coop
+Fogorv Sz
+Foi Lang
+Fol Ophthalmol
+Fold Des
+Foldr Ert
+Folha Med
+Folia Allergol (Roma)
+Folia Biol (Krakow)
+Folia Biol (Praha)
+Folia Cardiol
+Folia Clin Biol (Sao Paulo)
+Folia Clin Int (Barc)
+Folia Endocrinol
+Folia Endocrinol Mens Incretologia Incretoterapia
+Folia Geogr Ser Geogr Oecon
+Folia Haematol (Frankf)
+Folia Haematol Int Mag Klin Morphol Blutforsch
+Folia Hered Pathol (Milano)
+Folia Histochem Cytobiol
+Folia Histochem Cytochem (Krakow)
+Folia Humanist
+Folia Med (Napoli)
+Folia Med (Plovdiv)
+Folia Med Cracov
+Folia Med Neerl
+Folia Mendeliana
+Folia Microbiol (Praha)
+Folia Morphol (Praha)
+Folia Morphol (Warsz)
+Folia Neuropathol
+Folia Orient
+Folia Parasitol (Praha)
+Folia Phoniatr (Basel)
+Folia Phoniatr Logop
+Folia Primatol (Basel)
+Folia Psychiatr
+Folia Psychiatr Neurol Jpn
+Folia Psychiatr Neurol Neurochir Neerl
+Folia Stomatol
+Folia Vet
+Folia Vet Lat
+Folk
+Folkl Am
+Folkl Brabancon
+Folkl Champagne
+Folkl Suisse
+Folklore
+Fontanus
+Fonti Stor Sanita
+Food Addit Contam
+Food Biotechnol
+Food Chem Toxicol
+Food Cosmet Toxicol
+Food Drug Cosmet Law J
+Food Drug Cosmet Law Q
+Food Drug Cosmet Med Device Law Dig
+Food Drug Law J
+Food Foodways
+Food Manage
+Food Microbiol
+Food Nutr (Roma)
+Food Nutr Bull
+Food Policy
+Food Res
+Food Res Inst Stud
+Food Res Inst Stud Agric Econ Trade Dev
+Food Technol
+Foodborne Pathog Dis
+Fookien Times Yearb
+Foot Ankle
+Foot Ankle Clin
+Foot Ankle Int
+For Ecol Manage
+Forbes
+Ford Found Rep
+Fordham Int Law J
+Fordham Law Rev
+Fordham Urban Law J
+Foreign Aff
+Foreign Policy
+Forensic Sci
+Forensic Sci Int
+Foresight
+Formos J Med Humanit
+Formulary
+Foro Int
+Foro Mund Salud
+Forsch Fortbild Chir Bewegungsapparates
+Forsch J Neue Soz Beweg
+Forsch Komplementarmed
+Forsch Komplementarmed Klass Naturheilkd
+Forsch Volks Landeskd
+Forsvarsmedicin
+Fort Concho Rep
+Fortn Rev Chic Dent Soc
+Fortpflanz Besamung Aufzucht Haustiere
+Fortschr Androl
+Fortschr Angew Radioisot Grenzgeb
+Fortschr Arzneimittelforsch
+Fortschr Chem Org Naturst
+Fortschr Geb Rontgenstr
+Fortschr Geb Rontgenstr Nuklearmed
+Fortschr Geb Rontgenstrahlen Neuen Bildgeb Verfahr Erganzungsbd
+Fortschr Geb Rontgenstrahlen Nuklearmed Erganzungsbd
+Fortschr Geburtshilfe Gynakol
+Fortschr Hals Nasen Ohrenheilkd
+Fortschr Immunitatsforsch
+Fortschr Kiefer Gesichtschir
+Fortschr Kieferorthop
+Fortschr Med
+Fortschr Med Monogr
+Fortschr Med Orig
+Fortschr Med Suppl
+Fortschr Neurol Psychiatr
+Fortschr Neurol Psychiatr Grenzgeb
+Fortschr Ophthalmol
+Fortschr Psychosom Med
+Fortschr Tierphysiol Tierernahr
+Fortschr Verhaltensforsch
+Fortschr Zool
+Fortuna Vitrea
+Fortune
+Forum
+Forum (Genova)
+Forum (Wash)
+Forum Appl Res Public Policy
+Forum Dev Stud
+Forum Fam Plan West Hemisph
+Forum Med
+Forum Mond Sante
+Forum Nutr
+Forum Rev Invat Super
+Forum Stat
+Found News
+Found News Comment
+Foundations
+Fpop Bull
+Fprc
+Fr Am Rev
+Fr Hist Stud
+Fr Med
+Fr Stud
+Fra Sundhedsstyr
+Fracastoro
+Fractals
+Francia
+Frankf Hefte
+Frankf Z Pathol
+Frater Psi Omega
+Free China Rev
+Free Inq
+Free Inq Creat Sociol
+Free Radic Biol Med
+Free Radic Res
+Free Radic Res Commun
+Free World Horiz
+Freedom Issue
+Freedom Rev
+Freedomways
+Freeman
+Freib Forsch Medizingesch
+Freie Zahnarzt
+Frenesie
+Fresenius J Anal Chem
+Friends P I Nixon Med Hist Libr
+Friends Women Newsl
+Friuli Med
+Front Aging Ser
+Front Biol
+Front Biosci
+Front Gastrointest Res
+Front Health Policy Res
+Front Health Serv Manage
+Front Horm Res
+Front Lines
+Front Lines Res
+Front Med Biol Eng
+Front Neuroendocrinol
+Front Nurs Serv Q Bull
+Front Oral Physiol
+Front Radiat Ther Oncol
+Front Zool
+Frontiers (Boulder)
+Fruhmittelalt Stud
+Ftiziologia
+Fugitive Leaves Hist Collect
+Fukuoka Igaku Zasshi
+Fukuoka Shika Daigaku Gakkai Zasshi
+Fukushima Igaku Zasshi
+Fukushima J Med Sci
+Fulorrgegegyogyaszat
+Funct Dev Morphol
+Funct Integr Genomics
+Funct Neurol
+Funct Orthod
+Funct Plant Biol
+Fund Raising Manage
+Fundam Appl Toxicol
+Fundam Clin Pharmacol
+Fundam Sci
+Fungal Genet Biol
+Furrow
+Futur Anter
+Future
+Future Child
+Future Dent
+Future Oncol
+Futures
+Futuribles
+Futurist
+Fysiatr Revmatol Vestn
+Fysiatr Vestn Cesk Fysiatr Spol
+G Accad Med Torino
+G Anest Stomatol
+G Batteriol Immunol
+G Batteriol Virol Immunol
+G Batteriol Virol Immunol Clin
+G Batteriol Virol Immunol Microbiol
+G Biochim
+G Bot Ital
+G Chir
+G Clin Med
+G Crit Filos Ital
+G E N
+G Econ Ann Econ
+G Endodonzia
+G Fis Sanit Prot Radiaz
+G Gerontol
+G Gerontol Suppl
+G Ig Med Prev
+G Ital Anestesiol
+G Ital Cardiol
+G Ital Cardiol (Rome)
+G Ital Chemioter
+G Ital Chir
+G Ital Della Tuberc
+G Ital Dermatol
+G Ital Dermatol Minerva Dermatol
+G Ital Dermatol Venereol
+G Ital Endod
+G Ital Filol
+G Ital Mal Torace
+G Ital Med Lav
+G Ital Med Lav Ergon
+G Ital Nefrol
+G Ital Oftalmol
+G Ital Oncol
+G Ital Patol
+G Ital Tuberc Mal Torace
+G Mal Infett Parassit
+G Med Mil
+G Pneumol
+G Psichiatr Neuropatol
+G Sci Mediche
+G Stomatol Ortognatodonzia
+G Veneto Sci Med
+GAEA An Soc Argent Estud Geogr
+GHA Today
+GHAA J
+GIRE
+GLQ
+GMDA Bull
+GMHC Treat Issues
+GMU Law Rev
+GP
+GRMA News
+GROOTS
+GSA Today
+Ga Epidemiol Rep
+Ga Hist Q
+Ga Hosp Today
+Ga J Sci
+Ga Nurse
+Ga Pharm Q
+Ga State Univ Law Rev
+Gac CONASIDA
+Gac Med (Guayaquil)
+Gac Med Caracas
+Gac Med Esp
+Gac Med Mex
+Gac Med Norte
+Gac Sanit
+Gait Posture
+Galenika Cas Med Farm Hem Srod Nauke
+Galicia Clin
+Gallup Opin Index
+Gallup Rep
+Gamete Res
+Gan
+Gan No Rinsho
+Gan To Kagaku Ryoho
+Ganka
+Gann
+Gaoxiong Yi Xue Ke Xue Za Zhi
+Gard Hist
+Gastric Cancer
+Gastroenterol Clin Biol
+Gastroenterol Clin North Am
+Gastroenterol Fortbildungskurse Prax
+Gastroenterol Hepatol
+Gastroenterol J
+Gastroenterol Jpn
+Gastroenterol Nurs
+Gastroenterologia
+Gastroenterologist
+Gastroenterology
+Gastrointest Endosc
+Gastrointest Endosc Clin N Am
+Gastrointest Radiol
+Gateway Herit
+Gaz Arch
+Gaz Egypt Paediatr Assoc
+Gaz Grolier Club
+Gaz Hop Civ Mil Empire Ottoman
+Gaz Law Soc Upper Can
+Gaz Med Fr
+Gaz Med Lomb
+Gaz Med Port
+Gaz Woda Tech
+Gazette
+Gazz Chim Ital
+Gazz Int Med Chir
+Gazz Med Ital
+Gazz Osp Clin
+Gazz Sanit
+Ge Mag
+Geburtshilfe Frauenheilkd
+Gedrag Gezond
+Gegenbaurs Morphol Jahrb
+Geka Chiryo
+Gelis Derg Orta dogu Tek Univ
+Geloof Wet
+Gem State RN News Lett
+Gematol Transfuziol
+Gen
+Gen Comp Endocrinol
+Gen Cytochem Methods
+Gen Dent
+Gen Diagn Pathol
+Gen Hosp Psychiatry
+Gen Inf Programme UNISISTNewsl
+Gen Laws R I 1956 R I
+Gen Pharmacol
+Gen Physiol Biophys
+Gen Pract
+Gen Statut N C N C
+Gen Surg Laparosc News
+Gen Syst
+Gencho Hiroshima Igaku
+Gend Action
+Gend CG Newsl
+Gend Dev
+Gend Educ
+Gend Hist
+Gend Med
+Gend Place Cult
+Gend Soc
+Gend Technol Dev
+Gender Issues
+Genders (Austin Tex)
+Gene
+Gene Amplif Anal
+Gene Anal Tech
+Gene Expr
+Gene Expr Patterns
+Gene Geogr
+Gene Ther
+Gene Ther Mol Biol
+Geneeskd Bl
+Geneeskd Gids
+Geneeskd Sport
+Generations
+Genes Brain Behav
+Genes Cells
+Genes Chromosomes Cancer
+Genes Dev
+Genes Funct
+Genes Genet Syst
+Genes Immun
+Genesis
+Genet Anal
+Genet Anal Tech Appl
+Genet Couns
+Genet Eng
+Genet Eng (N Y)
+Genet Epidemiol
+Genet Epidemiol Suppl
+Genet Genet Eng
+Genet Iber
+Genet Med
+Genet Mol Res
+Genet Psychol Monogr
+Genet Res
+Genet Sel Evol
+Genet Soc Gen Psychol Monogr
+Genet Test
+Genet Vaccines Ther
+Genethics News
+Genetic Resour
+Genetica
+Genetics
+Genetika
+Geneva Afr
+Genewatch
+Genitif
+Genitourin Med
+Genome
+Genome Biol
+Genome Inform Ser Workshop Genome Inform
+Genome Res
+Genome Sci Technol
+Genomics
+Genomics Proteomics Bioinformatics
+Genre
+Genus
+Geo Eco Trop
+GeoJournal
+Geoadria
+Geobios Mem Spec
+Geocarto Int
+Geochem J
+Geochem Trans
+Geochim Cosmochim Acta
+Geoforum
+Geog Glas
+Geoge Mason Univ Civ Rights Law J
+Geogr Anal
+Geogr Ann Ser B
+Geogr Ber
+Geogr Bull
+Geogr Cas
+Geogr Helv
+Geogr J
+Geogr Mag
+Geogr Med
+Geogr Med Suppl
+Geogr Obs
+Geogr Perspect
+Geogr Pol
+Geogr Pregl
+Geogr Rech
+Geogr Rev
+Geogr Rev India
+Geogr Rev Jpn
+Geogr Rundsch
+Geogr Shkole
+Geogr Surv
+Geogr Tidsskr
+Geogr Tydschr
+Geogr Vestn
+Geogr Z
+Geographica
+Geographicalia
+Geography
+Geol Mag
+Geol Rundsch
+Geol Soc Am Bull
+Geology
+Geomicrobiol J
+Geophys Res Lett
+George Washington J Int Law Econ
+George Washington Law Rev
+George Wright Forum
+Georget Immgr Law J
+Georget J Leg Ethics
+Georget Mag
+Georgetown Dent J
+Georgetown Law J
+Georgetown Med Bull
+Georgetown Univ Right to Life J
+Georgetown Univ Sch Dent Mirror
+Georgia Law Rev
+Georgia State Bar J
+Georgian Med News
+Geotimes
+Ger Comments
+Ger J Ophthalmol
+Ger Life Lett
+Ger Med
+Ger Med Mon
+Ger Polit
+Ger Rom Monatsschr
+Ger Stud Rev
+Ger Yearb Int Law
+Geriatr Nephrol Urol
+Geriatr Nurs
+Geriatr Nurs (Lond)
+Geriatr Nurs (Minneap)
+Geriatr Nurs Home Care
+Geriatrics
+Germanistik
+Germant Crier
+Gerodontics
+Gerodontology
+Geron
+Gerontion
+Gerontol Clin (Basel)
+Gerontol Geriatr Educ
+Gerontol Soc
+Gerontologia
+Gerontologie
+Gerontologist
+Gerontology
+Gesch Ges
+Gesch Wiss Unterr
+Gesnerus
+Gesnerus Suppl
+Gesund Ing
+Gesundheit
+Gesundheitswesen
+Gewina
+Ghana J Sociol
+Ghana Med J
+Ghana Nurse
+Ghana Off News Bull
+Ghana Soc Sci J
+Gibralfaro
+Gifu Shika Gakkai Zasshi
+Gig Sanit
+Gig Tr Prof Zabol
+Ginecol Clin
+Ginecol Obstet (Lima)
+Ginecol Obstet Mex
+Ginecologia
+Ginekol Pol
+Glamorgan Hist
+Glas Belgrad Hig Inst NR Srb
+Glas Srp Akad Nauka [Med]
+Glas Zavodzdrav
+Glasg Dent J
+Glasgow Med J
+Glasnik
+Glasra
+Glaxo Vol Occas Contrib Sci Art Med
+Glendale Law Rev
+Glia
+Glimpse
+Glob AIDSnews
+Glob Access STD Diagn
+Glob Cent News
+Glob Child Health New Rev
+Glob Environ Change
+Glob Impacts
+Glob Issues
+Glob Planet Change
+Global Biogeochem Cycles
+Global Futures Dig
+Global Health
+Globe
+Glotta
+Glycobiology
+Glycoconj J
+God Vojnomed Akad
+God Zb Med Fak Skopje
+Gold Bull
+Gold Gate Law Rev
+Gold Gate Univ Law Rev
+Gonzaga Law Rev
+Good Housekeeping
+Gott Jahrb
+Gov Inf Q
+Gov Publ Rev
+Gov Relat Note
+Governing
+Grace Hosp Bul
+Grad Fac Philos J
+Graefes Arch Clin Exp Ophthalmol
+Grants Mag
+Grantsmanship Cent News
+Graph Models
+Grassroots Dev
+Gratz Coll Annual Jew Stud
+Gravit Space Biol Bull
+Great Ideas Today
+Great Ormond St J
+Greater Milw Dent Bull
+Greater St Louis Dent Soc Bull
+Greek Econ Rev
+Greek Roman Byz Stud (Cambridge Mass)
+Grenzgeb Wiss
+Groniek
+Ground Water
+Group Dyn
+Group Health J
+Group Pract
+Group Pract J
+Growth
+Growth Change
+Growth Dev Aging
+Growth Factors
+Growth Horm IGF Res
+Growth Regul
+Grud Serdechnososudistaia Khir
+Grudn Khir
+Grundfragen Silikoseforsch
+Gruzlica
+Gt Concern
+Guam Rec
+Guang Ming Ri Bao
+Guang Pu Xue Yu Guang Pu Fen Xi
+Guatem Indig
+Guerr Mond Conflits Contemp
+Guide Prat
+Guigoz Sci Rev
+Guildhall Misc
+Guildhall Stud Lond Hist
+Gujarat Stat Rev
+Gulf Coast Hist Rev
+Gunma J Med Sci
+Guo Li Taiwan Shi Fan Da Xue Di li Yan Jiu Suo Di Li Yan Jiu Bao Gaob
+Guru Nanak J Sociol
+Gut
+Gutenberg Jahrb
+Guthrie Bull
+Guthrie Clin Bull
+Guthrie J Donald Guthrie Found Med Res
+Guttmacher Rep Public Policy
+Guys Hosp Gaz
+Guys Hosp Rep
+Gyenbug Uidai Jabji
+Gyermekgyogyaszat
+Gymnasium
+Gynaecologia
+Gynakol Geburtshilfliche Rundsch
+Gynakol Prax
+Gynakol Rundsch
+Gynakologe
+Gynecol Endocrinol
+Gynecol Invest
+Gynecol Obstet (Paris)
+Gynecol Obstet Fertil
+Gynecol Obstet Invest
+Gynecol Oncol
+Gynecol Prat
+Gynecologie
+Gyogyszereink
+Gyogyszereszet
+HASL Rep
+HBI Rep
+HEC Forum
+HELR Harvard Environ Law Rev
+HIV AIDS Policy Law Rev
+HIV Capsule Rep
+HIV Clin
+HIV Clin Trials
+HIV Hotline
+HIV Inside
+HIV Med
+HIV Prev Plus
+HMO
+HMO Pract
+HNO
+HPB Surg
+HPN Hosp Purch News
+HRC CC J High Resolut Chromatogr Chromatogr Commun
+HRMAGAZINE
+HRSA Careaction
+HSMHA Health Rep
+Habitat Debate
+Habitat Int
+Habitation (Elmsford)
+Hacettepe Bull Med Surg
+Hacettepe Sosyal Bilimler Derg
+Hadassah Mag
+Hades
+Hadtort Kozl
+Haematol Blood Transfus
+Haematol Lat
+Haematologia (Budap)
+Haematologica
+Haemophilia
+Haemostasis
+Hahnemannian
+Halcyon
+Halsburys Statut Engl G B
+Halve Maen
+Hamatol Bluttransfus
+Hamb Arztebl
+Hamdard Islam
+Hamdard Med
+Hamline Law Rev
+Hamostaseologie
+Hampshire
+Hand
+Hand Clin
+Hand Surg
+Handb Exp Pharmacol
+Handchir Mikrochir Plast Chir
+Handchirurgie
+Handel Maatsch Geschied Oudheidk Gent
+Hanguk Kyun Hakoe Chi
+Hanguk Nonghwa Hakhoe Chi
+Hanguk Saenghwahakhoe Chi
+Hanguk Uiyak
+Hansenol Int
+Harb Dent Log
+Harefuah
+Harlem Hosp Bull
+Harlem Hosp Bull (N Y)
+Harm Reduct J
+Harofe Haivri Heb Med J
+Harper Hosp Bull
+Harpers (N Y N Y)
+Hartford Hosp Bull
+Harv AIDS Rev
+Harv Blacklett J
+Harv Blacklett Law J
+Harv Bus Rev
+Harv Civ Rights-Civil Lib Law Rev
+Harv Dent Alumni Bull
+Harv Dent Bull
+Harv Educ Rev
+Harv Health Lett
+Harv Heart Lett
+Harv Hum Rights J
+Harv J Asiat Stud
+Harv J Law Public Policy
+Harv J Law Technol
+Harv J Minor Public Health
+Harv Law Rev
+Harv Libr Bull
+Harv Mag
+Harv Med Alumni Bull
+Harv Mens Health Watch
+Harv Ment Health Lett
+Harv Rev Psychiatry
+Harv Stud Classic Philol
+Harv Theol Rev
+Harv Women's Law J
+Harv Womens Health Watch
+Harvard Int Law J
+Harvard Int Rev
+Harvard J Legis
+Harvey Lect
+Harz Z
+Hasifrut
+Hastane
+Hastings Cent Rep
+Hastings Constit Law Q
+Hastings Int Comp Law Rev
+Hastings Law J
+Hastings Womens Law J
+Hautarzt
+Haw Bar J
+Hawaii Dent J
+Hawaii J Hist
+Hawaii Med J
+Hawaii Nurse
+Hawaii Nurse (Honol)
+Hawaii Nurses Pipeline
+Hawaii Revis Statut Hawaii
+Hayes Hist J
+Head Face Med
+Head Neck
+Head Neck Surg
+Headache
+Heal Light
+Health (London)
+Health (N Y)
+Health Aff
+Health Aff (Millwood)
+Health Bull
+Health Bull (Edinb)
+Health Can Soc
+Health Care (Don Mills)
+Health Care Anal
+Health Care Can
+Health Care Cost Reengineering Rep
+Health Care Dimen
+Health Care Educ
+Health Care Ethics USA
+Health Care Financ Rev
+Health Care Financ Rev Annu Suppl
+Health Care Financ Rev Stat Suppl
+Health Care Financ Trends
+Health Care Food Nutr Focus
+Health Care Innov
+Health Care Law Mon
+Health Care Law Newsl
+Health Care Manag
+Health Care Manag (Frederick)
+Health Care Manag Sci
+Health Care Manage Rev
+Health Care Mark
+Health Care Mark Target Market
+Health Care Newsl
+Health Care Plann Mark
+Health Care Reform Week
+Health Care Secur Saf Manage
+Health Care Strateg Manage
+Health Care Superv
+Health Care Syst
+Health Care Week
+Health Care Women Int
+Health Commun
+Health Commun Informatics
+Health Cost Manage
+Health Data Manag
+Health Devices
+Health Econ
+Health Educ
+Health Educ Behav
+Health Educ Bull
+Health Educ J
+Health Educ Monogr
+Health Educ Q
+Health Educ Rep
+Health Educ Res
+Health Educ Work
+Health Estate
+Health Estate J
+Health Expect
+Health Facil Manage
+Health Forum J
+Health Hum Rights
+Health Ind Today
+Health Inf Manag
+Health Inf Syst Telemed
+Health Info Libr J
+Health Lab Sci
+Health Law Can
+Health Law J
+Health Law News
+Health Law Proj Libr Bull
+Health Law Rev
+Health Law Vigil
+Health Libr Rev
+Health Manag Technol
+Health Manage
+Health Manage Forum
+Health Manage Q
+Health Manpow Lit
+Health Manpow Manage
+Health Manpow Rep
+Health Mark Q
+Health Matrix
+Health Matrix Clevel
+Health Med
+Health Med Care Serv Rev
+Health Millions
+Health News
+Health PAC Bull
+Health People
+Health Perspect
+Health Phys
+Health Place
+Health Plann Manpow Rep
+Health Policy
+Health Policy (New York)
+Health Policy Educ
+Health Policy Plan
+Health Policy Q
+Health Policy Week
+Health Popul Perspect Issues
+Health Pract Physician Assist
+Health Prog
+Health Promot
+Health Promot Int
+Health Promot J Austr
+Health Promot Pract
+Health Psychol
+Health Qual Life Outcomes
+Health Reform Prior Serv
+Health Rep
+Health Res Policy Syst
+Health Risk Soc
+Health Saf Code Annot State Calif Adopt April 7 1939 Calif
+Health Serv J
+Health Serv Manage
+Health Serv Manage Res
+Health Serv Manager
+Health Serv Manpow Rev
+Health Serv Rep
+Health Serv Res
+Health Sex
+Health Soc Care Community
+Health Soc Serv J
+Health Soc Work
+Health Stat Nord Ctries
+Health Stat Q
+Health Syst Lead
+Health Syst Manage
+Health Syst Rev
+Health Technol
+Health Technol Assess
+Health Technol Assess (Rockv)
+Health Technol Assess Rep
+Health Technol Dir
+Health Transit Rev
+Health Trends
+Health Values
+Health Visit
+HealthAction
+Healthc Ala
+Healthc Benchmarks
+Healthc Bottom Line
+Healthc Comput Commun
+Healthc Demand Dis Manag
+Healthc Exec
+Healthc Exec Curr
+Healthc Facil Manag Ser
+Healthc Financ Manage
+Healthc Foodserv
+Healthc Foodserv Mag
+Healthc Forum
+Healthc Forum J
+Healthc Hazard Manage Monit
+Healthc Hazard Mater Manage
+Healthc Hum Resour
+Healthc Hum Resour Spec Rep
+Healthc Inf Manage
+Healthc Inform
+Healthc Leadersh Manag Rep
+Healthc Leadersh Rep
+Healthc Manage Forum
+Healthc Pap
+Healthc Prot Manage
+Healthc Q
+Healthc Strateg
+Healthc Syst Strategy Rep
+Healthc Trends Transit
+Healthcare Benchmarks Qual Improv
+Healthcover
+Healthmarketing
+Healthplan
+Healthscan
+Healthspan
+Healthtexas
+Healthy People 2000 Stat Notes
+Healthy People 2000 Stat Surveill
+Healthy People 2010 Stat Notes
+Hear Res
+Heart
+Heart Advis
+Heart Bull
+Heart Cent Bull (Roslyn)
+Heart Dis
+Heart Dis Stroke
+Heart Fail
+Heart Fail Monit
+Heart Fail Rev
+Heart Lung
+Heart Lung Circ
+Heart Rhythm
+Heart Surg Forum
+Heart Transplant
+Heart Vessels
+Heart Vessels Suppl
+Heartbeat
+Heat Piping Air Cond
+Hefte Unfallheilkd
+Heidelb Jahrb
+Heilkd Heilwege
+Heilkunst
+Heilpadagog Werkbl
+Heimen
+Helicobacter
+Hell Adelphe
+Hell Cheirourgike
+Hell Iatr
+Hell J Nucl Med
+Hell Period Stomat Gnathopathoprosopike Cheir
+Hell Stomatol Chron
+Hellenic J Cardiol
+Hellenike Pyrenike Iatr
+Helmantica
+Helv Chim Acta
+Helv Chir Acta
+Helv Chir Acta Suppl
+Helv Med Acta
+Helv Med Acta Suppl
+Helv Odontol Acta
+Helv Paediatr Acta
+Helv Paediatr Acta Suppl
+Helv Physiol Pharmacol Acta
+Helv Physiol Pharmacol Acta Suppl 1
+Hematol Cell Ther
+Hematol J
+Hematol Oncol
+Hematol Oncol Clin North Am
+Hematol Pathol
+Hematology
+Hematology (Am Soc Hematol Educ Program)
+Hematopathol Mol Hematol
+Hemingway Rev
+Hemispheres
+Hemodial Int
+Hemoglobin
+Hemostase
+Henry E Sigerist Suppl Bull Hist Med
+Henry Ford Hosp Med Bull
+Henry Ford Hosp Med J
+Hepatitis Wkly
+Hepatobiliary Pancreat Dis Int
+Hepatogastroenterology
+Hepatol Res
+Hepatology
+Her Geneal
+Herbal Rev
+Herbarist
+Hercynia
+Hereditas
+Heredity
+Herit Rev
+Hermathena
+Hermes (Wiesb)
+Hernia
+Herpes
+Herz
+Herzschrittmacherther Elektrophysiol
+Hesperian Found News
+Hesperis Tamuda
+Hess Arztebl
+Hess Familienkd
+Hexagon Roche [Engl]
+Hifu
+Hifu To Hinyo Dermatol Urol
+Hifuka Kiyo
+Hifuka No Rinsho
+Hig Cas Hig Mikrobiol Epidemiol Sanit Teh
+Hig Zdraveopaz
+Higashi Nippon Shigaku Zasshi
+High Alt Med Biol
+High Technol (Boston)
+High Technol Law J
+Higiene
+Hillside J Clin Psychiatry
+Him Prir Soedin
+Hindsight
+Hindu
+Hindustan Antibiot Bull
+Hinyokika Kiyo
+Hip
+Hippocampus
+Hippocrates
+Hippocrates (Sausalito)
+Hippokrates
+Hippokrates (Helsinki)
+Hiroshima Daigaku Shigaku Zasshi
+Hiroshima J Med Sci
+Hisp Am Hist Rev
+Hisp J Behav Sci
+Hisp Med
+Hisp Sacra
+Hispania
+Hist Accid Trav
+Hist Afr
+Hist Agric
+Hist Aikak
+Hist Anthropol
+Hist Archaeol
+Hist Ark
+Hist Casopis
+Hist Child Q
+Hist Cienc Saude Manguinhos
+Hist Comput
+Hist Cult
+Hist Demogr
+Hist Demogr Mitt
+Hist Econ Soc
+Hist Educ
+Hist Educ Q
+Hist Eur Ideas
+Hist Fam
+Hist Hosp
+Hist Human Sci
+Hist J
+Hist J Auckl Waikato
+Hist J Mass
+Hist J West Mass
+Hist Jahrb
+Hist Jahrb Stadt Linz
+Hist Litteraturhist Stud
+Hist Mag
+Hist Mag Protestant Episcop Church
+Hist Med
+Hist Med Univ South Ala Coll Med
+Hist Med Vet
+Hist Mes
+Hist Methods
+Hist Mex
+Hist N H
+Hist Nat
+Hist Nauk Biol Med
+Hist Neurosci
+Hist News
+Hist Nurs Bull
+Hist Nurs Soc J
+Hist Pap Can Hist Assoc
+Hist Philos Life Sci
+Hist Polit Econ
+Hist Polit Thought
+Hist Preserv
+Hist Psychiatry
+Hist Psychol
+Hist Rec Aust Sci
+Hist Reflect
+Hist Relig
+Hist Res
+Hist Sci
+Hist Sci (Tokyo)
+Hist Sci Med
+Hist Soc
+Hist Soz Forsch
+Hist Stud
+Hist Stud Phys Biol Sci
+Hist Teacher
+Hist Theory
+Hist Tidskr
+Hist Tidskr Finl
+Hist Tidsskr
+Hist Today
+Hist Vida
+Hist Workshop
+Hist Workshop J
+Hist Z
+Histochem Cell Biol
+Histochem J
+Histochemie
+Histochemistry
+Histoire
+Histoire Soc
+Histol Histopathol
+Histopathology
+Historama
+Historia
+Historia (Argentina)
+Historian
+Historie (Arhus)
+Historiel Foren Arsskr
+History (Lond)
+Hitotsubashi J Law Polit
+Hofstra Law Rev
+Hoitotiede
+Hoja Tisiol
+Hokenfu Zasshi
+Hokkaido Igaku Zasshi
+Hokkaido Shika Ishikai Shi
+Holdsworth Law Rev
+Holist Nurs Pract
+Holistic Assertive Nurse
+Holocaust Genocide Stud
+Home Care Econ
+Home Care Manag
+Home Care Provid
+Home Health Care Serv Q
+Home Health J
+Home Health Rev
+Home Healthc Nurse
+Home Healthc Nurse Manag
+Homemak Mag
+Homeopath Fr
+Homeopathy
+Homeost Health Dis
+Homilet Pastor Rev
+Homme
+Hommes Migr
+Hommes Terres Nord
+Homo
+Hong Kong Law J
+Hong Kong Med J
+Hong Kong Mon Dig Stat
+Honvedorvos
+Hopital
+Hopkins HIV Rep
+Hoppe Seylers Z Physiol Chem
+Horiz Biochem Biophys
+Horiz Med
+Horizon
+Horm Behav
+Horm Metab Res
+Horm Metab Res Suppl
+Horm Res
+Hormoner
+Hormones
+Hormones (Athens Greece)
+Hormoon
+HortScience
+Hortic Rev (Am Soc Hortic Sci)
+Horttechnology
+Horumon To Rinsho
+Hosp Adm (Chic)
+Hosp Adm (New Delhi)
+Hosp Adm Can
+Hosp Admin Curr
+Hosp Admitting Mon
+Hosp Aviat
+Hosp Bond Rev
+Hosp Cap Finance
+Hosp Case Manag
+Hosp Community Psychiatry
+Hosp Corps Q
+Hosp Cost Manag Account
+Hosp Dev
+Hosp Employee Health
+Hosp Eng
+Hosp Entrep Newsl
+Hosp Equip Supplies
+Hosp Ethics
+Hosp Financ Manage
+Hosp Food Nutr Focus
+Hosp Formul
+Hosp Forum
+Hosp Gen (Madr)
+Hosp Gift Shop Manage
+Hosp Guest Relations Rep
+Hosp Hazard Mater Manage
+Hosp Health Netw
+Hosp Health Netw 360
+Hosp Health Serv Adm
+Hosp Health Serv Rev
+Hosp Infect Control
+Hosp J
+Hosp Law Newsl
+Hosp Libr
+Hosp Manage
+Hosp Manage Commun
+Hosp Manage Q
+Hosp Manager
+Hosp Mater Manage
+Hosp Mater Manage Q
+Hosp Med
+Hosp Med Staff
+Hosp Outlook
+Hosp Patient Relat Rep
+Hosp Peer Rev
+Hosp Pharm
+Hosp Physician
+Hosp Pract
+Hosp Pract (Hosp Ed)
+Hosp Pract (Minneap)
+Hosp Pract (Off Ed)
+Hosp Prog
+Hosp Purch Manage
+Hosp Q
+Hosp Rec Study
+Hosp Revenue Rep
+Hosp Risk Manage
+Hosp Secur Saf Manage
+Hosp Strategy Rep
+Hosp Superv
+Hosp Superv Bull
+Hosp Technol Ser
+Hosp Top
+Hosp Trib
+Hosp Trustee
+Hospital (Rio J)
+Hospitalis
+Hospitalist
+Hospitals
+Hospitals (Lond)
+Hotetsu Rinsho
+Houst J Health Law Policy
+Houst J Int Law
+Houst Law Rev
+Houst Lawyer
+Houston Rev
+How Eval Health Programs
+Howard Law J
+Hstc Bull
+Hu Li Yan Jiu
+Hu Li Za Zhi
+Hua Xi Kou Qiang Yi Xue Za Zhi
+Hua Xi Yi Ke Da Xue Xue Bao
+Hua Zhong Shi Fan Da Xue Xue Bao Zi Ran Ke Xue Ban
+Huan Jing Ke Xue
+Huisarts Wet
+Hum Antibodies
+Hum Antibodies Hybridomas
+Hum Behav
+Hum Biol
+Hum Biol Oceania
+Hum Brain Mapp
+Hum Cell
+Hum Commun Res
+Hum Dev
+Hum Ecol
+Hum Ecol Interdiscip J
+Hum Events
+Hum Exp Toxicol
+Hum Factors
+Hum Fertil
+Hum Fertil (Camb)
+Hum Gene Ther
+Hum Genet
+Hum Genet Suppl
+Hum Genome News
+Hum Genomics
+Hum Health Care
+Hum Health Care Int
+Hum Hered
+Hum Immunol
+Hum Life Rev
+Hum Mol Genet
+Hum Mov Sci
+Hum Mutat
+Hum Nat
+Hum Neurobiol
+Hum Nutr Appl Nutr
+Hum Nutr Clin Nutr
+Hum Organ
+Hum Pathol
+Hum Perf Extrem Environ
+Hum Physiol
+Hum Psychopharmacol
+Hum Relat
+Hum Reprod
+Hum Reprod Genet Ethics
+Hum Reprod Update
+Hum Res Rep
+Hum Resour Health
+Hum Resour Manage
+Hum Rights Q
+Hum Stud
+Hum Toxicol
+Human Rights
+Humane Med
+Humangenetik
+Humanismus Tech
+Humanist
+Humanit Rep
+Humanitas
+Humanitas (Monterey N L)
+Humanity Soc
+Humboldt J Soc Relat
+Hunan Yi Ke Da Xue Xue Bao
+Hung Acta Physiol
+Hung Stud Rev
+Huntia
+Huntingt Libr Q
+Hybrid Hybridomics
+Hybridoma
+Hybridoma (Larchmt)
+Hydrobiologia
+Hydrother Physiother
+Hyg Ment
+Hyg Revy
+Hygie
+Hypatia
+Hypertens Pregnancy
+Hypertens Res
+Hypertension
+Hypotenuse
+IADS Newsl
+IAL News
+IAPAC Mon
+IARC Monogr Eval Carcinog Risk Chem Hum
+IARC Monogr Eval Carcinog Risk Chem Hum Suppl
+IARC Monogr Eval Carcinog Risk Chem Man
+IARC Monogr Eval Carcinog Risks Hum
+IARC Monogr Eval Carcinog Risks Hum Suppl
+IARC Sci Publ
+IASSIST Q
+IAVI Rep
+IAWA J
+IBLA
+IBS Materi
+ICAO J
+ICCW J
+ICCW News Bull
+ICD Sci Educ J
+ICMH Newsl
+ICPD 94
+ICRS J Int Res Commun
+ICRS Med Rep
+ICSSR Newsl
+IDAA Commun
+IDEA J Law Technol
+IDO Rep
+IDRC Rep
+IDS Bull
+IDrugs
+IEE Proc Nanobiotechnol
+IEEE Comput Graph Appl
+IEEE Eng Med Biol Mag
+IEEE Instrum Meas Mag
+IEEE Netw
+IEEE Pervasive Comput
+IEEE Sens J
+IEEE Trans Acoust
+IEEE Trans Aerosp Electron Syst
+IEEE Trans Biomed Eng
+IEEE Trans Comput
+IEEE Trans Image Process
+IEEE Trans Inf Technol Biomed
+IEEE Trans Magn
+IEEE Trans Med Imaging
+IEEE Trans Nanobioscience
+IEEE Trans Neural Netw
+IEEE Trans Neural Syst Rehabil Eng
+IEEE Trans Nucl Sci
+IEEE Trans Pattern Anal Mach Intell
+IEEE Trans Plasma Sci IEEE Nucl Plasma Sci Soc
+IEEE Trans Rehabil Eng
+IEEE Trans Rob Autom
+IEEE Trans Syst Man Cybern
+IEEE Trans Syst Man Cybern A Syst Hum
+IEEE Trans Syst Man Cybern B Cybern
+IEEE Trans Syst Man Cybern C Appl Rev
+IEEE Trans Ultrason Ferroelectr Freq Control
+IEEE Trans Vis Comput Graph
+IFO Stud
+IGCC News
+IHRIM
+IHS Prim Care Provid
+IIASA Rep
+IIC Int Rev Ind Prop Copyr Law
+IIPS Newsl
+ILAR J
+ILSA J Int Law
+IMA J Math Appl Med Biol
+IME bull
+IMF Surv
+IMJ Ill Med J
+IMS Ind Med Surg
+INS Rep
+INSTRAW News
+IPPA Newsl
+IPPF Eur Reg Inf
+IPPF Med Bull
+IPPF News
+IPPF Open File
+IPPF WHR News Serv
+IRB
+IRCS J Med Sci
+IRCS Med Sci
+IRCS Med Sci Biomed Technol
+IRCS Med Sci Cardiovasc Syst
+IRE Trans Med Electron
+ISA Trans
+ISL Law Rev
+IUBMB Life
+IUSSP Pap
+Icarus
+Icea News
+Icmr Bull
+Icsu Rev
+Ida Code Ida
+Ida Law Rev
+Ideas Prod
+Ideggyogy Sz
+Ig Mod
+Ig Sanita Pubbl
+Ig Sanita Pubblica Collana Monogr
+Igaku Butsuri
+Igaku Kenkyu
+Igaku To Seibutsugaku
+Igakushi Kenkyu
+Igiena
+Ilar News
+Ill Bar J
+Ill Classic Stud
+Ill Dent J
+Ill Med J
+Ill Res
+Ill Revis Statut Ill Ill
+Illimani
+Image (IN)
+Image J Nurs Sch
+Images Marquette Univ Dent Reflections
+Imago Mundi
+Imbonezamuryango
+Immigr Minor
+Immun Ageing
+Immun Infekt
+Immunity
+Immunobiol Suppl
+Immunobiology
+Immunochemistry
+Immunodefic Rev
+Immunodeficiency
+Immunogenetics
+Immunohematol
+Immunol Allergy Clin North Am
+Immunol Cell Biol
+Immunol Clin
+Immunol Commun
+Immunol Invest
+Immunol Lett
+Immunol Res
+Immunol Rev
+Immunol Ser
+Immunol Suppl
+Immunol Today
+Immunology
+Immunome Res
+Immunomethods
+Immunopharmacol Immunotoxicol
+Immunopharmacology
+Immunotechnology
+Impact HIV
+Impact Sci Soc
+Implant Dent
+Implant Soc
+Implantologist
+Important Adv Oncol
+Imprensa Medica
+Impressions (Orange)
+Imprimatur
+Imprint
+Imprint (N Y N Y 1976)
+Improv Hum Performance Q
+Impulse (Sydney)
+In Fire Ethics
+In Health
+In Point Fact
+In Pract
+In Silico Biol
+In Vitr Mol Toxicol
+In Vitro
+In Vitro Cell Dev Biol
+In Vitro Cell Dev Biol Anim
+In Vitro Cell Dev Biol Plant
+In Vitro Monogr
+In Vivo
+InTouch
+Inchiesta
+Ind Archaeol Rev
+Ind Eng
+Ind Eng Chem Res
+Ind Free China
+Ind Health
+Ind Health Care
+Ind Health Care (Cambridge Ma)
+Ind Health Mon
+Ind Hyg Newsl
+Ind Labor Relat Rev
+Ind Med Gaz
+Ind Med Surg
+Ind Rob
+Ind Week
+Indent [Engl]
+Indexer
+India Q
+Indian Arch
+Indian Econ J
+Indian Econ Rev
+Indian Econ Soc Hist Rev
+Indian Heart J
+Indian Heart J Teach Ser
+Indian Hist
+Indian J Am Stud
+Indian J Anim Sci
+Indian J Behav
+Indian J Biochem
+Indian J Biochem Biophys
+Indian J Cancer
+Indian J Chem
+Indian J Chest Dis
+Indian J Chest Dis Allied Sci
+Indian J Child Health
+Indian J Clin Psychol
+Indian J Dent Res
+Indian J Dermatol
+Indian J Dermatol Venereol Leprol
+Indian J Econ
+Indian J Environ Health
+Indian J Exp Biol
+Indian J Gastroenterol
+Indian J Gend Stud
+Indian J Hist Med
+Indian J Hist Sci
+Indian J Hosp Pharm
+Indian J Ind Relat
+Indian J Lepr
+Indian J Malariol
+Indian J Matern Child Health
+Indian J Med Educ
+Indian J Med Ethics
+Indian J Med Microbiol
+Indian J Med Res
+Indian J Med Sci
+Indian J Med Surg
+Indian J Nutr Diet
+Indian J Ophthalmol
+Indian J Otolaryngol
+Indian J Pathol Bacteriol
+Indian J Pathol Microbiol
+Indian J Pediatr
+Indian J Pharm
+Indian J Pharm Sci
+Indian J Pharmacol
+Indian J Phys Anthropol Hum Genet
+Indian J Physiol Pharmacol
+Indian J Prev Soc Med
+Indian J Psychiatry
+Indian J Psychol
+Indian J Public Adm
+Indian J Public Health
+Indian J Quant Econ
+Indian J Sex Transm Dis
+Indian J Soc Res
+Indian J Soc Sci
+Indian J Soc Work
+Indian J Surg
+Indian Med J
+Indian Med Trib
+Indian Pediatr
+Indian Pol Sci Rev
+Indian Pract
+Indian Psychol Rev
+Indian Sociol Bull
+Indian Vet J
+Indiana Code Indiana
+Indiana Folkl
+Indiana Int Comp Law Rev
+Indiana Law J
+Indiana Law Rev
+Indiana Mag Hist
+Indiana Med
+Indiana Med Hist Q
+Indiana Nurse
+Indiana Soc Stud Q
+Indicator
+Individ Psychol
+Indo Iran J
+Indo Koten Kenkyu
+Indones J Geogr
+Indones Q
+Indoor Air
+Inf Behav
+Inf Demogr
+Inf Dent
+Inf Geogr
+Inf Hist
+Inf Hist Art
+Inf Litt
+Inf Manage
+Inf Med Paramed (Montreal)
+Inf Medicas
+Inf Odontostomatol
+Inf Orthod Kieferorthop
+Inf Priv
+Inf Process Med Imaging
+Inf Psychiatr
+Inf Raumentwickl
+Inf Sci (Ny)
+Inf Summ
+Infancy
+Infant Ment Health J
+Infants Young Child
+Infanz Anorm
+Infect Agents Dis
+Infect Control
+Infect Control Can
+Infect Control Dig
+Infect Control Hosp Epidemiol
+Infect Control Rounds
+Infect Control Urol Care
+Infect Control Wkly
+Infect Dis
+Infect Dis Clin North Am
+Infect Dis Obstet Gynecol
+Infect Genet Evol
+Infect Immun
+Infect Med
+Infection
+Infertility
+Infez Med
+Infirm Aux
+Infirm Can
+Infirm Fr
+Infirm Haiti
+Infirm Que
+Infirmiere
+Infirmiers
+Inflamm Allergy Drug Targets
+Inflamm Bowel Dis
+Inflamm Res
+Inflammation
+Inflammopharmacology
+Info Trends
+Infocare
+Inform
+Inform Prim Care
+Inforum
+Infrared Phys
+Infusionsther Klin Ernahr
+Infusionsther Klin Ernahr Sonderh
+Infusionsther Transfusionsmed
+Infusionstherapie
+Ingu Pogon Nonjip
+Ingu munje nonjip
+Inhal Toxicol
+Inhaled Part
+Initiat Reprod Health Policy
+Initiatives Popul
+Inj Control Saf Promot
+Inj Prev
+Injury
+Inland Seas
+Inn Med
+Innov Oncol Nurs
+Innovations
+Inorg Chem
+Inorg Chim Acta
+Inquiry
+Inquiry (Oslo)
+Insect Biochem
+Insect Biochem Mol Biol
+Insect Mol Biol
+Insectes Sociaux
+Insight
+Inst Vol Feed
+Institutions
+Instr Course Lect
+Insur Couns J
+Insur Law J
+Insur Math Econ
+Int Abstr Surg
+Int Adv Surg Oncol
+Int Aff
+Int Anesthesiol Clin
+Int Angiol
+Int Arch Allergy Appl Immunol
+Int Arch Allergy Immunol
+Int Arch Arbeitsmed
+Int Arch Gewerbepathol Gewerbehyg
+Int Arch Occup Environ Health
+Int Asianforum
+Int Biodeterior Biodegradation
+Int Braz J Urol
+Int Child Health
+Int Clin Psychopharmacol
+Int Comp Law Q
+Int Congr Ser
+Int Demogr
+Int Dent J
+Int Dev Rev
+Int Dig Health Legis
+Int Disabil Stud
+Int Drug Ther Newsl
+Int Econ Insights
+Int Econ J
+Int Endod J
+Int Fam Plan Perspect
+Int Fam Plann Dig
+Int Fam Plann Persp
+Int Health Dev
+Int Health News
+Int Heart J
+Int Hist Nurs J
+Int Hist Rev
+Int Immunol
+Int Immunopharmacol
+Int J
+Int J Addict
+Int J Adolesc Med Health
+Int J Adolesc Youth
+Int J Adult Orthodon Orthognath Surg
+Int J Adv Couns
+Int J Afr Hist Stud
+Int J Aging Hum Dev
+Int J Air Pollut
+Int J Androl
+Int J Anesth
+Int J Anthropol
+Int J Antimicrob Agents
+Int J Appl Philos
+Int J Appl Radiat Isot
+Int J Artif Organs
+Int J Audiol
+Int J Aviat Psychol
+Int J Behav Dev
+Int J Behav Med
+Int J Behav Nutr Phys Act
+Int J Bifurcat Chaos
+Int J Biochem
+Int J Biochem Cell Biol
+Int J Biol Macromol
+Int J Biol Markers
+Int J Biol Res Pregnancy
+Int J Biol Sci
+Int J Biomed Comput
+Int J Biometeorol
+Int J Cancer
+Int J Cancer Suppl
+Int J Card Imaging
+Int J Cardiol
+Int J Cardiovasc Imaging
+Int J Cardiovasc Intervent
+Int J Cell Cloning
+Int J Child Psychother
+Int J Childbirth Educ
+Int J Chronobiol
+Int J Circumpolar Health
+Int J Clin Exp Hypn
+Int J Clin Lab Res
+Int J Clin Monit Comput
+Int J Clin Oncol
+Int J Clin Pharmacol
+Int J Clin Pharmacol Biopharm
+Int J Clin Pharmacol Res
+Int J Clin Pharmacol Ther
+Int J Clin Pharmacol Ther Toxicol
+Int J Clin Pract
+Int J Clin Pract Suppl
+Int J Cogn Ergon
+Int J Colorectal Dis
+Int J Comp Psychol
+Int J Comp Sociol
+Int J Comput Dent
+Int J Contemp Sociol
+Int J Control
+Int J Cosmet Sci
+Int J Crude Drug Res
+Int J Dent Hyg
+Int J Dent Symp
+Int J Dermatol
+Int J Dev Biol
+Int J Dev Neurosci
+Int J Eat Disord
+Int J Educ Dev
+Int J Emerg Ment Health
+Int J Environ Anal Chem
+Int J Environ Health Res
+Int J Environ Stud
+Int J Epidemiol
+Int J Equilib Res
+Int J Equity Health
+Int J Exp Diabesity Res
+Int J Exp Diabetes Res
+Int J Exp Pathol
+Int J Fertil
+Int J Fertil Menopausal Stud
+Int J Fertil Womens Med
+Int J Food Microbiol
+Int J Food Sci Nutr
+Int J Forecast
+Int J Forensic Dent
+Int J Gastrointest Cancer
+Int J Geogr Inf Sci
+Int J Geriatr Psychiatry
+Int J Group Psychother
+Int J Gynaecol Obstet
+Int J Gynecol Cancer
+Int J Gynecol Pathol
+Int J Health Care Finance Econ
+Int J Health Care Qual Assur
+Int J Health Care Qual Assur Inc Leadersh Health Serv
+Int J Health Educ
+Int J Health Geogr
+Int J Health Plann Manage
+Int J Health Sci
+Int J Health Serv
+Int J Hematol
+Int J Hist Sport
+Int J Hum Comput Interact
+Int J Hum Comput Stud
+Int J Hydrogen Energy
+Int J Hyg Environ Health
+Int J Hyperthermia
+Int J Immunogenet
+Int J Immunopathol Pharmacol
+Int J Immunopharmacol
+Int J Impact Eng
+Int J Impot Res
+Int J Ind Ergon
+Int J Infect Dis
+Int J Infrared Millimeter Waves
+Int J Inj Contr Saf Promot
+Int J Instr Media
+Int J Lang Commun Disord
+Int J Law Fam
+Int J Law Psychiatry
+Int J Legal Med
+Int J Lepr
+Int J Lepr Other Mycobact Dis
+Int J Low Extrem Wounds
+Int J Man Mach Stud
+Int J Mass Emerg Disasters
+Int J Mass Spectrom
+Int J Mass Spectrom Ion Process
+Int J Med Inform
+Int J Med Law
+Int J Med Microbiol
+Int J Med Sci
+Int J Ment Health
+Int J Ment Health Nurs
+Int J Methods Psychiatr Res
+Int J Microcirc Clin Exp
+Int J Middle East Stud
+Int J Mod Phys A
+Int J Mol Med
+Int J Nanomedicine
+Int J Neural Syst
+Int J Neurol
+Int J Neuropharmacol
+Int J Neuropsychiatry
+Int J Neuropsychopharmacol
+Int J Neurosci
+Int J Nucl Med Biol
+Int J Nurs Educ Scholarsh
+Int J Nurs Pract
+Int J Nurs Stud
+Int J Nurs Terminol Classif
+Int J Obes
+Int J Obes (Lond)
+Int J Obes Relat Metab Disord
+Int J Obstet Anesth
+Int J Occup Environ Health
+Int J Occup Health Saf
+Int J Occup Med Environ Health
+Int J Occup Saf Ergon
+Int J Offender Ther Comp Criminol
+Int J Oncol
+Int J Oral Implantol
+Int J Oral Maxillofac Implants
+Int J Oral Maxillofac Surg
+Int J Oral Myol
+Int J Oral Surg
+Int J Orofacial Myology
+Int J Orthod
+Int J Orthod Milwaukee
+Int J Paediatr Dent
+Int J Palliat Nurs
+Int J Pancreatol
+Int J Parasitol
+Int J Partial Hosp
+Int J Pediatr Nephrol
+Int J Pediatr Otorhinolaryngol
+Int J Pept Protein Res
+Int J Periodontics Restorative Dent
+Int J Pharm
+Int J Phytoremediation
+Int J Plant Sci
+Int J Polit
+Int J Popul Geogr
+Int J Primatol
+Int J Prosthodont
+Int J Protein Res
+Int J Psychiatr Nurs Res
+Int J Psychiatry
+Int J Psychiatry Med
+Int J Psychoanal
+Int J Psychoanal Psychother
+Int J Psychol
+Int J Psychol Relig
+Int J Psychophysiol
+Int J Psychosom
+Int J Qual Health Care
+Int J Quantum Chem
+Int J Quantum Chem Quantum Biol Symp
+Int J Rad Appl Instrum B
+Int J Rad Appl Instrum D
+Int J Rad Appl Instrum [A]
+Int J Radiat Biol
+Int J Radiat Biol Relat Stud Phys Chem Med
+Int J Radiat Oncol Biol Phys
+Int J Rehabil Res
+Int J Remote Sens
+Int J STD AIDS
+Int J Sexol
+Int J Soc Econ
+Int J Soc Educ
+Int J Soc Lang
+Int J Soc Psychiatry
+Int J Sociol Fam
+Int J Sociol Soc Policy
+Int J Spec Educ
+Int J Sport Nutr
+Int J Sport Nutr Exerc Metab
+Int J Sport Psychol
+Int J Sports Med
+Int J Study Anim Probl
+Int J Supercomput Appl
+Int J Surg Investig
+Int J Surg Pathol
+Int J Syst Bacteriol
+Int J Syst Evol Microbiol
+Int J Syst Sci
+Int J Technol Aging
+Int J Technol Assess Health Care
+Int J Technol Manag
+Int J Tissue React
+Int J Toxicol
+Int J Trauma Nurs
+Int J Tuberc Lung Dis
+Int J Urban Reg Res
+Int J Urol
+Int J Vitam Nutr Res
+Int J Vitam Nutr Res Suppl
+Int J Womens Stud
+Int J Zoonoses
+Int Labour Rev
+Int Libr Rev
+Int MS J
+Int Marit Health
+Int Microbiol
+Int Migr
+Int Migr Rev
+Int Nurs Rev
+Int Ophthalmol
+Int Ophthalmol Clin
+Int Organ
+Int Orthop
+Int Pharmacopsychiatry
+Int Philos Q
+Int Plann Parenthood News
+Int Psychiatry Clin
+Int Psychogeriatr
+Int Q Community Health Educ
+Int Rec Med
+Int Rec Med Gen Pract Clin
+Int Reg Sci Rev
+Int Rehabil Med
+Int Rescuer
+Int Rev
+Int Rev Connect Tissue Res
+Int Rev Cytol
+Int Rev Cytol Suppl
+Int Rev Educ
+Int Rev Exp Pathol
+Int Rev Immunol
+Int Rev Missions
+Int Rev Mod Sociol
+Int Rev Nat Fam Plann
+Int Rev Neurobiol
+Int Rev Physiol
+Int Rev Psychiatry
+Int Rev Psychoanal
+Int Rev Res Ment Retard
+Int Rev Soc Hist
+Int Rev Trop Med
+Int Sci Rev Ser
+Int Secur
+Int Semin Surg Oncol
+Int Ser Monogr Oral Biol
+Int Soc Sci J
+Int Soc Sci Rev
+Int Soc Secur Rev
+Int Soc Work
+Int Social Rev
+Int Sociol
+Int Spect
+Int Stat Rev
+Int Stud Q
+Int Surg
+Int Symp Fluorid Prev Dent
+Int Tinnitus J
+Int Urogynecol J Pelvic Floor Dysfunct
+Int Urol Nephrol
+Int Wildl
+Int Wound J
+Int Z Angew Physiol
+Int Z Klin Pharmakol Ther Toxikol
+Int Z Phys Med Rehabil
+Int Z Vitam Ernahrungsforsch Beih
+Int Z Vitaminforsch
+Int Z Vitaminforsch Beih
+Integr Cancer Ther
+Integr Environ Assess Manag
+Integr Healthc Rep
+Integr Physiol Behav Sci
+Integr Psychiatry
+Integration
+Intellect
+Intellect Dig
+Intellect Prop J
+Intelligence
+Intensive Care Med
+Intensive Care Nurs
+Intensive Care World
+Intensive Crit Care Nurs
+Intensivmed Prax
+Inter Am Econ Aff
+Inter Des
+Inter Econ
+Inter Nord
+Interact Comput
+Interaction
+Interam Rev Bibliogr
+Interciencia
+Intercom (Des Moines)
+Interdiscip Sci Rev
+Interferon
+Interiors
+Intermediar
+Intern Med
+Intern Med J
+Internet Healthc Strateg
+Internist
+Internist (Berl)
+Internist Prax
+Intersezioni
+Interuniv Fac Work Conf
+Intervirology
+Intus
+Invasion Metastasis
+Inverse Probl
+Invert Neurosci
+Invest Cell Pathol
+Invest Clin
+Invest Econ
+Invest Hist
+Invest Med Int
+Invest New Drugs
+Invest Ophthalmol
+Invest Ophthalmol Vis Sci
+Invest Radiol
+Invest Urol
+Investig Cienc
+Investig Urol (Berl)
+Investor Owned Hosp Rev
+Ion Channels
+Ion Exch Membr
+Iowa Code Annot Iowa
+Iowa Dent Bull
+Iowa Dent J
+Iowa Law Rev
+Iowa Med
+Iowa Orthop J
+Iowa State Univ Vet
+Ir Bank Rev
+Ir Econ Soc Hist
+Ir Geogr
+Ir Hist Stud
+Ir J Med Sci
+Ir J Psychol Med
+Ir Law Rep Mon
+Ir Med J
+Ir Nurs Hosp World
+Ir Nurs News
+Ir Nurses J
+Ir Sword
+Ir Vet J
+Iran J Public Health
+Iran Stud
+Iraq
+Iraqi Dent J
+Irc Med Sci Reprod Obstet Gynecol
+Ire Trans Biomed Electron
+Irish Theol Q
+Iryo
+Isis
+Isl Mag
+Isla
+Islam Q
+Islam Stud
+Isokinet Exerc Sci
+Isot Geosci
+Isotopes Environ Health Stud
+Isozymes Curr Top Biol Med Res
+Isr Ann Psychiatr Relat Discip
+Isr J Chem
+Isr J Dent Sci
+Isr J Med Sci
+Isr J Psychiatry Relat Sci
+Isr J Zool
+Isr Law Rev
+Isr Med Assoc J
+Isr Med J
+Isr Orient Stud
+Isr Soc Sci Res
+Issled Genet
+Issue Brief (Commonw Fund)
+Issue Brief (George Wash Univ Med Cent Ensuring Solut Alcohol Probl)
+Issue Brief (Inst Health Care Costs Solut)
+Issue Brief (Mass Health Policy Forum)
+Issue Brief (Public Policy Inst (Am Assoc Retired Pers))
+Issue Brief Cent Medicare Educ
+Issue Brief Cent Stud Health Syst Change
+Issue Brief George Wash Univ Cent Health Serv Res Policy
+Issue Brief Health Policy Track Serv
+Issue Brief Natl Health Policy Forum
+Issue brief (Grantmakers Health)
+Issues
+Issues (St Louis Mo)
+Issues Brief (Alan Guttmacher Inst)
+Issues Ciminol
+Issues Compr Pediatr Nurs
+Issues Emerg Health Technol
+Issues Ethics
+Issues Health Care
+Issues Health Care Women
+Issues Law Med
+Issues Med Ethics
+Issues Ment Health Nurs
+Issues Reprod Genet Eng
+Issues Sci Technol
+Issues Stud
+Istanbul Tip Fak Mecmuasi
+Istanbul Univ Dishekim Fak Derg
+Istor Biol Issled
+Istor Cas
+Istor Pregl
+Istor SSSR
+Istor Zapisi
+Istorija
+Ital Contemp
+Ital Gen Rev Dermatol
+Ital Gen Rev Otorhinolaryngol
+Ital Heart J
+Ital Heart J Suppl
+Ital J Anat Embryol
+Ital J Biochem
+Ital J Gastroenterol
+Ital J Gastroenterol Hepatol
+Ital J Neurol Sci
+Ital J Orthop Traumatol
+Ital J Orthop Traumatol Suppl
+Ital J Surg Sci
+Ital Medioev Um
+Italy Doc Notes
+Items
+Items Issues
+Itogi Nauk
+Iugosl Physiol Pharmacol Acta
+Iura
+Iustitia
+Iyodenshi To Seitai Kogaku
+Izv Akad Gruz Ssr. Ser Biol
+Izv Akad Nauk Kirg Ssr [Biol]
+Izv Akad Nauk SSSR Biol
+Izv Akad Nauk SSSR Ser Geogr
+Izv Akad Nauk Ser Biol
+Izv Akad Nauk Sssr Otdelenie Khim Nauk
+Izv Bulg Akad Naukite Sofia Nauchnoizsledovatelski Inst protivorak antibiot
+Izv Inst Fiziol (Sofiia)
+Izv Inst Istor BKP
+Izv Meditsinskite Inst Bulg Akad Naukite Sofia Otd Biol Meditsinski Nauki
+Izv Mikrobiol Inst (Sofiia)
+Izv Russ Geogr Obshchestva
+Izv Seriia Fiziol Meditsiny Qazaq SSR Ghylum Akad
+Izv Vses Geogr Obshchestva
+J AAPOS
+J AHIMA
+J AOAC Int
+J APDSA (Tokyo)
+J Abdom Surg
+J Abnorm Child Psychol
+J Abnorm Psychol
+J Abnorm Soc Psychol
+J Acad Gen Dent
+J Acad Hosp Adm
+J Accid Emerg Med
+J Acoust Soc Am
+J Acquir Immune Defic Syndr
+J Acquir Immune Defic Syndr Hum Retrovirol
+J Addict Dis
+J Adhes Dent
+J Adolesc
+J Adolesc Health
+J Adolesc Health Care
+J Adolesc Res
+J Adv Med Surg Nurs
+J Adv Nurs
+J Advert Res
+J Aerosol Med
+J Aerosol Sci
+J Aerosp Eng
+J Affect Disord
+J Afr Econ
+J Afr Hist
+J Afr Law
+J Afr Stud
+J Afro Am Hist Geneal Soc
+J Aging Health
+J Aging Jud
+J Aging Phys Act
+J Aging Soc Policy
+J Aging Stud
+J Agric Environ Ethics
+J Agric Ethics
+J Agric Food Chem
+J Agric Res
+J Agric Saf Health
+J Agric Tradit Bot Appl
+J Agric Trop Bot Appl
+J Agromedicine
+J Air Med Transp
+J Air Pollut Control Assoc
+J Air Waste Manag Assoc
+J Air Waste Manage Assoc
+J Aircr
+J Ala Dent Assoc
+J Albert Einstein Med Cent (Phila)
+J Alcohol Drug Educ
+J All India Dent Assoc
+J All India Ophthalmol Soc
+J Allergy
+J Allergy Clin Immunol
+J Allied Health
+J Altern Complement Med
+J Alzheimers Dis
+J Am Acad Audiol
+J Am Acad Child Adolesc Psychiatry
+J Am Acad Child Psychiatry
+J Am Acad Dermatol
+J Am Acad Gnathol Orthop
+J Am Acad Gold Foil Oper
+J Am Acad Nurse Pract
+J Am Acad Orthop Surg
+J Am Acad Physician Assist
+J Am Acad Psychiatry Law
+J Am Acad Psychoanal
+J Am Acad Psychoanal Dyn Psychiatry
+J Am Acad Relig
+J Am Anim Hosp Assoc
+J Am Assoc Gynecol Laparosc
+J Am Assoc Lab Anim Sci
+J Am Assoc Med Transcr
+J Am Assoc Nephrol Nurses Tech
+J Am Assoc Nurse Anesth
+J Am Aud Soc
+J Am Audiol Soc
+J Am Board Fam Med
+J Am Board Fam Pract
+J Am Chem Soc
+J Am Coll Cardiol
+J Am Coll Dent
+J Am Coll Health
+J Am Coll Health Assoc
+J Am Coll Neuropsychiatr
+J Am Coll Nutr
+J Am Coll Surg
+J Am Coll Toxicol
+J Am Cult
+J Am Dent Assoc
+J Am Dent Assoc (Ed Ital)
+J Am Dent Hyg Assoc
+J Am Dent Soc Anesthesiol
+J Am Diet Assoc
+J Am Ethn Hist
+J Am Folk
+J Am Geriatr Soc
+J Am Health Care
+J Am Health Care Assoc
+J Am Health Policy
+J Am Hist
+J Am Hist Soc Ger Russ
+J Am Inst Homeopath
+J Am Inst Plann
+J Am Insur
+J Am Intraocul Implant Soc
+J Am Med Assoc
+J Am Med Dir Assoc
+J Am Med Inform Assoc
+J Am Med Rec Assoc
+J Am Med Technol
+J Am Med Womens Assoc
+J Am Mosq Control Assoc
+J Am Mosq Control Assoc Suppl
+J Am Oil Chem Soc
+J Am Optom Assoc
+J Am Orient Soc
+J Am Osteopath Assoc
+J Am Paraplegia Soc
+J Am Pharm Assoc
+J Am Pharm Assoc (Wash DC)
+J Am Pharm Assoc (Wash)
+J Am Pharm Assoc Am Pharm Assoc
+J Am Pharm Assoc Am Pharm Assoc (Baltim)
+J Am Phys Ther Assoc
+J Am Plann Assoc
+J Am Podiatr Med Assoc
+J Am Podiatry Assoc
+J Am Psychiatr Nurses Assoc
+J Am Psychoanal Assoc
+J Am Sci Affil
+J Am Soc Echocardiogr
+J Am Soc Geriatr Dent
+J Am Soc Hortic Sci
+J Am Soc Inf Sci
+J Am Soc Mass Spectrom
+J Am Soc Nephrol
+J Am Soc Prev Dent
+J Am Soc Psychosom Dent Med
+J Am Soc Study Orthod
+J Am Stat Assoc
+J Am Stud
+J Am Vener Dis Assoc
+J Am Vet Med Assoc
+J Am Water Works Assoc
+J Ambul Care Manage
+J Ambul Care Mark
+J Anal Psychol
+J Anal Toxicol
+J Anat
+J Anat Soc India
+J Androl
+J Anesth
+J Anglocont Dent Soc
+J Anim Breed Genet
+J Anim Ecol
+J Anim Physiol Anim Nutr (Berl)
+J Anim Sci
+J Anthropol Res
+J Anti Aging Med
+J Antibiot (Tokyo)
+J Antibiot [B]
+J Antimicrob Chemother
+J Anxiety Disord
+J Appl Anim Welf Sci
+J Appl Bacteriol
+J Appl Behav Anal
+J Appl Behav Sci
+J Appl Biochem
+J Appl Biomater
+J Appl Biomech
+J Appl Cardiol
+J Appl Clin Med Phys
+J Appl Commun Res
+J Appl Dev Psychol
+J Appl Genet
+J Appl Gerontol
+J Appl Meas
+J Appl Microbiol
+J Appl Nutr
+J Appl Philos
+J Appl Phys
+J Appl Physiol
+J Appl Probab
+J Appl Psychol
+J Appl Soc Psychol
+J Appl Soc Sci
+J Appl Stat
+J Appl Toxicol
+J Arab Aff
+J Archaeol Sci
+J Archit Plann Res
+J Arid Environ
+J Ariz Hist
+J Ark Med Soc
+J Arnold Arbor
+J Arthroplasty
+J Artif Organs
+J Asian Afr Stud
+J Asian Econ
+J Asian Fed Obstet Gynaecol
+J Asian Nat Prod Res
+J Asian Stud
+J Asiat
+J Asiat Soc
+J Assam Science Society
+J Assist Reprod Genet
+J Assoc Acad Minor Phys
+J Assoc Adv Med Instrum
+J Assoc Care Child Health
+J Assoc Care Child Hosp
+J Assoc Genet Technol
+J Assoc Healthc Philanthr
+J Assoc Hosp Med Educ
+J Assoc Med Illus
+J Assoc Med Women India
+J Assoc Nurses AIDS Care
+J Assoc Off Anal Chem
+J Assoc Pediatr Oncol Nurses
+J Assoc Pers Sev Handicaps
+J Assoc Phys Ment Rehabil
+J Assoc Physicians India
+J Assoc Res Otolaryngol
+J Asthma
+J Asthma Res
+J Atheroscler Res
+J Atheroscler Thromb
+J Athl Train
+J Atmos Chem
+J Atten Disord
+J Aud Res
+J Audio Eng Soc
+J Audiov Media Med
+J Aust Inst Surg Dent Tech
+J Aust Popul Assoc
+J Autism Child Schizophr
+J Autism Dev Disord
+J Autoimmun
+J Autoimmune Dis
+J Auton Nerv Syst
+J Auton Pharmacol
+J Aux Odontol
+J Aviat Med
+J Ayub Med Coll Abbottabad
+J Back Musculoskeletal Rehabil
+J Bacteriol
+J Bahrain Med Soc
+J Balt Stud
+J Baltimore Coll Dent Surg
+J Basic Clin Physiol Pharmacol
+J Basic Microbiol
+J Behav Assess
+J Behav Econ
+J Behav Health Serv Res
+J Behav Med
+J Behav Ther Exp Psychiatry
+J Belge Med Phys
+J Belge Med Phys Rehabil
+J Belge Med Phys Rhumatol
+J Belge Radiol
+J Belge Rhumatol Med Phys
+J Belge Urol
+J Bergen Cty Dent Soc
+J Biblic Ethics Med
+J Bioact Compat Polym
+J Biochem (Tokyo)
+J Biochem Biophys Methods
+J Biochem Mol Biol
+J Biochem Mol Biol Biophys
+J Biochem Mol Toxicol
+J Biochem Toxicol
+J Biocommun
+J Bioenerg
+J Bioenerg Biomembr
+J Bioeng
+J Bioeth
+J Bioeth Inq
+J Biogeogr
+J Bioinform Comput Biol
+J Biol
+J Biol Buccale
+J Biol Chem
+J Biol Educ
+J Biol Inorg Chem
+J Biol Photogr
+J Biol Photogr Assoc
+J Biol Regul Homeost Agents
+J Biol Response Mod
+J Biol Rhythms
+J Biol Sci
+J Biol Stand
+J Biolaw Bus
+J Biolumin Chemilumin
+J Biomater Appl
+J Biomater Dent
+J Biomater Sci Polym Ed
+J Biomech
+J Biomech Eng
+J Biomed Biotechnol
+J Biomed Eng
+J Biomed Inform
+J Biomed Mater Res
+J Biomed Mater Res A
+J Biomed Mater Res B Appl Biomater
+J Biomed Opt
+J Biomed Sci
+J Biomol NMR
+J Biomol Screen
+J Biomol Struct Dyn
+J Biomol Tech
+J Biopharm Stat
+J Biophys Biochem Cytol
+J Biosci
+J Biosci Bioeng
+J Biosoc Sci
+J Biosoc Sci Suppl
+J Biotechnol
+J Black Health Perspect
+J Black Psychol
+J Black Stud
+J Bone Joint Surg
+J Bone Joint Surg Am
+J Bone Joint Surg Br
+J Bone Miner Metab
+J Bone Miner Res
+J Br Endod Soc
+J Br Fer Soc
+J Br Interplanet Soc
+J Br Menopause Soc
+J Br Stud
+J Bras Doencas Torac
+J Bras Ginecol
+J Bras Med
+J Bras Neurol
+J Bras Psiquiatr
+J Bryol
+J Buddh Ethics
+J Burn Care Rehabil
+J Burn Care Res
+J Bus
+J Bus Ethics
+J Bus Psychol
+J Bus Res
+J Bus Soc Stud
+J Bus Strategy
+J CANNT
+J Calif Alliance Ment Ill
+J Calif Dent Assoc
+J Calif Gt Basin Anthropol
+J Can Assoc Radiol
+J Can Church Hist Soc
+J Can Dent Assoc
+J Can Diet Assoc
+J Can Stud
+J Cancer Educ
+J Cancer Epidemiol Prev
+J Cancer Res
+J Cancer Res Clin Oncol
+J Capillary Electrophor
+J Carcinog
+J Card Fail
+J Card Surg
+J Cardiogr
+J Cardiogr Suppl
+J Cardiol
+J Cardiol Suppl
+J Cardiopulm Rehabil
+J Cardiothorac Anesth
+J Cardiothorac Vasc Anesth
+J Cardiovasc Diagn Proced
+J Cardiovasc Electrophysiol
+J Cardiovasc Magn Reson
+J Cardiovasc Manag
+J Cardiovasc Med
+J Cardiovasc Med (Hagerstown)
+J Cardiovasc Nurs
+J Cardiovasc Pharmacol
+J Cardiovasc Pharmacol Ther
+J Cardiovasc Risk
+J Cardiovasc Surg (Torino)
+J Caribb Hist
+J Case Manag
+J Cataract Refract Surg
+J Cathol Nurses Guild Engl Wales
+J Cell Biochem
+J Cell Biochem Suppl
+J Cell Biol
+J Cell Comp Physiol
+J Cell Mol Med
+J Cell Physiol
+J Cell Physiol Suppl
+J Cell Sci
+J Cell Sci Suppl
+J Cereb Blood Flow Metab
+J Charles H. Tweed Int Found
+J Chem Doc
+J Chem Ecol
+J Chem Educ
+J Chem Inf Comput Sci
+J Chem Inf Model
+J Chem Neuroanat
+J Chem Phys
+J Chem Soc
+J Chem Soc Chem Commun
+J Chem Soc [Perkin 1]
+J Chem Technol Biotechnol
+J Chem Thermodyn
+J Chemother
+J Cherokee Stud
+J Chester Archaeol Soc
+J Chiba Med Soc
+J Child Adolesc Psychiatr Ment Health Nurs
+J Child Adolesc Psychiatr Nurs
+J Child Adolesc Psychopharmacol
+J Child Asthma Res Inst Hosp Denver
+J Child Fam Nurs
+J Child Health Care
+J Child Lang
+J Child Neurol
+J Child Psychiatry
+J Child Psychol Psychiatry
+J Child Sex Abus
+J Chin Med Assoc
+J Chin Univ Hong Kong
+J Chir (Paris)
+J Christ Med Assoc India
+J Christ Nurs
+J Chromatogr
+J Chromatogr A
+J Chromatogr B Analyt Technol Biomed Life Sci
+J Chromatogr B Biomed Appl
+J Chromatogr B Biomed Sci Appl
+J Chromatogr Sci
+J Chromatogr Suppl
+J Chronic Dis
+J Church State
+J Circadian Rhythms
+J Clim
+J Clin Anesth
+J Clin Apher
+J Clin Chem Clin Biochem
+J Clin Child Adolesc Psychol
+J Clin Child Psychol
+J Clin Comput
+J Clin Densitom
+J Clin Dent
+J Clin Dysmorphol
+J Clin Endocrinol
+J Clin Endocrinol Metab
+J Clin Eng
+J Clin Epidemiol
+J Clin Ethics
+J Clin Exp Neuropsychol
+J Clin Exp Psychopathol
+J Clin Exp Psychopathol Q Rev Psychiatry Neurol
+J Clin Forensic Med
+J Clin Gastroenterol
+J Clin Hosp Pharm
+J Clin Hypertens
+J Clin Hypertens (Greenwich)
+J Clin Immunol
+J Clin Invest
+J Clin Issues Psychol
+J Clin Lab Anal
+J Clin Lab Immunol
+J Clin Laser Med Surg
+J Clin Microbiol
+J Clin Monit
+J Clin Monit Comput
+J Clin Neuroophthalmol
+J Clin Neurophysiol
+J Clin Neuropsychol
+J Clin Neurosci
+J Clin Nurs
+J Clin Oncol
+J Clin Orthod
+J Clin Pathol
+J Clin Pathol Suppl (Assoc Clin Pathol)
+J Clin Pathol Suppl (R Coll Pathol)
+J Clin Pathol Suppl Coll Pathol
+J Clin Pediatr Dent
+J Clin Periodontol
+J Clin Pharm Ther
+J Clin Pharmacol
+J Clin Pharmacol J New Drugs
+J Clin Pharmacol New Drugs
+J Clin Psychiatry
+J Clin Psychol
+J Clin Psychol Med Settings
+J Clin Psychopharmacol
+J Clin Res Drug Dev
+J Clin Rheumatol
+J Clin Sleep Med
+J Clin Stomatol Conf
+J Clin Ultrasound
+J Clin Virol
+J Cogn Neurosci
+J Coll Gen Pract
+J Coll Physicians Surg Pak
+J Coll Radiol Australas
+J Coll Sci Teach
+J Coll Univ Law
+J Collect Negotiations Public Sect
+J Colloid Interface Sci
+J Colloid Sci
+J Colo Dent Assoc
+J Comb Chem
+J Comb Optim
+J Common Mark Stud
+J Commonw Comp Polit
+J Commonw Polit Stud
+J Commun
+J Commun Dis
+J Commun Disord
+J Commun Inq
+J Community Appl Soc Psychol
+J Community Dev Soc
+J Community Health
+J Community Health Nurs
+J Community Psychol
+J Comp Econ
+J Comp Fam Stud
+J Comp Neurol
+J Comp Pathol
+J Comp Pathol Ther
+J Comp Physiol A Neuroethol Sens Neural Behav Physiol
+J Comp Physiol Psychol
+J Comp Physiol [A]
+J Comp Physiol [B]
+J Comp Psychol
+J Compliance Health Care
+J Comput Aided Mol Des
+J Comput Assist Microsc
+J Comput Assist Tomogr
+J Comput Biol
+J Comput Chem
+J Comput Electron
+J Comput Neurosci
+J Comput Tomogr
+J Conf Workshop
+J Conflict Resolut
+J Conn State Dent Assoc
+J Consult Clin Psychol
+J Consult Psychol
+J Consum Aff
+J Consum Res
+J Contam Hydrol
+J Contemp Afr Stud
+J Contemp Asia
+J Contemp Dent Pract
+J Contemp Ethnogr
+J Contemp Health Law Policy
+J Contemp Hist
+J Contemp Law
+J Contin Educ Health Prof
+J Contin Educ Nurs
+J Contra Costa Dent Soc
+J Contracept
+J Control Release
+J Corp Law
+J Cosmet Laser Ther
+J Cosmet Sci
+J Couns Dev
+J Couns Psychol
+J Craniofac Genet Dev Biol
+J Craniofac Genet Dev Biol Suppl
+J Craniofac Surg
+J Craniomandib Disord
+J Craniomandibular Pract
+J Craniomaxillofac Surg
+J Craniomaxillofac Trauma
+J Creat Behav
+J Crim Justice
+J Crim Law Criminol
+J Crim Law Criminol Police Sci
+J Crit Care
+J Crit Illn
+J Cross Cult Gerontol
+J Cross Cult Psychol
+J Crustacean Biol
+J Cryst Growth
+J Cult Divers
+J Curr Adolesc Med
+J Curr Soc Issues
+J Cutan Laser Ther
+J Cutan Med Surg
+J Cutan Pathol
+J Cyclic Nucleotide Protein Phosphor Res
+J Cyclic Nucleotide Res
+J Cyst Fibros
+J Dairy Res
+J Dairy Sci
+J Deaf Stud Deaf Educ
+J Dent
+J Dent Assoc S Afr
+J Dent Assoc Thai
+J Dent Aux
+J Dent Belge
+J Dent Child
+J Dent Child (Chic)
+J Dent Educ
+J Dent Guid Counc Handicap
+J Dent Handicap
+J Dent Hyg
+J Dent Med
+J Dent Pract Adm
+J Dent Que
+J Dent Res
+J Dent Sch Natl Univ Iran
+J Dent Symp
+J Dent Technol
+J Dermatol
+J Dermatol Sci
+J Dermatol Surg
+J Dermatol Surg Oncol
+J Dermatolog Treat
+J Dev Areas
+J Dev Behav Pediatr
+J Dev Comm
+J Dev Econ
+J Dev Physiol
+J Dev Soc
+J Dev Stud
+J Diabet Complications
+J Diabetes Complications
+J Dial
+J Diarrhoeal Dis Res
+J Digit Imaging
+J Dist Columbia Dent Soc
+J Divorce
+J Divorce & Remarriage
+J Doc
+J Drug Educ
+J Drug Issues
+J Drug Res
+J Drug Target
+J Drugs Dermatol
+J Dtsch Dermatol Ges
+J Dyn Syst Meas Control
+J ECT
+J ET Nurs
+J Early Adolesc
+J Early Repub
+J East Afr Res Dev
+J East Asian Aff
+J East West Stud
+J Ecclesiast Hist
+J Ecol
+J Econ Behav Organ
+J Econ Bus
+J Econ Dyn Control
+J Econ Educ
+J Econ Entomol
+J Econ Hist
+J Econ Issues
+J Econ Lit
+J Econ Perspect
+J Econ Psychol
+J Econ Soc Hist Orient
+J Econ Soc Meas
+J Econ Stud
+J Econ Surv
+J Econ Theory
+J Econom
+J Ecumenical Stud
+J Educ Adm Hist
+J Educ Psychol
+J Educ Res
+J Educ Soc Work
+J Egypt Archaeol
+J Egypt Med Assoc
+J Egypt Natl Canc Inst
+J Egypt Public Health Assoc
+J Egypt Soc Obstet Gynecol
+J Egypt Soc Parasitol
+J Elast
+J Elder Abuse Negl
+J Electr Electron Eng Aust
+J Electrocardiol
+J Electromyogr Kinesiol
+J Electron Microsc (Tokyo)
+J Electron Microsc Tech
+J Embryol Exp Morphol
+J Emerg Med
+J Emerg Nurs
+J Endocrinol
+J Endocrinol Invest
+J Endod
+J Endotoxin Res
+J Endourol
+J Endovasc Surg
+J Endovasc Ther
+J Eng Psychol
+J Enterostomal Ther
+J Entwickl Polit
+J Environ Biol
+J Environ Econ Manage
+J Environ Educ
+J Environ Health
+J Environ Manage
+J Environ Monit
+J Environ Pathol Toxicol
+J Environ Pathol Toxicol Oncol
+J Environ Psychol
+J Environ Qual
+J Environ Radioact
+J Environ Sci (China)
+J Environ Sci Eng
+J Environ Sci Health A Environ Sci Eng Toxic Hazard Subst Control
+J Environ Sci Health A Tox Hazard Subst Environ Eng
+J Environ Sci Health B
+J Environ Sci Health C
+J Environ Sci Health C Environ Carcinog Ecotoxicol Rev
+J Enzyme Inhib
+J Enzyme Inhib Med Chem
+J Epidemiol
+J Epidemiol Biostat
+J Epidemiol Community Health
+J Epilepsy
+J Erie Stud
+J Esthet Dent
+J Esthet Restor Dent
+J Ethics
+J Ethics Law Aging
+J Ethiop Stud
+J Ethn Migr Stud
+J Ethn Stud
+J Ethn Subst Abuse
+J Ethnobiol Ethnomedicine
+J Ethnopharmacol
+J Eukaryot Microbiol
+J Eur Acad Dermatol Venereol
+J Eur Econ Hist
+J Eur Soc Policy
+J Eur Stud
+J Eval Clin Pract
+J Evang Theol Soc
+J Evol Biochem Physiol
+J Evol Biol
+J Existent
+J Exp Anal Behav
+J Exp Anim Sci
+J Exp Biol
+J Exp Bot
+J Exp Child Psychol
+J Exp Clin Assist Reprod
+J Exp Clin Cancer Res
+J Exp Med
+J Exp Med Sci
+J Exp Pathol
+J Exp Pathol (Oxford)
+J Exp Psychol
+J Exp Psychol Anim Behav Process
+J Exp Psychol Appl
+J Exp Psychol Gen
+J Exp Psychol Hum Percept Perform
+J Exp Psychol Learn Mem Cogn
+J Exp Psychol [Hum Learn]
+J Exp Soc Psychol
+J Exp Ther Oncol
+J Exp Zool
+J Exp Zool Suppl
+J Exp Zoolog A Comp Exp Biol
+J Exp Zoolog B Mol Dev Evol
+J Expo Anal Environ Epidemiol
+J Expo Sci Environ Epidemiol
+J Extra Corpor Technol
+J Fac Med Baghdad
+J Fam Cult
+J Fam Econ Issues
+J Fam Health Care
+J Fam Hist
+J Fam Issues
+J Fam Law
+J Fam Nurs
+J Fam Plann Reprod Health Care
+J Fam Pract
+J Fam Psychol
+J Fam Psychother
+J Fam Violence
+J Fam Welf
+J Famil Health Train
+J Feline Med Surg
+J Fem Fam Ther
+J Fish Biol
+J Fish Dis
+J Fla Med Assoc
+J Fla State Dent Soc
+J Fluency Disord
+J Fluid Mech
+J Fluor Chem
+J Fluoresc
+J Foetal Med
+J Food Compost Anal
+J Food Process Eng
+J Food Prot
+J Food Qual
+J Food Sci
+J Foot Ankle Surg
+J Foot Surg
+J Forecast
+J Forensic Med
+J Forensic Odontostomatol
+J Forensic Sci
+J Forensic Sci Soc
+J Formos Med Assoc
+J Fr Med Chir Thorac
+J Fr Ophtalmol
+J Fr Otorhinolaryngol Audiophonol Chir Maxillofac
+J Fr Otorhinolaryngol Chir Maxillofac
+J Free Radic Biol Med
+J Friends Hist Soc
+J Ga Dent Assoc
+J Gambl Stud
+J Gard Hist
+J Gastroenterol
+J Gastroenterol Hepatol
+J Gastrointest Surg
+J Gastrointestin Liver Dis
+J Gay Lesbian Med Assoc
+J Gen Appl Microbiol
+J Gen Educ
+J Gen Intern Med
+J Gen Microbiol
+J Gen Orthod
+J Gen Physiol
+J Gen Psychol
+J Gen Virol
+J Gend Specif Med
+J Gene Med
+J Genet
+J Genet Couns
+J Genet Hum
+J Genet Psychol
+J Geog
+J Geol
+J Geol Soc London
+J Geophys Res
+J Geriatr Phys Ther
+J Geriatr Psychiatry
+J Geriatr Psychiatry Neurol
+J Germantown Hosp
+J Gerontol
+J Gerontol A Biol Sci Med Sci
+J Gerontol B Psychol Sci Soc Sci
+J Gerontol Nurs
+J Gerontol Soc Work
+J Gesch
+J Glass Stud
+J Glaucoma
+J Gnathol
+J Gov Inf
+J Gravit Physiol
+J Great Lakes Res
+J Group Psychother Psychodrama Sociom
+J Gt Houst Dent Soc
+J Guid Control Dyn
+J Gynaecol Endocrinol
+J Gynecol Obstet Biol Reprod (Paris)
+J Gynecol Surg
+J HIV Ther
+J Halacha Contemporary Society
+J Hand Surg [Am]
+J Hand Surg [Br]
+J Hand Ther
+J Hawaii Dent Assoc
+J Hawaii State Dent Assoc
+J Hazard Mater
+J Head Trauma Rehabil
+J Headache Pain
+J Health Adm Educ
+J Health Care Benefits
+J Health Care Chaplain
+J Health Care Finance
+J Health Care Inter Des
+J Health Care Law Policy
+J Health Care Mark
+J Health Care Poor Underserved
+J Health Care Technol
+J Health Commun
+J Health Econ
+J Health Educ
+J Health Hosp Law
+J Health Hum Behav
+J Health Hum Resour Adm
+J Health Hum Serv Adm
+J Health Law
+J Health Manag
+J Health Organ Manag
+J Health Polit Policy Law
+J Health Popul Dev Ctries
+J Health Popul Nutr
+J Health Psychol
+J Health Serv Res Policy
+J Health Soc Behav
+J Health Soc Policy
+J Healthc Des
+J Healthc Educ Train
+J Healthc Inf Manag
+J Healthc Manag
+J Healthc Mater Manage
+J Healthc Prot Manage
+J Healthc Qual
+J Healthc Resour Manag
+J Healthc Risk Manag
+J Heart Lung Transplant
+J Heart Transplant
+J Heart Valve Dis
+J Hell Stud
+J Helminthol
+J Hematother
+J Hematother Stem Cell Res
+J Hepatobiliary Pancreat Surg
+J Hepatol
+J Hepatol Suppl
+J Herb Pharmacother
+J Hered
+J Heterocycl Chem
+J High Resolut Chromatogr
+J Higher Educ
+J Hillside Hosp
+J Hirnforsch
+J Hist Arabic Sci
+J Hist Behav Sci
+J Hist Biol
+J Hist Dent
+J Hist Geogr
+J Hist Ideas
+J Hist Med
+J Hist Med Allied Sci
+J Hist Neurosci
+J Hist Philos
+J Hist Sex
+J Hist Sociol
+J Histochem Cytochem
+J Histotechnol
+J Holist Nurs
+J Home Econ
+J Homosex
+J Hong Kong Branch R Asiat Soc
+J Hortic Sci Biotechnol
+J Hosp Admit Manage
+J Hosp Dent Pract
+J Hosp Infect
+J Hosp Mark
+J Hosp Mark Public Relations
+J Hosp Supply Process Distrib
+J Houston Dist Dent Soc
+J Huazhong Univ Sci Technolog Med Sci
+J Hum Behav Soc Environ
+J Hum Ecol
+J Hum Ergol (Tokyo)
+J Hum Evol
+J Hum Genet
+J Hum Hypertens
+J Hum Lact
+J Hum Nutr
+J Hum Nutr Diet
+J Hum Resour
+J Hum Virol
+J Human Stress
+J Humanist Educ Dev
+J Hyg (Lond)
+J Hyg Epidemiol Microbiol Immunol
+J Hypertens
+J Hypertens Suppl
+J Idaho Acad Sci
+J Image Guid Surg
+J Immigr Health
+J Immigr Minor Health
+J Immune Based Ther Vaccines
+J Immunoassay
+J Immunoassay Immunochem
+J Immunogenet
+J Immunol
+J Immunol Methods
+J Immunopharmacol
+J Immunother
+J Immunother Emphasis Tumor Immunol
+J Imp Commonw Hist
+J In Vitro Fert Embryo Transf
+J Ind Econ
+J Ind Hyg Toxicol
+J Ind Microbiol
+J Ind Microbiol Biotechnol
+J Indian Acad Dent
+J Indian Anthropol Soc
+J Indian Assoc Commun Dis
+J Indian Dent Assoc
+J Indian Forensic Sci
+J Indian Law Inst
+J Indian Med Assoc
+J Indian Med Prof
+J Indian Orthod Soc
+J Indian Soc Pedod Prev Dent
+J Indiana Dent Assoc
+J Indiana State Dent Assoc
+J Indiana State Med Assoc
+J Indianap Dist Dent Soc
+J Individ Psychol
+J Inf Image Manage
+J Infect
+J Infect Chemother
+J Infect Dis
+J Inflamm
+J Inflamm (Lond)
+J Infor Ethics
+J Infus Chemother
+J Infus Nurs
+J Inherit Metab Dis
+J Inorg Biochem
+J Insect Physiol
+J Insect Sci
+J Inst Actuar
+J Inst Chart Account Sri Lanka
+J Inst Econ Res
+J Inst Hosp Eng
+J Inst Med
+J Inst Sterile Serv Manage
+J Inst Theor Econ
+J Insur Med
+J Int Acad Periodontol
+J Int Aff
+J Int Assoc Dent Child
+J Int Assoc Physicians AIDS Care
+J Int Assoc Physicians AIDS Care (Chic Ill)
+J Int Bioethique
+J Int Chir
+J Int Coll Dent (Jpn)
+J Int Coll Surg
+J Int Dev
+J Int Econ
+J Int Fed Clin Chem
+J Int Fed Gynaecol Obstet
+J Int Law Econ
+J Int Med Res
+J Int Neuropsychol Soc
+J Int Trade Econ Dev
+J Int Wine Food Soc
+J Integr Neurosci
+J Intellect Disabil
+J Intellect Disabil Res
+J Intensive Care Med
+J Inter Am Stud World Aff
+J Intercult Stud
+J Interdiscip Hist
+J Interdiscipl Cycle Res
+J Interferon Cytokine Res
+J Interferon Res
+J Intern Med
+J Intern Med Suppl
+J Interpers Violence
+J Interprof Care
+J Interv Card Electrophysiol
+J Interv Cardiol
+J Intraven Nurs
+J Invasive Cardiol
+J Invertebr Pathol
+J Invest Dermatol
+J Invest Surg
+J Investig Allergol Clin Immunol
+J Investig Dermatol Symp Proc
+J Investig Med
+J Iowa Med Soc
+J Iowa State Med Soc
+J Ir Coll Physicians Surg
+J Ir Dent Assoc
+J Ir Med Assoc
+J Jew Communal Serv
+J Jew Med Ethics Halacha
+J Jj Group Hosp Grant Med Coll
+J Jpn Int Econ
+J Jpn Obstet Gynecol Soc
+J Kans Bar Assoc
+J Kans Dent Assoc
+J Kans Entomol Soc
+J Kans Med Soc
+J Kans State Dent Assoc
+J Knee Surg
+J Korean Astron Soc
+J Korean Med Sci
+J Korean Res Soc Dent Hypn
+J Kuwait Med Assoc
+J Ky Dent Assoc
+J Ky Med Assoc
+J Ky State Med Assoc
+J La Dent Assoc
+J La State Med Soc
+J Lab Clin Med
+J Labelled Comp Radiopharm
+J Labelled Compd
+J Labor Econ
+J Labor Res
+J Lanc Cty Hist Soc
+J Lancet
+J Laparoendosc Adv Surg Tech A
+J Laparoendosc Surg
+J Laryngol Otol
+J Laryngol Otol Suppl
+J Laser Appl
+J Lat Am Lore
+J Lat Am Stud
+J Law Econ
+J Law Ethics Dent
+J Law Health
+J Law Med
+J Law Med Ethics
+J Law Policy
+J Law Polit
+J Law Relig
+J Law Soc
+J Law Technol
+J Learn Disabil
+J Leg Med
+J Leg Med (N Y)
+J Legal Educ
+J Legal Hist
+J Legal Stud
+J Legis
+J Leukoc Biol
+J Leukoc Biol Suppl
+J Libert Stud
+J Libr
+J Libr Hist
+J Lipid Mediat
+J Lipid Mediat Cell Signal
+J Lipid Res
+J Liposome Res
+J Liq Chromatogr
+J Lithotr Stone Dis
+J Long Isl Hist
+J Long Term Care Adm
+J Long Term Eff Med Implants
+J Long Term Home Health Care
+J Low Genit Tract Dis
+J Macomb Dent Soc
+J Macroecon
+J Magn Reson
+J Magn Reson B
+J Magn Reson Imaging
+J Maine Dent Assoc
+J Maine Med Assoc
+J Mal Vasc
+J Malays Branch R Asiat Soc
+J Mamm Evol
+J Mammal
+J Mammary Gland Biol Neoplasia
+J Manag Care Pharm
+J Manag Med
+J Manipulative Physiol Ther
+J Marital Fam Ther
+J Mark
+J Mark Ment Health
+J Mark Prof
+J Mark Res
+J Mark Res Soc
+J Marmara Univ Dent Fac
+J Marriage Fam
+J Marriage Fam Couns
+J Mass Dent Soc
+J Mass Spectrom
+J Mater Res
+J Mater Sci Lett
+J Mater Sci Mater Med
+J Matern Fetal Investig
+J Matern Fetal Med
+J Matern Fetal Neonatal Med
+J Math Anal Appl
+J Math Biol
+J Math Chem
+J Math Psychol
+J Math Sociol
+J Maxillofac Orthop
+J Maxillofac Surg
+J Md State Dent Assoc
+J Mechanochem Cell Motil
+J Med
+J Med (Oporto)
+J Med Assoc Eire
+J Med Assoc Ga
+J Med Assoc Prev War
+J Med Assoc State Ala
+J Med Assoc Thai
+J Med Besancon
+J Med Biogr
+J Med Bord
+J Med Caen
+J Med Chem
+J Med Chir Prat
+J Med Dent Sci
+J Med Educ
+J Med Eng Technol
+J Med Entomol
+J Med Entomol Suppl
+J Med Ethics
+J Med Food
+J Med Genet
+J Med Humanit
+J Med Humanit Bioeth
+J Med Internet Res
+J Med Invest
+J Med Lab Technol
+J Med Leg Droit Med
+J Med Liban
+J Med Libr Assoc
+J Med Lyon
+J Med Microbiol
+J Med Nantes
+J Med Paris
+J Med Pernamb
+J Med Pharm Chem
+J Med Philos
+J Med Pract Manage
+J Med Primatol
+J Med Sci
+J Med Screen
+J Med Soc N J
+J Med Strasb
+J Med Syst
+J Med Technol
+J Med Vet Mycol
+J Med Virol
+J Medicaid Manage
+J Mediev Hist
+J Mediev Renaiss Stud
+J Mem Lang
+J Memb Sci
+J Membr Biol
+J Ment Defic Res
+J Ment Health Adm
+J Ment Health Policy Econ
+J Ment Sci
+J Ment Subnorm
+J Mercer Dent Soc
+J Mex Am Hist
+J Mich Dent Assoc
+J Mich State Dent Assoc
+J Mich State Med Soc
+J Microbiol
+J Microbiol Biotechnol
+J Microbiol Epidemiol Immunobiol
+J Microbiol Immunol Infect
+J Microbiol Methods
+J Microcolumn Sep
+J Microelectromech Syst
+J Microencapsul
+J Microgr
+J Microsc
+J Microsc (Paris)
+J Microsc Biol Cell
+J Microsurg
+J Microw Power
+J Microw Power Electromagn Energy
+J Midwifery Womens Health
+J Mil Hist
+J Minim Invasive Gynecol
+J Minn Acad Sci
+J Miss Dent Assoc
+J Miss Hist
+J Miss State Med Assoc
+J Mo Bar
+J Mo Dent Assoc
+J Mod Afr Stud
+J Mod Hist
+J Mol Appl Genet
+J Mol Biol
+J Mol Cell Cardiol
+J Mol Cell Immunol
+J Mol Diagn
+J Mol Endocrinol
+J Mol Evol
+J Mol Graph
+J Mol Graph Model
+J Mol Histol
+J Mol Med
+J Mol Microbiol Biotechnol
+J Mol Model (Online)
+J Mol Neurosci
+J Mol Recognit
+J Mol Spectrosc
+J Mol Struct
+J Molluscan Stud
+J Moral Educ
+J Mormon Hist
+J Morphol
+J Mot Behav
+J Mt Sinai Hosp N Y
+J Multicult Couns Devel
+J Multicult Soc Work
+J Muscle Res Cell Motil
+J Musculoskelet Neuronal Interact
+J Music Ther
+J N C Dent Soc
+J N H Dent Soc
+J N J Dent Assoc
+J N J Dent Hyg Assoc
+J N J State Dent Soc
+J N Y State Nurses Assoc
+J N Y State Sch Nurse Teach Assoc
+J N Z Soc Periodontol
+J NIH Res
+J Nal Assoc
+J Nanobiotechnology
+J Nanosci Nanotechnol
+J Nat Hist Mus Inst Chiba
+J Nat Prod
+J Nat Toxins
+J Natl Analg Soc
+J Natl Assoc Chiropodists
+J Natl Assoc Hosp Dev
+J Natl Assoc Priv Psychiatr Hosp
+J Natl Assoc Seventh Day Advent Dent
+J Natl Black Nurses Assoc
+J Natl Cancer Inst
+J Natl Cancer Inst Monogr
+J Natl Compr Canc Netw
+J Natl Malar Soc
+J Natl Med Assoc
+J Near East Stud
+J Nebr Dent Assoc
+J Negat Results Biomed
+J Negro Educ
+J Negro Hist
+J Nematol
+J Nephrol
+J Nephrol Nurs
+J Nerv Ment Dis
+J Neural Eng
+J Neural Transm
+J Neural Transm Gen Sect
+J Neural Transm Park Dis Dement Sect
+J Neural Transm Suppl
+J Neural Transplant
+J Neural Transplant Plast
+J Neurobiol
+J Neurochem
+J Neurocytol
+J Neuroendocrinol
+J Neuroengineering Rehabil
+J Neurogenet
+J Neuroimaging
+J Neuroimmunol
+J Neuroimmunol Suppl
+J Neuroinflammation
+J Neurol
+J Neurol Neurosurg Psychiatry
+J Neurol Phys Ther
+J Neurol Rehabil
+J Neurol Sci
+J Neurolinguistics
+J Neurooncol
+J Neuroophthalmol
+J Neuropathol Exp Neurol
+J Neurophysiol
+J Neuropsychiatr
+J Neuropsychiatry Clin Neurosci
+J Neuroradiol
+J Neurosci
+J Neurosci Methods
+J Neurosci Nurs
+J Neurosci Res
+J Neurosurg
+J Neurosurg Anesthesiol
+J Neurosurg Nurs
+J Neurosurg Sci
+J Neurosurg Spine
+J Neurotrauma
+J Neurovirol
+J Neurovisc Relat
+J New Drugs
+J Newark Beth Isr Hosp
+J Niger Assoc Dent Stud
+J Nigeria Med Assoc
+J Nihon Univ Sch Dent
+J Nippon Med Sch
+J North La Hist Assoc
+J Nucl Biol Med
+J Nucl Cardiol
+J Nucl Med
+J Nucl Med Allied Sci
+J Nucl Med Technol
+J Nurs Adm
+J Nurs Care
+J Nurs Care Qual
+J Nurs Educ
+J Nurs Ethics
+J Nurs Hist
+J Nurs Law
+J Nurs Manag
+J Nurs Meas
+J Nurs Qual Assur
+J Nurs Res
+J Nurs Scholarsh
+J Nurs Staff Dev
+J Nurse Midwifery
+J Nurses Staff Dev
+J Nutr
+J Nutr Biochem
+J Nutr Diet
+J Nutr Educ
+J Nutr Educ Behav
+J Nutr Elder
+J Nutr Health Aging
+J Nutr Sci Vitaminol (Tokyo)
+J Obstet Gynaecol
+J Obstet Gynaecol Br Commonw
+J Obstet Gynaecol Br Emp
+J Obstet Gynaecol Can
+J Obstet Gynaecol East Cent Africa
+J Obstet Gynaecol India
+J Obstet Gynaecol Res
+J Obstet Gynecol Neonatal Nurs
+J Occup Environ Hyg
+J Occup Environ Med
+J Occup Health
+J Occup Health Psychol
+J Occup Med
+J Occup Rehabil
+J Ocul Pharmacol
+J Ocul Pharmacol Ther
+J Odontol Conserv
+J Off Repub Fr Ed Lois Decrets
+J Off Stat
+J Okla Dent Assoc
+J Okla State Dent Assoc
+J Okla State Med Assoc
+J Oncol Manag
+J Oncol Pharm Pract
+J Ont Dent Assoc
+J Oper Res Soc
+J Oper Room Res Inst
+J Ophthalmic Nurs Technol
+J Ophthalmic Photogr
+J Opt Soc Am
+J Opt Soc Am A
+J Opt Soc Am A Opt Image Sci Vis
+J Opt Soc Am B
+J Oral Implant Transplant Surg
+J Oral Implantol
+J Oral Maxillofac Surg
+J Oral Med
+J Oral Pathol
+J Oral Pathol Med
+J Oral Rehabil
+J Oral Sci
+J Oral Surg
+J Oral Surg Anesth Hosp Dent Serv
+J Oral Ther Pharmacol
+J Oreg Dent Assoc
+J Org Chem
+J Organomet Chem
+J Orofac Orthop
+J Orofac Pain
+J Orthod
+J Orthop Res
+J Orthop Sci
+J Orthop Sports Phys Ther
+J Orthop Surg (Hong Kong)
+J Orthop Trauma
+J Osaka Dent Univ
+J Osaka Univ Dent Sch
+J Oslo City Hosp
+J Osteopath (Kirksvill)
+J Otolaryngol
+J Otolaryngol Soc Aust
+J Otolaryngol Suppl
+J Otto Rank Assoc
+J Outcome Meas
+J Pa Acad Sci
+J Pac Hist
+J Paediatr Child Health
+J Paediatr Dent
+J Pain
+J Pain Palliat Care Pharmacother
+J Pain Symptom Manage
+J Pak Med Assoc
+J Paleolimnol
+J Paleontol
+J Palest Stud
+J Palliat Care
+J Palliat Med
+J Parapsychol
+J Parasitol
+J Parenter Drug Assoc
+J Parenter Sci Technol
+J Parodontol
+J Pastoral Care
+J Pastoral Care Counsel
+J Pat Off Soc
+J Pat Trademark Off Soc
+J Pathol
+J Pathol Bacteriol
+J Patient Acc Manage
+J Peace Res
+J Peasant Stud
+J Pediatr
+J Pediatr (Rio J)
+J Pediatr Adolesc Gynecol
+J Pediatr Endocrinol
+J Pediatr Endocrinol Metab
+J Pediatr Gastroenterol Nutr
+J Pediatr Health Care
+J Pediatr Hematol Oncol
+J Pediatr Nurs
+J Pediatr Oncol Nurs
+J Pediatr Ophthalmol
+J Pediatr Ophthalmol Strabismus
+J Pediatr Orthop
+J Pediatr Orthop B
+J Pediatr Perinat Nutr
+J Pediatr Psychol
+J Pediatr Surg
+J Pedod
+J Pept Res
+J Pept Sci
+J Perianesth Nurs
+J Perinat Med
+J Perinat Neonatal Nurs
+J Perinatol
+J Periodontal Res
+J Periodontal Res Suppl
+J Periodontol
+J Perioper Pract
+J Peripher Nerv Syst
+J Pers
+J Pers Assess
+J Pers Soc Psychol
+J Personal Disord
+J Pharm Belg
+J Pharm Biomed Anal
+J Pharm Law
+J Pharm Mark Manage
+J Pharm Pharm Sci
+J Pharm Pharmacol
+J Pharm Sci
+J Pharm Sci Technol
+J Pharm Technol
+J Pharmacobiodyn
+J Pharmacokinet Biopharm
+J Pharmacokinet Pharmacodyn
+J Pharmacol
+J Pharmacol Exp Ther
+J Pharmacol Methods
+J Pharmacol Sci
+J Pharmacol Toxicol Methods
+J Pharmacother
+J Phila Cty Dent Soc
+J Philipp Dent Assoc
+J Philipp Dev
+J Philipp Fed Priv Med Pract
+J Philipp Med Assoc
+J Philipp Stat
+J Philos
+J Photochem Photobiol B
+J Phycol
+J Phys B At Mol Opt Phys
+J Phys Chem
+J Phys Chem A Mol Spectrosc Kinet Environ Gen Theory
+J Phys Chem B Condens Matter Mater Surf Interfaces Biophys
+J Phys Chem B Mater Surf Interfaces Biophys
+J Phys Colloid Chem
+J Phys Condens Matter
+J Phys D Appl Phys
+J Phys G Nucl Part Phys
+J Phys [E]
+J Physical Soc Japan
+J Physicians Assoc AIDS Care
+J Physiol
+J Physiol (Paris)
+J Physiol Anthropol
+J Physiol Anthropol Appl Human Sci
+J Physiol Biochem
+J Physiol Paris
+J Physiol Pharmacol
+J Physiol Sci
+J Physiol Suppl (Paris)
+J Pierre Fauchard Acad
+J Pineal Res
+J Plant Biol
+J Plant Growth Regul
+J Plant Nutr
+J Plant Physiol
+J Plant Res
+J Plast Reconstr Aesthet Surg
+J Plast Reconstr Surg Nurs
+J Policy Anal Manage
+J Policy Hist
+J Polit
+J Polit Econ
+J Polit Philos
+J Polit Sci
+J Polym Sci A
+J Polym Sci [A1]
+J Polym Sci [B]
+J Polyn Soc
+J Pop Cult
+J Popul
+J Popul Assoc Korea
+J Popul Econ
+J Popul Res
+J Post Anesth Nurs
+J Post Keynes Econ
+J Postgrad Med
+J Pract Nurs
+J Prat Rev Gen Clin Ther
+J Presbyt Hist
+J Prev Dent
+J Prev Interv Community
+J Prev Med Pub Health
+J Prev Psychiatry
+J Prev Soc Med
+J Prim Prev
+J Prison Health
+J Prison Jail Health
+J Prod Liability
+J Prof Nurs
+J Prof Serv Mark
+J Proj Tech
+J Proj Tech Pers Assess
+J Prosthet Dent
+J Prosthodont
+J Protein Chem
+J Proteome Res
+J Protozool
+J Psicol
+J Psychedelic Drugs
+J Psychiatr Educ
+J Psychiatr Ment Health Nurs
+J Psychiatr Nurs
+J Psychiatr Nurs Ment Health Serv
+J Psychiatr Pract
+J Psychiatr Res
+J Psychiatr Soc Work
+J Psychiatr Treat Eval
+J Psychiatry Law
+J Psychiatry Neurosci
+J Psychoactive Drugs
+J Psychohist
+J Psychol
+J Psychol Human Sex
+J Psychol Judaism
+J Psychol Norm Pathol (Paris)
+J Psychol Theol
+J Psycholinguist Res
+J Psychopathol Behav Assess
+J Psychopharmacol
+J Psychosoc Nurs Ment Health Serv
+J Psychosoc Oncol
+J Psychosom Obstet Gynaecol
+J Psychosom Res
+J Psychother Pract Res
+J Public Econ
+J Public Health (Bangkok)
+J Public Health (Oxf)
+J Public Health Dent
+J Public Health Manag Pract
+J Public Health Med
+J Public Health Policy
+J Public Policy
+J Qual Assur
+J Qual Clin Pract
+J Quant Econ
+J Quant Spectrosc Radiat Transf
+J R Anthropol Inst
+J R Army Med Corps
+J R Asiat Soc GB Irel
+J R Astron Soc Can
+J R Aust Hist Soc
+J R Coll Gen Pract
+J R Coll Gen Pract Occas Pap
+J R Coll Physicians Edinb
+J R Coll Physicians Lond
+J R Coll Surg Edinb
+J R I State Dent Soc
+J R Inst Public Health
+J R Microsc Soc
+J R Nav Med Serv
+J R Sanit Inst
+J R Soc Antiq Irel
+J R Soc Arts
+J R Soc Health
+J R Soc Med
+J R Soc West Aust
+J R Stat Soc Ser A Stat Soc
+J R Stat Soc Ser C Appl Stat
+J R Stat Soc [Ser A]
+J Radiat Res (Tokyo)
+J Radiol
+J Radiol Electrol Arch Electr Medicale
+J Radiol Electrol Med Nucl
+J Radiol Prot
+J Recept Res
+J Recept Signal Transduct Res
+J Reconstr Microsurg
+J Reform Jud
+J Refract Corneal Surg
+J Refract Surg
+J Refug Stud
+J Reg Policy
+J Reg Sci
+J Rehabil
+J Rehabil Med
+J Rehabil R D
+J Rehabil Res Dev
+J Rehabil Res Dev Clin Suppl
+J Relig
+J Relig Afr
+J Relig Aging
+J Relig Ethics
+J Relig Gerontol
+J Relig Health
+J Relig Hist
+J Ren Nutr
+J Renin Angiotensin Aldosterone Syst
+J Reprod Dev
+J Reprod Fertil
+J Reprod Fertil Abstr Ser
+J Reprod Fertil Suppl
+J Reprod Immunol
+J Reprod Infant Psychol
+J Reprod Med
+J Res Adolesc
+J Res Educ Indian Med
+J Res Indian Med
+J Res Natl Inst Stand Technol
+J Res Pers
+J Res Read
+J Respir Dis
+J Reticuloendothel Soc
+J Rheumatol
+J Rheumatol Suppl
+J Risk Insur
+J Risk Uncertain
+J Robot Syst
+J Rocky Mt Analg Soc
+J Rocky Mt Mediev Renaiss Assoc
+J Rural Dev
+J Rural Health
+J Rural Stud
+J Rutgers Univ Libr
+J S Afr Logop Soc
+J S Afr Speech Hear Assoc
+J S Afr Vet Assoc
+J S Afr Vet Med Assoc
+J S C Med Assoc
+J SOGC
+J Safety Res
+J San Antonio Dent Soc
+J San Diego Hist
+J Sch Health
+J Sch Nurs
+J Sch Psychol
+J Sci Food Agric
+J Sci Ind Res (C)
+J Sci Ind Res (India)
+J Sci Instrum
+J Sci Med Lille
+J Sci Med Sport
+J Sci Soc Thailand
+J Sci Study Relig
+J Scott Labour Hist Soc
+J Seattle Dist Dent Soc
+J Seattle King Cty Dent Soc
+J Sediment Petrol
+J Sediment Res A Sediment Petrol Process
+J Semit Stud
+J Sens Stud
+J Sep Sci
+J Sex Educ
+J Sex Educ Ther
+J Sex Marital Ther
+J Sex Med
+J Sex Res
+J Shoulder Elbow Surg
+J Siam Soc
+J Singapore Paediatr Soc
+J Sleep Res
+J Small Anim Pract
+J Smooth Muscle Res
+J Soc Adm Pharm
+J Soc Am
+J Soc Arch
+J Soc Army Hist Res
+J Soc Behav Pers
+J Soc Biol
+J Soc Biol Struct
+J Soc Christ Ethics
+J Soc Cienc Med Lisb
+J Soc Clin Psychol
+J Soc Cosmet Chem
+J Soc Dev Afr
+J Soc Econ Stud
+J Soc Gynecol Investig
+J Soc Health Syst
+J Soc Hist
+J Soc Hyg
+J Soc Issues
+J Soc Occup Med
+J Soc Ocean
+J Soc Pediatr Nurs
+J Soc Pers Relat
+J Soc Philos
+J Soc Policy
+J Soc Polit Aff
+J Soc Polit Econ Stud
+J Soc Polit Stud
+J Soc Psychol
+J Soc Res Adm
+J Soc Serv Res
+J Soc Stat Paris
+J Soc Stud
+J Soc Welfare Law
+J Soc Work Hum Sex
+J Sociol (Melb)
+J Sociol Soc Welf
+J Sociol Stud
+J South Afr Stud
+J South Asian Middle East Stud
+J South Calif Dent Assistants Assoc
+J South Calif Dent Assoc
+J South Calif State Dent Hyg Assoc
+J South Hist
+J South Orthop Assoc
+J Southeast Asian Earth Sci
+J Southeast Asian Stud
+J Sov Natly
+J Soz Forsch
+J Spacecr Rockets
+J Spec Educ
+J Spec Pediatr Nurs
+J Speech Disord
+J Speech Hear Disord
+J Speech Hear Disord Monogr Suppl
+J Speech Hear Res
+J Speech Lang Hear Res
+J Spinal Cord Med
+J Spinal Disord
+J Spinal Disord Tech
+J Sport Hist
+J Sports Med
+J Sports Med Phys Fitness
+J Sports Sci
+J Sri Lanka Branch R Asiat Soc
+J State Gov
+J Sterile Serv Manage
+J Steroid Biochem
+J Steroid Biochem Mol Biol
+J Stomatol Belg
+J Stone Dis
+J Stored Prod Res
+J Strength Cond Res
+J Stroke Cerebrovasc Dis
+J Struct Biol
+J Struct Funct Genomics
+J Stud Alcohol
+J Stud Alcohol Suppl
+J Submicrosc Cytol
+J Submicrosc Cytol Pathol
+J Subst Abuse
+J Subst Abuse Treat
+J Support Oncol
+J Supramol Struct
+J Supramol Struct Cell Biochem
+J Supramol Struct Cell Biochem Suppl
+J Supramol Struct Suppl
+J Surg Oncol
+J Surg Oncol Suppl
+J Surg Orthop Adv
+J Surg Res
+J Sykepleien
+J Synchrotron Radiat
+J Technol Transf
+J Telemed Telecare
+J Tenn Dent Assoc
+J Tenn Med Assoc
+J Tenn State Dent Assoc
+J Texas Dent Hyg Assoc
+J Texture Stud
+J Thai Assoc Volunt Steriliz
+J Thanatol
+J Theol South Afr
+J Theor Biol
+J Theory Soc Behav
+J Therm Biol
+J Thorac Cardiovasc Surg
+J Thorac Imaging
+J Thorac Surg
+J Thought
+J Thromb Haemost
+J Thromb Thrombolysis
+J Tissue Cult Methods
+J Tissue Viability
+J Tn State Med Assoc
+J Tongji Med Univ
+J Toxicol Clin Exp
+J Toxicol Clin Toxicol
+J Toxicol Cutaneous Ocul Toxicol
+J Toxicol Environ Health
+J Toxicol Environ Health A
+J Toxicol Environ Health B Crit Rev
+J Toxicol Environ Health Suppl
+J Toxicol Sci
+J Toxicol Toxin Rev
+J Trace Elem Electrolytes Health Dis
+J Trace Elem Med Biol
+J Tradit Chin Med
+J Transcult Nurs
+J Transl Med
+J Transnatl Law Policy
+J Transpl Coord
+J Trauma
+J Trauma Dissociation
+J Trauma Nurs
+J Trauma Stress
+J Travel Med
+J Trop Geogr
+J Trop Med
+J Trop Med Hyg
+J Trop Pediatr
+J Trop Pediatr Afr Child Health
+J Trop Pediatr Environ Child Health
+J UOEH
+J Ultrasound Med
+J Ultrastruct Mol Struct Res
+J Ultrastruct Res
+J Urban Econ
+J Urban Health
+J Urban Hist
+J Urban Law
+J Urol
+J Urol (Paris)
+J Urol Medicale Chir
+J Urol Nephrol (Paris)
+J Urol Nurs
+J Vac Sci Technol A
+J Value Inq
+J Vasc Access
+J Vasc Interv Radiol
+J Vasc Nurs
+J Vasc Res
+J Vasc Surg
+J Vector Borne Dis
+J Vector Ecol
+J Vener Dis Inf
+J Vestib Res
+J Vet Dent
+J Vet Diagn Invest
+J Vet Intern Med
+J Vet Med A Physiol Pathol Clin Med
+J Vet Med B Infect Dis Vet Public Health
+J Vet Med Educ
+J Vet Med Sci
+J Vet Pharmacol Ther
+J Vet Sci
+J Viral Hepat
+J Virol
+J Virol Methods
+J Vis
+J Vis Commun Med
+J Vis Impair Blind
+J Vitaminol (Kyoto)
+J Vocat Behav
+J Voice
+J Volunt Action Res
+J Volunt Adm
+J W Va Hist Assoc
+J Warburg Courtauld Inst
+J Wash Acad Sci
+J Water Health
+J Water Pollut Control Fed
+J West
+J West Aust Nurses
+J West Soc Periodontol Periodontal Abstr
+J West Va Philo Soc
+J Wilderness Med
+J Wildl Dis
+J Wildl Manage
+J Wis Dent Assoc
+J Wis State Dent Soc
+J Women Aging
+J Womens Heal
+J Womens Health
+J Womens Health (Larchmt)
+J Womens Health Gend Based Med
+J Womens Hist
+J World Intellect Prop
+J Wound Care
+J Wound Ostomy Continence Nurs
+J Youth Adolesc
+J Zhejiang Univ Sci
+J Zhejiang Univ Sci B
+J Zoo Wildl Med
+J Zool
+JAAPA
+JACEP
+JAMA
+JAPCA
+JBR-BTR
+JCAH Perspect
+JEMS
+JFMA
+JFORL J Fr Otorhinolaryngol Audiophonol Chir Maxillofac
+JICA Newsl
+JK Pract
+JNMA J Nepal Med Assoc
+JOGN Nurs
+JOICFP News
+JOICFP Rev
+JONAS Healthc Law Ethics Regul
+JOP
+JPEN J Parenter Enteral Nutr
+JPO J Pract Orthod
+JSAC Grapevine
+JSLS
+Jaarb K Ned Bot Ver
+Jaarb Kankeronderz Kankerbestrijd Ned
+Jaarb Numaga
+Jabega
+Jahrb Albertus Univ Koenigsb
+Jahrb Antike Christentum
+Jahrb Brandenbg Landesgesch
+Jahrb Coburg Landesstift
+Jahrb Gesch
+Jahrb Gesch Osteur
+Jahrb Gesch Staat Wirtsch Ges Lateinam
+Jahrb Hist Forsch Bundesrepub Dtschl
+Jahrb Hist Ver Furst Liechtenstein
+Jahrb Inst Dtsch Gesch
+Jahrb Inst Gesch Med Robert Bosch Stift
+Jahrb Landeskd Niederosterr
+Jahrb Natl Okon Stat
+Jahrb Oberoesterr Musealver
+Jahrb Schlesisch Friedrich Wilhelms Univ Breslau
+Jahrb Sozialwiss
+Jahrb Univ Duesseld
+Jahrb Vereins Gesch Stadt Wien
+Jahrb Wirtschaftsgesch
+Jahresber Schweiz Akad Med Wiss
+Jam J
+Jamaican Nurse
+James Arthur Lect
+James Joyce Q
+Janasamkhya
+Janasamkhya Siksha Mukhapatra
+Janus
+Japan World Econ
+Japanese Econ Stud
+Jednota Annu Furdek
+Jerusalem Q
+Jeune Afr
+Jew Aff
+Jew Dig
+Jew J Sociol
+Jew Life
+Jew Mem Hosp Bull
+Jew Obs Middle East Rev
+Jew Q Rev
+Jew Soc Stud
+Ji Sheng Chong Xue Yu Ji Sheng Chong Bing Za Zhi
+Jian Kang Bao
+Jibiinkoka
+Jikeikai Med J
+Jikken Dobutsu
+Jikoketsu Yuketsu
+Jimlar Mutane
+Jing Ji Lun Wen
+Jinko Mondai Kenkyu
+Jinko Mondai Kenkyusho Nenpo
+Jinkogaku Kenkyu
+Jinrui Idengaku Zasshi
+Jinruigaku Zasshi
+John Marshall J Comput Inf Law
+John Marshall J Pract Proced
+John Marshall Law Rev
+Johns Hopkins Mag
+Johns Hopkins Med J
+Johns Hopkins Med J Suppl
+Johns Hopkins Med Lett Health After 50
+Johns Hopkins Nurses Alumni Mag
+Joint Bone Spine
+Jordan Dent J
+Jordemodern
+Jordmorbladet
+Josai Shika Daigaku Kiyo
+Josanpu Zasshi
+Journ Annu Diabetol Hotel Dieu
+Journal
+Journal Hist
+Journal Q
+Journalism Monogr
+Jpn Circ J
+Jpn Dent J
+Jpn Heart J
+Jpn Hosp
+Jpn J Antibiot
+Jpn J Appl Phys
+Jpn J Cancer Res
+Jpn J Clin Oncol
+Jpn J Crop Sci
+Jpn J Dermatol B
+Jpn J Exp Med
+Jpn J Gastroenterol
+Jpn J Genet
+Jpn J Hum Genet
+Jpn J Infect Dis
+Jpn J Med
+Jpn J Med Sci Biol
+Jpn J Microbiol
+Jpn J Obstet Gynecol
+Jpn J Ophthalmol
+Jpn J Pharmacol
+Jpn J Physiol
+Jpn J Psychiatry Neurol
+Jpn J Psychon Sci
+Jpn J Surg
+Jpn J Thorac Cardiovasc Surg
+Jpn J Tuberc
+Jpn J Tuberc Chest Dis
+Jpn J Vet Res
+Jpn Med J
+Jpn Med J (Natl Inst Health Jpn)
+Jpn Psychol Res
+Jpn Q
+Jpn Stud Hist Sci
+Jt Comm J Qual Improv
+Jt Comm J Qual Patient Saf
+Jt Comm J Qual Saf
+Jt Comm Perspect
+Jud Librariansh
+Judaism
+Judges J
+Judicature
+Jugosl Ginekol Opstet
+Jugosl Ginekol Perinatol
+Jugosl Pregl
+Julk Suom Naishammaslaak Ryhma
+Jurid Rev
+Jurimetrics
+Juris Dr
+Jurist
+Juristat
+Justus Liebigs Ann Chem
+Juv Fam Court J
+Jyske Saml
+K Krigsvetenskapakad Handlingar Tidskr
+K Rep
+KIPH Bull
+KY Law J
+KY Rep
+Kagakushi Kenkyu
+Kaibogaku Zasshi
+Kaiin Dayori Nippon Kontakuto Renzu Gakkai
+Kajok Kyehoek Nonjip
+Kaku Igaku
+Kampener Alm
+Kanagawa Shigaku
+Kangaroo
+Kango
+Kango Gijutsu
+Kango Kenkyu
+Kango Kyoiku
+Kango Kyoshitsu
+Kango Tenbo
+Kangogaku Zasshi
+Kanho Hakhoe Chi
+Kanhohak Tamgu
+Kans Geogr
+Kans Hist
+Kans Hist Q
+Kans Med
+Kans Nurse
+Kans Statut Annot Kans
+Kansai Daigaku Keizai Ronshu
+Kansenshogaku Zasshi
+Kanzo
+Kao Ku Jen Lei Hsueh Kan
+Kaohsiung J Med Sci
+Kardiol Pol
+Kardiologiia
+Kassenzahnarzt Colloq Med Dent
+Kathmandu Univ Med J (KUMJ)
+Katilolehti
+Katollik Taehak Uihakpu Nonmunjip
+Katunob
+Kazan Med Zh
+Keep Posted
+Keio Econ Stud
+Keio J Med
+Keisei Geka
+Kekkaku
+Kemi
+Kennedy Inst Ethics J
+Kenya Nurs J
+Kenya Stat Dig
+Key Report
+Keystone Folkl Q
+Khelmintologiia
+Khimiia Meditsina
+Khirurgiia (Mosk)
+Khirurgiia (Sofiia)
+Kidney
+Kidney Blood Press Res
+Kidney Int
+Kidney Int Suppl
+Kiel Geogr Schr
+Kieler Beitr Gesch Med Pharm
+Kinderarztl Prax
+Kinderkrankenschwester
+Kinetoplastid Biol Dis
+King Faisal Spec Hosp Med J
+Kiryat Sefer
+Kisaengchunghak Chapchi
+Kisechugaku Zasshi
+Kiserl Orvostud
+Kita Kanto Igaku
+Kitasato Arch Exp Med
+Kiva
+Kleio
+Klin Anasthesiol Intensivther
+Klin Khir
+Klin Lab Diagn
+Klin Lech Zlokach Novoobraz
+Klin Med (Mosk)
+Klin Med Osterr Z Wiss Prakt Med
+Klin Mikrobiol Infekc Lek
+Klin Monatsbl Augenheilkd
+Klin Oczna
+Klin Padiatr
+Klin Sygepleje
+Klin Wochenschr
+Knappschaftsarzt
+Knee
+Knee Surg Sports Traumatol Arthrosc
+Ko Hsueh Tung Pao
+Kobe Daigaku Igakubu Kiyo
+Kobe Ika Daigaku Kiyo
+Kobe J Med Sci
+Koinonia
+Koku Eisei Gakkai Zasshi
+Kokubyo Gakkai Zasshi
+Kokuritsu Iyakuhin Shokuhin Eisei Kenkyusho Hokoku
+Kokusaigaku revyu
+Kokyu To Junkan
+Kokyuki Shinryo
+Koln Med Beitr
+Kolner Z Soz Sozialpsychol (Aufl)
+Komun Mazur Warm
+Kongressbd Dtsch Ges Chir Kongr
+Konjunkturpolitik
+Korea (South)
+Korea J
+Korea J Popul Dev
+Korean J Biochem
+Korean J Biol Sci
+Korean J Gastroenterol
+Korean J Hepatol
+Korean J Intern Med
+Korean J Ophthalmol
+Korean J Parasitol
+Korean J Radiol
+Korot
+Kos
+Koshu Eisei In Kenkyu Hokoku
+Kosm Biol Aviakosm Med
+Kosm Biol Med
+Kosm Ser A Biol
+Kosmas
+Kotiseutu
+Krankengymnastik
+Krankenpfl J
+Krankenpfl Soins Infirm
+Krankenpflege (Frankf)
+Krankenschwester
+Krebsarzt
+Krebsforsch Krebsbekampf
+Kritiek
+Kroc Found Ser
+Kronika
+Kulak Burun Bogaz Ihtis Derg
+Kult Fiz
+Kult Spolecz
+Kult Tech
+Kulturpflanze
+Kumamoto Igakkai Zasshi
+Kumamoto Med J
+Kun Chong Xue Bao
+Kunst Orient
+Kunst Ther
+Kupr Spoud
+Kurinikaru Sutadi
+Kurume Med J
+Kwart Hist
+Kwart Hist Kult Mater
+Kwart Hist Nauki Tech
+Kwart Hist Ruchu Zaw
+Ky Bench Bar
+Ky Dent J
+Ky Folkl Rec
+Ky Hosp Mag
+Ky Med J
+Ky Nurse
+Kybernetik
+Kyklos
+Kyobu Geka
+Kyoto Daigaku Kekkaku Kenkyusho Kiyo
+Kyoto Daigaku Kokukagaku Kiyo
+Kyushu J Med Sci
+LA Law Rev
+LA Rep
+LARC Med
+LC GC
+LDA J
+LDI Issue Brief
+LJ Spec Rep
+LMT
+LOVTID K DAN A
+LTC Regul Risk Liabil Advis
+La Hist
+La Stud
+Lab Anim
+Lab Anim (NY)
+Lab Anim Care
+Lab Anim Sci
+Lab Chip
+Lab Delo
+Lab Hematol
+Lab Invest
+Lab Med
+Lab Pract
+Lab Res Methods Biol Med
+Lab World
+Labor Hist
+Labor Law J
+Labor Lawyer
+Laboratorio
+Labour
+Labour Cap Soc
+Labour Hist
+Labour Soc
+Lactation Rev
+Ladies Home J
+Laeknabladid
+Lahey Clin Bull
+Lahey Clin Found Bull
+Lait
+Lakartidningen
+Lamp
+Lampada
+Lancet
+Lancet Infect Dis
+Lancet Neurol
+Lancet Oncol
+Land Aalst
+Land Econ
+Land Water Law Rev
+Land use policy
+Landarzt
+Landsc Urban Plan
+Landscape
+Lang Commun
+Lang Speech
+Lang Speech Hear Serv Sch
+Langages
+Langenbecks Arch Chir
+Langenbecks Arch Chir Suppl II Verh Dtsch Ges Chir
+Langenbecks Arch Chir Suppl Kongressbd
+Langenbecks Arch Klin Chir Ver Dtsch Z Chir
+Langenbecks Arch Surg
+Langmuir
+Laryngol Rhinol Otol (Stuttg)
+Laryngorhinootologie
+Laryngoscope
+Laser Phys
+Lasers Med Sci
+Lasers Surg Med
+Lasers Surg Med Suppl
+Lat Am Perspect
+Lat Am Popul Hist Bull
+Lat Am Popul Hist News
+Lat Am Res Rev
+Late Imp China
+Lateinamerika
+Laterality
+Latomus
+Lattante
+Laund News
+Lav Ist Anat Istol Patol Univ Studi Perugia
+Lav Um
+Laval Med
+Law Contemp Probl
+Law Context
+Law File
+Law Hum Behav
+Law Hum Genome Rev
+Law Inequal
+Law Inst J
+Law Justice
+Law Lib
+Law Libr J
+Law Med Health Care
+Law Med News
+Law Ment Health
+Law Philos
+Law Policy
+Law Policy Int Bus
+Law Policy Q
+Law Psychol Rev
+Law Q Rev
+Law Rev
+Law Soc Inq
+Law Soc Rev
+Law Teach
+Laws Ill
+Laws P R Annot P R
+Laws State N M N M
+Laws State Or Or
+Leadersh Health Serv
+League Exch
+League Lines
+Learn Behav
+Learn Mem
+Learn Motiv
+Leban Pharm J
+Lebensversicher Med
+Leber Magen Darm
+Lect Sci Basis Med
+Leech
+Leeds Dent J
+Leg Aspects Med Pract
+Leg Ethics
+Leg Med
+Leg Med (Tokyo)
+Leg Med Annu
+Leg Med Q
+Leg Stud
+Leg Stud (Soc Leg Scholars)
+Legal Ref Serv Q
+Lege Artis Med
+Lek List
+Lek Obz
+Lek Pr
+Lek Veda Zahr
+Lek Wojsk
+Lemouzi
+Lens Eye Toxic Res
+Lens Res
+Leodium
+Leopoldina
+Lepr India
+Lepr Rev
+Lethaia
+Lett Appl Microbiol
+Lett Ford Found
+Lett Ital
+Leuk Lymphoma
+Leuk Res
+Leukemia
+Leuvense Bijdr
+Leveltari Kozl
+Leveltari Sz
+Lex Sci
+Lexis DC Code DC
+Leyte Samar Stud
+Lias
+Lib Life Fam
+Lib Sci Slant Doc
+Liberation
+Liberian Stud J
+Libr Assoc Rec
+Libr Bull Univ Lond
+Libr Chron
+Libr Chron Univ Tex Austin
+Libr Cult
+Libr Hist
+Libr J
+Libr Q
+Libr Resour Tech Serv
+Libr Rev (Lond)
+Libr Technol Rep
+Libr Trends
+Library (Lond)
+Lichenologist
+Liet Istor Metrast
+Liet Tsr Aukst Mokyklu Mokslo Darb
+Life
+Life Sci
+Life Sci Adv
+Life Sci Adv Exp Clin Endocrinol
+Life Sci Adv Plant Physiol
+Life Sci I
+Life Sci II
+Life Sci Space Res
+Life Support Biosph Sci
+Life Support Syst
+Life Threat Behav
+Lifelong Learn Adult Years
+Lifetime Data Anal
+Ligament
+Liguorian
+Lijec Vjesn
+Lik Sprava
+Lilith
+Lille Chir
+Lille Med
+Limburg
+Limnol Oceanogr
+Lin Chuang Er Bi Yan Hou Ke Za Zhi
+Lin Chuang Kan Tan Ping Tsa Chih
+Linacre Q
+Linc Her
+Linc Law Rev
+Lincoln Rev
+Linen Supply News
+Lingua Nostra
+Links
+Linnean
+Lipids
+Lipids Health Dis
+Lipp Mitt Gesch Landeskd
+Lippincott Health Promot Lett
+Lippincotts Case Manag
+Lippincotts Prim Care Pract
+Lishi Yanjiu
+Listener
+Listening
+Lit Discuss
+Lit Eildienst Roche
+Lit Hist
+Lit Med
+Literatura
+Litt Med Soc
+Lituanus
+Liver
+Liver Int
+Liver Transpl
+Liver Transpl Surg
+Liverp Classical Mon
+Liverp Law Rev
+Livre Estampe
+Livrustkammaren
+Llano Estac Heritage
+Lloydia
+Llull
+Local Hist
+Local Popul Stud
+Locus
+Log
+Logbuch
+Logoped Phoniatr Vocol
+Logos
+Logos (Santa Clara)
+Loma Linda Univ Dent Mag
+Lond Archaeol
+Lond Clin Med J
+Lond Hosp Gaz
+Lond J
+Long Isl Forum
+Long Island Hist J
+Long Range Plann
+Long Term Care (Don Mills)
+Long Term Care Health Serv Adm Q
+Long Term Care Q
+Longevita
+Los Alamos Sci
+Los Angel Lawyer
+Los Angeles Times
+Loss Grief Care
+Loteria (Panama)
+Lotta Tuberc
+Louv Med
+Louv Stud
+Lovelace Clin Rev
+Loyola Consum Law Rev
+Loyola Law Rev
+Loyola Los Angel Int Comp Law J
+Loyola Los Angel Law Rev
+Loyola Univ Chicago Law J
+Luft Raumfahrt
+Luminescence
+Lung
+Lung Cancer
+Lupus
+Luso-Braz Rev
+Lutheran Q
+Lutheran Theol J
+Lutte Cancer
+Luzif Amor
+Lychnos Lardomshist Samf Arsb
+Lymphat Res Biol
+Lymphokine Cytokine Res
+Lymphokine Res
+Lymphology
+Lyon Chir
+Lyon Med
+Lyon Mediterr Med Med Sud Est
+Lyon Pharm
+M B Pharm Bull
+MAAS J Islam Sci
+MAGMA
+MARHIA
+MCH News PAC
+MCN Am J Matern Child Nurs
+MD
+MD Comput
+MD Law Rev
+MGH News
+MGMA Connex
+MH
+MIMS Mag
+MITS Technol Rev
+MLM Rep
+MLN Bull
+MLO Med Lab Obs
+MMW Fortschr Med
+MMW Munch Med Wochenschr
+MMWR CDC Surveill Summ
+MMWR Morb Mortal Wkly Rep
+MMWR Recomm Rep
+MMWR Surveill Summ
+MNA Accent
+MPS
+MRS Bull
+MSDA J
+Ma Zui Xue Za Zhi
+Maandbl Oud Utrecht
+Maandschr Cent Bur Stat
+Maandschr Kindergeneeskd
+Maandstat Bevolking
+Maandstat Bevolking Volksgezond
+Maastrich J Eur Comp Law
+Macleans
+Macromol Biosci
+Macromolecules
+Mademoiselle
+Madj Persat Dokt Gigi Indones
+Madjalah Kedokt Indones
+Mag Albemarle Cty Hist
+Mag Antiq
+Mag Istor
+Mag Litt
+Maghreb Machrek
+Maghreb Med
+Maghreb Rev
+Magn Reson Annu
+Magn Reson Chem
+Magn Reson Imaging
+Magn Reson Imaging Clin N Am
+Magn Reson Med
+Magn Reson Med Sci
+Magn Reson Q
+Magnes Res
+Magnes Trace Elem
+Magnesium
+Magy Belorv Arch
+Magy Noorv Lapja
+Magy Onkol
+Magy Radiol
+Magy Seb
+Magy Traumatol Orthop Helyreallito Seb
+Magy Traumatol Ortop Kezseb Plasztikai Seb
+Magy Tud
+Maharashtra Med J
+Mahidol Popul Q Gaz
+Maia
+Maine Hist Soc Q
+Maine Law Rev
+Maine Nurse
+Maine Revis Statut Annot 1964 Maine
+Mainlines
+Majalah Demografi Indones
+Majalah Obstet Ginekol Indones
+Majallah Jamia Dandan Pazshki
+Majallat Albuhuth Waaldirasat Alarabiyah
+Majallat Aldiwan Alqawmi Lilusrah Waal Umran lbashari
+Majallat Ma had al Makhtutat al Arabiy
+Majallat Niqabat Attiba Alasnnan Alurduniyah
+Majallat Tibb Alasnan Alsuriyah
+Majallat Tibb Alfamm Alsuriyah
+Major Probl Clin Pediatr
+Major Probl Clin Surg
+Major Probl Intern Med
+Major Probl Obstet Gynecol
+Major Probl Pathol
+Mak Rounds Health Faith Ethics
+Makedon Med Pregl
+Mal Cardiovasc
+Malacologia
+Malaga
+Malar J
+Malar Wkly
+Malawi Med J
+Malawian Geogr
+Malay Econ Rev
+Malays Dent J
+Malays J Pathol
+Malays J Reprod Health
+Malays J Trop Geogr
+Male Nurses J
+Malloch Room Newsl
+Malpract Dig
+Mamm Genome
+Mammalia
+Man Hist
+Man India
+Man Med
+Man Ther
+Manag Care
+Manag Care Interface
+Manag Care Q
+Manag Care Strateg
+Manag Compliance Ser
+Manag Int Dev
+Manag Medicare Medicaid News
+Manage Focus
+Manage Rev
+Manage Sci
+Manage World
+Manch Med Gaz
+Manch Men
+Manchester Sch Econ Soc Stud
+Manedsskr Prakt Laegegern
+Manit Law J
+Manit Med Rev
+Mankind
+Mankind Q
+Manshur Atmajallat Dirasat Alkhalij Waal Jazirah Alarabiyah
+Manuf Chem Aerosol News
+Manuscripta
+Manuscripts (N Y)
+Manushi
+Mar Behav Physiol
+Mar Biotechnol (NY)
+Mar Chem
+Mar Corps Gaz
+Mar Ecol Prog Ser
+Mar Environ Res
+Mar Geol
+Mar Mirror
+Mar Pollut Bull
+Mar Rundsch
+Marbg Geogr Schr
+Marbg Schrift Medgesch
+Marga
+Margin
+Marginalia Dermatol
+Margins (Baltim)
+Mark Health Serv
+Markers
+Maroc Med
+Marquette Law Rev
+Marquette Med Rev
+Marriage
+Marriage Fam Living
+Marriage Fam Newsl
+Marriage Fam Rev
+Mars Chir
+Mars Med
+Marx Perspect
+Mass Gen Laws Annot Mass
+Mass Hist Soc Proc
+Mass Journal Ment Health
+Mass Law Rev
+Mass Nurse
+Mass Physician
+Mass Rep Mass Supreme Judic Court
+Mass Rev
+Mass Spectrom Rev
+Masterkey Ind Lore Hist
+Masui
+Matekon
+Mater Bevolkwiss
+Mater Cult
+Mater Des
+Mater Hist Bull
+Mater Manag Health Care
+Mater Med Greca
+Mater Med Nordmark
+Mater Med Pol
+Mater Perform
+Mater Pr Antropol
+Mater Res Bull
+Matern Child Health J
+Matern Child Nurs J
+Matern Infanc (Sao Paulo)
+Maternite
+Materwiss Werksttech
+Math Biosci
+Math Comput Model
+Math Comput Simul
+Math Med Biol
+Math Popul Stud
+Math Soc Sci
+Matrix
+Matrix Biol
+Matrix Suppl
+Maturitas
+Mawazo
+Maxwell Rev
+Mayan
+Mayo Clin Health Lett
+Mayo Clin Proc
+Mayo Clin Womens Healthsource
+Mazingira
+McCalls
+McGeorge Law Rev
+McGill J Educ
+McGill Law J
+McGill News
+McKinneys Consol Laws N Y Annot N Y State
+Mcgill Dent Rev
+Mcgill Med J
+Mcgill Queens Hannah Inst Stud Hist Med Health Soc
+Mcgraw Hills Med Health
+Mcgraw Hills Wash Rep Med Health
+Md Bar J
+Md Geneal Soc Bull
+Md Hist
+Md Hist Mag
+Md J Contemp Leg Issues
+Md J Int Law Trade
+Md Law Forum
+Md Med
+Md Med J
+Md Nurs News
+Md Nurse
+Md State Med J
+Mead Johnson Symp Perinat Dev Med
+Meander
+Meas Sci Technol
+Mech Ageing Dev
+Mech Dev
+Mech Eng
+Med Actual
+Med Aeronaut
+Med Aeronaut Spat
+Med Aff
+Med Afr Noire
+Med Ann Dist Columbia
+Med Annu
+Med Anthropol
+Med Anthropol Newsl
+Med Anthropol Q
+Med Arh
+Med Armees
+Med Art
+Med Arts Sci
+Med Aspects Hum Sex
+Med Audio Vis
+Med Bild Dienst
+Med Biol
+Med Biol Eng
+Med Biol Eng Comput
+Med Biol Illus
+Med Bohringer (Overseas)
+Med Br (Lond)
+Med Bull (Ann Arbor)
+Med Bull Harrisburg Polyclin Hosp
+Med Bull Istanbul Med Fac
+Med Bull St Louis Univ
+Med Bull U S Army Army 1st
+Med Bull U S Army Eur Command Med Div
+Med Bull US
+Med Bull US Army Eur
+Med Bull Uganda
+Med Bull Vet Adm
+Med Care
+Med Care Res Rev
+Med Care Rev
+Med Chem
+Med Chem Res
+Med Chir Dig
+Med Cir (Bogota)
+Med Cir Farm
+Med Cir Guerra
+Med Claims Manag
+Med Clin (Barc)
+Med Clin North Am
+Med Clin Sper
+Med Colon
+Med Confl Surviv
+Med Contact (Bussum)
+Med Contemp
+Med Counterpoint
+Med Cutan Ibero Lat Am
+Med Decis Making
+Med Dent J
+Med Deporte Trab
+Med Des Mater
+Med Desarrollo
+Med Device Technol
+Med Dig
+Med Dimens
+Med Dok
+Med Dosim
+Med Dosw
+Med Dosw Mikrobiol
+Med Econ
+Med Educ
+Med Egypte
+Med Electron
+Med Electron Biol Eng
+Med Electron Microsc
+Med Emory
+Med Eng Phys
+Med Esp
+Med Ethics
+Med Ethics (Burlingt, Mass)
+Med Ethics Pract Royal Pharm Soc G B
+Med Etika Bioet
+Med Exp Int J Exp Med
+Med Fis Rehabil
+Med Forum
+Med Fr (Bombay)
+Med Gen Fr
+Med Geogr Herit
+Med Ges
+Med Ges Gesch
+Med Ges Gesch Beih
+Med Gesch Kult
+Med Glas
+Med Glob Surviv
+Med Group Manage
+Med Group Manage J
+Med Gynaecol Androl Sociol
+Med Gynaecol Sociol
+Med Health
+Med Health Care Philos
+Med Health R I
+Med Herit
+Med Hist
+Med Hist (Barc)
+Med Hist Aust
+Med Hist Suppl
+Med Hoje
+Med Humanit
+Med Humanit Rev
+Med Hyg (Geneve)
+Med Hypotheses
+Med Illus
+Med Image Anal
+Med Immunol
+Med Infant
+Med Infant (Paris)
+Med Inform (Lond)
+Med Inform Internet Med
+Med Insight
+Med Instrum
+Med Int
+Med Int (Milano)
+Med Intensiva
+Med Interface
+Med Interna
+Med Interna (Bucur)
+Med Interne
+Med J Aust
+Med J Cairo Univ
+Med J Egypt Armed Forces
+Med J Islam Repub Iran
+Med J Malaya
+Med J Malaysia
+Med J Osaka Univ
+Med J Southwest
+Med J Zambia
+Med Klin
+Med Klin (Munich)
+Med Klin Suppl
+Med Klin [Klin]
+Med Klin [Prax]
+Med Lab
+Med Lab (Stuttg)
+Med Lab Sci
+Med Lab Technol
+Med Lat
+Med Lav
+Med Law
+Med Law Int
+Med Law Rev
+Med Leg Bull
+Med Leg Dommage Corpor
+Med Leg J
+Med Lett Drugs Ther
+Med Libre
+Med Maandbl
+Med Mal Infect
+Med Malpract Cost Containment J
+Med Manag Netw
+Med Mark Media
+Med Microbiol Immunol (Berl)
+Med Mol Morphol
+Med Monatsschr
+Med Monatsschr Pharm
+Med Monde
+Med Moral Newsl
+Med Mycol
+Med Netw Strategy Rep
+Med Newsl (Lond)
+Med Nov
+Med Nowozytna
+Med Off
+Med Oncol
+Med Oncol Tumor Pharmacother
+Med Opin Rev
+Med Oral
+Med Oral Patol Oral Cir Bucal
+Med Panam
+Med Parazitol (Mosk)
+Med Pediatr Oncol
+Med Pediatr Oncol Suppl
+Med Periskop Ingelheim
+Med Pharmacol Exp Int J Exp Med
+Med Phys
+Med Pr
+Med Pract
+Med Pract (Zaragoza)
+Med Pregl
+Med Press
+Med Princ Pract
+Med Probl Perform Art
+Med Proc (Johannesb)
+Med Prod Sales
+Med Prog
+Med Prog Technol
+Med Prom SSSR
+Med Psicosom
+Med Radiogr
+Med Radiogr Photogr
+Med Radiol
+Med Radiol (Mosk)
+Med Razgl
+Med Rec Ann
+Med Rec Health Care Inf J
+Med Rec News
+Med Ref Serv Q
+Med Res Eng
+Med Res Rev
+Med Reserve (Paris)
+Med Rev
+Med Sachverstand
+Med Sci
+Med Sci (Paris)
+Med Sci Law
+Med Sci Monit
+Med Sci Res
+Med Sci Sports
+Med Sci Sports Exerc
+Med Secoli
+Med Sect Proc
+Med Segur Trab (Madr)
+Med Self Care
+Med Serv
+Med Serv J Can
+Med Sestra
+Med Soc (Berkeley)
+Med Sport (Berl)
+Med Sport (Roma)
+Med Sport Sci
+Med Staff Couns
+Med Teach
+Med Tech (Stuttg)
+Med Techn Bull
+Med Tekh
+Med Thorac
+Med Times
+Med Today
+Med Toxicol
+Med Toxicol Adverse Drug Exp
+Med Tr Prom Ekol
+Med Tradic
+Med Trial Tech Q
+Med Trib Med News
+Med Trop (Madr)
+Med Trop (Mars)
+Med Ultrasound
+Med Usine Rev Hyg Ind Mal Prof
+Med Vet Entomol
+Med Vjesn (Osijek)
+Med War
+Med Waste Anal
+Med Welt
+Med Weter
+Med Wieku Rozwoj
+Med Womans J
+Med World
+Med World (Lond)
+Med World News
+Med Year
+Med Zh Uzb
+MedGenMed
+Medd Flyg Navalmed Namnd Statens Namnd For Flyg Navalmed Forsk Forsoksvererksamhet
+Medd Nor Farm Selsk
+Medd Sundhedsstyr Beredskabsafdelingen
+Meded K Acad Wet Lett Schone Kunsten Belg Kl Wet
+Meded Rijksuniv Gent Fak Landbouwkd Toegep Biol Wet
+Medi-Cal Policy Inst Issue Brief
+Media & Gend Monit
+Media Asia
+Media Dev
+Mediaev Philos Pol
+Mediaev Stud
+Mediators Inflamm
+Medicam Hist Soc
+Medicamenta (Madr)
+Medicamentum
+Medicamundi
+Medicare Brief
+Medicina
+Medicina (B Aires)
+Medicina (Firenze)
+Medicina (Kaunas)
+Medicina (Madr)
+Medicina (Mex)
+Medicinar
+Medicine
+Medicine (Baltimore)
+Medico
+Medico (Eur)
+Medico (Int)
+Medico (Porto)
+Medicographia
+Medicoleg News
+Medicus
+Mediev Archaeol
+Mediev Humanist
+Medievales
+Medika (Zagreb)
+Medinfo
+Medioev Romanzo
+Medioevo Lat
+Mediterr Med
+Mediterr Peoples
+Medizinhist J
+Medizinische
+Medizinrecht
+Medscape Womens Health
+Medsurg Nurs
+Meharri Dent
+Meikai Daigaku Shigaku Zasshi
+Meisheimer Entomol Ser
+Melanges Casa Velazquez
+Melanges Ec Fr Rome Moyen Age Temps Mod
+Melanoma Res
+Melb Hist J
+Melb Univ Law Rev
+Melita Theol
+Mellen Hist Med
+Mem Acad Chir (Paris)
+Mem Acad Cienc Lisb Cl Cienc
+Mem Acad R Med Belg
+Mem Acad Sci Inscr B-Lett Toulouse
+Mem Cognit
+Mem Col Nac (Mex)
+Mem Inst Butantan
+Mem Inst Oswaldo Cruz
+Mem Soc Agric Commer Sci Arts Marne
+Memai Heiko Igaku
+Membr Biochem
+Membr Cell Biol
+Membranes
+Memo Med Res Counc
+Memo Rep Nav Med Res Inst (US)
+Memory
+Memphis Med J
+Memphis Mid South Med J
+Memphis State Univ Law Rev
+Men's Reprod Health
+Mendel Newsl
+Menninger Perspect
+Mennon Q Rev
+Menopause
+Mens Ondernem
+Ment Disabil Law Rep
+Ment Health (Lond)
+Ment Health Care
+Ment Health Nurs
+Ment Health Serv Res
+Ment Health Soc
+Ment Health Stat Note
+Ment Health Today
+Ment Hosp
+Ment Hyg
+Ment Phys Disabil Law Rep
+Ment Retard
+Ment Retard Abstr
+Ment Retard Dev Disabil Res Rev
+Ment Retard Law
+Mentalis
+Mentalities
+Mercer Dent Soc Newsl
+Mercer Law Rev
+Merck Rep
+Merrill Palmer Q
+Merrill Palmer Q Behav Dev
+Mese Sanit
+Mesoamerica (Antigua Guatem)
+Met Ions Biol Syst
+Meta
+Metab Bone Dis Relat Res
+Metab Brain Dis
+Metab Eng
+Metab Ophthalmol
+Metab Pediatr Ophthalmol
+Metab Pediatr Syst Ophthalmol
+Metabolism
+Metabolomics
+Metamedicine
+Meteorit Planet Sci
+Meteoritics
+Methodist Hist
+Methodol Sci
+Methods
+Methods Achiev Exp Pathol
+Methods Biochem Anal
+Methods Cell Biol
+Methods Cell Sci
+Methods Enzymol
+Methods Find Exp Clin Pharmacol
+Methods Inf Med
+Methods Inf Med Suppl
+Methods Med Res
+Methods Mol Biol
+Methods Mol Med
+Mex Stud
+Mezzog Eur
+Mich Acad
+Mich Appeals Rep Mich Court Appeals
+Mich Compil Laws Annot Mich
+Mich Health Hosp
+Mich Hist
+Mich Hist Rev
+Mich Hosp
+Mich J Gend Law
+Mich Jew Hist
+Mich Law Rev
+Mich Med
+Mich Nurse
+Mich Q Rev
+Mich Statut Annot Mich
+Michies Annot Code Public Gen Laws Md Md
+Michies W Va Code Annot W Va
+Microb Cell Fact
+Microb Comp Genomics
+Microb Drug Resist
+Microb Ecol
+Microb Pathog
+Microb Releases
+Microbes Infect
+Microbiol Esp
+Microbiol Immunol
+Microbiol Mol Biol Rev
+Microbiol Parazitol Epidemiol (Bucur)
+Microbiol Res
+Microbiol Rev
+Microbiol Sci
+Microbiologia
+Microbiologica
+Microbiology
+Microbios
+Microchem J
+Microcirc Endothelium Lymphatics
+Microcirculation
+Microgravity Q
+Microgravity Sci Technol
+Micron
+Micronesica
+Microsc Acta
+Microsc Acta Suppl
+Microsc Electron Biol Celular
+Microsc Microanal
+Microsc Res Tech
+Microscope
+Microscopy
+Microsurgery
+Microvasc Res
+Mid Am
+Mid Am Rev Sociol
+MidCont J Archaeol
+Middle East Dent Oral Health
+Middle East Health
+Middle East J
+Middle East J Anaesthesiol
+Middle East J Anesthesiol
+Middle East Med J
+Middle East Rep
+Middle East Stud
+Midl Hist
+Midstream (N Y)
+Midway
+Midwest Alliance Nurs J
+Midwest Dent
+Midwest Med Ethics
+Midwest Q (Pittsb)
+Midwife Health Visit
+Midwife Health Visit Community Nurse
+Midwifery
+Midwifery Today Childbirth Educ
+Midwifery Today Int Midwife
+Midwives
+Midwives Chron
+Mie Med J
+Migr News
+Migr Teme
+Migr Today
+Migr World Mag
+Migration
+Mikrobiol Z
+Mikrobiol Zh
+Mikrobiologiia
+Mikrobiyol Bul
+Mikrochim Acta
+Mikrochim Ichnoanal Acta
+Mikroskopie
+Mil Aff
+Mil Law Rev
+Mil Med
+Mil Psychol
+Mil Rev
+Mil Surg
+Milbank Mem Fund Q
+Milbank Mem Fund Q Health Soc
+Milbank Q
+Milieux
+Milit Collect Hist
+Milit Hist Tex Southwest
+Militaerlaegen
+Militargeschichte
+Milw Hist
+Mind
+Miner Electrolyte Metab
+Mineral Mag
+Minerva
+Minerva Anestesiol
+Minerva Cardioangiol
+Minerva Cardioangiol Eur
+Minerva Chir
+Minerva Dermatol
+Minerva Dietol
+Minerva Dietol Gastroenterol
+Minerva Ecol Idroclimatol Fis Sanit
+Minerva Ecol Idroclimatol Fisiconucl
+Minerva Endocrinol
+Minerva Farm
+Minerva Fisiconucl
+Minerva Fisioter Radiobiol
+Minerva Gastroenterol
+Minerva Gastroenterol Dietol
+Minerva Ginecol
+Minerva Med
+Minerva Medicoleg
+Minerva Nefrol
+Minerva Neurochir
+Minerva Nipiol
+Minerva Nucl
+Minerva Oftalmol
+Minerva Ortognatod
+Minerva Ortop
+Minerva Otorinolaringol
+Minerva Pediatr
+Minerva Psichiatr
+Minerva Radiol
+Minerva Radiol Fisioter Radiobiol
+Minerva Stomatol
+Minerva Urol
+Minerva Urol Nefrol
+Mini Rev Med Chem
+Minim Invasive Neurosurg
+Minim Invasive Surg Nurs
+Minim Invasive Ther Allied Technol
+Minn Hist
+Minn Law Rev
+Minn Med
+Minn Nurs Accent
+Minn Pharm
+Minn Statut Annot Minn
+Minneap Dist Dent J
+Minor Nurse Newsl
+Minzoku Eisei
+Mirovaia Ekon Mezdunar Otnosheiia
+Misc Estud Arabes Hebraicos
+Mises Point Accouch Pediatre
+Miss Coll Law Rev
+Miss Dent Assoc J
+Miss Doct
+Miss Law J
+Miss Law Rev
+Miss Q
+Miss RN
+Miss Valley Med J
+Mission Hisp
+Mitochondrion
+Mitt Arbeitsmarkt Berufsforsch
+Mitt Dtsch Archaeol Inst Abt Kairo
+Mitt Dtsch Pharm Ges Pharm Ges DDR
+Mitt Forschungsbeitr Cusanus Ges
+Mitt Geb Lebensmittelunters Hyg
+Mitt Ges Salzburger Landeskd
+Mitt Krems Stadtarch
+Mitt Lebensmitteluntersuchung Hyg
+Mitt Med Abt Schweiz Unfallversicherunst
+Mitt Osterr Ges Tropenmed Parasitol
+Mitt Osterr Sanitatsverwalt
+Mittbl Osterr Ges Stat Inform
+Mitteldtsch Famkd
+Mittellat Jahrb
+Mmg
+Mnemosyne
+Mo Dent J
+Mo Hist Rev
+Mo Med
+Mo Nurse
+Mo Statut Annot Mo
+Mobius
+Mod Age
+Mod Asian Stud
+Mod China
+Mod Churchm
+Mod Concepts Cardiovasc Dis
+Mod Dent Pract
+Mod Healthc
+Mod Healthc (Short Term Care)
+Mod Hosp
+Mod Jud
+Mod Lang Notes
+Mod Law Rev
+Mod Med
+Mod Med Asia
+Mod Midwife
+Mod Nurs Home
+Mod Pathol
+Mod Philol
+Mod Probl Ophthalmol
+Mod Probl Paediatr
+Mod Probl Pharmacopsychiatry
+Mod Rheumatol
+Mod Schoolman
+Mod Treat
+Mod Trends Hum Reprod Physiol
+Mod Trends Immunol
+Mod Trends Med Virol
+Mod Trends Neurol
+Mod Trends Orthop
+Mod Trends Plast Surg
+Mod Trends Radiother
+Mod Trends Rheumatol
+Mod Trends Surg
+Mod Vet Pract
+Mohlomi
+Mol Aspects Med
+Mol Biochem Parasitol
+Mol Biol
+Mol Biol (Mosk 1972)
+Mol Biol (Mosk)
+Mol Biol Biochem Biophys
+Mol Biol Cell
+Mol Biol Evol
+Mol Biol Med
+Mol Biol Rep
+Mol Biotechnol
+Mol Biother
+Mol Cancer
+Mol Cancer Res
+Mol Cancer Ther
+Mol Carcinog
+Mol Cell
+Mol Cell Biochem
+Mol Cell Biol
+Mol Cell Biol Hum Dis Ser
+Mol Cell Biol Res Commun
+Mol Cell Endocrinol
+Mol Cell Neurosci
+Mol Cell Probes
+Mol Cell Proteomics
+Mol Cells
+Mol Chem Neuropathol
+Mol Diagn
+Mol Diagn Ther
+Mol Divers
+Mol Ecol
+Mol Ecol Notes
+Mol Endocrinol
+Mol Gen Genet
+Mol Gen Mikrobiol Virusol
+Mol Genet Genomics
+Mol Genet Med
+Mol Genet Metab
+Mol Hum Reprod
+Mol Imaging
+Mol Imaging Biol
+Mol Immunol
+Mol Interv
+Mol Mar Biol Biotechnol
+Mol Med
+Mol Med (Sofia)
+Mol Med Today
+Mol Membr Biol
+Mol Microbiol
+Mol Neurobiol
+Mol Nutr Food Res
+Mol Pain
+Mol Pathol
+Mol Pharm
+Mol Pharmacol
+Mol Phylogenet Evol
+Mol Phys
+Mol Plant Microbe Interact
+Mol Psychiatry
+Mol Reprod Dev
+Mol Syst Biol
+Mol Ther
+Mol Toxicol
+Mol Urol
+Mol Vis
+Moment
+Momentum
+Mon Bull Minist Health Public Health Lab Serv
+Mon Bull Stat U N Stat Off
+Mon Labor Rev
+Mon Not R Astron Soc
+Mon Rev
+Mon Vital Stat Rep
+Monaldi Arch Chest Dis
+Monash Bioeth Rev
+Monash Univ Law Rev
+Monastic Stud
+Monatsh Tierheilkd
+Monatsh Veterinarmed
+Monatskurse Arztl Fortbild [Bildbeil]
+Monatsschr Dtsch Zahnarzte Freie Zahnarzt
+Monatsschr Kinderheilkd
+Monatsschr Kriminol
+Monatsschr Ohrenheilkd Laryngorhinol
+Monatsschr Psychiatr Neurol
+Monatsschr Tuberkulosebekampf
+Monatsschr Unfallheilkd
+Monatsschr Unfallheilkd Versicher Versorg Verkehrsmed
+Monatsschr Unfallheilkd Versicherungsmed
+Mondai To Kenkyu
+Monday Dev
+Monde Alp Rhodan
+Monde Dent
+Monde Med
+Mondes Cult
+Mondes Dev
+Mondo Cin
+Mondo Odontostomatol
+Mondo Ortod
+Mong J Demogr
+Monist
+Monit Ostet Ginecol Endocrinol Metab
+Monit Ostet Ginecolog
+Monit Pharm Lab
+Monogr Allergy
+Monogr Am Assoc Ment Defic
+Monogr Am Assoc Ment Retard
+Monogr Atheroscler
+Monogr Clin Cytol
+Monogr Corp Cent Reg Poblac
+Monogr Dev Biol
+Monogr Endocrinol
+Monogr Gesamtgeb Neurol Psychiatr
+Monogr Gesamtgeb Psychiatr Psychiatry Ser
+Monogr Hum Genet
+Monogr Neoplast Dis Var Sites
+Monogr Neural Sci
+Monogr Oral Sci
+Monogr Paediatr
+Monogr Pathol
+Monogr Physiol Soc
+Monogr Popul Biol
+Monogr Quad Int Stor Med Sanita
+Monogr Ser World Health Organ
+Monogr Soc Res Child Dev
+Monogr Sov Med Sci
+Monogr Surg Sci
+Monogr Virol
+Monspel Hippocrates
+Mont Code Annot Mont
+Mont Law Rev
+Mont Wildl
+Montana
+Montfort
+Month
+Montp Med
+Montreal Med
+Monum Hist
+Monum Nippon
+Morb Mortal
+Morb Mortal Wkly Rep Surveill Summ
+Moreana
+Morfologiia
+Morphol Embryol (Bucur)
+Morphol Igazsagugyi Orv Sz
+Morphol Med
+Morphologie
+Mosq News
+Mother Jones
+Mothers Child
+Motor Control
+Mouth
+Mouv Soc
+Mov Disord
+Mov Operaio Soc
+Moyo
+Ms
+Mt Res Dev
+Mt Sinai J Med
+Mult Scler
+Multibody Syst Dyn
+Multis
+Multivariate Behav Res
+Munca Sanit
+Munch Med Wochenschr
+Mund Kiefer Gesichtschir
+Mundo Med
+Munger Afr Libr Notes
+Munnpleien
+Munstersche Beitr Gesch Theor Med
+Mus Geneve
+Mus Helv
+Muscle Biol
+Muscle Nerve
+Muscle Nerve Suppl
+Museon
+Music Lett
+Muslim World
+Muslim World (Hartford)
+Mutagenesis
+Mutat Res
+Mycol Res
+Mycologia
+Mycopathol Mycol Appl
+Mycopathologia
+Mycorrhiza
+Mycoses
+Mykosen
+Mykosen Suppl
+Mymensingh Med J
+N C Dent Gaz
+N C Dent J
+N C Folklor
+N C Hist Rev
+N C J Ment Health
+N C Med J
+N D Century Code N D
+N D Hist
+N D Law Rev
+N Engl Galaxy
+N Engl Hist Geneal Regist
+N Engl J Crim Civ Confin
+N Engl J Hum Serv
+N Engl J Med
+N Engl J Public Policy
+N Engl Reg Allergy Proc
+N H Revis Statut Annot N H
+N HC Perspect Community
+N J Healthc
+N J Hist
+N J Hist Comm Newsl
+N J J Pharm
+N J League Nurs News
+N J Med
+N J Nurse
+N J Statut Annot N J
+N M Dent J
+N M Hist Rev
+N M Law Rev
+N M Nurse
+N M Statut 1978 Annot N M
+N S Hist Rev
+N S Med Bull
+N S W Public Health Bull
+N Y Aff
+N Y Folk
+N Y Hist
+N Y J Dent
+N Y Law Forum
+N Y Law J
+N Y Law Sch J Hum Rights
+N Y Law Sch J Int Comp Law
+N Y Law School Hum Rights Annu
+N Y Med Coll News Notes
+N Y Med Q
+N Y Physician
+N Y State Bar J
+N Y State Dent J
+N Y State J Med
+N Y State Nurse
+N Y Suppl Second Ser
+N Y Times Mag
+N Y Univ J Dent
+N Y Univ J Int Law Polit
+N Y Univ J Legis Public Policy
+N Y Univ Law Rev
+N Z Bioeth J
+N Z Cartogr Geogr Inf Sys
+N Z Dent J
+N Z Fam Physician
+N Z Geog
+N Z Health Hospital
+N Z Hosp
+N Z J Fam Plann
+N Z J Geogr
+N Z J Hist
+N Z Law J
+N Z Law Rep
+N Z Libr
+N Z Med J
+N Z Nurs Forum
+N Z Nurs J
+N Z Popul Newsl
+N Z Popul Rev
+N Z Sch Dent Serv Gaz
+N Z Vet J
+NAACOG Newsl
+NAACOG Tech Bull
+NAACOGS Clin Issu Perinat Womens Health Nurs
+NABER Rep
+NACDL J
+NACUBO Bus Off
+NADA
+NADC MA United States Nav Air Dev Cen Johnsville Pa Aviat Med Accel Lab
+NADC-MR Rep
+NADL J
+NAHAM Access Manag J
+NAHAM Manage J
+NANR News
+NAPHT News
+NARESA Mongr
+NASA Contract Rep NASA CR
+NASPCP Newsl
+NATNEWS
+NBER Work Pap Ser
+NCBHR Commun
+NCCE News
+NCEHR Commun
+NCI Monogr
+NCP Bull
+NCSDHA Dent Hyg
+NCSL Legisbrief
+NDA J
+NEHW Health Watch
+NFAIS Newsl
+NFI Bull
+NFPA J
+NHPF Issue Brief
+NIAID AIDS Agenda
+NIDA Res Monogr
+NIH Consens Dev Conf Summ
+NIH Consens State Sci Statements
+NIH Consens Statement
+NIH Guide Grants Contracts
+NIHAE Bull
+NIPH Ann
+NITA
+NJPC Bull
+NJSNA Newsl
+NLN Conv Pap
+NLN Publ
+NLN Update
+NMR Biomed
+NP News
+NPG Forum Ser
+NPN Med
+NS Hist Q
+NT Learn Curve
+NTM
+NTP CERHR MON
+NY Dent J
+NY Folkl Q
+NY Hist Soc Q
+NY Law Sch Law Rev
+NY Med
+NY Times (Print)
+NYO Rep
+NYSSNTA J
+NZ J Psychol
+NZ Psychol
+Nachr Akad Wiss Gott Philol Hist Kl
+Nachr Inst Gesch Naturwiss Hamburg
+Nachrichtenbl Dtsch Ges Gesch Med Naturwiss Tech
+Nagasaki Igakkai Zasshi
+Nagoya J Med Sci
+Nagoya Med J
+Nahrung
+Naika
+Naika Hokan
+Nakadori Byoin Iho
+Nan Fang Yi Ke Da Xue Xue Bao
+Nano Lett
+Nanomedicine
+Nanotechnology
+Nanyang Wenti Yenchiu
+Nar Zdrav
+Narodonaselenie
+Naselenie
+Nasnewsletter
+Nasu Suteshon
+Nasza Przeslosc
+Nat Biotechnol
+Nat Cell Biol
+Nat Chem Biol
+Nat Clin Pract Cardiovasc Med
+Nat Clin Pract Gastroenterol Hepatol
+Nat Clin Pract Oncol
+Nat Clin Pract Urol
+Nat Genet
+Nat Hist
+Nat Immun
+Nat Immun Cell Growth Regul
+Nat Immunol
+Nat Law Forum
+Nat Mater
+Nat Med
+Nat Methods
+Nat Monspel Ser Bot
+Nat Neurosci
+Nat New Biol
+Nat Prod Lett
+Nat Prod Rep
+Nat Prod Res
+Nat Resour Forum
+Nat Resour J
+Nat Rev Cancer
+Nat Rev Drug Discov
+Nat Rev Genet
+Nat Rev Immunol
+Nat Rev Microbiol
+Nat Rev Mol Cell Biol
+Nat Rev Neurosci
+Nat Struct Biol
+Nat Struct Mol Biol
+Nat Syst
+Nat Toxins
+Nation
+Nationalokon Tidsskr
+Nations Bus
+Nations Health
+Natl AIDS Bull
+Natl Black Law J
+Natl Bur Econ Res Bull Aging Health
+Natl Cancer Inst Carcinog Tech Rep Ser
+Natl Cancer Inst Monogr
+Natl Cancer Inst Res Rep
+Natl Cathol Bioeth Q
+Natl Cathol Report
+Natl Civ Rev
+Natl Conf Dent Public Relat
+Natl Dent Assoc J
+Natl Eng
+Natl Fam Health Surv Bull
+Natl Fed Cathol Physicians Guild Newsl
+Natl Forum
+Natl Geogr Mag
+Natl Geogr Res
+Natl Hosp Health Care
+Natl Inst Anim Health Q (Tokyo)
+Natl Inst Drug Abuse Res Monogr Ser
+Natl Inst Econ Rev
+Natl Inst Health Consens Dev Conf Consens Statement
+Natl Inst Health Consens Dev Conf Summ
+Natl J (Wash)
+Natl Jew Law Rev
+Natl Jew Mon
+Natl Law J
+Natl Med Care Util Expend Surv B
+Natl Med Care Util Expend Surv C
+Natl Med J India
+Natl Med Leg J
+Natl Netw
+Natl Nosocomial Infect Study Rep
+Natl Obs
+Natl Pap
+Natl Popul News Bull
+Natl Rep Subacute Care
+Natl Rev
+Natl Saf News
+Natl Tax J
+Natl Toxicol Program Tech Rep Ser
+Natl Underwrit [Life Health]
+Natl Univ Law Rev
+Natl Vital Stat Rep
+Natl Widl
+Natl Womens Health Rep
+Natur Mus
+Natura
+Nature
+Naturwiss Rundsch
+Naturwissenschaften
+Nauchni Tr Med Acad Vulko Chervenkov
+Nauchni Tr Nauchnoizsled Stomatol Inst (Sofiia)
+Nauchni Tr Vissh Med Inst Sofiia
+Nauchnye Doki Vyss Shkoly Biol Nauki
+Naunyn Schmiedebergs Arch Exp Pathol Pharmakol
+Naunyn Schmiedebergs Arch Pharmacol
+Naunyn Schmiedebergs Arch Pharmakol
+Naunyn Schmiedebergs Arch Pharmakol Exp Pathol
+Nav Rev
+Navy Med
+Neb Law Rev
+Nebr Hist
+Nebr Med J
+Nebr Nurse
+Nebr State Med J
+Nebr Symp Motiv
+Ned Milit Geneeskd Tijdschr
+Ned Tandartsenbl
+Ned Tijdschr Geneeskd
+Ned Tijdschr Gerontol
+Ned Tijdschr Psychol
+Ned Tijdschr Tandheelkd
+Ned Tijdschr Verloskd Gynaecol
+Neerl Volksleven
+Nefrologia
+Negro Hist Bull
+Neirofiziologiia
+Neonatal Intensive Care
+Neonatal Netw
+Neoplasia
+Neoplasma
+Neopsichiatria
+Nepal Med Coll J
+Nepal Popul Dev J
+Nepegeszseguegy
+Nephrol Dial Transplant
+Nephrol News Issues
+Nephrol Nurs J
+Nephrol Nurse
+Nephrologie
+Nephrology (Carlton)
+Nephron
+Nephron Clin Pract
+Nephron Exp Nephrol
+Nephron Physiol
+Nerv Child
+Nerv Sist
+Nervenarzt
+Nestle Nutr Workshop Ser Clin Perform Programme
+Nestle Nutr Workshop Ser Pediatr Program
+Neth J Med
+Neth J Surg
+Netw Fr
+Netw Int Fertil Res Program
+Netw Res Triangle Park N C
+Network
+Neue Heimat
+Neue Jurist Wochenschr
+Neue Munch Beitr Gesch Med Naturwiss Medizinhist Reihe
+Neue Osterr Z Kinderheilkd
+Neue Polit Lit
+Neue Rundsch
+Neuere Med Wiss Quellen Stud
+Neues Jahrb Geol Palaontol Abh
+Neumol Cir Torax
+Neuphilol Mitt
+Neural Comput
+Neural Netw
+Neural Plast
+Neuro Endocrinol Lett
+Neuro-oncol
+NeuroRehabilitation
+NeuroRx
+Neurobehav Toxicol
+Neurobehav Toxicol Teratol
+Neurobiol Aging
+Neurobiol Dis
+Neurobiol Learn Mem
+Neurobiologia
+Neurobiology
+Neurobiology (Bp)
+Neurocase
+Neurochem Int
+Neurochem Pathol
+Neurochem Res
+Neurochirurgia (Stuttg)
+Neurochirurgie
+Neurocirugia
+Neurocirugia (Astur)
+Neurocomputing
+Neurocrit Care
+Neurodegeneration
+Neuroendocrinology
+Neuroepidemiology
+Neurofibromatosis
+Neurogastroenterol Motil
+Neurogenetics
+Neuroimage
+Neuroimaging Clin N Am
+Neuroimmunomodulation
+Neuroinformatics
+Neurol Clin
+Neurol Clin Neurophysiol
+Neurol Croat
+Neurol India
+Neurol Med Chir (Tokyo)
+Neurol Neurochir Pol
+Neurol Neurochir Psychiatr Pol
+Neurol Neurocir Psiquiatr
+Neurol Psihiatr Neurochir
+Neurol Psychiatr (Bucur)
+Neurol Psychiatr Ceskoslov
+Neurol Res
+Neurol Sci
+Neurologia
+Neurologija
+Neurologist
+Neurology
+Neuromolecular Med
+Neuromuscul Disord
+Neuron
+Neuron Glia Biol
+Neuroophthalmology
+Neuropadiatrie
+Neuropathol Appl Neurobiol
+Neuropathology
+Neuropatol Pol
+Neuropediatrics
+Neuropeptides
+Neuropharmacology
+Neurophysiol Clin
+Neurophysiology
+Neuropsichiatria
+Neuropsihijatrija
+Neuropsychiatr Enfance Adolesc
+Neuropsychiatry
+Neuropsychiatry Neuropsychol Behav Neurol
+Neuropsychobiology
+Neuropsychol Dev Cogn B Aging Neuropsychol Cogn
+Neuropsychol Rehabil
+Neuropsychol Rev
+Neuropsychologia
+Neuropsychology
+Neuropsychopharmacol Hung
+Neuropsychopharmacology
+Neuroradiology
+Neurorehabil Neural Repair
+Neuroreport
+Neurosci Behav Physiol
+Neurosci Biobehav Rev
+Neurosci Lett
+Neurosci Lett Suppl
+Neurosci News
+Neurosci Res
+Neurosci Res (N Y)
+Neurosci Res Commun
+Neurosci Res Program Bull
+Neurosci Res Suppl
+Neuroscience
+Neuroscientist
+Neurosignals
+Neurosurg Clin N Am
+Neurosurg Focus
+Neurosurg Rev
+Neurosurgery
+Neurotox Res
+Neurotoxicol Teratol
+Neurotoxicology
+Neurotoxins
+Neurourol Urodyn
+Neuss Jahrb
+Nev Hist Soc Q
+Nev Nurses Assoc Q Newsl
+Nev Revis Statut Nev
+Nev Rnformation
+Nevrone Period Neurol Psichiatr Sci Affin
+Nevropatol Psikhiatriia
+New Afr
+New Am Rev
+New Atlantis
+New Biol
+New Blackfriars
+New Cathol World
+New Dent
+New Dir Child Adolesc Dev
+New Dir Child Dev
+New Dir Ment Health Serv
+New Dir Youth Dev
+New Egypt J Med
+New Engl J Prison Law
+New Engl Law Rev
+New Engl Q
+New Era Nurs Image Int
+New Genet Soc
+New Ger Crit
+New Guard
+New Horiz
+New Humanist
+New Hung Q
+New Int
+New Istanbul Contrib Clin Sci
+New Law J
+New Lead
+New Left Rev
+New Lit Hist
+New Microbiol
+New Orleans Med Surg J
+New Perspect Q
+New Perspect Turk
+New Philipp
+New Philos
+New Physician
+New Phytol
+New Repub
+New Rev Bioeth
+New Scholasticism
+New Sci
+New Sci Sci J
+New Soc
+New South
+New Statesman
+New Theol Rev
+New Times
+New Univ Q
+New West
+New World
+New York
+New York Rev Books
+New Yorker
+New community
+Newborn Infant Nurs Rev
+Newcastle Med J
+Newfoundl Med Assoc J
+Newport
+Newport Hist
+News Bull Indian Dent Assoc
+News Lett Florence Nightingale Int Nurs Assoc
+News Lett Maine State Nurses Assoc
+News Nor
+News Notes Ohio Dent J
+News Physiol Sci
+News Views
+Newsette
+Newsl Am Acad Health Adm
+Newsl Am Acad Implant Dent
+Newsl Am Acad Pediatr
+Newsl Am Acad Psychiatry Law
+Newsl Am Assoc Tissue Banks
+Newsl Biomed Saf Stand
+Newsl Br Soc Hist Sci
+Newsl Can Soc Hist Med
+Newsl Fam Plan Assoc Hong Kong
+Newsl Fam Plan Int Assist
+Newsl Feminism Philos
+Newsl Group Use Psychol Hist
+Newsl Hist Anthropol
+Newsl Infant Feed Action Coalit
+Newsl Int Acad Periodontol
+Newsl Int Coll Dent India Sect
+Newsl Int Union Sci Study Popul
+Newsl Inter Afr Comm Tradit Pract Affect Health Women Child
+Newsl Ky Nurses Assoc
+Newsl Macro Syst Inst Resour Dev Demogr Health Surv
+Newsl Med Sci Hist Soc
+Newsl Scand Soc Forensic Odontol
+Newsl Sci Technol Human Values
+Newsl Soc Anc Med
+Newsl Soc Anc Med Pharm
+Newsl Soc Welf Hist Group
+Newsl Springfield Dent Soc
+Newsl Wis League Nurs
+Newsl Womens Glob Netw Reprod Rights
+Newsline People AIDS Coalit N Y
+Newswatch
+Newsweek
+Niagara Frontier
+Nicar Med
+Nicar Odontol
+Nice Hist
+Nice Med
+Nichibunken Newsl
+Nichidai Igaku Zasshi
+Nichidai Koko Kagaku
+Nicotine Tob Res
+Niederdtsch Mitt
+Niederhein Jahrb
+Niedersachs Arztebl
+Niedersachs Zahnarztebl
+Nieuwe Ned Bijdr Geschied Geneeskd Natuurwet
+Nieuwe West Indische Gids
+Niger Classics
+Niger Dent J
+Niger J Clin Pract
+Niger J Econ Soc Stud
+Niger J Med
+Niger J Paediatr
+Niger Mag
+Niger Med J
+Niger Med Pract
+Niger Nurse
+Niger Pop
+Niger Postgrad Med J
+Nihon Ago Kansetsu Gakkai Zasshi
+Nihon Arukoru Yakubutsu Igakkai Zasshi
+Nihon Eiyo Shokuryo Gakkai Shi
+Nihon Hansenbyo Gakkai Zasshi
+Nihon Hotetsu Shika Gakkai Zasshi
+Nihon Kango Kagakkaishi
+Nihon Kokyuki Gakkai Zasshi
+Nihon Kyobu Shikkan Gakkai Zasshi
+Nihon Rinsho Meneki Gakkai Kaishi
+Nihon Rinsho Shishubyo Danwakai Kaishi
+Nihon Shinkei Seishin Yakurigaku Zasshi
+Nihon Sutoma Rihabiriteshon Gakkaishi J Jpn Soc Stoma Rehabil
+Nihon Univ Comp Law
+Nihon Univ J Med
+Nihonshi Kenkyu
+Niigata Igakkai Zasshi
+Ninet Century
+Ninet Century Contexts
+Nineteenth Century Fict
+Nippon Byori Gakkai Kaishi
+Nippon Chokucho Komonbyo
+Nippon Daicho Komonbyo Gakkai Zasshi
+Nippon Densenbyo Gakkai Zasshi
+Nippon Eiseigaku Zasshi
+Nippon Funin Gakkai Zasshi
+Nippon Gan Chiryo Gakkai Shi
+Nippon Ganka Gakkai Zasshi
+Nippon Ganka Kiyo
+Nippon Geka Gakkai Zasshi
+Nippon Geka Hokan
+Nippon Heikatsukin Gakkai Zasshi
+Nippon Hifuka Gakkai Zasshi
+Nippon Hinyokika Gakkai Zasshi
+Nippon Hoigaku Zasshi
+Nippon Hoshasen Gijutsu Gakkai Zasshi
+Nippon Igaku Hoshasen Gakkai Zasshi
+Nippon Ika Daigaku Zasshi
+Nippon Ishigaku Zasshi
+Nippon Ishikai Zasshi
+Nippon Ishinkin Gakkai Zasshi
+Nippon Jibiinkoka Gakkai Kaiho
+Nippon Jinzo Gakkai Shi
+Nippon Juigaku Zasshi
+Nippon Ketsueki Gakkai Zasshi
+Nippon Koku Geka Gakkai Zasshi
+Nippon Koshu Eisei Zasshi
+Nippon Kyobu Geka Gakkai Zasshi
+Nippon Kyosei Shika Gakkai Zasshi
+Nippon Naibunpi Gakkai Zasshi
+Nippon Naika Gakkai Zasshi
+Nippon Rai Gakkai Zasshi
+Nippon Rinsho
+Nippon Ronen Igakkai Zasshi
+Nippon Saikingaku Zasshi
+Nippon Sanka Fujinka Gakkai Zasshi
+Nippon Seikeigeka Gakkai Zasshi
+Nippon Seirigaku Zasshi
+Nippon Shika Hyoron
+Nippon Shika Ishikai Zasshi
+Nippon Shika Zairyo Kikai Gakkai Zasshi
+Nippon Shishubyo Gakkai Kaishi
+Nippon Shokakibyo Gakkai Zasshi
+Nippon Shonika Gakkai Zasshi
+Nippon Suisan Gakkai Shi
+Nippon Yakurigaku Zasshi
+Nisshin Igaku Jpn J Med Prog
+Nitric Oxide
+No Shinkei Geka
+No To Hattatsu
+No To Shinkei
+Nobel Symp
+Noesis
+Noise Health
+Nomad People
+Nonlinear Dynamics Psychol Life Sci
+Nonlinearity Biol Toxicol Med
+Nonprofit Manag Leadersh
+Noordgouw
+Nor Geogr Tidsskr
+Nor Tannlaegeforen Tid
+Nor Teol Tidsskr
+Nord Hyg Tidskr
+Nord Hyg Tidskr Suppl
+Nord J Psychiatry
+Nord J Sov East Eur Stud
+Nord Med
+Nord Medicinhist Arsb
+Nord Psyk Medl
+Nord Psykiatr Tidsskr
+Nord Psykol
+Nord Sexol
+Nord Vet Med
+Norm Pathol Anat (Stuttg)
+Norois
+North Am Rev
+North Am Vet
+North Carol Centr Law J
+North Carol Law Rev
+North East Rep Second Ser
+North Hist
+North Ill Univ Law Rev
+North Irel Law Rep
+North Irel Leg Q
+North KY Law Rev
+North Ky State Law Forum
+North Scotl
+North Va Herit
+North West Rep Second Ser
+Northeast Afr Stud
+Northeast Folk
+Northeast Nev Hist Soc Q
+Northwest Dent
+Northwest Dent Res
+Northwest Med
+Northwest Ohio Q
+Northwest Rep
+Northwest Univ Law Rev
+Noseleutike
+Noshuyo Byori
+Nosokom Chron
+Not Farm
+Not Man Apart
+Not Polit
+Notas Poblacion
+Notatki Plockie
+Notes Queries
+Notes Rec R Soc Lond
+Notes Undergr
+Notre Dame J Law Ethics Public Policy
+Notre Dame Law Rev
+Notre Dame Lawyer
+Nourrisson
+Nous
+Nouv Com Int Cathol Infirm Assist Med Soc
+Nouv Ec
+Nouv J Chim
+Nouv Presse Med
+Nouv Repub Lett
+Nouv Rev 16e Siecle
+Nouv Rev Deux Mondes
+Nouv Rev Fr Hematol
+Nouv Rev Fr Hematol Blood Cells
+Nov Khir
+Nov Med Priborostr
+Nov Med Tekh
+Nov Novejsaja Istor
+Nova Acta Leopold
+Nova Acta Paracelsica
+Nova Acta Stomatol
+Nova Law J
+Nova Law Rev
+Novartis Found Symp
+Nowe Drogi
+Nowotwory
+Nucl Eng Des/Fusion
+Nucl Instrum Methods Phys Res A
+Nucl Instrum Methods Phys Res B
+Nucl Med (Stuttg)
+Nucl Med Biol
+Nucl Med Commun
+Nucl Med Rev Cent East Eur
+Nucl Phys A
+Nucl Recept
+Nucl Recept Signal
+Nucl Sci Eng
+Nucl Technol
+Nucl Tracks Radiat Meas
+Nucleic Acids Res
+Nucleic Acids Res Spec Publ
+Nucleic Acids Res Suppl
+Nucleic Acids Symp Ser
+Nucleonics
+Nucleosides Nucleotides
+Nucleosides Nucleotides Nucleic Acids
+Nucleus
+Nuestra Hist
+Nueva Enferm
+Nuevos Libros
+Nufusbil Derg
+Nuklearmedizin
+Numen
+Nuncius
+Nunt Radiol
+Nuova Antologia
+Nuova Riv Stor
+Nuova Vet
+Nuovi Ann Ig Microbiol
+Nuovo Cimento C
+Nurs Adm Q
+Nurs BC
+Nurs Care
+Nurs Careers
+Nurs Case Manag
+Nurs Clin North Am
+Nurs Crit Care
+Nurs Diagn
+Nurs Dig
+Nurs Dyn
+Nurs Econ
+Nurs Educ Microworld
+Nurs Educ Monogr
+Nurs Educ Perspect
+Nurs Elder
+Nurs Ethics
+Nurs Focus
+Nurs Forum
+Nurs Forum (Auckl)
+Nurs Health Care
+Nurs Health Care Perspect
+Nurs Health Sci
+Nurs Hist Rev
+Nurs Homes
+Nurs Homes Sr Citiz Care
+Nurs Inq
+Nurs J (Manila)
+Nurs J India
+Nurs J Singapore
+Nurs Law Ethics
+Nurs Law Regan Rep
+Nurs Leadersh
+Nurs Leadersh Forum
+Nurs Life
+Nurs Manag (Harrow)
+Nurs Manage
+Nurs Mirror
+Nurs Mirror Midwives J
+Nurs Montreal
+Nurs N Z
+Nurs News
+Nurs News (Meriden)
+Nurs Older People
+Nurs Outlook
+Nurs Pap
+Nurs Philos
+Nurs Pract
+Nurs Prax N Z
+Nurs Qual Connect
+Nurs Que
+Nurs RSA
+Nurs Res
+Nurs Res Conf
+Nurs Res Rep
+Nurs Rev
+Nurs Sci
+Nurs Sci Q
+Nurs Spectr (Fla Ed)
+Nurs Spectr (Gt Chic Ne Ill Nw Indiana Ed)
+Nurs Spectr (Gt Phila Tri State Ed)
+Nurs Spectr (N Engl Ed)
+Nurs Spectr (Wash D C)
+Nurs Staff Dev Insid
+Nurs Stand
+Nurs Stand Spec Suppl
+Nurs Success Today
+Nurs Times
+Nurs Times Nurs Homes
+Nurs World
+Nurse Anesth
+Nurse Author Ed
+Nurse Educ
+Nurse Educ Oppor Innov
+Nurse Educ Today
+Nurse Managers Bookshelf
+Nurse Pract
+Nurse Pract Forum
+Nurse Res
+Nurses Lamp
+Nursing
+Nursing (Brux)
+Nursing (Lond)
+Nursingconnections
+Nutr Abstr Rev
+Nutr Abstr Rev Ser Hum Exp
+Nutr Cancer
+Nutr Clin Care
+Nutr Clin Pract
+Nutr Dieta Eur Rev Nutr Diet
+Nutr Health
+Nutr Healthy Living
+Nutr Hist Notes
+Nutr Hosp
+Nutr J
+Nutr Life
+Nutr Metab
+Nutr Metab (Lond)
+Nutr Metab Cardiovasc Dis
+Nutr Neurosci
+Nutr Rep Int
+Nutr Res
+Nutr Rev
+Nutr Today
+Nutr Update
+Nutrition
+OCCUP Outlook Q
+OECD Obs
+OH
+OHMS Dig
+OMICS
+ONA J
+ONS News
+OR Manager
+OR Tech
+ORGYN
+ORINS Rep US At Energy Comm
+ORL Head Neck Nurs
+ORL J Otorhinolaryngol Relat Spec
+ORNL
+ORNL-NSIC Rep
+ORNL-TM Rep
+ORO Rep
+Ob Gyn News
+Ob/Gyn World
+Obes Res
+Obes Rev
+Obes Surg
+Obesity (Silver Spring)
+Objets Mondes
+Obs Diagn Econ
+Obstet Ginecol (Bucur)
+Obstet Ginecol Lat Am
+Obstet Gynecol
+Obstet Gynecol Annu
+Obstet Gynecol Clin North Am
+Obstet Gynecol Surv
+Obstet Prat
+Occas Newsl Lindsay Club
+Occas Pap Med Hist Aust
+Occas Pap R Coll Gen Pract
+Occup Environ Med
+Occup Health (Auckl)
+Occup Health (Lond)
+Occup Health Nurs
+Occup Health Nurse (Auckl)
+Occup Health Rev
+Occup Health Saf
+Occup Med
+Occup Med (Lond)
+Occup Ther Int
+Occup Ther Rehabil
+Oceania
+Ochanomizu Igaku Zasshi
+Ochsner Clin Rep
+Ocul Immunol Inflamm
+Ocul Surf
+Odonatologica
+Odont
+Odontes
+Odontiatriki
+Odontoestomatologia
+Odontoiatr Prat
+Odontoiatr Rev Iberoam Med Boca
+Odontol Am
+Odontol Atual
+Odontol Bull
+Odontol Capixaba
+Odontol Chil
+Odontol Clin
+Odontol Conserv
+Odontol Din
+Odontol Foren Tidskr
+Odontol Jalisc
+Odontol Peru
+Odontol Postgrado
+Odontol Pract
+Odontol Revy
+Odontol Revy Suppl
+Odontol Tidskr
+Odontol Urug
+Odontol [Mexico]
+Odontologia
+Odontologia (Lima)
+Odontologica
+Odontologie
+Odontologo
+Odontology
+Odontoprotesi
+Odontostomatol Implantoprotesi
+Odontostomatol Proodos
+Odontostomatol Trop
+Oecologia
+Oecon Pol
+Off J Can Assoc Crit Care Nurs
+Off Our Backs
+Off Publ Dent Hyg Assoc State N Y
+Offentl Gesundheitsdienst
+Offentl Gesundheitswes
+Office
+Oftalmol Zh
+Oftalmologia
+Ohio Dent J
+Ohio Hist
+Ohio J Sci
+Ohio Med
+Ohio North Univ Law Rev
+Ohio Nurses Rev
+Ohio State Law J
+Ohio State Med J
+Oil Paint Drug Report
+Okajimas Folia Anat Jpn
+Okla Nurse
+Okla Statut Annot Okla
+Oklahoma City Univ Law Rev
+Oklahoma Law Rev
+Old Engl Newsl
+Old Northwest
+Oldenburg Familienkd
+Oligonucleotides
+Omaly Anio
+Omega (Westport)
+Omni
+Omnia Med
+Omnia Med Suppl
+Omnia Med Ther
+Omnia Ther
+Omnia Ther Suppl
+Omvardaren
+Oncodev Biol Med
+Oncogene
+Oncogene Res
+Oncol Nurs Forum
+Oncol Rep
+Oncol Res
+Oncologia
+Oncologica
+Oncologist
+Oncology
+Oncology (Williston Park)
+Onderstepoort J Vet Res
+Ondontol Bonaer
+One One
+Onkologie
+Online J Curr Clin Trials
+Online J Issues Nurs
+Online J Knowl Synth Nurs
+Ons Amst
+Ons Heem
+Onsen Kagaku
+Ont Dent
+Ont Hist
+Ont Med Rev
+Ont Psychol
+Ontogenez
+Oostvlaam Zanten
+Oper Dent
+Oper Dent Suppl
+Oper Orthop Traumatol
+Oper Res
+Oper Res Q
+Oper Tech Sports Med
+Ophtalmologie
+Ophthal Plast Reconstr Surg
+Ophthalmic Epidemiol
+Ophthalmic Genet
+Ophthalmic Lit
+Ophthalmic Paediatr Genet
+Ophthalmic Physiol Opt
+Ophthalmic Res
+Ophthalmic Semin
+Ophthalmic Surg
+Ophthalmic Surg Lasers
+Ophthalmic Surg Lasers Imaging
+Ophthalmol Clin North Am
+Ophthalmologe
+Ophthalmologica
+Ophthalmology
+Opin Atty Gen State Md Md Atty Gen Off
+Opt Acta (Lond)
+Opt Commun
+Opt Express
+Opt J Rev Optom
+Opt Lett
+Optician
+Options Policy Pract
+Optom Clin
+Optom Vis Sci
+Optom Wkly
+Optometry
+Opusc Med
+Opusculum
+Or Revis Statut Or
+Oral Dis
+Oral Health
+Oral Health Prev Dent
+Oral Hist Rev
+Oral Hyg
+Oral Implantol
+Oral Microbiol Immunol
+Oral Oncol
+Oral Sci Rev
+Oral Surg Oral Diagn
+Oral Surg Oral Med Oral Pathol
+Oral Surg Oral Med Oral Pathol Oral Radiol Endod
+Orale Implantol
+Oralprophylaxe
+Orange Cty Dent Soc Bull
+Orbis
+Orbit
+Orchester
+Ordre Natl Chir Dent Cons Natl Bull Off
+Oreg Hist Q
+Oreg Nurse
+Oreg State Dent J
+Oregon Law Rev
+Org Biomol Chem
+Org Geochem
+Org Lett
+Org Prep Proced Int
+Organ
+Organ Behav Hum Decis Process
+Organ Behav Hum Perform
+Organ Dyn
+Organometallics
+Organon
+Oriens Extremus
+Oriente Crist
+Oriente Mod
+Orig Life
+Orig Life Evol Biosph
+Origins
+Orizz Ortop Odie Riabil
+Ornicar
+Orthod Craniofac Res
+Orthod Epitheorese
+Orthod Fr
+Orthod Rev
+Orthodontist
+Orthop Clin North Am
+Orthop Nurs
+Orthop Rev
+Orthop Surg
+Orthopade
+Orthopedics
+Ortodoncia
+Ortodontia
+Ortop Maxilar
+Ortop Travmatol Protez
+Orv Hetil
+Orvoskepzes
+Orvosok Lapja Nepeu
+Orvostort Kozl
+Osaka City Med J
+Osaka Daigaku Shigaku Zasshi
+Osaka Univ Law Rev
+Osgoode Hall Law J
+Osiris
+Osler Libr Newsl
+Osp Ital Chir
+Osp Maggiore
+Osp Psichiatr
+Osteoarthritis Cartilage
+Osteopath Hosp
+Osteopath Hosp Leadersh
+Osteopath Prof
+Osteoporos Int
+Osterr Apoth Ztg
+Osterr Arzteztg
+Osterr Dent Z
+Osterr Hebammenztg
+Osterr Krankenpflegez
+Osterr Schwesternztg
+Osterr Z Erforsch Bekampf Krebskr
+Osterr Z Onkol
+Osterr Z Stat Inform
+Osterr Z Stomatol
+Osterr Zahnarzteztg
+Osterr Zahnprothet
+Osterr Zahntech Handw
+Osterr Zahntech Handwerk
+Osterreichische Pflegezeitschrift
+Osteuropa
+Ostomy Wound Manage
+Otago Law Rev
+Otol Neurotol
+Otolaryngol Clin North Am
+Otolaryngol Head Neck Surg
+Otolaryngol Pol
+Otolaryngology
+Otorinolaringol Ital
+Otorinolaringologie
+Ottawa Law Rev
+Ou Daigaku Shigakushi
+Oudheidkd Meded Rijksmus Oudheiden Leiden
+Ouest Med
+Our Planet
+Outcomes Manag
+Outcomes Manag Nurs Pract
+Outlook Agric
+Outlook Bull South Dent Soc N J
+Outlook Mag
+Overijssel
+Overseas Postgrad Med J
+Oxf Bull Econ Stat
+Oxf Econ Pap
+Oxf Ger Stud
+Oxf J Leg Stud
+Oxf Med Sch Gaz
+Oxf Rev Reprod Biol
+Oxf Slav Pap
+Oxf Surv Eukaryot Genes
+P N G Med J
+P R Enferm
+P R Health Sci J
+P S Q
+PA Bar Assoc Q
+PA J
+PAN
+PAS Rep
+PCR Methods Appl
+PCR Rep
+PDA J Pharm Sci Technol
+PDM
+PFCA Rev
+PGRSA Q
+PHC4 FYI
+PI Perspect
+PIACT Pap
+PIACT Prod News
+PIDSA Abstr
+PLoS Biol
+PLoS Comput Biol
+PLoS Genet
+PLoS Med
+PLoS Pathog
+POPCEN Newsl
+POPIN Bull
+PP News
+PR J Public Health Trop Med
+PRB Rep
+PRN Forum
+PS (Wash DC)
+PSD Commun Newsl
+Pa Archaeol
+Pa Dent J (Harrisb)
+Pa Dist Cty Rep
+Pa Folklife
+Pa Health You
+Pa Herit
+Pa Hist
+Pa Mag Hist Biogr
+Pa Med
+Pa Med J
+Pa Mennon Herit
+Pa Nurse
+Pac AIDS Alert Bull
+Pac Aff
+Pac Health Dialog
+Pac Hist
+Pac Hist Rev
+Pac Law J
+Pac Med Surg
+Pac Northwest
+Pac Northwest Q
+Pac Rep Second Ser
+Pac Sociol Rev
+Pac Stud
+Pac Symp Biocomput
+Pac Viewp
+Pace Law Rev
+Pacifica
+Pacing Clin Electrophysiol
+Padiatr Grenzgeb
+Padiatr Padol
+Padiatr Padol Suppl
+Paedagog Hist
+Paediatr Anaesth
+Paediatr Danub
+Paediatr Drugs
+Paediatr Indones
+Paediatr Jpn
+Paediatr Nurs
+Paediatr Perinat Epidemiol
+Paediatr Respir Rev
+Paediatr Univ Tokyo
+Paediatrician
+Paedovita
+Pag Bull
+Pages Ohio Revis Code Annot Ohio
+Pagine Stor Med [Collana]
+Pahlavi Med J
+Pain
+Pain Headache
+Pain Manag Nurs
+Pain Med
+Pain Res Manag
+Pain Suppl
+Pak Dent Rev
+Pak Dev Rev
+Pak Econ Soc Rev
+Pak J Fam Plann
+Pak J Health
+Pak J Med Res
+Pak J Pharm Sci
+Pak J Psychol
+Pak J Sci
+Pak J Sci Ind Res
+Pak J Surg Gynaecol Obstet
+Pak J Zool
+Pak Nurs Health Rev
+Pak Pediatr J
+Pak Popul Rev
+Palacio
+Palaeogeogr Palaeoclimatol Palaeoecol
+Palaeontology
+Palaios
+Paleobiology
+Paleoceanography
+Paleopathol Newsl
+Palest Explor Q
+Palimpsest (Iowa City)
+Palliat Med
+Palliat Support Care
+Pammatone
+Pan Afr J
+Pan Am Health
+Pan Am Med Womans J
+Pancreas
+Pancreatology
+Panhand-Plains Hist Rev
+Panminerva Med
+Panoscope
+Panstwo Prawo
+Pap Bibliogr Soc Am
+Pap Bibliogr Soc Can
+Pap Br Sch Rome
+Pap Far East Hist
+Pap Fr Seventeenth Century Lit
+Pap Natl Conf Prof Nurses Physicians
+Pap Reg Sci
+Pap Reg Sci Assoc
+Pap Ser United Hosp Fund N Y
+Pap Slov Stud
+Papeles Poblac
+Paracelsus Arch Prakt Med
+Parade
+Paradentologie
+Paramed Int
+Parameters
+Paraplegia
+Parasite
+Parasite Immunol
+Parasitol Int
+Parasitol Res
+Parasitol Today
+Parasitology
+Parassitologia
+Parazitologiia
+Parents Mag
+Parents Mag (NY)
+Paris Med
+Parivar Ayojan
+Parking
+Parkinsonism Relat Disord
+Parliam Aff
+Parma Med
+Parodontol
+Parodontol Acad Rev
+Parodontol Stomatol (Nuova)
+Parodontologie
+Parodontopathies
+Paroi Arterielle
+Parola Passato
+Part A News
+Part B News
+Part Fibre Toxicol
+Part J
+Partis Rev
+Parttort Kozl
+Passato Presente
+Passau Schr Psychologiegesch
+Past Present
+Pastoral Psychol
+Pathobiol Annu
+Pathobiology
+Pathol Annu
+Pathol Biol
+Pathol Biol (Paris)
+Pathol Eur
+Pathol Gen
+Pathol Immunopathol Res
+Pathol Int
+Pathol Microbiol (Basel)
+Pathol Oncol Res
+Pathol Res Pract
+Pathol Vet
+Pathologe
+Pathologica
+Pathologist
+Pathology
+Pathology (Phila)
+Pathophysiol Haemost Thromb
+Pathophysiology
+Pathways
+Patient Acc
+Patient Care
+Patient Care Manag
+Patient Couns Health Educ
+Patient Educ Couns
+Patient Educ Newsl
+Patient Focus Care
+Patient Focus Care Satisf
+Patma Banasirakan Handes
+Patna J Med
+Patol Clin Ostet Ginecol
+Patol Fiziol Eksp Ter
+Patol Pol
+Patologia (Mex)
+Pattern Recognit
+Patterns Prejudice
+Pavlov J Biol Sci
+Pavlov J High Nerv Act
+Pawlow Z Hohere Nerventatigkeit
+Peasant Stud
+Pediatr AIDS HIV Infect
+Pediatr Adolesc Gynecol
+Pediatr Akus Ginekol
+Pediatr Alert
+Pediatr Allergy Immunol
+Pediatr Am
+Pediatr Ann
+Pediatr Blood Cancer
+Pediatr Cardiol
+Pediatr Case Rev
+Pediatr Cerrahi Derg
+Pediatr Clin India
+Pediatr Clin North Am
+Pediatr Crit Care Med
+Pediatr Dent
+Pediatr Dermatol
+Pediatr Dev Pathol
+Pediatr Diabetes
+Pediatr Emerg Care
+Pediatr Endocrinol Rev
+Pediatr Hematol Oncol
+Pediatr Infect Dis
+Pediatr Infect Dis J
+Pediatr Int
+Pediatr Listy
+Pediatr Med Chir
+Pediatr Nephrol
+Pediatr Neurol
+Pediatr Neurosci
+Pediatr Neurosurg
+Pediatr News
+Pediatr Nurs
+Pediatr Panam
+Pediatr Pathol
+Pediatr Pathol Lab Med
+Pediatr Pathol Mol Med
+Pediatr Pharmacol (New York)
+Pediatr Phys Ther
+Pediatr Pol
+Pediatr Prat
+Pediatr Pulmonol
+Pediatr Pulmonol Suppl
+Pediatr Radiol
+Pediatr Rehabil
+Pediatr Res
+Pediatr Rev
+Pediatr Surg Int
+Pediatr Transplant
+Pediatria (Bucur)
+Pediatria (Napoli)
+Pediatria (Rio)
+Pediatria (Santiago)
+Pediatrician
+Pediatrics
+Pediatrie
+Pediatrie (Bucur)
+Pediatriia
+Pedod Fr
+Pelican News
+Penal Code Annot State Calif Calif
+Penelope
+Penn Dent J (Phila)
+Penn Med
+Pensee
+Pensee Hommes
+Pensiero Polit
+People
+People (Chicago)
+People Count
+People Place
+People Planet
+Pepperdine Law Rev
+Pept Res
+Peptide Protein Rev
+Peptides
+Percept Mot Skills
+Percept Psychophys
+Perception
+Perf Latinoam
+Perform Improv Advis
+Perfusion
+Perinat Care
+Perinatol Reprod Hum
+Period Biol
+Period Counc Am Mil Past
+Periodontal Abstr
+Periodontal Case Rep
+Periodontal Clin Investig
+Periodontics
+Periodontol 2000
+Perioper Nurs Q
+Perit Dial Int
+Perkins J
+Perm Found Med Bull
+Permian Hist Annu
+Pers Adm
+Pers Derecho
+Pers Guid J
+Pers Individ Dif
+Pers J
+Pers Relatsh
+Pers Soc Psychol Bull
+Pers Soc Psychol Rev
+Personalist
+Personnel
+Persoonia
+Perspect Accredit
+Perspect Addict Nurs
+Perspect Am Hist
+Perspect Biol Med
+Perspect Dev Neurobiol
+Perspect Health
+Perspect Healthc Risk Manage
+Perspect Infirm
+Perspect Int Planif Fam
+Perspect Internation Plan Fam
+Perspect Med Virol
+Perspect Medicaid Manage
+Perspect Medicaid Medicare Manage
+Perspect Mex Am Stud
+Perspect Nephrol Hypertens
+Perspect Pediatr Pathol
+Perspect Psychiatr Care
+Perspect Respir Nurs
+Perspect Sci Christ Faith
+Perspect Sex Reprod Health
+Perspect Soc Work
+Perspect Vasc Surg Endovasc Ther
+Perspectivas
+Perspectives
+Perspectives (Montclair)
+Perspectives Prof
+Pesqui Odontol Bras
+Pesqui Planej Econ
+Pest Manag Sci
+Pestic Biochem Physiol
+Pestic Monit J
+Petermanns Geogr Mitt
+Pflege
+Pflege Aktuell
+Pflege Z
+Pflugers Arch
+Pflugers Arch Gesamte Physiol Menschen Tiere
+Pharm Acta Helv
+Pharm Beih
+Pharm Biol
+Pharm Biotechnol
+Pharm Bull
+Pharm Dev Technol
+Pharm Fr
+Pharm Hist
+Pharm Hist (Lond)
+Pharm Hist Aust
+Pharm Ind
+Pharm Manage Comb Am J Pharm
+Pharm Pract Manag Q
+Pharm Prax
+Pharm Res
+Pharm Rundsch
+Pharm Times
+Pharm Unserer Zeit
+Pharm Update
+Pharm Weekbl
+Pharm Weekbl Sci
+Pharm World Sci
+Pharm Zentralhalle Dtschl
+Pharmacoeconomics
+Pharmacoepidemiol Drug Saf
+Pharmacogenet Genomics
+Pharmacogenetics
+Pharmacogenomics
+Pharmacogenomics J
+Pharmacol Biochem Behav
+Pharmacol Physicians
+Pharmacol Rep
+Pharmacol Res
+Pharmacol Res Commun
+Pharmacol Rev
+Pharmacol Ther
+Pharmacol Ther Dent
+Pharmacol Ther [B]
+Pharmacol Toxicol
+Pharmacology
+Pharmacopsychiatria
+Pharmacopsychiatry
+Pharmacotherapy
+Pharmakopsychiatr Neuropsychopharmakol
+Pharmakotherapia
+Pharmatherapeutica
+Pharmazie
+Pharmaziehist Bibliogr
+Pharmaziehist Forsch
+Pharmeuropa Bio
+Pharmeuropa Spec Issue Biol
+Pharmindex
+Pharos Alpha Omega Alpha Honor Med Soc
+Phi Delta Kappan
+Phi Kappa Phi J
+Phila Med
+Philipp Dev
+Philipp Econ J
+Philipp Geogr J
+Philipp J Cancer
+Philipp J Cardiol
+Philipp J Intern Med
+Philipp J Ment Health
+Philipp J Nurs
+Philipp J Nutr
+Philipp J Obstet Gynecol
+Philipp J Ophthalmol
+Philipp J Ophthalmol Otolaryngol
+Philipp J Pediatr
+Philipp J Public Adm
+Philipp J Surg
+Philipp J Surg Spec
+Philipp J Surg Surg Spec
+Philipp Law J
+Philipp Popul J
+Philipp Popul Newsl
+Philipp Q Cult Soc
+Philipp Rev Econ Bus
+Philipp Soc Sci Humanit Rev
+Philipp Sociol Rev
+Philipp Stud
+Phillip J
+Phillip J Restaur Zahnmed
+Philobiblon
+Philologus
+Philos Context
+Philos East West
+Philos Exch
+Philos Forum
+Philos J
+Philos Nat
+Philos Phenomenol Res
+Philos Public Aff
+Philos Public Policy Q
+Philos Q
+Philos Res Analysis
+Philos Res Arch
+Philos Rev
+Philos Sci
+Philos Soc Sci
+Philos Stud
+Philos Theol
+Philos Trans Phys Sci Eng
+Philos Trans R Soc Lond A
+Philos Trans R Soc Lond B Biol Sci
+Philos Transact A Math Phys Eng Sci
+Philosophia (Mendoza)
+Philosophy
+Phlebologie
+Phlebolymphology
+Phonetica
+Phosphorus Sulfur Silicon Relat Elem
+Photochem Photobiol
+Photochem Photobiol Sci
+Photodermatol
+Photodermatol Photoimmunol Photomed
+Photomed Laser Surg
+Photonics Spectra
+Photophysiology
+Photosynth Res
+Photosynthetica
+Phronesis
+Phycologia
+Phylon
+Phys Biol
+Phys Chem Chem Phys
+Phys East
+Phys Lett A
+Phys Lett B
+Phys Med
+Phys Med Biol
+Phys Med Rehabil Clin N Am
+Phys Occup Ther Geriatr
+Phys Occup Ther Pediatr
+Phys Rep
+Phys Rev A
+Phys Rev A Gen Phys
+Phys Rev B Condens Matter
+Phys Rev C Nucl Phys
+Phys Rev D Part Fields
+Phys Rev E Stat Nonlin Soft Matter Phys
+Phys Rev E Stat Phys Plasmas Fluids Relat Interdiscip Topics
+Phys Rev Lett
+Phys Sportsmed
+Phys Ther
+Phys Ther Rev
+Phys Today
+Physica A
+Physica D
+Physician Assist
+Physician Assist Health Pract
+Physician Exec
+Physician Perform Paym Rep
+Physician Relat Update
+Physicians Manage
+Physicians World
+Physiol Behav
+Physiol Biochem Zool
+Physiol Bohemoslov
+Physiol Chem Phys
+Physiol Chem Phys Med NMR
+Physiol Comp Ocol Int J Comp Physiol Ecol
+Physiol Entomol
+Physiol Genomics
+Physiol Meas
+Physiol Pharmacol Physicians
+Physiol Plant
+Physiol Res
+Physiol Rev
+Physiol Rev Suppl
+Physiol Teach
+Physiol Veg
+Physiol Zool
+Physiologie
+Physiologist
+Physiology (Bethesda)
+Physiother Can
+Physiother Res Int
+Physiother Theory Pract
+Physiotherapy
+Physis
+Physis Riv Int Stor Sci
+Phytochem Anal
+Phytochemistry
+Phytomedicine
+Phyton
+Phyton (Buenos Aires)
+Phytopathology
+Phytother Res
+Picturescope
+Pie
+Pieleg Polozna
+Pigment Cell Res
+Pinheiros Ter
+Pioneer Am Soc Trans
+Pisani
+Pituitary
+Placenta
+Placenta Suppl
+Plains Anthropol
+Plan Arb
+Plan Parent Chall
+Plan Parent Eur
+Plan Parent Rev
+Plan Parent World Popul Wash Memo
+Planej Agora
+Planet Rep
+Planet Space Sci
+Planned Parent (India)
+Plant Biol (Stuttg)
+Plant Breed Rev
+Plant Cell
+Plant Cell Environ
+Plant Cell Physiol
+Plant Cell Rep
+Plant Cell Tissue Organ Cult
+Plant Foods Hum Nutr
+Plant Genet Resour Newsl
+Plant Growth Regul
+Plant J
+Plant Methods
+Plant Mol Biol
+Plant Mol Biol Rep
+Plant Physiol
+Plant Physiol Biochem
+Plant Prod Sci
+Plant Sci
+Plant Soil
+Planta
+Planta Med
+Plasmid
+Plast Reconstr Surg
+Plast Surg Nurs
+Platelets
+Platon (Athens)
+Plucne Bolesti
+Plucne Bolesti Tuberk
+Plugger
+Plural Soc
+Plzen Lek Sb
+Pneumoftiziologia
+Pneumologia
+Pneumologie
+Pneumonol Alergol Pol
+Pneumonol Phymatiologike Epitheor
+Pneumonol Pol
+Pneumonologie
+Poblac Desarro
+Poblacion
+Pogon Sahoe Yongu
+Pol Am Stud
+Pol Arch Med Wewn
+Pol Arch Weter
+Pol Ecol Stud
+Pol J Chem
+Pol J Microbiol
+Pol J Occup Med
+Pol J Occup Med Environ Health
+Pol J Pathol
+Pol J Pharmacol
+Pol J Pharmacol Pharm
+Pol J Vet Sci
+Pol Med J
+Pol Med Sci Hist Bull
+Pol Merkuriusz Lek
+Pol Perspect
+Pol Popul Rev
+Pol Przegl Chir
+Pol Przegl Radiol
+Pol Przegl Radiol Med Nukl
+Pol Rev
+Pol Tyg Lek
+Pol Tyg Lek (Wars)
+Pol West Aff
+Polar Biol
+Polar Rec (Gr Brit)
+Polar Res
+Polarforschung
+Policlinico [Chir]
+Policlinico [Med]
+Policlinico [Prat]
+Policy Anal
+Policy Anal Brief H Ser
+Policy Anal Brief W Ser
+Policy Brief Commonw Fund
+Policy Brief George Wash Univ Cent Health Serv Res Policy
+Policy Brief UCLA Cent Health Policy Res
+Policy Polit
+Policy Polit Nurs Pract
+Policy Rev
+Policy Sci
+Policy Statement R Coll Gen Pract
+Policy Stud J
+Policy Stud Rev
+Polim Med
+Polio news
+Polis
+Polit Aff
+Polit Afr
+Polit Ekon
+Polit Etrang
+Polit Geogr
+Polit Int
+Polit Mein
+Polit Q
+Polit Res Q
+Polit Sci
+Polit Sci Q
+Polit Soc
+Polit Soc (Madrid)
+Polit Stud
+Polit Theory
+Polit Today
+Polit Vierteljahresschr
+Polit Zeitgesch
+Politico (Pavia)
+Politics (Syd)
+Politics Life Sciences
+Politiq Popul
+Polity
+Polymer Prepr
+Pommern
+Ponte
+Pop Gov
+Pop Sahel
+Pope Speaks
+Popline
+Popnet
+Popul
+Popul Avenir
+Popul Briefs
+Popul Bull
+Popul Bull ECWA
+Popul Bull ESCWA
+Popul Bull U N Econ Comm West Asia
+Popul Bull UN
+Popul Chron
+Popul Commun Tech Doc
+Popul Concern News
+Popul Data Inf Serv
+Popul Desenvolv
+Popul Dev Rev
+Popul Dyn Q
+Popul Educ Asia Ocean Newsl
+Popul Educ Asia Pac Newsl Forum
+Popul Educ Interchange
+Popul Environ
+Popul Environ Psychol Newsl
+Popul Famille
+Popul Forum
+Popul Geogr
+Popul Headl
+Popul Health Metr
+Popul Index
+Popul Manag
+Popul Newsl
+Popul Notes
+Popul Policy Compend
+Popul Rep
+Popul Rep A
+Popul Rep B
+Popul Rep C
+Popul Rep D
+Popul Rep E
+Popul Rep F
+Popul Rep G
+Popul Rep H
+Popul Rep I
+Popul Rep J
+Popul Rep K
+Popul Rep L
+Popul Rep M
+Popul Rep Spec Top Monogr
+Popul Res
+Popul Res Abstr
+Popul Res Bull
+Popul Res Leads
+Popul Res Policy Rev
+Popul Rev
+Popul Sci
+Popul Soc (Paris)
+Popul Stud (Camb)
+Popul Stud (NY)
+Popul Today
+Popul Trends
+Popul Trends Public Policy
+Popul Zpr
+Populasi
+Population (NY)
+Population (Paris)
+Port Med
+Portland Clin Bull
+Posit Aware
+Posit Dir News
+Posit Health News
+Posit Living
+Posit Outlook
+Post Hist
+Post Sov Geogr
+Post Sov Geogr Econ
+Postep Chir
+Postep Neurol Neurochir Psychiatr
+Postep Reumatol
+Postep Wiedzy Med
+Postepy Biochem
+Postepy Hig Med Dosw
+Postepy Hig Med Dosw (Online)
+Postgrad Dent
+Postgrad Med
+Postgrad Med J
+Postgrad Semin Am Urol Assoc North Cent
+Postharvest Biol Technol
+Poult Sci
+Poumon
+Poumon Coeur
+Pr Dejin Prir Ved
+Pr Lodz Tow Nauk [IV]
+Prac Lek
+Prac Studi Geograf
+Pract Dent Monogr
+Pract Dig
+Pract Farm
+Pract Gastroenterol
+Pract Lawyer
+Pract Midwife
+Pract Odontol
+Pract Otorhinolaryngol (Basel)
+Pract Periodontics Aesthet Dent
+Pract Proced Aesthet Dent
+Pract Psychol Physicians
+Practitioner
+Prague Med Rep
+Prairie Rose
+Prakt Anaesth
+Prakt Arzt
+Prakt Kieferorthop
+Prakt Tierarzt
+Prakt Zubn Lek
+Prax Kinderpsychol Kinderpsychiatr
+Prax Kinderpsychol Kinderpsychiatr Beih
+Prax Klin Pneumol
+Prax Kur
+Prax Pneumol
+Prax Psychother
+Prax Psychother Psychosom
+Praxis
+Precambrian Res
+Precis Anal Trav
+Preconf Papers Natl Dent Health Conf (U S)
+Pregon
+Prehosp Emerg Care
+Prehospital Disaster Med
+Prenat Diagn
+Prenat Neonatal Med
+Prensa Med Argent
+Prensa Med Mex
+Prensa Pediatr Rev Am Puericu Pediatr
+Prep Biochem
+Prep Biochem Biotechnol
+Pres Stud Q
+Presbyt St Lukes Hosp Med Bull
+Prescr J
+Prescrire Int
+Presence (Camb)
+Presence Afr
+Presence Normande
+Present Tense
+Presse Med
+Presse Therm Clim
+Prev Assist Dent
+Prev Cardiol
+Prev Chronic Dis
+Prev Hum Serv
+Prev Med
+Prev Sci
+Prev Stomatol
+Prev Vet Med
+Prevent
+Previdenza Soc
+Pride Inst J Long Term Home Health Care
+Priest
+Prikl Biokhim Mikrobiol
+Prilozi
+Prim Care
+Prim Care Companion J Clin Psychiatry
+Prim Dent Care
+Prim Sens Neuron
+Primary Cardiol
+Primates
+Primates Med
+Princ Viana
+Princess Takamatsu Symp
+Princet Hist
+Princet J Bioeth
+Princeton Univ Libr Chron
+Print Q
+Prir Clovek Zdravje
+Prism
+Priv Pact
+Pro Fam Inf
+Pro Fono
+Pro Infirm
+Pro Re Nata
+Probate Law J
+Probate Prop
+Probe
+Probe (Adelaide)
+Probe (Lond)
+Probl Actuels Endocrinol Nutr
+Probl Actuels Otorhinolaryngol
+Probl Am Lat
+Probl Cardiol
+Probl Desarro
+Probl Econ
+Probl Econ Transit
+Probl Endokrinol (Mosk)
+Probl Endokrinol Gormonoter
+Probl Gematol Pereliv Krovi
+Probl Geogr
+Probl Hematol Blood Transfus
+Probl Istor Demogr SSSR
+Probl Khig
+Probl Kosm Biol
+Probl Med Wieku Rozwoj
+Probl Oncol
+Probl Onkol
+Probl Rodziny
+Probl Sev
+Probl Sotsialnoi Gig Istor Med
+Probl Sotsialnoi Gig Zdravookhranenniiai Istor Med
+Probl Symp Aktuell Ther
+Probl Ter
+Probl Tuberculoza
+Probl Tuberk
+Probl Tuberk Bolezn Legk
+Probl Vet Med
+Probl Virol
+Proc (Bayl Univ Med Cent)
+Proc AMIA Annu Fall Symp
+Proc AMIA Symp
+Proc Acad Polit Sci
+Proc Am Antiq Soc
+Proc Am Assoc Cancer Res
+Proc Am Cathol Philos Assoc
+Proc Am Philos Soc
+Proc Am Thorac Soc
+Proc Anim Care Panel
+Proc Annu Clin Spinal Cord Inj Conf
+Proc Annu Conf Res Med Educ
+Proc Annu Manage Conf Am Dent Assoc
+Proc Annu Meet Am Assoc Cancer Res
+Proc Annu Meet Am Psychopathol Assoc
+Proc Annu Meet Med Sect Am Counc Life Insur
+Proc Annu Meet Med Sect Am Life Conv
+Proc Annu Meet Med Sect Am Life Insur Assoc
+Proc Annu Meet U S Anim Health Assoc
+Proc Annu Meet West Soc Fr Hist
+Proc Annu Symp Comput Appl Med Care
+Proc Annu Symp Eugen Soc
+Proc Assoc Am Physicians
+Proc Aust Assoc Neurol
+Proc Biol Sci
+Proc Biol Soc Wash
+Proc Bot Soc Br Isles
+Proc Br Paedod Soc
+Proc Br Soc Dent Maxillofac Radiol
+Proc Camb Philol Soc
+Proc Can Cancer Conf
+Proc Cardiff Med Soc
+Proc Chem Soc
+Proc Chin Acad Med Sci Peking Union Med Coll
+Proc Clin Dial Transplant Forum
+Proc Conf Oral Cancer
+Proc Consort Revolut Eur
+Proc Dorset Nat Hist Archeol Soc
+Proc Entomol Soc Wash
+Proc Eur Dial Transplant Assoc
+Proc Eur Dial Transplant Assoc Eur Ren Assoc
+Proc Eur Prosthodontic Assoc
+Proc Finn Dent Soc
+Proc Found Orthod Res
+Proc Geol Assoc
+Proc Health Policy Forum
+Proc Huguenot Soc Lond
+Proc IEEE Comput Soc Bioinform Conf
+Proc IEEE Comput Syst Bioinform Conf
+Proc Indiana Acad Sci
+Proc Inst Mech Eng G J Aerosp Eng
+Proc Inst Mech Eng [H]
+Proc Inst Med Chic
+Proc Int Acad Oral Pathol
+Proc Int Conf Intell Syst Mol Biol
+Proc Int Congr Endocrinol
+Proc Jpn Acad
+Proc K Ned Akad Wet C
+Proc Mine Med Off Assoc
+Proc Mine Med Off Assoc SA
+Proc NIPR Symp Antarct Meteorites
+Proc Natl Acad Sci U S A
+Proc Natl Cancer Conf
+Proc Natl Conf Methadone Treat
+Proc Natl Sci Counc Repub China B
+Proc Nurs Theory Conf
+Proc Nutr Soc
+Proc Nutr Soc India
+Proc Pap Annu Conf Calif Mosq Control Assoc
+Proc Pap Ga Assoc Hist
+Proc R Coll Physicians Edinb
+Proc R Inst G B
+Proc R Ir Acad C Archaeol Celt Stud Hist Linguist Lit
+Proc R Ir Acad [B]
+Proc R Soc Edinb [Biol]
+Proc R Soc Edinb [Nat Environ]
+Proc R Soc Lond B Biol Sci
+Proc R Soc Med
+Proc Rudolf Virchow Med Soc City N Y
+Proc S C Hist Assoc
+Proc Soc Exp Biol Med
+Proc Soc Study Fertil
+Proc Staff Meet Honol Clin
+Proc Staff Meet Pethah Tiqva Isr Beilinson Hosp
+Proc State Secr Manage Conf Am Dent Assoc
+Proc U S Nav Inst
+Proc Univ Otago Med Sch
+Proc Veterans Adm Spinal Cord Inj Conf
+Proc Virchow Pirquet Med Soc
+Proc Wesley Hist Soc
+Proc West Pharmacol Soc
+Proc Wkly Semin Neurol
+Process Biochem
+Prod Pharm
+Prod Probl Pharm
+Prof Care Mother Child
+Prof Dev Ser (Chic Ill)
+Prof Ethics
+Prof Ethics Rep
+Prof Flashes
+Prof Geogr
+Prof Inferm
+Prof Nurs Home
+Prof Nurse
+Prof Psychol
+Prof Psychol Res Pr
+Prof Saf
+Prof Sanit Manage
+Profamilia
+Profile Med Pract
+Profiles Healthc Mark
+Profiles Hosp Mark
+Prog AIDS Pathol
+Prog Allergy
+Prog Arch
+Prog At Med
+Prog Behav Modif
+Prog Biochem Pharmacol
+Prog Biocybern
+Prog Biometeorol
+Prog Biophys Biophys Chem
+Prog Biophys Mol Biol
+Prog Brain Res
+Prog Cardiovasc Dis
+Prog Cardiovasc Nurs
+Prog Cell Cycle Res
+Prog Chem Fats Other Lipids
+Prog Chem Toxicol
+Prog Clin Biol Res
+Prog Clin Cancer
+Prog Clin Immunol
+Prog Clin Parasitol
+Prog Clin Pathol
+Prog Colloid Polym Sci
+Prog Drug Res
+Prog Exp Pers Psychopathol Res
+Prog Exp Pers Res
+Prog Exp Tumor Res
+Prog Food Nutr Sci
+Prog Growth Factor Res
+Prog Gynecol
+Prog Hematol
+Prog Hemost Thromb
+Prog Histochem Cytochem
+Prog Hum Geogr
+Prog Hum Reprod Res
+Prog Immunobiol Stand
+Prog Ind Microbiol
+Prog Lipid Res
+Prog Liver Dis
+Prog Med
+Prog Med Chem
+Prog Med Genet
+Prog Med Virol
+Prog Mol Subcell Biol
+Prog Neurobiol
+Prog Neurol Psychiatry
+Prog Neuropsychopharmacol
+Prog Neuropsychopharmacol Biol Psychiatry
+Prog Nucl Med
+Prog Nucleic Acid Res Mol Biol
+Prog Odontoiatr
+Prog Odontostomatol
+Prog Orthod
+Prog Pediatr Cardiol
+Prog Pediatr Surg
+Prog Phys Ther
+Prog Plann
+Prog Psychobiol Physiol Psychol
+Prog Psychother
+Prog Radiat Ther
+Prog Rep Health Dev South Afr
+Prog Retin Eye Res
+Prog Stereochem
+Prog Surf Sci
+Prog Surg
+Prog Transplant
+Prog Urol
+Prog Vet Microbiol Immunol
+Progr Med
+Progr Med (Napoli)
+Progr Med (Paris)
+Progr Pediatr Puericult
+Progr Ter Clin
+Program Notes Assoc Univ Programs Health Adm
+Program Q Asia Found
+Progress Nurse
+Progressive
+Proj Inf Perspect
+Projet
+Prologue J Natl Arch
+Prometeo (Milan)
+Promot Dent
+Promot Educ
+Promot Health
+Prophyl Antivenerienne
+Prophyl Sanit Morale
+Prose Stud
+Prospects
+Prostaglandins
+Prostaglandins Leukot Essent Fatty Acids
+Prostaglandins Leukot Med
+Prostaglandins Med
+Prostaglandins Other Lipid Mediat
+Prostate
+Prostate Cancer Prostatic Dis
+Prostate Suppl
+Prosthet Orthot Int
+Prot Hum Subj
+Protein Eng
+Protein Eng Des Sel
+Protein Expr Purif
+Protein J
+Protein Pept Lett
+Protein Profile
+Protein Sci
+Protein Seq Data Anal
+Proteins
+Proteome Sci
+Proteomics
+Protes Dent
+Protet Stomatol
+Proteus
+Protist
+Protoplasma
+Prov Med
+Provence Hist
+Provider
+Prz Geogr
+Prz Orient
+Prz Zach
+Przegl Dermatol
+Przegl Epidemiol
+Przegl Hist
+Przegl Lek
+Przegl Pol
+Przeszlosc Demograf Pol
+Psihoterapija
+Psikholog Zh
+Psyche (Stuttg)
+Psyche Rev Int Sci Homme Psychanal
+Psychiatr Ann
+Psychiatr Bull R Coll Psychiatr
+Psychiatr Clin (Basel)
+Psychiatr Clin North Am
+Psychiatr Commun
+Psychiatr Danub
+Psychiatr Dev
+Psychiatr Enfant
+Psychiatr Forum
+Psychiatr Genet
+Psychiatr Hosp
+Psychiatr Hung
+Psychiatr J Univ Ott
+Psychiatr Med
+Psychiatr Med Update Mass Gen Hosp Rev Physicians
+Psychiatr Neurol (Basel)
+Psychiatr Neurol Med Psychol (Leipz)
+Psychiatr Neurol Med Psychol Beih
+Psychiatr Neurol Neurochir
+Psychiatr News
+Psychiatr Opin
+Psychiatr Pol
+Psychiatr Prax
+Psychiatr Q
+Psychiatr Q Suppl
+Psychiatr Rehabil J
+Psychiatr Res Rep Am Psychiatr Assoc
+Psychiatr Serv
+Psychiatry
+Psychiatry Clin Neurosci
+Psychiatry Dig
+Psychiatry Med
+Psychiatry Res
+Psychoanal Psychoanal Rev
+Psychoanal Q
+Psychoanal Rev
+Psychoanal Soc Sci
+Psychoanal Study Child
+Psychodyn Couns
+Psychohist Rev
+Psychol Abstr
+Psychol Addict Behav
+Psychol Aging
+Psychol Assess
+Psychol Belg
+Psychol Bull
+Psychol Dev Soc J
+Psychol Forsch
+Psychol Health
+Psychol Issues
+Psychol Med
+Psychol Med (Paris)
+Psychol Med Monogr Suppl
+Psychol Methods
+Psychol Monogr
+Psychol Neuropsychiatr Vieil
+Psychol Prax
+Psychol Psychother
+Psychol Public Policy Law
+Psychol Rec
+Psychol Rep
+Psychol Res
+Psychol Rev
+Psychol Rundsch
+Psychol Sch
+Psychol Sci
+Psychol Stud (Mysore)
+Psychol Stud Popul Fam Plan
+Psychol Today
+Psychol Women Q
+Psychologist
+Psychology
+Psychometrika
+Psychon Bull Rev
+Psychon Sci
+Psychoneuroendocrinology
+Psychooncology
+Psychopathology
+Psychopharmacol Bull
+Psychopharmacol Commun
+Psychopharmacol Ser
+Psychopharmacol Serv Cent Bull
+Psychopharmacologia
+Psychopharmacologie
+Psychopharmacology (Berl)
+Psychopharmacology Suppl
+Psychophysiology
+Psychosom Med
+Psychosomatics
+Psychother Med Psychol (Stuttg)
+Psychother Psychosom
+Psychother Psychosom Med Psychol
+Psychotherapy
+Pszichol Tanulman
+Publ Am Inst Hist Pharm
+Publ B Aires Cent Investig
+Publ Child Dev Univ Calif
+Publ Cour Eur Droits Homme Sr A Arrets Decis
+Publ Group Adv Psychiatry
+Publ Inst Antitubers Francisco Moragas
+Publ Inst Pasteur Guyane Fr Inini
+Publ Mus Farm
+Publ Psychol
+Publ Public Health Univ Calif
+Publ Soc Hist Archeol Limbg
+Publ Soc Lit Sci
+Publ Soc Savante Alsace Reg Est
+Publ Thoresby Soc
+Public Adm
+Public Adm Dev
+Public Adm Rev
+Public Aff Q
+Public Aff Rep
+Public Choice
+Public Citiz
+Public Finan Q
+Public Financ
+Public Health
+Public Health Monogr
+Public Health Nurs
+Public Health Nutr
+Public Health Pap
+Public Health Rep
+Public Health Rev
+Public Hist
+Public Interest
+Public Law
+Public Law Forum
+Public Opin Q
+Public Pers Manage
+Public Policy
+Public Relat J
+Public Sect Contract Rep
+Public Sector Health Care Risk Manage
+Public Underst Sci
+Public Welf
+Public health tech monogr US
+Publius
+Pula
+Pulaski Cty Hist Rev
+Pulm Pharmacol
+Pulm Pharmacol Ther
+Pulse
+Punjab Med J
+Purch Adm
+Purdons Pa Statut Annot Pa
+Pure Appl Chem
+Purinergic Signal
+Q Bull Northwest Univ Med Sch
+Q Bull S Afr Libr
+Q Bull Sea View Hosp
+Q Demogr Bull
+Q Dent Rev
+Q J Adm
+Q J Crude Drug Res
+Q J Econ
+Q J Exp Physiol
+Q J Exp Physiol Cogn Med Sci
+Q J Exp Psychol
+Q J Exp Psychol (Colchester)
+Q J Exp Psychol A
+Q J Exp Psychol B
+Q J Libr Cong
+Q J Med
+Q J Microsc Sci
+Q J Nucl Med
+Q J Nucl Med Mol Imaging
+Q J Soc Aff
+Q J Speech
+Q J Stud Alcohol
+Q Med Rev
+Q Natl Dent Assoc
+Q Popul Bull
+Q Prog Rep United States Air Force Radiat Lab Univ Chic
+Q Rev
+Q Rev Allergy Appl Immunol
+Q Rev Biol
+Q Rev Biophys
+Q Rev DC Nurses Assoc
+Q Rev Drug Metab Drug Interact
+Q Rev Econ Bus
+Q Rev Econ Finance
+Q Rev Med
+Q Rev Pediatr
+Q Rev Surg
+Q Rev Surg Obstet Gynecol
+QA Brief
+QA Rev
+QJM
+QRB Qual Rev Bull
+QRC Advis
+Qadmoniot
+Qld Nurse
+Qld Nurses J
+Quad Antibiot
+Quad Azione Soc
+Quad Clin Ostet Ginecol
+Quad Criminol Clin
+Quad Int Stor Med Sanita
+Quad Merceol
+Quad Nutr
+Quad Radiol
+Quad Raggruppamento Toscoumbroemiliano Stor Med
+Quad Sclavo Diagn
+Quad Stor
+Quad Stor Med Sci
+Quad Stor Univ Padova
+Quaerendo
+Quaker Hist
+Qual Assur
+Qual Assur Health Care
+Qual Assur Util Rev
+Qual Connect
+Qual Health Care
+Qual Health Res
+Qual Lett Healthc Lead
+Qual Life Cardiovasc Care
+Qual Life Res
+Qual Manag Health Care
+Qual Quant
+Qual Saf Health Care
+Qual Sociol
+Queen's Q
+Queens Gazette
+Queens Law J
+Queens Nurs J
+Quellen Forsch Ital Arch Bibl
+Quellen Stud Gesch Pharm
+Quercy Rech
+Quest
+Quim Nova
+Quinnipiac Health Law J
+Quintessence Dent Technol
+Quintessence Int
+Quintessence Int [Fr]
+Quintessencia
+Quintessencia Protese Lab
+Quintessenz
+Quintessenz Impulse
+Quintessenz J
+Quintessenz Zahntech
+Quintessenza
+Quinto Cent
+Quipu
+R Can Dent Corps Q
+R I Dent J
+R I Hist
+R I Med
+R I Med J
+R Inst Public Health Hyg J
+R S Rep
+R Sitios
+R Soc Health J
+RANF Rev
+RCM Midwives
+RCM Midwives J
+RCN Nurs Stand
+RDH
+RF
+RF Illus
+RGO
+RISO Rep
+RN
+RN (For Managers)
+RN Ida
+RNA
+RNABC News
+RNAO News
+RURDS Rev Urb Reg Dev Stud
+Race Cl
+Rad Med Fak Zagrebu
+Radiat Data Rep
+Radiat Eff
+Radiat Environ Biophys
+Radiat Meas
+Radiat Med
+Radiat Oncol Investig
+Radiat Prot Dosimetry
+Radiat Res
+Radiat Res Suppl
+Radiats Biol Radioecol
+Radic Am
+Radic Hist Rev
+Radiobiol Radioter Fis Med
+Radiobiol Radiother (Berl)
+Radiobiologiia
+Radiocarbon
+Radiogr Today
+Radiographics
+Radiography
+Radioisotopes
+Radiol
+Radiol Austriaca
+Radiol Clin
+Radiol Clin (Basel)
+Radiol Clin Biol
+Radiol Clin North Am
+Radiol Diagn (Berl)
+Radiol Health Data Rep
+Radiol Interam
+Radiol Manage
+Radiol Med (Torino)
+Radiol Prat
+Radiol Technol
+Radiologe
+Radiologia
+Radiology
+Radioprotection
+Radioter Radiobiol Fis Medica
+Radiother Oncol
+Rajasthan Med J
+Ramparts
+Rand J Econ
+Rapid Commun Mass Spectrom
+Raspr Grada Povij Znan
+Rass Arch Stato
+Rass Clin Sci
+Rass Clin Ter
+Rass Dermatol Sifilogr
+Rass Fisiopatol Clin Ter
+Rass Giuliana Med
+Rass Int Clin Ter
+Rass Int Stomatol Prat
+Rass Ital Chir Med
+Rass Ital Gastroenterol
+Rass Ital Sociol
+Rass Lett Ital
+Rass Med Ind Ig Lav
+Rass Med Sarda
+Rass Med Sper
+Rass Medica
+Rass Neurol Veg
+Rass Neuropsichiatr
+Rass Odontotec
+Rass Penititenziaria Crim
+Rass Stor Risorgim
+Rass Studi Psichiatr
+Rass Trimest Odontoiatr
+Rasy Nar Sovrem Etn Ras Probl
+Ratio Juris
+Ration Drug Ther
+Raumforsch Raumordn
+Ravintsara
+Rays
+Reach Out
+Read Dig
+Real Clin
+Real Property Probate Trust J
+Reanim Organes Artif
+Reason
+Reason Pap
+Rec Assoc Bar City New York
+Rec Bucks
+Rec Chem Prog
+Rec Columb Hist Soc
+Rec Med Vet Ec Alfort
+Recent Adv Biol Psychiatry
+Recent Adv Clin Nucl Med
+Recent Adv Nurs
+Recent Adv Stud Cardiac Struct Metab
+Recent Dev Alcohol
+Recent Prog Horm Res
+Recent Results Cancer Res
+Recenti Prog Med
+Recept Signal Transduct
+Receptor
+Receptors Channels
+Rech Amerindien Que
+Rech Econ Louvain
+Rech Geogr Strasbg
+Rech Soc
+Rech Sociogr
+Rech Soins Infirm
+Rech fem
+Recher Fed Groupes Etud Recher Inst
+Recherche
+Recomb DNA Tech Bull
+Reconstr Surg Traumatol
+Reconstructionist
+Record (Washington)
+Recruit Retain
+Recruit Retent Rep
+Recruit Retent Restruct Rep
+Red River Val Hist Rev
+Redbook
+Redox Rep
+Ref Gynecol Obstet
+Ref Serv Rev
+Reflect Nurs Leadersh
+Reflections
+Reform J
+Refract Corneal Surg
+Refu Vet
+Refuat Hapeh Vehashinayim
+Refuat Hashinayim
+Refug Rep
+Reg Anaesth
+Reg Anesth
+Reg Anesth Pain Med
+Reg Dev Dialogue
+Reg Immunol
+Reg Sci Perspect
+Reg Sci Urban Econ
+Reg Stud
+Regan Rep Nurs Law
+Regards Actual
+Regensb Jahrb Arztl Fortbild
+Regist Ky Hist Soc
+Regist Nurse
+Regul Aff J
+Regul Anal Med Waste
+Regul Pept
+Regul Pept Suppl
+Regul Toxicol Pharmacol
+Regulation
+Rehab Manag
+Rehabil Couns Bull
+Rehabil Fyz Lek
+Rehabil Lit
+Rehabil Nurs
+Rehabil Psychol
+Rehabil Rec
+Rehabilitation
+Rehabilitation (Bonn)
+Rehabilitation (Stuttg)
+Reihe Med Forsch
+Rein Foie
+Rejuvenation Res
+Relac Int
+Relig Educ
+Relig Humanism
+Relig Life
+Relig Stud Rev
+Rem Actual
+Ren Fail
+Ren Kou Xue Kan
+Ren Physiol
+Ren Physiol Biochem
+Renaiss Mod Stud
+Renaiss Q
+Renaiss Reform
+Rend Accad Naz XL
+Rend Ist Sup Sanit
+Rend Lincei Sci Fis Nat
+Rendezvous (Buffalo)
+Renkou Yanjiu
+Renkou Yu Jingji
+Rep Am Univ Field Staff
+Rep Can Def Res Chem Lab
+Rep Can Def Res Kings Lab
+Rep Carcinog
+Rep Cases Argued Determ Supreme Court N J N J Supreme Court
+Rep Cases Decided Appell Div Supreme Court State NY NY State Supreme Court Appell Div
+Rep Cases Decided Supreme Court State Ga Ga Supreme Court
+Rep Cases Determ Court Appeal State Calif
+Rep Civ Aeromed Res Inst US
+Rep Comm Accredit Rehabil Facil
+Rep Congr Eur Orthod Soc
+Rep Del Nurses Assoc
+Rep Group Adv Psychiatry
+Rep Health Soc Subj (Lond)
+Rep Inst Hum Values
+Rep Inst Philos Public Policy
+Rep Int Dev Res Cent Can
+Rep Med Guidel Outcomes Res
+Rep Natl Forum Hosp Health Aff
+Rep Nav Med Neuropsychiatr Res Unit
+Rep No NAEC ACEL United States Aerosp Crew Equip Lab Phila
+Rep Popul Fam Plann
+Rep Proc Scott Soc Hist Med
+Rep Public Health Med Subj (Lond)
+Rep Rheum Dis
+Rep Sel Cases Decided Courts State N Y Court Appeals Appell Div Supreme Court
+Rep US Army Med Res Lab
+Rep US Army Med Res Nutr Lab Denver
+Rep US Nav Med Res Lab
+Rep US Nav Submar Med Cent
+Rep US Navy Exp Diving Unit
+Report Hum Reprod Law
+Represent Res Soc Psychol
+Representations (Berkeley)
+ReproWatch (Youth Ed)
+Reprod Abstr Ser
+Reprod Biol
+Reprod Biol Endocrinol
+Reprod Biomed Online
+Reprod Domest Anim
+Reprod Fertil Dev
+Reprod Freedom News
+Reprod Genet Eng
+Reprod Health
+Reprod Health Matters
+Reprod Nutr Dev
+Reprod Suppl
+Reprod Toxicol
+Reproduccion
+Reproduction
+Reprowatch
+Repura
+Res Agenda Brief
+Res Aging
+Res Clin Stud Headache
+Res Commun Chem Pathol Pharmacol
+Res Commun Inst Ferment
+Res Commun Mol Pathol Pharmacol
+Res Commun Psychol Psychiatr Behav
+Res Commun Subst Abuse
+Res Community Ment Health
+Res Dev Disabil
+Res Dev Tech Rep
+Res Econ Anthropol
+Res Econ Hist
+Res Exp Med (Berl)
+Res Front Fertil Regul
+Res Health Econ
+Res Hum Cap Dev
+Res Hum Dev
+Res Immunol
+Res Initiat Treat Action
+Res Ipsa Loquitur
+Res Manage
+Res Microbiol
+Res Nurs Health
+Res Pap Hist Med Assoc
+Res Popul Ecol (Kyoto)
+Res Popul Econ
+Res Prog Org Biol Med Chem
+Res Prostaglandins
+Res Publ Assoc Res Nerv Ment Dis
+Res Publica
+Res Publica Litt
+Res Q
+Res Q Exerc Sport
+Res Rep Health Eff Inst
+Res Rep Nav Med Res Inst (US)
+Res Rep U S Nav Med Field Res Lab
+Res Rep U S Nav Sch Aviat Med
+Res Reprod
+Res Resour Rep
+Res Rural Sociol Dev
+Res Serv Med
+Res Soc Work Pract
+Res Sociol Educ Social
+Res Sociol Health Care
+Res Sports Med
+Res Stat Note
+Res Stat Note Health Care Financ Adm Off Policy Plan Res
+Res Steroids (Amst)
+Res Stud
+Res Theory Nurs Pract
+Res Today
+Res Urban Econ
+Res Vet Sci
+Res Virol
+Research
+Reseaux Ciephum
+Resen Clin Cient
+Resena Hist
+Resid Staff Physician
+Residue Rev
+Resour Biomed Res Educ
+Resour Fem Res
+Respir Care
+Respir Care Clin N Am
+Respir Manage
+Respir Med
+Respir Physiol
+Respir Physiol Neurobiol
+Respir Res
+Respir Ther
+Respiration
+Respirology
+Responsa Meridiana
+Responsive Community
+Restaurants Inst
+Restor Neurol Neurosci
+Restor Q
+Restorative Dent
+Results Probl Cell Differ
+Resume
+Resuscitation
+Retina
+Retrovirology
+Reumatismo
+Reumatizam
+Reumatologia
+Rev ABPAPAL
+Rev ADM
+Rev Acad Arabe Damas
+Rev Acad Pol Sci
+Rev Actual Estomatol Esp
+Rev Actual Odontoestomatol Esp
+Rev Adm Munic
+Rev Afr Polit Econ
+Rev Agrup Odontol Cap Fed
+Rev Alerg
+Rev Alerg Mex
+Rev Allem
+Rev Allergy
+Rev Alsace
+Rev Am Hist
+Rev Annu Soc Odontostomatol Nordest
+Rev Arch Bibl Mus
+Rev Archeol Centre France
+Rev Archeol Mod Archeol Gen
+Rev Argent Alerg
+Rev Argent Cardiol
+Rev Argent Cir
+Rev Argent Endocrinol Metab
+Rev Argent Implantol Estomatol
+Rev Argent Microbiol
+Rev Argent Neurol Psiquiatr
+Rev Argent Reumatol
+Rev Argent Tuberc Enferm Pulm
+Rev Argent Urol
+Rev Argent Urol Nefrol
+Rev Arh
+Rev Asoc Argent Microbiol
+Rev Asoc Med Argent
+Rev Asoc Mex Enferm
+Rev Asoc Odontol Argent
+Rev Asoc Odontol Costa Rica
+Rev Asoc Prof Hosp Nac Odontol
+Rev Assoc Med Bras
+Rev Assoc Med Minas Gerais
+Rev Assoc Med Rio Grande Do Sul
+Rev Assoc Paul Cir Dent
+Rev Assoc Paul Cir Dent Reg Aracatuba
+Rev Assyriol Archeol Orient
+Rev Ateneo Argent Odontol
+Rev Ateneo Catedra Tec Oper Dent
+Rev Atheroscler
+Rev Atheroscler (Paris)
+Rev Auvergne
+Rev Avranchin Pays Granville
+Rev Belg Homoeopath
+Rev Belg Pathol Med Exp
+Rev Belge Archeol Hist Art
+Rev Belge Geogr
+Rev Belge Hist Milit
+Rev Belge Med Dent
+Rev Belge Philol Hist
+Rev Belge Stomatol
+Rev Bibl Nac Jose Marti
+Rev Bibl Natl
+Rev Biol Oral
+Rev Biol Trop
+Rev Black Polit Econ
+Rev Bras Anestesiol
+Rev Bras Biol
+Rev Bras Cir
+Rev Bras Enferm
+Rev Bras Estat
+Rev Bras Estud Popul
+Rev Bras Gastroenterol
+Rev Bras Geogr
+Rev Bras Leprol
+Rev Bras Malariol Doencas Trop
+Rev Bras Med
+Rev Bras Odontol
+Rev Bras Odontol Mil
+Rev Bras Oftalmol
+Rev Bras Otorinolaringol
+Rev Bras Otorrinolaringol (Engl Ed)
+Rev Bras Parasitol Vet
+Rev Bras Pesqui Med Biol
+Rev Bras Psiquiatr
+Rev Bras Tuberc Doencas Torac
+Rev CEPAE
+Rev Can Biol
+Rev Can Biol Exp
+Rev Can Etud Dev
+Rev Cardiovasc Med
+Rev Catarinense Odontol
+Rev Cent Am Odontol
+Rev Cent Estud Demograficos
+Rev Cent Etud Pays Est
+Rev Centro Cienc Biomed Univ Fed Uberlandia
+Rev Centroam Econ
+Rev Chil Hig Med Prev
+Rev Chil Hist Geogr
+Rev Chil Nutr
+Rev Chil Obstet Ginecol
+Rev Chil Odontoestomatol
+Rev Chil Pediatr
+Rev Chilena Infectol
+Rev Chir
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Chir
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Otorinolaringol
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Ser Oftalmol
+Rev Chir Oncol Radiol O R L Oftalmol Stomatol Ser Stomatol
+Rev Chir Orthop Reparatrice Appar Mot
+Rev Cinc Biomed
+Rev Cir (Mex)
+Rev Cir Urug
+Rev Circ Argent Odontol
+Rev Circ Odontol Cordoba
+Rev Circ Odontol Sur
+Rev Circul Ondontol Ros
+Rev Clin Basic Pharm
+Rev Clin Esp
+Rev Clin Exp Hematol
+Rev Clin Gerontol
+Rev Clin Inst Matern Lisb
+Rev Clin Sao Paulo
+Rev Col Estomatol Guatem
+Rev Col Med Cir Guatem
+Rev Col Med Guatem
+Rev Col Nac Enferm
+Rev Colomb Obstet Ginecol
+Rev Colomb Pediatr Pueric
+Rev Comminges
+Rev Coree
+Rev Corps Sante Armees Terre Mer Air
+Rev Corps Sante Mil
+Rev Costarric Cienc Med
+Rev Cubana Cardiol
+Rev Cubana Enferm
+Rev Cubana Estomatol
+Rev Cubana Hig Epidemiol
+Rev Cubana Med
+Rev Cubana Med Trop
+Rev Cubana Pediatr
+Rev Cuhana Adm Salud
+Rev Cult Vozes
+Rev Cytol Clin
+Rev Czech Med
+Rev Dent (San Salv)
+Rev Dent (St Domingo)
+Rev Dent Chile
+Rev Dent Liban
+Rev Deux Mondes
+Rev Dialoctol Tradiciones Pop (Madrid)
+Rev Droit
+Rev Dromoise
+Rev Drug Metab Drug Interact
+Rev East Med Sci
+Rev Econ
+Rev Econ Cond Italy
+Rev Econ Dev
+Rev Econ Nordeste
+Rev Econ Polit
+Rev Econ Soc
+Rev Econ Stat
+Rev Econ Stud
+Rev Econ Sud Ouest
+Rev Ecuat Hig Med Trop
+Rev Ecuat Med Cienc Biol
+Rev Ecuat Pediatr
+Rev Ecuat Pediatr Pueric
+Rev Egyptol
+Rev Electroencephalogr Neurophysiol Clin
+Rev Elev Med Vet Pays Trop
+Rev Endocr Metab Disord
+Rev Enferm
+Rev Enferm (Lisboa)
+Rev Enferm Nov Dimens
+Rev Enseign Philos
+Rev Enseign Super
+Rev Environ Contam Toxicol
+Rev Environ Health
+Rev Epidemiol Med Soc Sante Publique
+Rev Epidemiol Sante Publique
+Rev Esc Enferm USP
+Rev Esc Odontol Tucuman
+Rev Esp Anestesiol
+Rev Esp Anestesiol Reanim
+Rev Esp Antropol Am
+Rev Esp Cardiol
+Rev Esp Doc Cient
+Rev Esp Endodoncia
+Rev Esp Enferm Apar Dig
+Rev Esp Enferm Apar Dig Nutr
+Rev Esp Enferm Dig
+Rev Esp Estomatol
+Rev Esp Fisiol
+Rev Esp Geriatr Gerontol
+Rev Esp Invest Sociol
+Rev Esp Med Nucl
+Rev Esp Obstet Ginecol
+Rev Esp Oncol
+Rev Esp Otoneurooftalmol Neurocir
+Rev Esp Paleontol
+Rev Esp Parad
+Rev Esp Pediatr
+Rev Esp Quimioter
+Rev Esp Reum Enferm Osteoartic
+Rev Esp Reumatol
+Rev Esp Salud Publica
+Rev Esp Tuberc
+Rev Est
+Rev Estad
+Rev Estud Extremenos
+Rev Estud Vida Local
+Rev Ethnol Que
+Rev Etud Byz
+Rev Etud Comp Est Ouest
+Rev Etud Grec
+Rev Etud Ital
+Rev Etud Juives
+Rev Etud Lat
+Rev Etud Slaves
+Rev Etud Sud Est Eur
+Rev Eur Dermatol MST
+Rev Eur Estud Latinoam Caribe
+Rev Eur Etud Clin Biol
+Rev Eur Migr Int
+Rev Eur Odontoestomatol
+Rev FOA
+Rev Fac Cien Med Univ Nac Cordoba
+Rev Fac Cienc Med
+Rev Fac Cienc Med Cordoba
+Rev Fac Farm Odontol Araraquara
+Rev Fac Farm Odontol Ribeiro Preto
+Rev Fac Farm Univ Cent Venez
+Rev Fac Med Tucuman
+Rev Fac Med Univ Nac Auton Mex
+Rev Fac Med Univ Nac Colomb
+Rev Fac Odontol Aracatuba
+Rev Fac Odontol P Alegre
+Rev Fac Odontol Pernambuco
+Rev Fac Odontol Ribeiro Preto
+Rev Fac Odontol Sao Jose Dos Campos
+Rev Fac Odontol Sao Paulo
+Rev Fac Odontol Tucuman
+Rev Fac Odontol Univ Antioq
+Rev Fac Odontol Univ Chile
+Rev Fac Odontol Univ Fed Bahia
+Rev Fac Odontol Univ Nac (Cordoba)
+Rev Fac Odontol Univ Nac Colomb
+Rev Faculdade Odontol FZL
+Rev Faculdade Odontol Lins
+Rev Farm Bioquim Univ Sao Paulo
+Rev Farm Odontol
+Rev Fed Am Health Syst
+Rev Fed Am Hosp
+Rev Fed Odontol Colomb
+Rev Fed Odontol Ecuat
+Rev Fom Soc
+Rev Fr Aff Soc
+Rev Fr Allergol
+Rev Fr Endocrinol Clin
+Rev Fr Endod
+Rev Fr Etud Am
+Rev Fr Etud Clin Biol
+Rev Fr Gerontol
+Rev Fr Gynecol Obstet
+Rev Fr Hist Livre
+Rev Fr Hist Outre Mer
+Rev Fr Mal Respir
+Rev Fr Odontostomatol
+Rev Fr Prothes Dent
+Rev Fr Prothese Dent
+Rev Fr Psychanal
+Rev Fr Sci Polit
+Rev Fr Sociol
+Rev Fr Transfus
+Rev Fr Transfus Hemobiol
+Rev Fr Transfus Immunohematol
+Rev Fuerzas Armadas Venez
+Rev Gastroenterol
+Rev Gastroenterol Disord
+Rev Gastroenterol Mex
+Rev Gastroenterol Peru
+Rev Gaucha Enferm
+Rev Gaucha Odontol
+Rev Gen Droit
+Rev Gen Mar
+Rev Gen Sci Pures Appl Bull Assoc Fr Av Sci
+Rev Geogr Alp
+Rev Geogr Est
+Rev Geogr Inst Panam Geogr Hist
+Rev Geogr Lyon
+Rev Geogr Montr
+Rev Geogr Pyren Sud Ouest
+Rev Gerontol Expr Fr
+Rev Ginecol Obstet (Sao Paulo)
+Rev Goiana Med
+Rev Guatem Estomatol
+Rev Gynecol Obstet
+Rev Haute Auvergne
+Rev Hematol
+Rev Hist (Arg)
+Rev Hist (Brazil)
+Rev Hist (Costa Rica)
+Rev Hist (Paris)
+Rev Hist Am Argent
+Rev Hist Am Fr
+Rev Hist Armees
+Rev Hist Art Dent
+Rev Hist Bordeaux Dep Gironde
+Rev Hist Canaria
+Rev Hist Demogr
+Rev Hist Deuxieme Guerre Mond
+Rev Hist Eccles
+Rev Hist Econ Soc
+Rev Hist Eglise Fr
+Rev Hist Jeronimo Zurita
+Rev Hist Litt Fr
+Rev Hist Maghreb
+Rev Hist Med Heb
+Rev Hist Mil
+Rev Hist Mod Contemp
+Rev Hist Naval
+Rev Hist Pharm (Paris)
+Rev Hist Philos Relig
+Rev Hist Psicol
+Rev Hist Relig
+Rev Hist Sci (Paris)
+Rev Hist Sci [Paris]
+Rev Hist Textes
+Rev Hosp Clin Fac Med Sao Paulo
+Rev Hosp Nino (Lima)
+Rev Hosp Ninos Alejandro Mann
+Rev Hyg Med Soc
+Rev Hyg Prof
+Rev Iber Endocrinol
+Rev Iber Parasitol
+Rev Iberoam Micol
+Rev Iberoam Ortod
+Rev Ibys
+Rev Ig Bacteriol Virusol Parazitol Epidemiol Pneumoftiziol Bacteriol Virusol Parazitol Epidemiol
+Rev Ig Bacteriol Virusol Parazitol Epidemiol Pneumoftiziol Pneumoftiziol
+Rev Ig Med Muncii Med Soc Bacteriol Virusol Parazitol Epidemiol Pneumoftiziol Pneumoftiziol
+Rev Immunogenet
+Rev Immunol (Paris)
+Rev Immunol Ther Antimicrob
+Rev Indias
+Rev Inf Legis
+Rev Infect Dis
+Rev Infirm
+Rev Infirm Assist Soc
+Rev Infirm Infirm Aux Que
+Rev Infirm [Inf]
+Rev Inform Med Ter
+Rev Inst Adolfo Lutz
+Rev Inst Antibiot (Recife)
+Rev Inst Eur
+Rev Inst Hist Geogr Bras
+Rev Inst Hyg Mines (Hasselt)
+Rev Inst Med Trop Sao Paulo
+Rev Inst Nac Cancerol (Mex)
+Rev Inst Napoleon
+Rev Inst Salubr Enferm Trop
+Rev Inst Sociol
+Rev Int Comm Jurists
+Rev Int Criminol Police Tech
+Rev Int Hepatol
+Rev Int Hist Mil
+Rev Int Hist Psychanal
+Rev Int Hist Psychiatr
+Rev Int Pediatr
+Rev Int Philos
+Rev Int Serv Sante Armees
+Rev Int Serv Sante Forces Armees
+Rev Int Sociol
+Rev Int Trach
+Rev Int Trach Pathol Ocul Trop Subtrop
+Rev Int Trach Pathol Ocul Trop Subtrop Sante Publique
+Rev Interam Planif
+Rev Interam Radiol
+Rev Invest (Guadalajara)
+Rev Invest Clin
+Rev Invest Salud Publica
+Rev Islenha
+Rev Istor
+Rev Jurid Univ P R
+Rev Kuba Med Trop Parasitol
+Rev Lang Rom
+Rev Laryngol Otol Rhinol (Bord)
+Rev Lat Am Enfermagem
+Rev Latinoam Anat Patol
+Rev Latinoam Cir Plast
+Rev Latinoam Microbiol
+Rev Latinoam Microbiol Parasitol (Mex)
+Rev Latinoam Patol
+Rev Latinoam Perinatol
+Rev Latinoam Psicol
+Rev Law Soc Change
+Rev Louvre Mus Fr
+Rev Lyon Med
+Rev Mal Respir
+Rev Mar
+Rev Med (Mex)
+Rev Med (Paris)
+Rev Med Aeron
+Rev Med Aeron Braz
+Rev Med Aeronaut
+Rev Med Alpes Fr
+Rev Med Brux
+Rev Med Chil
+Rev Med Chir Mal Foie
+Rev Med Chir Soc Med Nat Iasi
+Rev Med Cienc Afines
+Rev Med Cordoba
+Rev Med Costa Rica
+Rev Med Cubana
+Rev Med Hondur
+Rev Med Hosp Cent Empl
+Rev Med Hosp Gen (Mex)
+Rev Med Hosp Obrero
+Rev Med Hyg Outre Mer
+Rev Med IPSS
+Rev Med Inst Mex Seguro Soc
+Rev Med Int Photo Cinema Telev
+Rev Med Interna Neurol Psihiatr Neurochir Dermatovenerol Med Interna
+Rev Med Interna Neurol Psihiatr Neurochir Dermatovenerol Neurol Psihiatr Neurochir
+Rev Med Interne
+Rev Med Liege
+Rev Med Louvain
+Rev Med Mil
+Rev Med Miniere
+Rev Med Moamb
+Rev Med Moyen Orient
+Rev Med Nancy
+Rev Med Normandes
+Rev Med Panama
+Rev Med Peru
+Rev Med Picardie
+Rev Med Psychosom
+Rev Med Psychosom Psychol Med
+Rev Med Suisse
+Rev Med Suisse Romande
+Rev Med Toulouse
+Rev Med Tours
+Rev Med Univ Navarra
+Rev Med Valparaiso
+Rev Med Vet (B Aires)
+Rev Med Vet (Toulouse)
+Rev Med Virol
+Rev Medica Hosp Esp
+Rev Medica Minas
+Rev Mediterr Sci Med
+Rev Metaphys
+Rev Metaphys Morale
+Rev Mex Anal Conducta
+Rev Mex Cienc Polit Soc
+Rev Mex Cir Ginecol Cancer
+Rev Mex Sociol
+Rev Mex Urol
+Rev Mus Nac
+Rev Museo Fac Odontol B Aires
+Rev Neurol
+Rev Neurol (Paris)
+Rev Neurol Dis
+Rev Neuropsiquiatr
+Rev Neuropsychiatr Infant
+Rev Neurosci
+Rev Nord
+Rev Nouv
+Rev Numis
+Rev OIM Sobre Migr Am Lat
+Rev Obstet Ginecol Venez
+Rev Occident Musulman Mediterr
+Rev Occidente
+Rev Oculomot Res
+Rev Odontoestomatol
+Rev Odontoimplantol
+Rev Odontol (Cordoba)
+Rev Odontol (La Paz)
+Rev Odontol Circ Odontol Parag
+Rev Odontol Concepcion
+Rev Odontol Costa Rica
+Rev Odontol Ecuat
+Rev Odontol P R
+Rev Odontol P R (Santurce)
+Rev Odontol Parana
+Rev Odontol St Catarina
+Rev Odontol UNESP
+Rev Odontol UNICID
+Rev Odontol Univ Sao Paulo
+Rev Odontostomatol
+Rev Odontostomatol (Paris)
+Rev Odontostomatol Midi Fr
+Rev Odontostomatol Nordest
+Rev Ordem Med
+Rev Orthop Chir Appar Mot
+Rev Orthop Dento Faciale
+Rev Ortop Traumatol Ed Lat Am
+Rev Oto Neuro Oftalmol Cir Neurol Sud Am
+Rev Otoneuroophtalmol
+Rev Otorrinolaringol
+Rev Palaeobot Palynol
+Rev Palais Decouv
+Rev Palud Med Trop
+Rev Panam Salud Publica
+Rev Pathol Comp
+Rev Pathol Gen Physiol Clin
+Rev Patient Focus Care Assoc
+Rev Pau Bearn
+Rev Paul Endodontia
+Rev Paul Enferm
+Rev Paul Hosp
+Rev Paul Med
+Rev Paul Odontol
+Rev Pediatr
+Rev Pediatr Obstet Ginecol Obstet Ginecol
+Rev Pediatr Obstet Ginecol Pediatr
+Rev Pernambucana Odontol
+Rev Peru Pediatr
+Rev Peru Poblac
+Rev Philol Litt Hist Anc
+Rev Philos Louv
+Rev Physiol Biochem Pharmacol
+Rev Planeac Desarro
+Rev Pneumol Clin
+Rev Pol Acad Sci
+Rev Polit
+Rev Port Cardiol
+Rev Port Cir Cardiotorac Vasc
+Rev Port Estomatol Cir Maxilofac
+Rev Port Farm
+Rev Port Hist
+Rev Port Med Mil
+Rev Port Pediatr
+Rev Port Pediatr Pueric
+Rev Port Pneumol
+Rev Prat
+Rev Psicoanal
+Rev Psiquiatr Psicol Med Eur Am Lat
+Rev Psychol Appl
+Rev Publ Nav
+Rev Public Data Use
+Rev Pure Appl Pharmacol Sci
+Rev Quest Sci
+Rev Quim Farm
+Rev Quir Esp
+Rev Radic Polit Econ
+Rev Reg Aracatuba Assoc Paul Cir Dent
+Rev Reg Stud
+Rev Relig
+Rev Relig Res
+Rev Reprod
+Rev Rev Interam
+Rev Rhum
+Rev Rhum Ed Fr
+Rev Rhum Engl Ed
+Rev Rhum Mal Osteoartic
+Rev Roche
+Rev Rom Bioet
+Rev Rom Stat
+Rev Rouergue
+Rev Roum Endocrinol
+Rev Roum Hist
+Rev Roum Inframicrobiol
+Rev Roum Med
+Rev Roum Med Intern
+Rev Roum Morphol Embryol
+Rev Roum Morphol Physiol
+Rev Roum Neurol
+Rev Roum Neurol Psychiatr
+Rev Roum Physiol
+Rev Roum Virol
+Rev SESDA
+Rev Saintonge Aunis
+Rev Salud Publica (Bogota)
+Rev Sanid Asist Soc
+Rev Sanid Hig Publica (Madr)
+Rev Sanid Mil Peru
+Rev Sanid Milit
+Rev Sanid Milit Argent
+Rev Sanid Polic
+Rev Saude Publica
+Rev Sci Edu
+Rev Sci Hum
+Rev Sci Instrum
+Rev Sci Med
+Rev Sci Morales Polit
+Rev Sci Philos Theol
+Rev Sci Soc Fr Est
+Rev Sci Tech
+Rev Sci Tech Ser Sci Hum
+Rev Serv Nac Salud
+Rev Serv Nac Tubers
+Rev Soc Argent Biol
+Rev Soc Biom Hum
+Rev Soc Bolivar Venez
+Rev Soc Bras Hist Cienc
+Rev Soc Bras Med Trop
+Rev Soc Colomb Endocrinol
+Rev Soc Colomb Ortod
+Rev Soc Colomb Pediatr Pueric
+Rev Soc Econ
+Rev Soc Fr Hist Art Dent
+Rev Soc Fr Hist Hop
+Rev Soc Ginecol Obstet
+Rev Soc Mex Hist Nat
+Rev Soc Odontol La Plata
+Rev Soc Peru Endocrinol
+Rev Soc Policy
+Rev Soc Savantes Haute Normandie
+Rev Soc Venez Hist Med
+Rev Social Law
+Rev Sociol
+Rev Stat
+Rev Stat Appl
+Rev Stiint Med
+Rev Stomatol Chir Maxillofac
+Rev Stomatoodontol Nord Fr
+Rev Suisse Zool
+Rev Surg
+Rev Synth
+Rev Tarn
+Rev Trav Acad Sci Morales Polit C R Seances
+Rev Tuberc
+Rev Tuberc Pneumol (Paris)
+Rev Tuberc Urug
+Rev Tunis Etud Popul
+Rev Tunis Geogr
+Rev Tunis Sci Soc
+Rev Univ
+Rev Univ Brux
+Rev Univ Nac Cordoba
+Rev Univ Ottawa
+Rev Urol(mexico)
+Rev Urug Psicoanal
+Rev Venez Sanid Asist Soc
+Rev Venez Urol
+Rev Viernes Med
+Rev Vivarais
+Revis Biol Celular
+Revis Statut La
+Revmatologiia (Mosk)
+Revolution
+Revue Geriatr
+Revue Stomatol
+Rhein Mus Philol
+Rhein Vierteljahrsbl
+Rheinische Lebensbilder
+Rheinisches Arztebl
+Rheum Dis Clin North Am
+Rheumatism
+Rheumatol Balneol Allergol
+Rheumatol Int
+Rheumatol Phys Med
+Rheumatol Rehabil
+Rheumatology
+Rheumatology (Oxford)
+Rhinol Suppl
+Rhinology
+Rhod Nurse
+Rhodesiana
+Rhumatologie
+Ric Clin Lab
+Ric Sci
+Ric Sci 2 Ser Pt 1 Riv
+Ric Sci 2 Ser Pt 2 Rend [B]
+Richmond Cty Hist
+Riforma Med
+Rig
+Rinascimento
+Rinsho Biseibutshu Jinsoku Shindan Kenkyukai Shi
+Rinsho Byori
+Rinsho Eiyo
+Rinsho Fujinka Sanka
+Rinsho Ganka
+Rinsho Geka
+Rinsho Hoshasen
+Rinsho Ketsueki
+Rinsho Kyobu Geka
+Rinsho Naika Shonika Intern Med
+Rinsho Shika
+Rinsho Shinkeigaku
+Rinsho Shishubyo Danwakai Kaishi
+Rinsho Shokakibyogaku
+Ripon Forum
+Risk
+Risk Anal
+Risk Issues Health Saf
+Risk Manag Can Health Care
+Risk Manage
+Rittenhouse
+Riv Anat Patol Oncol
+Riv Biol
+Riv Chir Med
+Riv Chir Pediatr
+Riv Clin Pediatr
+Riv Crit Clin Med
+Riv Crit Stor Filos
+Riv Emoter Immunoematol
+Riv Estet
+Riv Eur Sci Med Farmacol
+Riv Filol Istruz Classica
+Riv Filos
+Riv Gastroenterol
+Riv Geogr Ital
+Riv Inferm
+Riv Infort Mal Prof
+Riv Int Psicol Ipn
+Riv Int Sci Sociali
+Riv Ist Sieroter Ital
+Riv Ist Vaccinogeno Consorzi Prov Antituberc
+Riv Istochim Norm Patol
+Riv Ital Econ Demogr Stat
+Riv Ital Ginecol
+Riv Ital Ig
+Riv Ital Med Leg
+Riv Ital Odontoiatr Infant
+Riv Ital Odontotec
+Riv Ital Pediatr
+Riv Ital Stomatol
+Riv Ital Trac Patol Ocul Esotica
+Riv Malariol
+Riv Maritt
+Riv Med Aeronaut
+Riv Med Aeronaut Spaz
+Riv Meteorol Aeronaut
+Riv Mil
+Riv Neurobiol
+Riv Neurol
+Riv Odontoiatr Amici Brugg
+Riv Odontostomatol Implantoprotesi
+Riv Ostet Ginecol
+Riv Ostet Ginecol Prat
+Riv Ostet Ginecol Prat Med Perinat
+Riv Otoneurooftalmol
+Riv Parassitol
+Riv Patol Clin
+Riv Patol Clin Sper
+Riv Patol Nerv Ment
+Riv Pediatr Sicil
+Riv Psicoanal
+Riv Psicol
+Riv Psicol Scr
+Riv Psicol Soc Arch Ital Psicol Gen
+Riv Radiol
+Riv Rosmin Filos Cult
+Riv Sicil Tuberc
+Riv Sper Freniatr Med Leg Alien Ment
+Riv Stor Calabr
+Riv Stor Chiesa Ital
+Riv Stor Filos
+Riv Stor Ital
+Riv Stor Med
+Riv Stor Sci
+Riv Stor Sci Mediche Nat
+Riv Studi Class
+Riv Studi Orient
+Riv Tuberc Mal Appar Respir
+Rob Auton Syst
+Rochester Hist
+Rocky Mt Med J
+Rocky Mt Soc Sci J
+Rocz Akad Med Bialymst
+Rocz Akad Med Bialymst Supl
+Rocz Akad Med Im Juliana Marchlewskiego Bialymst
+Rocz Akad Med Im Juliana Marchlewskiego Bialymst Suppl
+Rocz Filoz
+Rocz Humanist
+Rocz Panstw Zakl Hig
+Rocz Pomor Akad Med
+Rodo Kagaku
+Rofo
+Rogerian Nurs Sci News
+Roll Stone
+Rom Hist Mitt
+Rom J Endocrinol
+Rom J Gastroenterol
+Rom J Gerontol Geriatr
+Rom J Intern Med
+Rom J Morphol Embryol
+Rom J Neurol Psychiatry
+Rom J Physiol
+Rom J Virol
+Rom Med Rev
+Roman Notes
+Romania
+Romantisme
+Ronenbyo
+Rontgen Laborator
+Rontgenblatter
+Rontgendiagnostik Ergeb
+Rontgeneur Radiodiagn Clin Eur
+Rontgenpraxis
+Rooilijn
+Roshni
+Ross Fiziol Zh Im I M Sechenova
+Ross Gastroenterol Zh
+Ross Med Zh
+Rostrum
+Roum Arch Microbiol Immunol
+Round Table
+Rozhl Chir
+Rozhl Tuberk
+Rozpr Wydz Nauk Med
+Ruch Praw Ekon Socjol
+Rudolstaedter Heimath
+Rum Med Rev
+Rumah Tangga Kesehatan
+Ruperto Carola
+Rural Afr
+Rural Cond Trends
+Rural Demogr
+Rural Policy Brief
+Rural Reconstr Forum
+Rural Remote Health
+Rural Sociol
+Russ Coiles Health Trends
+Russ J Immunol
+Russ J Plant Physiol
+Russ Rev
+Russ Soc Sci Rev
+Rutgers Camden Law J
+Rutgers Comput Technol Law J
+Rutgers J Comput Technol Law
+Rutgers Law J
+Rutgers Law Rev
+Rutland Rec
+Ryan Advis Health Serv Gov Boards
+Ryoikibetsu Shokogun Shirizu
+Ryumachi
+S A Nurs J
+S Afr Cancer Bull
+S Afr Dent J
+S Afr Geogr J
+S Afr Hist J
+S Afr J Afr Aff
+S Afr J Anim Sci
+S Afr J Clin Sci
+S Afr J Commun Disord
+S Afr J Econ
+S Afr J Econ Hist
+S Afr J Hum Rights
+S Afr J Lab Clin Med
+S Afr J Med Sci
+S Afr J Obstet Gynaecol
+S Afr J Psychol
+S Afr J Sci
+S Afr J Surg
+S Afr Labour Bull
+S Afr Law J
+S Afr Law Rep
+S Afr Med J
+S Afr Nursing J
+S Afr Sociol Rev
+S Afr Tydskr Sosiol
+S Afr Tydskr Strafr Kriminol
+S C Dent J
+S C Hist Illus
+S C Hist Mag
+S C Law Rev
+S C Nurs
+S C Nurse
+S D Codif Laws S D
+S D Farm Home Res
+S D Hist Collect
+S D J Med
+S D Law Rev
+S D Med
+S D Nurse
+S TA NU
+SA J Contin Med Educ
+SA Nurs J
+SAAD Dig
+SAAS Bull Biochem Biotechnol
+SADJ
+SAFE J
+SAIS Rev
+SAR QSAR Environ Res
+SAfAIDS News
+SBC
+SC Rep
+SC Trodent
+SCADA J
+SCHA J
+SCI Nurs
+SCN News
+SCNA Newsl
+SD J Med Pharm
+SDA Dent
+SECOLAS Ann
+SHSTF
+SIAM J Appl Math
+SIECUS Rep
+SMU Law Rev
+SOLAIAT
+SPUMS J
+SPVN
+SRA J
+SSO Schweiz Monatsschr Zahnheilkd
+ST Vincents Hosp Med Bull
+STD Bull
+STEP Perspect
+STNS J Trauma Nurs
+Saalburg Jahrb
+Saarl Arztebl
+Sabouraudia
+Sac Explor
+Sacitra Ayurveda
+Saeculum
+Safe Mother
+Sage
+Saguenay Med
+Sairaanh Vuosik
+Sairaanhoitaja
+Sairaanhoitajalehti
+Saishin Igaku
+Saisons Alsace
+Saline Systems
+Salisbury Med Bull
+Salmagundi
+Salubritas
+Salud Bucal
+Salud Publica Mex
+Salud Reprod Soc
+Salus
+Salzburger Beitr Paracelsusforsch
+Same Day Surg
+Samid Aliqtisadi
+Samiska
+Samml Zwangl Abh Geb Psychiatr Neurol
+Sample Regist Bull
+Samtiden
+San Diego Law Rev
+San Fernando Val Dent Soc Bull
+San Gabriel Val Dent Soc Bull
+San Jose Stud
+Sanfujinka Chiryo
+Sanfujinka No Jissai
+Sanfujinka No Shinpo
+Sang
+Sangre (Barc)
+Sangue
+Sangyo Eiseigaku Zasshi
+Sangyo Igaku
+Sanita Pubblica
+Sankhya
+Sankhya Ser B
+Sanop Misaengmul Hakhoe Chi
+Santa Clara Comput High Technol Law J
+Santa Clara Law Rev
+Santa Clara Lawyer
+Sante
+Sante Ment Que
+Sante Publique
+Sante Publique (Bucur)
+Sante Que
+Sante Salud
+Santiago
+Sao Paulo Med J
+Sapienza
+Sapporo Igaku Zasshi
+Sarcoidosis
+Sarcoidosis Vasc Diffuse Lung Dis
+Sarvekshana
+Sask Hist
+Sask Law Rev
+Saturday Rev
+Saturday Rev Sci
+Saturday Rev World
+Saudi Med J
+Savanna
+Sb Arch Pr
+Sb Lek
+Sb Nar Muz Pr Rada C Lit Hist
+Sb Nar Muz Praze Rada A Hist
+Sb Nar Muz Praze Rada B
+Sb Pathofysiol Traveni Vyz Gastroenterol Bohema
+Sb Pr Filos Fak Brnenske Univ Rada Hist
+Sb Tr Azerbaidzhanskii Gos Meditsinskii Inst N Narimanova
+Sb Ved Pr Lek Fak Karlovy Univerzity Hradci Kralove
+Sb Ved Pr Lek Fak Karlovy Univerzity Hradci Kralove Suppl
+Sbs Ces Geogr Spol
+Scalpel (Brux)
+Scalpel Tongs
+Scan Electron Microsc
+Scand Actuar J
+Scand Audiol
+Scand Audiol Suppl
+Scand Cardiovasc J
+Scand Cardiovasc J Suppl
+Scand Econ Hist Rev
+Scand J Caring Sci
+Scand J Clin Lab Invest
+Scand J Clin Lab Invest Suppl
+Scand J Dent Res
+Scand J Dev Altern
+Scand J Econ
+Scand J Gastroenterol
+Scand J Gastroenterol Suppl
+Scand J Haematol
+Scand J Haematol Suppl
+Scand J Hist
+Scand J Immunol
+Scand J Immunol Suppl
+Scand J Infect Dis
+Scand J Infect Dis Suppl
+Scand J Med Sci Sports
+Scand J Occup Ther
+Scand J Plast Reconstr Surg
+Scand J Plast Reconstr Surg Hand Surg
+Scand J Plast Reconstr Surg Hand Surg Suppl
+Scand J Plast Reconstr Surg Suppl
+Scand J Prim Health Care
+Scand J Prim Health Care Suppl
+Scand J Psychol
+Scand J Public Health
+Scand J Public Health Suppl
+Scand J Rehabil Med
+Scand J Rehabil Med Suppl
+Scand J Respir Dis
+Scand J Respir Dis Suppl
+Scand J Rheumatol
+Scand J Rheumatol Suppl
+Scand J Soc Med
+Scand J Soc Med Suppl
+Scand J Soc Welf
+Scand J Surg
+Scand J Thorac Cardiovasc Surg
+Scand J Thorac Cardiovasc Surg Suppl
+Scand J Urol Nephrol
+Scand J Urol Nephrol Suppl
+Scand J Work Environ Health
+Scand Popul Stud
+Scand Stud
+Scand Stud Law
+Scandia
+Scanning
+Scanning Microsc
+Scanning Microsc Suppl
+Scanodont
+Scapel Quill
+Sch Couns
+Sch Dent Serv Gaz N Z
+Sch Health Rev
+Sch Inq Nurs Pract
+Sch Psychol Dig
+Sch Psychol Int
+Sch Sci Math
+Sch Sci Rev
+Schaffhauser Beitr Vaterl Gesch
+Scheidewege
+Schiff Zeit
+Schizophr Bull
+Schizophr Res
+Schmerz
+Scholast Update
+School Nurse News
+School Psych Rev
+Schr Gesch Kult Alten Orients
+Schriftenr Geb Off Gesundheitswes
+Schriftenr Ges Freunde Med Hochsch Hannover
+Schriftenr Gesch Versamml Dtsch Naturforsch Arzte
+Schriftenr Neurol
+Schriftenr Ver Wasser Boden Lufthyg
+Schriftenr Zentralbl Arbeitsmed Arbeitsschutz Prophyl
+Schriftenr Zentralbl Arbeitsmed Arbeitsschutz Prophyl Ergonomie
+Schweiz Apoth Ztg
+Schweiz Arch Neurol Neurochir Psychiatr
+Schweiz Arch Neurol Psychiatr
+Schweiz Arch Tierheilkd
+Schweiz Arzteztg
+Schweiz Med Wochenschr
+Schweiz Med Wochenschr Suppl
+Schweiz Monatsh
+Schweiz Monatsschr Zahnmed
+Schweiz Rundsch Med Prax
+Schweiz Z Gesch
+Schweiz Z Gynakol Geburtshilfe
+Schweiz Z Homoopath
+Schweiz Z Med Traumatol
+Schweiz Z Pathol Bakteriol
+Schweiz Z Psychol Anwend
+Schweiz Z Soziol
+Schweiz Z Sportmed
+Schweiz Z Tuberc Pneumonol
+Schweiz Z Tuberk
+Schweiz Z Volkswirtsch Stat
+Schwest Rev
+Sci Act
+Sci Aging Knowledge Environ
+Sci Aliments
+Sci Am
+Sci Avenir
+Sci Basis Med Annu Rev
+Sci Bio J
+Sci Can
+Sci China B
+Sci China C Life Sci
+Sci Christ Belief
+Sci Commun
+Sci Context
+Sci Cult
+Sci Cult (Lond)
+Sci Dig
+Sci Educ
+Sci Educ Bull
+Sci Educ J
+Sci Eng Ethics
+Sci Forum
+Sci Genet
+Sci Geol Mem
+Sci Gov Rep
+Sci Hortic (Amsterdam)
+Sci Justice
+Sci Med (Phila)
+Sci Med Ital
+Sci Med Man
+Sci New Guinea
+Sci News
+Sci People
+Sci Pharm
+Sci Proc Cardiff Med Soc
+Sci Prog
+Sci Prog (New Haven)
+Sci Public Policy
+Sci Rech Odontostomatol
+Sci Rep Ist Super Sanita
+Sci Rep Res Inst Tohoku Univ [Med]
+Sci Rep Tohoku Univ Ser 7
+Sci STKE
+Sci Sin
+Sci Sin [B]
+Sci Soc
+Sci Soc Sante
+Sci Sports
+Sci Stud
+Sci Teach
+Sci Tech Perspect
+Sci Technol Human Values
+Sci Total Environ
+Science
+Science (80- )
+Sciences (New York)
+Scientia
+ScientificWorldJournal
+Scientist
+Scientometrics
+Scott Edu Rev
+Scott Geogr Mag
+Scott Hist Rev
+Scott J Crim Justice Stud
+Scott J Polit Econ
+Scott J Sociol
+Scott Med J
+Scr Ethnol
+Scr Med (Brno)
+Scr Minora
+Screening
+Scriptorium
+Sdc Mag
+Se Pu
+Sea View Hosp Bull
+Seagull Theatre Q
+Seaprap Res Rep
+Seara Med
+Search Medford N J
+Seattle Univ Law Rev
+Second Messengers Phosphoproteins
+Second Opin
+Second Opin (Chic)
+Second Opin Health Care Issues
+Sedimentology
+Seeds
+Sefarad
+Seibutsu Kankyo Chosetsu
+Seikagaku
+Seikatsu Eisei
+Seikei Geka
+Seiroka Kango Daigaku Kiyo
+Seishin Igaku
+Seishin Igaku Kenkyusho Gyosekishu
+Seishin Shinkeigaku Zasshi
+Seitai Kagaku
+Seizure
+Sel Cancer Ther
+Sel Odontol (Sao Paulo)
+Self
+Sem Epidemiol
+Sem Hop
+Sem Hop Inf
+Sem Hop Paris
+Sem Hop Ther
+Sem Hop Ther Paris
+Sem Med
+Sem Med Mex
+Sem Med Prof Med Soc
+Sem Medicale Med Soc
+Sem Ther
+Semin Adolesc Med
+Semin Anesth
+Semin Arthritis Rheum
+Semin Arthroplasty
+Semin Cancer Biol
+Semin Cardiothorac Vasc Anesth
+Semin Cell Biol
+Semin Cell Dev Biol
+Semin Clin Neuropsychiatry
+Semin Cutan Med Surg
+Semin Dent Hyg
+Semin Dermatol
+Semin Diagn Pathol
+Semin Dial
+Semin Drug Treat
+Semin Ensen Odontopediatr
+Semin Fam Med
+Semin Fetal Neonatal Med
+Semin Gastrointest Dis
+Semin Hear
+Semin Hematol
+Semin Immunol
+Semin Int
+Semin Interv Cardiol
+Semin Laparosc Surg
+Semin Liver Dis
+Semin Musculoskelet Radiol
+Semin Neonatol
+Semin Nephrol
+Semin Neurol
+Semin Nucl Med
+Semin Nurse Manag
+Semin Oncol
+Semin Oncol Nurs
+Semin Ophthalmol
+Semin Orthod
+Semin Pediatr Infect Dis
+Semin Pediatr Neurol
+Semin Pediatr Surg
+Semin Perinatol
+Semin Perioper Nurs
+Semin Psychiatry
+Semin Radiat Oncol
+Semin Rep Merck Sharp Dohme
+Semin Reprod Endocrinol
+Semin Reprod Med
+Semin Respir Crit Care Med
+Semin Respir Infect
+Semin Roentgenol
+Semin Speech Lang
+Semin Surg Oncol
+Semin Thorac Cardiovasc Surg
+Semin Thorac Cardiovasc Surg Pediatr Card Surg Annu
+Semin Thromb Hemost
+Semin Ultrasound CT MR
+Semin Urol
+Semin Urol Oncol
+Semin Vasc Med
+Semin Vasc Surg
+Semin Vet Med Surg (Small Anim)
+Semina
+Seminar
+Senckenb Biol
+Senefiance
+Sens Actuators A Phys
+Sens Actuators B Chem
+Sens Processes
+Sens Syst
+Sentinel Event Alert
+Seoul J Med
+Ser Filol Ros
+Ser Haematol
+Ser Libr
+Ser Nurs Adm
+Ser Paedopsychiatr
+Serv Migr
+Servir
+Sess Laws Guam
+Sess Laws Hawaii Passed State Legis Hawaii
+Sess Laws Kans Kans
+Sess Laws State Minn Minn
+Sessualita
+Sessuologia
+Seton Hall Law Rev
+Seton Hall Legis J
+Settim Med
+Seven Days
+Seventeenth Century
+Sewage Ind Waste
+Sewage Work J
+Sex Abuse
+Sex Disabil
+Sex Health
+Sex Health Exch
+Sex Marital Ther
+Sex Med Today
+Sex Planeam Fam
+Sex Roles
+Sex Transm Dis
+Sex Transm Infect
+Sex Wkly Plus
+Shakai Keizai Shigaku
+Shakespeare Stud
+Shandong Yi Kan
+Shanghai Kou Qiang Yi Xue
+Shendet Pop
+Sheng Li Ke Xue Jin Zhan
+Sheng Li Xue Bao
+Sheng Wu Gong Cheng Xue Bao
+Sheng Wu Hua Xue Yu Sheng Wu Wu Li Xue Bao (Shanghai)
+Sheng Wu Yi Xue Gong Cheng Xue Za Zhi
+Shengzhi Yu Biyun
+Shi Yan Sheng Wu Xue Bao
+Shigaku
+Shigaku Zasshi
+Shika Igaku
+Shika Kiso Igakkai Zasshi
+Shika Rikogaku Zasshi
+Shika Zairyo Kikai
+Shikai Tenbo
+Shikwa Gakuho
+Shimane J Med Sci
+Shinkei Kenkyu No Shimpo
+Shinrigaku Kenkyu
+Shinryo
+Shiso
+Shiyo
+Shma
+Shock
+Shokubutsu Kenkyu Zasshi
+Shokuhin Eiseigaku Zasshi
+Shoni Shikagaku Zasshi
+Shonika
+Shonika Kiyo
+Showa Igakkai Zasshi
+Showa Shigakkai Zasshi
+Shui sheng sheng wu hsueh bao
+Shujutsu
+Si Yu Yan
+Sichuan Da Xue Xue Bao
+Sichuan Da Xue Xue Bao Yi Xue Ban
+Sichuan Yi Xue Yuan Xue Bao
+Sicil Sanit
+Sicilia Med
+SidAlerte
+Sidahora
+Sieccan J
+Sierra
+Sight Sav Rev
+Sightsav Rev
+Sightsaving
+Sigmund Freud House Bull
+Sign
+Signature
+Signs (Chic)
+Siirtolaisuus
+Simon Wiesenthal Cent Annu
+Simul Games
+Simul Gaming
+Sinai Hosp J (Balt)
+Singap J Trop Geogr
+Singap Libr
+Singap Stat Bull
+Singapore Dent J
+Singapore Fam Physician
+Singapore J Obstet Gynecol
+Singapore Med J
+Singapore Public Health Bull
+Singmul Hakhoe Chi
+Sintesi Medica
+Sir Lanka J Popul Stud
+Siriraj Hosp Gaz
+Sist Nerv
+Sist Urbani
+Sistole
+Sitzungsber Heidelb Akad Wiss Math Naturwiss Kl
+Sixt Century J
+Sjukskoterskan
+Skand Arch Physiol
+Skeletal Radiol
+Skeptic
+Skin (Los Angeles)
+Skin Pharmacol
+Skin Pharmacol Appl Skin Physiol
+Skin Pharmacol Physiol
+Skin Res Technol
+Skin Therapy Lett
+Skinmed
+Skull Base
+Sky Telescope
+Slaski Kwat Hist Sabotka
+Slav East Eur Rev
+Slavery Abol
+Slavic Rev
+Sleep
+Sleep Breath
+Sleep Med
+Sleep Med Rev
+Sleep Res Online
+Slezsky Sb
+Sloan Manage Rev
+Slov Arch
+Slov Lek
+Slov Prehl
+Slown Biogr Pol Med XX Wieku
+Smith Coll Stud Soc Work
+Smith-Hurd Ill Annot Statut Ill
+Smithson Ann Flight
+Smithson Stud Hist Technol
+Smithsonian
+Snakeroot Extr
+Soap Perfum Cosmet (Lond)
+Soc Action
+Soc Afr SIDA
+Soc Anc Med Rev
+Soc Anim
+Soc Appl Bacteriol Symp Ser
+Soc Behav
+Soc Behav Pers
+Soc Biol
+Soc Biol Hum Aff
+Soc Casework
+Soc Change
+Soc Choice Welfare
+Soc Cult
+Soc Dev
+Soc Dev Issues
+Soc Econ Adm (Lond)
+Soc Econ Stud
+Soc Forces
+Soc Gen Physiol Ser
+Soc Hist
+Soc Hist Med
+Soc Indic Res
+Soc Ital Dermatol Sifilogr Sezioni Interprov Soc Ital Dermatol Sifilogr
+Soc Justice
+Soc Justice Res
+Soc Justice Rev
+Soc Labour Bull
+Soc Leg Stud
+Soc Malawi J
+Soc Mar Q
+Soc Mark Forum
+Soc Mark Update
+Soc Med Tidskr
+Soc Nat Resour
+Soc Networks
+Soc Nurs Hist Gaz
+Soc Philos Policy
+Soc Policy
+Soc Policy Rep
+Soc Prax
+Soc Probl
+Soc Psychiatry
+Soc Psychiatry Psychiatr Epidemiol
+Soc Psychol
+Soc Psychol Q
+Soc Rehabil Rec
+Soc Res (New York)
+Soc Responsib Journal Law Med
+Soc Sci
+Soc Sci (Kans)
+Soc Sci China
+Soc Sci Comput Rev
+Soc Sci Hist
+Soc Sci Inf (Paris)
+Soc Sci Inf Stud
+Soc Sci J
+Soc Sci Med
+Soc Sci Med [A]
+Soc Sci Med [B]
+Soc Sci Med [C]
+Soc Sci Med [D]
+Soc Sci Med [E]
+Soc Sci Med [F]
+Soc Sci Med [Med Anthropol]
+Soc Sci Med [Med Econ]
+Soc Sci Med [Med Geogr]
+Soc Sci Med [Med Psychol Med Sociol]
+Soc Sci New Lett
+Soc Sci Q
+Soc Sci Res
+Soc Secur Bull
+Soc Secur Bull Annu Stat Suppl
+Soc Serv Rev
+Soc Soc Hist Med Bull (Lond)
+Soc Stor
+Soc Stud (Maynooth)
+Soc Stud Sci
+Soc Theory Pract
+Soc Thought
+Soc Welfare (India)
+Soc Wetenschap
+Soc Work
+Soc Work Educ
+Soc Work Educ Dev Newsl
+Soc Work Groups
+Soc Work Health Care
+Soc Work Res
+Soc Work Res Abstr
+Social Rev
+Social Revolut
+Socialisme
+Socialmed Tidskr Skriftser
+Societas
+Society
+Socioecon Issues Health
+Socioecon Plann Sci
+Sociol Anal
+Sociol Bull
+Sociol Cas
+Sociol Diritto
+Sociol Educ
+Sociol Focus
+Sociol Gids
+Sociol Health Illn
+Sociol Inq
+Sociol Lav
+Sociol Methodol
+Sociol Methods Res
+Sociol Neerl
+Sociol Perspect
+Sociol Q
+Sociol Relig
+Sociol Res
+Sociol Rev
+Sociol Rev [Monogr]
+Sociol Rom
+Sociol Ruralis
+Sociol Sela
+Sociol Soc
+Sociol Soc Res
+Sociol Spectr
+Sociol Sport J
+Sociol Symp
+Sociol Trav
+Sociol Work Occup
+Sociologia
+Sociologus
+Sociology
+Sociometry
+Soemmering Forsch
+Softw Healthc
+Softw Law J
+Sogo Igaku
+Sogo Kango
+Sogo Rinsho
+Soil Biol Biochem
+Soil Sci
+Soil Sci Soc Am J
+Soins
+Soins Cardiol
+Soins Chir
+Soins Chir Gen Spec
+Soins Form Pedagog Encadr
+Soins Gerontol
+Soins Gynecol Obstet Pueric
+Soins Gynecol Obstet Pueric Pediatr
+Soins Pathol Trop
+Soins Pediatr Pueric
+Soins Psychiatr
+Sojourn
+Sojourners
+Solicit J
+Solid State Nucl Magn Reson
+Somat Cell Mol Genet
+Somatic Cell Genet
+Somatosens Mot Res
+Somatosens Res
+Somerset Archaeol Net Hist
+Sonde
+Sonderb Strahlenther Onkol
+Sonderb Z Strahlenther Onkol
+Soobshch Akad Nauk Gruz Ssr
+Soproden
+Sos Laaketiet Aikak
+Sosiologia
+Sotahist Aikak
+Sotilaslaak Aikak
+Sotsiol Issled
+Soud Lek
+Soundings
+Source Notes Hist Art
+South Afr J Demogr
+South Afr J Geol
+South Afr Polit Econ Mon
+South Am Indian Stud
+South Asia Bull
+South Asia Res
+South Asian Rev
+South Atl Q
+South Calif Interdiscip Law J
+South Calif Law Rev
+South Calif Q
+South Calif Rev Law Womens Stud
+South East Report Second Ser
+South Econ J
+South Eur Soc Polit
+South Expos
+South Folk Q
+South Hist
+South Hosp
+South Ill Univ Law J
+South Indian Stud
+South J Educ Res
+South J Optom
+South J Philos
+South Med
+South Med J
+South Pac Bull
+South Report Second Ser Cases Argued Determ Courts Ala Fla La Miss
+South Stud
+South Tex Law J
+South Tex Law Rev
+South Univ Law Rev
+Southampt Med J
+Southeast Asian J Soc Sci
+Southeast Asian J Surg
+Southeast Asian J Trop Med Public Health
+Southeast Eur
+Southeast Eur Ser
+Southeast Geogr
+Southwest Hist Q
+Southwest J Anthropol
+Southwest J Philos
+Southwest Law J
+Southwest Med
+Southwest Rev
+Southwest Univ Law Rev
+Sov Anthropol Archeol
+Sov Arkh
+Sov Econ
+Sov Educ
+Sov Etnogr
+Sov Genet
+Sov Geogr
+Sov Gos Pravo
+Sov J Bioorg Chem
+Sov J Dev Biol
+Sov J Ecol
+Sov Jew Aff
+Sov Law Gov
+Sov Med
+Sov Rev
+Sov Soc
+Sov Stud
+Sov Stud Hist
+Sov Stud Philos
+Sov Union
+Sov Varchebnyii Sb
+Sov Zdravookhr
+Sov Zdravookhr Kirg
+Sovrem Probl Onkol
+Sovrem Probl Tuberk
+Sowjetwiss Naturwiss Beitr
+Soz Praventivmed
+Sozialgesch Med
+Sozialmed Padagog Jugendkd
+Space Commun
+Space Life Sci
+Space Med Med Eng (Beijing)
+Space Policy
+Space Sci Rev
+Space Technol
+Space World
+Spaceflight (Lond)
+Spagna Contemp
+Span J Psychol
+Spat Cogn Comput
+Spat Vis
+Spec Care Dentist
+Spec Collect
+Spec Educ
+Spec Educ Forward Trends
+Spec Law Dig Health Care (Mon)
+Spec Law Dig Health Care Law
+Spec Libr
+Spec Publ Am Philos Soc
+Spec Rep Ser Indian Counc Med Res
+Spec Rep Ser Med Res Counc (G B)
+Spec Top Endocrinol Metab
+Specif Eng
+Spectator
+Spectrochim Acta A Mol Biomol Spectrosc
+Spectrochim Acta [A]
+Spectrum
+Speculations Sci Technol
+Speculum
+Speech Pathol Ther
+Sperimentale
+Spieg Hist
+Spinal Cord
+Spine
+Spine J
+Spinner
+Spirales
+Spis Blg Akad Nauk
+Spitalul
+Splice Life
+Spore
+Sportarzt Ver Sportmed
+Sports Biomech
+Sports Med
+Sportverletz Sportschaden
+Spraw Czynnosci Posiedz Lodz Tow Nauk
+Springer Semin Immunopathol
+Springer Ser Health Care Soc
+Springfielder
+Sr Nurse
+Sr Scholast
+Sri Lanka J Soc Sci
+Sri Lanka Popul Dig
+Srp Arh Celok Lek
+St Anthony Messenger
+St Barnabas Hosp Med Bull
+St Bartholomews Hosp J
+St Johns Law Rev
+St Louis Univ Law J
+St Louis Univ Public Law Rev
+St Luc Med
+St Lukes Hosp Gaz (Guardamangia)
+St Lukes J Theol
+St Marys Hosp Gaz
+St Marys Law J
+St Thomas Law Rev
+St Tomas J Med
+St Tomas Nurs J
+St Vladmirs Theol Q
+Stadion
+Stadion (Koln)
+Stadtehygiene
+Staff Pap Int Monet Fund
+Stain Technol
+Stal
+Stand News
+Stanford Fr Rev
+Stanford J Int Law
+Stanford Law Pol Rev
+Stanford Law Rev
+Stanford Med Bull
+Stanovnistvo
+Stat
+Stat Appl Genet Mol Biol
+Stat Bull Metrop Insur Co
+Stat Bull Metrop Life Found
+Stat Bull Metropol Life Insur Co
+Stat Etud Midi Pyren
+Stat Inf Bull Afr
+Stat J UN Econ Comm Eur
+Stat Med
+Stat Methods Med Res
+Stat Navy Med
+Stat Neerl
+Stat News Pol
+Stat Notes Health Plann
+Stat Notes Jpn
+Stat Report
+Stat Sci
+Stat Szle
+Stat Tidskr
+State Black Am
+State Coverage Initiat Issue Brief
+State Demogr
+State Gov
+State Health Care Am
+State Health Legis Rep
+State Legis
+State Nurs Legis Q
+State Reprod Health Monit
+Stateco
+States Health
+Statist Efterret
+Statist Medd Ser Be Befolk Val
+Statistica
+Statistician
+Statut Calif Dig Meas Calif
+Stem Cells
+Stem Cells Dev
+Stereotact Funct Neurosurg
+Sterile World
+Sterkiana
+Sterne Weltraum
+Steroidologia
+Steroids
+Steroids Lipids Res
+Stetson Law Rev
+Stimmen Zeit
+Stoma (Heidelb)
+Stoma (Lisb)
+Stoma (Thessaloniki)
+Stomatol Chron (Athenai)
+Stomatol DDR
+Stomatol Glas Srb
+Stomatol Mediterr
+Stomatol Vestn
+Stomatol Vjesn
+Stomatol Zpr
+Stomatologia (Athenai)
+Stomatologia (Bucur)
+Stomatologica (Genova)
+Stomatologie
+Stomatologiia (Mosk)
+Stomatologiia (Sofiia)
+Stomatologija
+Stor Contemp
+Stor Crit Psicol
+Stor Lomb
+Stor Med Pop
+Stor Urbana
+Strabismus
+Strada Maestra
+Strahlenschutz Forsch Prax
+Strahlenther Onkol
+Strahlentherapie
+Strahlentherapie [Sonderb]
+Strasb Med
+Strateg Healthc Excell
+Strategy Tactics
+Stratigr Geol Correl
+Strenna Stor Bolognese
+Stress
+Stress Med
+Stroke
+Structure
+Stud Am Renaiss
+Stud Anc Med
+Stud Bibliogr
+Stud Burke Time
+Stud Cartesiana
+Stud Cercet Endocrinol
+Stud Cercet Fiziol
+Stud Cercet Inframicrobiol
+Stud Cercet Med Interna
+Stud Cercet Neurol
+Stud Cercet Virusol
+Stud Christ Ethics
+Stud Chur Hist
+Stud Cl
+Stud Cl Orient
+Stud Comeniana Hist
+Stud Comp Int Dev
+Stud Demogr
+Stud Eighteenth Cent Cult
+Stud Emigrazione
+Stud Engl Lit
+Stud Eur Thought
+Stud Fam Plann
+Stud Filoz
+Stud Gen (Berl)
+Stud Geogr
+Stud Gesch Krankenhauswesens
+Stud Hastings Cent
+Stud Health Technol Inform
+Stud Hist
+Stud Hist Biol
+Stud Hist Gandensia
+Stud Hist Med
+Stud Hist Med Sci
+Stud Hist Philos Biol Biomed Sci
+Stud Hist Philos Sci
+Stud Hum Ecol
+Stud Islam
+Stud Labour Hist
+Stud Lawyer
+Stud Leibnitiana
+Stud Mater Dziej Nauk Pol Ser B
+Stud Mater Dziejow Nauk Pol
+Stud Med
+Stud Med Popul Subj
+Stud Mediev Cult
+Stud Mitt Gesch Benediktinerorden
+Stud Mycol
+Stud Neophilol
+Stud Neuroanat
+Stud Papyrol
+Stud Philol
+Stud Philos Med
+Stud Profertility Ser
+Stud Psychol (Bratisl)
+Stud Relig
+Stud Renaissance
+Stud Romanticism
+Stud Rosenthaliana
+Stud Russ Econ Dev
+Stud Scott Lit
+Stud Soc Sci West Ga Coll
+Stud Sov Thought
+Stud Stor
+Stud Surf Sci Catal
+Stud Third World Soc
+Stud Tokugawa Inst
+Stud Voltaire 18th Century
+Studenterraad Med
+Studi Emigr
+Studi Filol Ital
+Studi Fr
+Studi Ital Filol Cl
+Studi Sassar
+Studi Sociol
+Studi Veneziani
+Studia
+Studia Demogr
+Studies
+Studime Hist
+Studium
+Study Encounter
+Study Sess Can Cathol Hist Assoc
+Subacute Care
+Subcell Biochem
+Subsid Med
+Subst Abus
+Subst Alcohol Actions Misuse
+Subst Use Misuse
+Sucasnist
+Sud Med Chir
+Sud Med Ekspert
+Sudan J Popul Stud
+Sudan Med J
+Sudan Notes Rec
+Suddtsch Optikerztg
+Sudhoffs Arch Gesch Med Naturwiss
+Sudhoffs Arch Vierteljahrsschr Gesch Med Naturwiss Pharm Math
+Sudhoffs Arch Z Wissenschaftsgesch
+Sudhoffs Arch Z Wissenschaftsgesch Beih
+Sudosteuropa
+Suffield Tech Note Can Suffield Exp Stn Ralston Alta
+Suffield Tech Pap Can Suffield Exp Stn Ralston Alta
+Suffolk Transnatl Law R
+Suffolk Univ Law Rev
+Suicide
+Suicide Life Threat Behav
+Summ Shute Inst
+Sun
+Sunday Times
+Sunday Times Magazine
+Suom Apteenkkaril
+Suom Hammaslaak Toim
+Suom Hammaslaakarilehti
+Suom Laakaril
+Suom Tiedeakat Toim A5 Medica Anthropol
+Superv Manage
+Superv Nurse
+Suppl Clin Neurophysiol
+Suppl Eur J Neurosci
+Suppl Int J Gynecol Obstet
+Suppl J Med Oncol Tumor Pharmacother
+Suppl Public Health Rep
+Suppl Thromb Haemost
+Suppl Tumori
+Support Care Cancer
+Supreme Court Rev
+Surg Annu
+Surg Bus
+Surg Circ Lett U S Army Far East Command Med Sect
+Surg Clin North Am
+Surg Endosc
+Surg Forum
+Surg Gastroenterol
+Surg Gynecol Obstet
+Surg Infect (Larchmt)
+Surg Innov
+Surg Laparosc Endosc
+Surg Laparosc Endosc Percutan Tech
+Surg Neurol
+Surg Oncol
+Surg Oncol Clin N Am
+Surg Radiol Anat
+Surg Rounds
+Surg Technol
+Surg Technol Int
+Surg Today
+Surgeon
+Surgery
+Surv Biol Prog
+Surv Immunol Res
+Surv Methodol
+Surv Ophthalmol
+Surv Synth Pathol Res
+Surv World Obstet Gynecol (Jpn)
+Surveill Soc
+Survey (Lond)
+Surviv News (Atlanta Ga)
+Survival (Lond)
+SusPop News
+Suvr Med (Sofiia)
+Sveikatos Apsauga
+Sven Farm Tidskr
+Sven Geogr Arsb
+Sven Juristtidn
+Sven Lakartidn
+Sven Linnesallskap Arsskr
+Sven Med Tidskr
+Sven Tandlak Tidskr
+Sven Tidskr
+Sver Tandlakarforb Tidn
+Swasth Hind
+Swed Am Hist Q
+Swed Dent J
+Swed Dent J Suppl
+Swed Pioneer Hist Q
+Swiss Biotech
+Swiss Dent
+Swiss Med Wkly
+Swiss Surg
+Swiss Surg Suppl
+Syd Law Rev
+Sydn Univ Med J
+Sydsven Medicinhist Sallsk Arsskr
+Sydsven Medicinhist Sallsk Arsskr Suppl
+Syesis
+Sygeplejersken
+Sykepl Fag
+Sykepleien
+Symb Oslo
+Symbiosis
+Symp Fundam Cancer Res
+Symp Oral Sens Percept
+Symp Pharmacol Ther Toxicol Group
+Symp Ser Soc Appl Microbiol
+Symp Soc Dev Biol
+Symp Soc Exp Biol
+Symp Swed Nutr Found
+Synapse
+Synlett
+Synth Philos
+Synthese
+Synthesis (Mass)
+Syracuse Law Rev
+Syst Appl Microbiol
+Syst Biol
+Syst Entomol
+Syst Parasitol
+Syst Res
+Syst Zool
+Szazadok
+Szczecin Tow Nauk Wydz Nauk Lek
+Szemeszet
+TB HIV
+TDR News
+TEC Bull (Online)
+TIC
+TID Rep
+TIT J Life Sci
+TMJ Update
+TVZ
+Tablet
+Taehan Chikkwa Uisa Hyophoe Chi
+Taehan Kan Hakhoe Chi
+Taehan Kanho
+Taehan Kanho Hakhoe Chi
+Taehan Naekwa Hakhoe Chapchi
+Taehan Oekwa Hakhoe Chapchi
+Taehan Pinyogikwa Hakhoe Chapchi
+Taehan Sanbuinkwa Hakhoe Chapchi
+Taehan Uihak Hyophoe Chi
+Taiwan Yi Xue Hui Za Zhi
+Takamine Kenkyusho Nenpo
+Talanta
+Tampa Bay Hist
+Tandlaegebladet
+Tandlaegernes Tidsskr
+Tandlakartidningen
+Tandteknikern
+Tani Girisim Radyol
+Tannlaeknabladid
+Tanpakushitsu Kakusan Koso
+Tanzan Notes Rec
+Tar Heel Nurse
+Targeted Diagn Ther
+Taxon
+TeLindes Oper Gynecol Updates
+Teach Coll Rec
+Teach In
+Teach Learn Med
+Teach Notes Popul
+Teach Philos
+Teach Polit Sci
+Teach Psychol
+Teach Sociol
+Tech Belge Prothese Dent
+Tech Bull Dep Army
+Tech Bull Regist Med Technol
+Tech Bull United States Veterans Admin
+Tech Coloproctol
+Tech Doc Rep ARL TDR
+Tech Doc Rep Arct Aeromed Lab US
+Tech Doc Rep SAMTDR USAF Sch Aerosp Med
+Tech Doc Rep U S Air Force Syst Command Electron Syst Div
+Tech Eau Assainissement
+Tech Hand Up Extrem Surg
+Tech Hosp Med Soc Sanit
+Tech Man US Army Biol Lab
+Tech Manuscr US Army Biol Lab
+Tech Note Arct Aeromed Lab (US)
+Tech Note U S Natl Aeronaut Space Adm
+Tech Orthop
+Tech Rep Arct Aeromed Lab US
+Tech Rep Brookhaven Natl Lab
+Tech Rep CRDLR US Army Chem Res Dev Lab
+Tech Rep CWLR US Army Chem Warf Lab
+Tech Rep NAVTRADEVCEN
+Tech Rep NY Nav Shipyard Mater Lab
+Tech Rep SAM-TR
+Tech Urol
+Tech Vasc Interv Radiol
+Technikgesch Einzeldarst
+Technikgeschichte
+Technol Cancer Res Treat
+Technol Cult
+Technol Eval Cent Asses Program Exec Summ
+Technol Forecast Soc Change
+Technol Health Care
+Technol Rev
+Technol Soc
+Technologia
+Technology
+Tecnologica
+Tecnologica MAP Suppl
+Tel Aviver Jahrb Dtsch Gesch
+Telecomm Policy
+Telemed J
+Telemed J E Health
+Telemed Telehealth Netw
+Telemed Today
+Telemed Virtual Real
+Tellus B Chem Phys Meteorol
+Temas Odontol
+Temas Poblac
+Temple Dent Rev
+Temple Int Comp Law J
+Temple Law Q
+Temple Law Rev
+Temps Mod
+Tenn Code Annot Tenn
+Tenn Folk Soc Bull
+Tenn Hist Q
+Tenn Law Rev
+Tenn Med
+Tenn Nurse
+Tenn Publ Welfare Rec
+Tenn Stud Lit
+Teor Prak Fiz Kult
+Teor Prax Teles Vychovy
+Tequesta
+Ter Arkh
+Terapia
+Teratog Carcinog Mutagen
+Teratology
+Terr Incogn
+Terra
+Terramycine Inf
+Terrorism
+Tetrahedron
+Tetrahedron Asymmetry
+Tetrahedron Lett
+Tex Dent Assist Assoc Bull
+Tex Dent J
+Tex Heart Inst J
+Tex Hosp
+Tex Int Law J
+Tex J Pharm
+Tex J Sci
+Tex J Women Law
+Tex Law Rev
+Tex Med
+Tex Mon
+Tex Nurs
+Tex Rep Biol Med
+Tex Rev Law Polit
+Tex State J Med
+Tex Tech Law Rev
+Texana
+Texas Bar J
+Texas Stud Lit Lang
+Text Rent
+Textos NEPO
+Textual Pract
+Thai J Nurs
+Thalassia Salentina
+Theatre Surv
+Theol Dig
+Theol Stud
+Theol Sz
+Theol Today
+Theor Appl Genet
+Theor Biol Med Model
+Theor Chim Acta
+Theor Med
+Theor Med Bioeth
+Theor Popul Biol
+Theor Prax Korperkult
+Theor Surg
+Theoria
+Theory Decis
+Theory Psychol
+Theory Soc
+Ther Apher
+Ther Apher Dial
+Ther Ber
+Ther Drug Monit
+Ther Ggw
+Ther Hung
+Ther Immunol
+Ther Nova
+Ther Recreation J
+Ther Umsch
+Ther Umsch Med Bibliogr
+Therapeutikon
+Therapeutique
+Therapie
+Therapiewoche
+Theriaca
+Theriogenology
+Third Repub
+Third World Plann Rev
+Third World Q
+ThisWeek
+Thomas Jefferson Law Rev
+Thomist
+Thorac Cardiovasc Surg
+Thorac Surg Clin
+Thorax
+Thoraxchir Vask Chir
+Thoraxchirurgie
+Thought
+Thromb Diath Haemorrh
+Thromb Diath Haemorrh Suppl
+Thromb Haemost
+Thromb J
+Thromb Res
+Thromb Res Suppl
+Thurgood Marshall Law Rev
+Thymus
+Thyroid
+Thyroidology
+Tianjin Yi Yao
+Tibet News Rev
+Ticitl
+Tid Tann
+Tidskr Mil Halsov
+Tidskr Sjukvardspedagog
+Tidskr Sver Sjukskot
+Tidsskr Nor Laegeforen
+Tidsskr Prakt Tandlaeg
+Tidsskr Rettsvitenskap
+Tidsskr Samfunnsforsk
+Tidsskr Sygepl
+Tidsskr Sykepl
+Tidsskr Tandlaeger
+Tierarztl Prax
+Tierarztl Prax Ausg G Grosstiere Nutztiere
+Tierarztl Prax Ausg K Klientiere Heimtiere
+Tierarztl Prax Suppl
+Tierarztl Umsch
+Tiers Monde
+Tijdschr Bejaarden Kraam Ziekenverzorging
+Tijdschr Diergeneeskd
+Tijdschr Econ Soc Geogr
+Tijdschr Entomol
+Tijdschr Filos
+Tijdschr Gastroenterol
+Tijdschr Geneeskd
+Tijdschr Gerontol Geriatr
+Tijdschr Geschied
+Tijdschr Geschied Geneeskd Natuurwet Wiskd Tech
+Tijdschr Kindergeneeskd
+Tijdschr Soc Geneeskd
+Tijdschr Soc Geschied
+Tijdschr Soc Gezondheidsz
+Tijdschr Soc Wet
+Tijdschr Sociol
+Tijdschr Voor Tandheelkd
+Tijdschr Voor Ziekenverpl
+Tijdschr Ziekenverpl
+Tikkun
+Timarit Hjukrunarfel Isl
+Time
+Timeline
+Timely Top Med Cardiovasc Dis
+Times
+Times (Lond)
+Timisoara Med
+Tip Fak Mecm
+Tip Tarihi Arastirmalari
+Tirol Heim
+Tirol Heimatbl
+Tissue Antigens
+Tissue Cell
+Tissue Eng
+Tob Control
+Todays Cathol Teach
+Todays Christ Dr
+Todays FDA
+Todays Health
+Todays Nurs Home
+Todays OR Nurse
+Todays Surg Nurse
+Toho Igakkai Zasshi
+Tohogaku
+Tohoku Igaku Zasshi
+Tohoku J Exp Med
+Tohoku Shika Daigaku Gakkai Shi
+Tokai J Exp Clin Med
+Tokoginecol Pract
+Tokushima J Exp Med
+Tokyo Igaku
+Tokyo Ika Shika Daigaku Iyo Kizai Kenkyusho Hokoku
+Tokyo Jikeikai Ika Daigaku Zasshi
+Tonan Ajia Kenkyu
+Tongmul Hakhoe Chi
+Top Clin Nurs
+Top Clin Nutr
+Top Curr Chem
+Top Emerg Med
+Top HIV Med
+Top Health Care Financ
+Top Health Inf Manage
+Top Health Rec Manage
+Top Hosp Law
+Top Hosp Pharm Manage
+Top Magn Reson Imaging
+Top Probl Psychother
+Top Spinal Cord Inj Rehabil
+Top Stroke Rehabil
+Tor J Theol
+Tor Star
+Torace
+Torah U Madda J
+Torax
+Torreon Med
+Tort Insur Law J
+Tort Stat Tanulm
+Tort Trial Insur Pract Law J
+Tortenelmi Sz
+Touchstone (Nashv)
+Toulouse Med
+Toung Pao
+Tourbe Philos
+Touro Law Rev
+Town Ctry Plann
+Town Hall J
+Town Plan Rev
+Toxic Rep Ser
+Toxic Subst Mech
+Toxicol Appl Pharmacol
+Toxicol Eur Res
+Toxicol In Vitro
+Toxicol Ind Health
+Toxicol Lett
+Toxicol Pathol
+Toxicol Rev
+Toxicol Sci
+Toxicologist
+Toxicology
+Toxicon
+Toyoshi Kenkyu
+Tr Akad Med Nauk Sssr Mosc Inst Grudn Khir
+Tr Akad Nauk SSSR Inst Genet
+Tr Fiziol Lab Akad I P Pavlova
+Tr Inst Biol
+Tr Inst Fiz Akad Nauk Gruz Ssr
+Tr Inst Fiziol Im I P Pavlova
+Tr Inst Im Pastera
+Tr Inst Morfol Zhivotn An Severtsova
+Tr Inst Norm Patol Fiziol
+Tr Latv Padomju Soc Repub Zinat Akad Mikrobiol Inst
+Tr Leningr Inst Epidemiol Mikrobiol
+Tr Leningr Nauchnoissled Inst Epidemiol Mikrobiol
+Tr Leningr Sanitarnogig Med Inst
+Tr Mosk Nauchnoissled Inst Epidmiol Mikrobiol
+Trab Inst Cajal
+Trab Inst Cajal Invest Biol
+Trace Elem Med
+Track Rep
+Tractrix
+Tradimus
+Traditio
+Tradition
+Traffic
+Traffic Inj Prev
+Train Dev J
+Train Sch Bull (Vinel)
+Trained Nurse Hosp Rev
+Training
+Trans ASAE
+Trans Action
+Trans Am Acad Insur Med
+Trans Am Acad Ophthalmol Otolaryngol
+Trans Am Assoc Genitourin Surg
+Trans Am Clin Climatol Assoc
+Trans Am Coll Cardiol
+Trans Am Laryngol Assoc
+Trans Am Laryngol Rhinol Otol Soc
+Trans Am Microsc Soc
+Trans Am Neurol Assoc
+Trans Am Nucl Soc
+Trans Am Ophthalmol Soc
+Trans Am Otol Soc
+Trans Am Soc Artif Intern Organs
+Trans Am Soc Neurochem
+Trans Am Soc Ophthalmol Otolaryngol Allergy
+Trans Amer Fish Soc
+Trans Annu Meet Am Bronchoesophagol Assoc
+Trans Annu Meet Natl Tuberc Assoc
+Trans Assoc Am Physicians
+Trans Assoc Ind Med Off
+Trans Assoc Life Insur Med Dir Am
+Trans Aust Coll Ophthalmol
+Trans Aust Med Congr
+Trans Br Soc Hist Pharm
+Trans Br Soc Study Orthod
+Trans Bristol Glos Archaeol Soc
+Trans Camb Bibliogr Soc
+Trans Can Opthalmolog Soc
+Trans Engl Ceram Circle
+Trans Essex Archaeol Soc
+Trans Eur Orthod Soc
+Trans Hist
+Trans Hunter Soc
+Trans Ill State Acad Sci
+Trans Indiana Acad Ophthalmol Otolaryngol
+Trans Inst Br Geogr
+Trans Inst Indian Geogr
+Trans Int Conf Endod
+Trans Int Conf Oral Surg
+Trans Jew Hist Soc Engl
+Trans Jpn Soc Aeronaut Space Sci
+Trans Kans Acad Sci
+Trans Lancaster Cheshire Antiq Soc
+Trans Leicester Archaeol Hist Soc
+Trans Med Soc Lond
+Trans Meet Am Surg Assoc Am Surg Assoc
+Trans N Engl Obstet Gynecol Soc
+Trans N Y Acad Sci
+Trans Natl Saf Congr
+Trans New Orleans Acad Ophthalmol
+Trans Ophthalmol Soc Aust
+Trans Ophthalmol Soc N Z
+Trans Ophthalmol Soc U K
+Trans Opthal Soc U K
+Trans Pa Acad Ophthalmol Otolaryngol
+Trans Pac Coast Obstet Gynecol Soc
+Trans Pac Coast Otoophthalmol Soc Annu Meet
+Trans Proc Am Philol Assoc
+Trans R Hist Soc
+Trans R Sch Dent Stockh Umea
+Trans R Soc Can
+Trans R Soc Edinb Earth Sci
+Trans R Soc Trop Med Hyg
+Trans Rep Liverp Med Inst
+Trans Sect Ophthalmol Am Acad Ophthalmol Otolaryngol
+Trans Sect Otolaryngol Am Acad Ophthalmol Otolaryngol
+Trans Soc Occup Med
+Trans South Cent Sect Am Urol Assoc
+Trans Southeast Sect Am Urol Assoc
+Trans St Johns Hosp Dermatol Soc
+Trans Stud Coll Physicians Phila
+Trans West Sect Am Urol Assoc
+Transcult Psychiatry
+Transfus Apher Sci
+Transfus Clin Biol
+Transfus Med
+Transfus Med Rev
+Transfus Sci
+Transfusion
+Transfusion (Paris)
+Transgenic Res
+Transp Theory Stat Phys
+Transpl Immunol
+Transpl Infect Dis
+Transpl Int
+Transplant Bull
+Transplant Proc
+Transplant Rev
+Transplant Rev (Orlando)
+Transplant Sci
+Transplantation
+Trauma Violence Abuse
+Trav Hum
+Trav Lab Matiere Med Pharm Galenique Fac Pharm Paris
+Trav Sci Cherch Serv Sante Armees
+Trav Soc Pharm Montp
+Treat Endocrinol
+Treat Guidel Med Lett
+Treat Respir Med
+Treat Rev
+Treat Serv Bull
+Treatmentupdate
+Tree Physiol
+Trends Amplif
+Trends Analyt Chem
+Trends Biochem Sci
+Trends Biotechnol
+Trends Cardiovasc Med
+Trends Cell Biol
+Trends Cogn Sci
+Trends Ecol Evol
+Trends Endocrinol Metab
+Trends Genet
+Trends Health Care Law Ethics
+Trends Hist
+Trends Immunol
+Trends Microbiol
+Trends Mol Med
+Trends Neurosci
+Trends Parasitol
+Trends Pharmacol Sci
+Trends Plant Sci
+Trends Tech Contemp Dent Lab
+Trial
+Trial Lawyers Q
+Trials
+Triangle
+Triangulo Rev Sandoz Cienc Med
+Trib Int Womens Trib Cent
+Trib Odontol (B Aires)
+Trib Odontol (Guanabara)
+Trimest Econ
+Triumph
+Trodent
+Trop Anim Health Prod
+Trop Biomed
+Trop Dis Bull
+Trop Doct
+Trop Gastroenterol
+Trop Geogr Med
+Trop Heal
+Trop J Obstet Gynaecol
+Trop Med Int Health
+Trop Med Parasitol
+Tropenmed Parasitol
+Trudy Mosc Russ Vsesoiuznyi Nauchno Issled Vitam Inst
+Trudy Vsesoiuznoe Fiziol Obshchestvo Im I P Pavlov Orenb Otd
+Trustee
+Trusts Estates
+Tsa Chih Gaoxiong Yi Xue Yuan Tong Xue Hui
+Tsitol Genet
+Tsitologiia
+Tsukuba Daigaku Jinbun Chirigaku Kenkyu
+Tsurumi Shigaku
+Tuber Lung Dis
+Tuberc Airborne Dis Wkly
+Tubercle
+Tuberculol Thorac Dis
+Tuberculosis (Edinb)
+Tuberk Grenzgeb Einzeldarst
+Tuberk Kerdesei
+Tuberk Toraks
+Tuberk Tudobetegsegek
+Tuberkulosearzt
+Tuberkuloza
+Tuberkulozis
+Tufts Dent Outlook
+Tufts Folia Med
+Tufts Health Sci Rev
+Tulane Law Rev
+Tulsa Law J
+Tumor Res
+Tumori
+Tumour Biol
+Tunis Med
+Turk Hemsire Derg
+Turk Hij Deney Biyol Derg
+Turk Hij Tecr Biyol Derg
+Turk J Gastroenterol
+Turk J Pediatr
+Turk Ortodonti Derg
+Turk Psikiyatri Derg
+Turk Tip Cemiy Mecm
+Turk Tip Dernegi Derg
+Turnbull Libr Rec
+Turun Hist Ark (Finl)
+Twin Res
+Twin Res Hum Genet
+TydsKr Rasse Aangel
+Tydskrif Hedendaagse Romeins Holandse Reg
+U S Armed Forces Med J
+U S Code Annot U S
+U S Nav Med Bull
+U S Rep U S Supreme Court
+UCLA Forum Med Sci
+UCLA Law Rev
+UCLA Pac Basin Law J
+UCLA Rep
+UCLA Symp Mol Cell Biol
+UCLA Womens Law J
+UFSI Rep
+UJCD Union Jeunes Chir Dent
+UMKC Law Rev
+UN Chron
+UN Mon Chron
+UNA Commun
+UNA Nurs J
+UNDP News
+UNESCO Cour
+UNICEF News
+UNOS Update
+UR Rep
+US Cathol
+US Healthc
+US Long Term Rev
+US Med
+US Navy Med
+US Navy Med News Lett
+US News World Rep
+US Pharm
+US Statut Large
+US Supreme Court Rep
+USA Today
+USA Weekend
+USAF Med Serv Dig
+USAID Dev
+USAID Highlights
+USQR Union Semin Q Rev
+UWFL Rep
+Uch Zap Inst Farmakol Khimioter AMN SSSR
+Uchenye Zap Mosk Meditsinskii Inst
+Uchenye Zap Seriia Biolog Leningr Univ
+Uchu Koku Kankyo Igaku
+Uddannelse
+Udim
+Ugeskr Laeger
+Uirusu
+Uisahak
+Ukr Biokhim Zh
+Ukr Bot Z Ukr Bot Tovarystvo
+Ukr Istor Z
+Ukr Q
+Ukr Rev (Lond)
+Ulster Folklife
+Ulster Med J
+Ultramicroscopy
+Ultraschall Med
+Ultrason Imaging
+Ultrason Sonochem
+Ultrasonics
+Ultrasound Med Biol
+Ultrasound Obstet Gynecol
+Ultrasound Q
+Ultrastruct Pathol
+Ulus Travma Acil Cerrahi Derg
+Ulus Travma Derg
+Umsch Wiss Tech
+Undergrad J Philos
+Undersea Biomed Res
+Undersea Hyperb Med
+Unesco Bull Libr
+Unesco Sources
+Unfallchirurg
+Unfallchirurgie
+Unfallheilkunde
+Union Burma J Life Sci
+Union Med Can
+Unitas
+United Evangelical Action
+United Synag Rev
+Univ Ark Little Rock Law J
+Univ B C Law Rev
+Univ Baltimore Law Rev
+Univ Calif Davis Law Rev
+Univ Calif Publ Zool
+Univ Chic Law Rev
+Univ Chic Leg Forum
+Univ Cincinnati Law Rev
+Univ Colo Law Rev
+Univ Dayton Law Rev
+Univ Detroit J Urban Law
+Univ Detroit Law Rev
+Univ Detroit Mercy Law Rev
+Univ Edinb J
+Univ Fla J Law Public Policy
+Univ Fla Law Rev
+Univ Ghana Law J
+Univ Ill Law Forum
+Univ Ill Law Rev
+Univ Kans Law Rev
+Univ Louisv J Fam Law
+Univ Md Law Forum
+Univ Memphis Law Rev
+Univ Miami Entertain Sports Law Rev
+Univ Miami Law Rev
+Univ Mich J Law Reform
+Univ Mich Med Cent J
+Univ Minn Med Bull
+Univ N C News Lett
+Univ N S W Law J
+Univ Newcastle Tyne Med Gaz
+Univ PA Law Rev
+Univ Pa Med Bull
+Univ Pittsbg Law Rev
+Univ Puget Sound Law Rev
+Univ Qld Law J
+Univ Richmond Law Rev
+Univ San Fernando Valley Law Review
+Univ San Francisco Law Rev
+Univ Tasman Law Rev
+Univ Toledo Law Rev
+Univ Tor Dent J
+Univ Tor Fac Law Rev
+Univ Tor Law J
+Univ Toronto Med J
+Univ Toronto Undergrad Dent J
+Univ West Los Angel Law Rev
+Univ West Ont Law Rev
+Univ Wyo Publ
+Universitas (Ghana)
+Universitas (Stuttg)
+Unsere Heim
+Update
+Update Ethics
+Update Natl Minor AIDS Counc
+Update Pediatr Dent
+Ups J Med Sci
+Ups J Med Sci Suppl
+Upsala Lakareforen Forh
+Urban Aff Q
+Urban Geogr
+Urban Health
+Urban Health Newsl
+Urban Hist Rev
+Urban Hist Yearb
+Urban Leag Rev
+Urban Rev
+Urban Soc Change Review
+Urban Stud
+Urban anthropol
+Urbana
+Urbi
+Uremia Invest
+Urol Clin North Am
+Urol Cutaneous Rev
+Urol Int
+Urol Mosc
+Urol Nefrol (Mosk)
+Urol Nurs
+Urol Oncol
+Urol Pol
+Urol Radiol
+Urol Res
+Urol Surv
+Urologe
+Urologe A
+Urologe B
+Urologia
+Urologiia
+Urology
+Ursus
+User Model User-adapt Interact
+Usp Biol Khim
+Usp Fiziol Nauk
+Usp Khim
+Usp Sovrem Biol
+Utafiti
+Utah Code Annot 1953 Utah
+Utah Hist Q
+Utah Law Rev
+Utah Nurse
+Utah Sci
+Utne Read
+Utop Stud
+Uttar Pradesh State Dent J
+VA J Int Law
+VDI Ber
+VINA Q
+Va Bar News
+Va Cavalcade
+Va Dent J
+Va Explor
+Va J Sci
+Va J Soc Policy Law
+Va Law Rev
+Va Mag Hist Biogr
+Va Med
+Va Med Mon (1918)
+Va Med Q
+Va Nurse
+Va Nurse Q
+Va Q Rev
+Va Rev Sociol
+Vaccine
+Vaccine Immun News
+Vaccine Wkly
+Vakbl Biol
+Valparaiso Univ Law Rev
+Valsalva
+Value Health
+Vanc Sun
+Vanderbilt Law Rev
+Vard Nord Utveckl Forsk
+Vardfacket
+Vasa
+Vasa Suppl
+Vasc Dis
+Vasc Endovascular Surg
+Vasc Med
+Vasc Surg
+Vascul Pharmacol
+Vascular
+Vector Borne Zoonotic Dis
+Veja
+Veltro
+Venereology
+Venez Odontol
+Verh Anat Ges
+Verh Dtsch Ges Herz Kreislaufforsch
+Verh Dtsch Ges Inn Med
+Verh Dtsch Ges Kreislaufforsch
+Verh Dtsch Ges Pathol
+Verh Dtsch Ges Rheumatol
+Verh K Acad Geneeskd Belg
+Verh K Vlaam Acad Geneeskd Belg
+Verh Zool Bot Ges Wien
+Vernons Annot Revis Civ Statut State Tex Tex
+Veroff Int Ges Gesch Pharm
+Veroff Morphol Pathol
+Veroff Pathol
+Veroff Schweiz Ges Gesch Med Naturwiss
+Veroff Schweiz Ges Gesch Pharm
+Verpleegkunde
+Versicherungsmedizin
+Versl Meded K Viaam Acad Taal Lett
+Versl Meded K Vlaam Acad Taal Lett
+Vertex
+Vesalius
+Veska
+Vestis Lat Psr Zinat Akad
+Vestn Akad Med Nauk SSSR
+Vestn Akad Nauk SSSR
+Vestn Dermatol Venerol
+Vestn Drevnej Istor (Moskva)
+Vestn Khir Im I I Grek
+Vestn Leningr Univ Geol Geogr
+Vestn Leningr Univ [Biol]
+Vestn Mosk Univ Ser 8 Istor
+Vestn Mosk Univ Ser V
+Vestn Oftalmol
+Vestn Otorinolaringol
+Vestn Rentgenol Radiol
+Vestn Ross Akad Med Nauk
+Vestn Statistiki
+Vestn Venerol Dermatol
+Vet Anaesth Analg
+Vet Bull
+Vet Clin North Am
+Vet Clin North Am Equine Pract
+Vet Clin North Am Exot Anim Pract
+Vet Clin North Am Food Anim Pract
+Vet Clin North Am Large Anim Pract
+Vet Clin North Am Small Anim Pract
+Vet Clin Pathol
+Vet Comp Orthop Traumatol
+Vet Dermatol
+Vet Herit
+Vet Hist
+Vet Historisch Genoot Cah
+Vet Hum Toxicol
+Vet Immunol Immunopathol
+Vet Ital
+Vet J
+Vet Med
+Vet Med (Praha)
+Vet Med Nachr
+Vet Med Nauki
+Vet Med Rev
+Vet Med Small Anim Clin
+Vet Microbiol
+Vet Ophthalmol
+Vet Parasitol
+Vet Pathol
+Vet Pathol Suppl
+Vet Q
+Vet Radiol Ultrasound
+Vet Rec
+Vet Res
+Vet Res Commun
+Vet Surg
+Vet Ther
+Veterinaria
+Veterinarian
+Veterinariia
+Veterinarstvi
+Viata Med
+Viata Med Rev Inf Prof Stiint Cadrelor Medii Sanit
+Viator
+Vibro
+Vic Hist J
+Vic Hist Mag
+Vic Newsl
+Vic Period Newsl
+Vic Period Rev
+Vic Stud
+Vic Vet Proc
+Victimology
+Victor Bostrom Fund Rep
+Vida Nueva
+Vida Odontol
+Vie Lang
+Vie Med
+Vie Med Can Fr
+Vie Med Evolut Med Ther
+Vie Sante
+Vierteljahresberichte
+Vierteljahrschr Soz Wirtschaftsgesch
+Vierteljahrsh Zeitgesch
+Vierteljahrsschr Schweiz Sanitatsoff
+Vigilando
+Viitorul Soc
+Vikalpa
+Village Voice
+Villanova Law Rev
+Vingtieme Siecle
+Violence Against Women
+Violence Update
+Violence Vict
+Viral Immunol
+Virchows Arch
+Virchows Arch A Pathol Anat Histol
+Virchows Arch A Pathol Anat Histopathol
+Virchows Arch A Pathol Pathol Anat
+Virchows Arch B Cell Pathol
+Virchows Arch B Cell Pathol Incl Mol Pathol
+Virchows Arch Pathol Anat Physiol Klin Med
+Virol J
+Virol Monogr
+Virologie
+Virology
+Virtual Mentor
+Virus
+Virus Genes
+Virus Inf Exch Newsl South East Asia West Pac
+Virus Res
+Virus Res Suppl
+Vis Neurosci
+Vision Res
+Vita Hum Int Z Lebensalterforsch
+Vitae Scholasticae
+Vital Health Stat 1
+Vital Health Stat 10
+Vital Health Stat 11
+Vital Health Stat 13
+Vital Health Stat 14
+Vital Health Stat 2
+Vital Health Stat 20
+Vital Health Stat 21
+Vital Health Stat 23
+Vital Health Stat 3
+Vital Health Stat 4
+Vital Health Stat 5
+Vital Signs
+Vital Speeches Day
+Vitalst Zivilisationskr
+Vitam Horm
+Vitam Horm Leipzig
+Vivre Autrement
+Vnitr Lek
+Voeding
+Voen Istor Zh
+Voen Med Delo
+Voen Med Zh
+Vogue
+Voix Dent
+Voix Femme
+Vojen Zdrav Listy
+Vojenskozdrav Knih
+Vojnois Glas
+Vojnosanit Pregl
+Volkskunde
+Volunt Action Leadersh
+Volunt Adm
+Volunt Leader
+Vopr Biokhim Mozga
+Vopr Ekon
+Vopr Eksper Klinicheskoi Urol
+Vopr Elektropatol Elektrotravmatizma Elektrobezop
+Vopr Filos
+Vopr Fiziol
+Vopr Geogr
+Vopr Istor
+Vopr Istor Estestvozn Tekh
+Vopr Istor KPSS
+Vopr Klin Lecheniia Zlokachestvennykh Novoobraz
+Vopr Kurortol Fizioter Lech Fiz Kult
+Vopr Med Khim
+Vopr Med Virusol
+Vopr Neirokhir
+Vopr Okhr Materin Det
+Vopr Onkol
+Vopr Patol Serdechno Sosud Sist
+Vopr Pitan
+Vopr Psikhiatr Nevropatol
+Vopr Psikhol
+Vopr Revm
+Vopr Virusol
+Vopros Stat
+Vorgange
+Vox Sang
+Vrach Delo
+Vt Hist
+Vt Hist News
+Vt Law Rev
+Vt Regist Nurse
+Vt Statut Annot Vt
+Vutr Boles
+Vyziva Lidu
+W V Dent J
+W V Med J
+W Va Hist
+WADC Tech Note United States Air Force Wright Air Dev Cen Day Ohio
+WADC Tech Rep United States Air Force Wright Air Dev Cent Day Ohio
+WASH Rep
+WDA J
+WE Int
+WHO AIDS Tech Bull
+WHO Chron
+WHO Feature
+WHO Offset Publ
+WHO Reg Publ Eur Ser
+WIN News
+WMJ
+WORLD
+WRI Issues Ideas
+Waage
+Wakayama Med Rep
+Wake Forest Law Rev
+Waking Sleeping
+Wall St J (East Ed)
+Wall St J [Midwest Ed]
+War Hung
+War Soc
+Warasan Prachakon Lae Sangkhom
+Warta Demografi
+Warwickshire Hist
+Wash Drug Device Lett
+Wash Law Rev
+Wash Lee Law Rev
+Wash Memo Alan Guttmacher Inst
+Wash Mon
+Wash Nurse
+Wash Q
+Wash Rep Med Health
+Wash State Dent J
+Wash State J Nurs
+Wash Times
+Wash Univ Dent J
+Wash Univ J Urban Contemp Law
+Wash Univ Law Q
+Washburn Law J
+Washington Post
+Washingtonian
+Waste Manag
+Waste Manag Res
+Water Air Soil Pollut
+Water Environ Res
+Water Res
+Water Resour Res
+Water Sci Technol
+Watermark
+Watermark (Arch Libr Hist Health Sci)
+Way
+Wayne Law Rev
+Weather Vane
+Weekend Mag
+Weekly Mail
+Wehrmed Mitt
+Wehrmed Monatsschr
+Wei Sheng Wu Xue Bao
+Wei Sheng Yan Jiu
+Weleda Korrespondenzbl Aerzte
+Welf Rev
+Welsh Hist Rev
+Weltwirtsch Arch
+Wen Wu
+Wereld Zending
+Wesleyan Theol J
+West Afr J Med
+West Afr J Pharmacol Drug Res
+West Afr Med J
+West Afr Med J Niger Med Dent Pract
+West Afr Med J Niger Pract
+West Afr Pharm
+West Can J Anthropol
+West Dent Soc Bull
+West Engl Med J
+West Eur Polit
+West Folk
+West Hist Q
+West Hum Rev
+West Ill Reg Stud
+West Indian Med J
+West J Black Stud
+West J Med
+West J Med Surg
+West J Nurs Res
+West J Speech Commun
+West J Surg Obstet Gynecol
+West Med Med J West
+West New Engl Law Rev
+West Ont Law Rev
+West Pa Hist Mag
+West Polit Q
+West State Univ Law Rev
+West States Jew Hist
+West States Jew Hist Q
+West Tenn Hist Soc Pap
+West Tex Hist Asso Year B
+West VA Law Rev
+West's Atl Report
+Westminst Inst Rev
+Wests Annot Calif Codes Calif
+Wests Annot Indiana Code
+Wests Calif Report
+Wests Fed Rep
+Wests Fed Suppl
+Wests Fla Statut Annot Fla
+Wests N Y Suppl
+Wests North East Rep
+Wests North West Rep
+Wests Pac Report
+Wests Revis Code Wash Annot Wash State
+Wests South East Report
+Wests South Report
+Wests South West Report
+Wests Supreme Court Report
+Wests Wis Statut Annot Wis
+Wet Tijd
+Whittier Law Rev
+Whole Earth Rev
+Wiad Lek
+Wiad Lek Wars Pol 1948
+Wiad Parazytol
+Wiad Stat (Warsaw)
+Wiederherstellungschir Traumatol
+Wien Arch Psychol Psychiatr Neurologie
+Wien Beitr Chir
+Wien Beitr Dermatol
+Wien Beitr Geburtshilfe Gynakol
+Wien Beitr Gesch Neuzeit
+Wien Beitr Hals Nasen Ohrenheilkd
+Wien Beitr Hyg
+Wien Beitr Kinderheilkd
+Wien Beitr Zahnheilkd
+Wien Geschichtsbl
+Wien Klin Wochenschr
+Wien Klin Wochenschr Suppl
+Wien Med Wochenschr
+Wien Med Wochenschr Beih
+Wien Med Wochenschr Suppl
+Wien Stud Med Gesch Philos
+Wien Tierarztl Monatsschr
+Wien Z Inn Med
+Wien Z Kunde Morgenl
+Wien Z Nervenheilkd Grenzgeb
+Wies Wspolcz
+Wijsgerig Perspect Maatsch Wet
+Wilderness Environ Med
+Wildl Dis
+Wildl Soc Bull
+Willamette Law J
+Willamette Law Rev
+William Mary Bill Rights J
+William Mary J Women Law
+William Mary Law Rev
+William Mary Q
+William Mitchell Law Rev
+Wilson Libr Bull
+Wilson Q
+Windows Time
+Winnipeg Clin Q
+Winterthur Portf
+Wirtsch Stat
+Wirtschaftsdienst
+Wirtschaftswissenschaft
+Wirtschaftswissenschaftliches Stud
+Wis Acad Rev
+Wis Bar Bull
+Wis Dent Assoc J
+Wis L Rev
+Wis Mag Hist
+Wis Med J
+Wis Sess Laws Wis
+Wis Womens Law J
+Wisc Sociol
+Wiss Beitr Gesch Seelenheilkd
+Wiss Beitr Martin Luther Univ Halle Wittenberg
+Wiss Dienst Sudosteur
+Wiss Glaube
+Wiss Veroff Dtsch Ges Ernahr
+Wiss Z Ernst Moritz Arndt Univ [Ges Sprachwiss]
+Wiss Z Ernst Moritz Arndt Univ [Math]
+Wiss Z Friedrich Schiller Univ Jena Ges Sprachwiss
+Wiss Z Humboldt Univ Berl (Ges Sprachwiss)
+Wiss Z Humboldt Univ Berl [Math Naturwiss]
+Wiss Z Karl Marx Univ Leipzig [Ges Sprachwiss]
+Wiss Z Karl Marx Univ Math Naturwiss
+Wiss Z Wilhelm Pieck Univ Rostock [Ges Sprachwiss]
+Wistar Inst Symp Monogr
+Wkly Epidemiol Rec
+Wkly Law Rep
+Wolfenbutteler Renaiss Mitt
+Woman Physician
+Women 2000
+Women AIDS
+Women Action
+Women Alive
+Women Crim Justice
+Women Environ
+Women Envis
+Women Health
+Women Lawyers J
+Women Polit
+Women Ther
+Women Vietnam
+Women's Rights Law Report
+Womens Annu
+Womens Health
+Womens Health Data Book
+Womens Health Issues
+Womens Health J
+Womens Health Newsl
+Womens Health Update
+Womens Health Wkly
+Womens Stud
+Womens Stud Forum
+Womens Stud Int Forum
+Womens Stud Int Q
+Womens Watch
+Womens World
+Worcester Med News
+Word World
+Work
+Work Environ Health
+Work Occup
+Work Pap Mag
+Work Pap New Soc
+Work Stress
+Work Woman
+Worklife
+World AIDS Day Features
+World AIDS Day Newsl
+World Archaeol
+World Bank Econ Rev
+World Bank Res News
+World Bank Res Obs
+World Citz News
+World Conserv
+World Dev Forum
+World Educ Rep
+World Environ Rep
+World Futures
+World Health
+World Health Forum
+World Health Organ Tech Rep Ser
+World Health Stat Q
+World Health Stat Rep
+World Hosp
+World Hosp Health Serv
+World Ir Nurs
+World J Biol Psychiatry
+World J Gastroenterol
+World J Orthod
+World J Surg
+World J Surg Oncol
+World J Urol
+World Med
+World Med J
+World Neighb Action
+World Neurol
+World News Maxillofac Radiol
+World Policy J
+World Polit
+World Psychiatry
+World Rev Nutr Diet
+World Review
+World Smoking Health
+World Tob
+World Today
+World Watch
+World Wide Abstr Gen Med
+World Work
+WorldAIDS
+Worlds Poult Sci J
+Worldview
+Wound Repair Regen
+Wurzbg Medizinhist Forsch
+Wurzbg Medizinhist Mitt
+Wyo Nurses Newsl
+Wyo Statut Annot Wyo
+Wythe Cty Hist Rev
+Xaloc
+Xenobiotica
+Xenotransplantation
+Xi Bao Yu Fen Zi Mian Yi Xue Za Zhi
+Xi Psi Phi Q
+Xianggang Hu Li Za Zhi
+Xray Tech
+Y
+Y Rep
+Yad Vashem Stud Eur Jew Catastrophe Resist
+Yakubutsu Seishin Kodo
+Yakugaku Kenkyu
+Yakugaku Zasshi
+Yakushigaku Zasshi
+Yale J Biol Med
+Yale J Health Policy Law Ethics
+Yale J Law Fem
+Yale J Regul
+Yale Law J
+Yale Law Policy Rev
+Yale Univ Libr Gaz
+Yalkut Moreshet
+Yan Jiu Bao Gao
+Yan Ke Xue Bao
+Yan ke xue shu hui kan
+Yao Xue Xue Bao
+Yatros
+Year B Leo Baeck Inst
+Year Immunol
+Yearb Am Philos Soc
+Yearb Conf Lat Am Geogr
+Yearb Ger Am Stud
+Yearb Phys Anthropol
+Yearb Popul Res Finl
+Yeast
+Yeni Tip Tarihi Arastirmalari
+Yi Chuan
+Yi Chuan Xue Bao
+Ying Yang Xue Bao
+Ying Yong Sheng Tai Xue Bao
+Yngre Laeger
+Yod
+Yojana
+Yokohama Med Bull
+Yokufuen Chosa Kenkyu Kiyo
+Yonago Acta Med
+Yonago Igaku Zasshi
+Yonsei Med J
+York State Trad
+Young Child
+Your Child Patient
+Your Okla Dent Assoc J
+Youth Soc
+Yugosl Surv
+Z Aachener Geschichtsver
+Z Aegypt Sprach Altertumskd
+Z Allg Mikrobiol
+Z Allg Wissenschaftstheor
+Z Allgemeinmed
+Z Alternsforsch
+Z Anat Entwicklungsgesch
+Z Angew Bader Klimaheilkd
+Z Arbeitswiss
+Z Arztl Fortbild (Berl)
+Z Arztl Fortbild (Jena)
+Z Arztl Fortbild Beih (Jena)
+Z Arztl Fortbild Qualitatssich
+Z Bayer Landesgesch
+Z Bevolkerungswiss
+Z Bibliothekswes Bibliogr
+Z Biol
+Z Dtsch Altert Dtsch Lit
+Z Dtsch Morgenl Ges
+Z Entwicklungspsychol Padagog Psychol
+Z Erdkundeunterr
+Z Erkr Atmungsorgane
+Z Erkr Atmungsorgane Folia Bronchol
+Z Ernahrungswiss
+Z Ernahrungswiss Suppl
+Z Ethnol
+Z Exp Angew Psychol
+Z Exp Chir
+Z Exp Chir Transplant Kunstliche Organe
+Z Exp Psychol
+Z Flugwiss Weltraumforsch
+Z Fr Spr Lit
+Z Gastroenterol
+Z Gastroenterol Verh
+Z Geburtshilfe Gynakol
+Z Geburtshilfe Neonatol
+Z Geburtshilfe Perinatol
+Z Gerontol
+Z Gerontol Geriatr
+Z Ges Schleswig Holstein Gesch
+Z Gesamte Exp Med
+Z Gesamte Hyg
+Z Gesamte Inn Med
+Z Gesamte Staatswiss
+Z Gesch Arab Islam Wiss
+Z Geschichtswiss
+Z Haut Geschlechtskr
+Z Hautkr
+Z Heereskd
+Z Hist Forsch
+Z Hist Ver Schwaben Neuburg
+Z Hist Ver Steiermark
+Z Hyg Infektionskr
+Z Immun exp ther
+Z Immunitats Allergieforsch
+Z Immunitatsforsch Allerg Klin Immunol
+Z Immunitatsforsch Exp Klin Immunol
+Z Immunitatsforsch Immunobiol
+Z Indukt Abstamm Vererbungsl
+Z Jagdwiss
+Z Kardiol
+Z Kardiol Suppl
+Z Kinder Jugendpsychiatr
+Z Kinder Jugendpsychiatr Psychother
+Z Kinderchir
+Z Kinderchir Grenzgeb
+Z Kinderheilkd
+Z Kirchengesch
+Z Klin Chem Klin Biochem
+Z Klin Med
+Z Klin Psychol Psychiatr Psychother
+Z Klin Psychol Psychopathol Psychother
+Z Klin Psychol Psychother
+Z Krankenpfl
+Z Krebsforsch
+Z Krebsforsch Klin Onkol Cancer Res Clin Oncol
+Z Kreislaufforsch
+Z Kunstgesch
+Z Laryngol Rhinol Otol
+Z Lebensm Unters Forsch
+Z Lymphol
+Z Mag
+Z Med Isotopenforsch Deren Grenzgeb
+Z Med Lab Diagn
+Z Med Labortech
+Z Med Mikrobiol Immunol
+Z Med Phys
+Z Med Psychol
+Z Mensch Vererb Konstitutionsl
+Z Mikrosk Anat Forsch
+Z Morphol Anthropol
+Z Naturforsch B
+Z Naturforsch [B]
+Z Naturforsch [C]
+Z Naturwiss Med Grundlagenforsch
+Z Neurol
+Z Orthop Ihre Grenzgeb
+Z Ostforsch
+Z Papyrologie Epigraphik
+Z Parasitenkd
+Z Philos Forsch
+Z Phys A At Nucl
+Z Physiother
+Z Phytother
+Z Plast Chir
+Z Pola Walki
+Z Polit
+Z Prakt Anasth
+Z Prakt Anasth Wiederbeleb Intensivther
+Z Praventivmed
+Z Psychol Z Angew Psychol
+Z Psychosom Med
+Z Psychosom Med Psychoanal
+Z Psychosom Med Psychother
+Z Psychother Med Psychol
+Z Rechtsmed
+Z Relig Geistesgesch
+Z Rheumaforsch
+Z Rheumatol
+Z Rom Philol
+Z Saugetierkd
+Z Savigny Stift Rechtsgesch Ger Abt
+Z Savigny Stift Rechtsgesch Kanon Abt
+Z Sozialreform
+Z Soziol
+Z Stomatol
+Z Tierphysiol Tierernahr Futtermittelkd
+Z Tierpsychol
+Z Tierzuecht Zuechtungsbiol
+Z Tropenmed Parasitol
+Z Tuberk
+Z Tuberk Erkr Thoraxorg
+Z Unfallchir Versicherungsmed
+Z Unfallchir Versicherungsmed Berufskr
+Z Unfallmed Berufskr
+Z Urol
+Z Urol Nephrol
+Z Ver Lubeck Gesch Altertumskd
+Z Vererbungsl
+Z Versuchstierkd
+Z Vitam Horm Fermentforsch
+Z Wirtsch Sozialwiss
+Z Wirtschgeogr
+Z Wiss Mikrosk
+Z Wiss Zool
+Z Wurttemb Landesgesch
+Z Zellforch Microsk Anat Histochem
+Z Zellforsch Mikrosk Anat
+ZFA
+ZFA (Stuttgart)
+ZPG Report
+ZWR
+Zacchia
+Zahn Mund Kieferheilkd Zentralbl
+Zahnarzt
+Zahnarztebl Baden Wurttemb
+Zahnarztl Gesundheitsdienst
+Zahnarztl Mitt
+Zahnarztl Nachr Sudwurttemb
+Zahnarztl Prax
+Zahnarztl Praxisfuhr
+Zahnarztl Rundsch
+Zahnarztl Welt
+Zahnarztl Welt Zahnarztl Reform Zwr
+Zahnarztl Welt Zahnarztl Rundsch ZWR Zahnarztl Reform
+Zahntechnik (Berl)
+Zahntechnik (Zur)
+Zahntechniker (Basel)
+Zaire Afr
+Zambezia
+Zambia Nurse
+Zambia Nurse J
+Zaragoza
+Zasshi Kanazawa Daigaku Igakubu Juzen Igakkai
+Zasshi Tokyo Ika Daigaku
+Zb Istor
+Zb Lek Fak Kosice
+Zb Vojnomed Akad
+Zb Zgodovino Naravoslovja Teh
+Zdr Delo
+Zdrav Aktual
+Zdrav Prac
+Zdrav Rev Vestn Minist Zdrav
+Zdrav Vestn
+Zdravookhr Beloruss
+Zdravookhr Kirg
+Zdravookhr Ross Fed
+Zdravookhr Turkmenistana
+Zdravookhranenie
+Zdravookhranenie Belarusi
+Zdravookhranenie Kazakhstana
+Zdrow Publiczne
+Zeitgesch
+Zent Arb Arb Beih
+Zentralasiat Stud
+Zentralbl Allg Pathol
+Zentralbl Arbeitsmed
+Zentralbl Arbeitsmed Arbeitsschutz Prophyl
+Zentralbl Arbeitsmed Arbeitsschutz Prophyl Ergonomie
+Zentralbl Bakteriol
+Zentralbl Bakteriol A
+Zentralbl Bakteriol Mikrobiol Hyg [A]
+Zentralbl Bakteriol Mikrobiol Hyg [B]
+Zentralbl Bakteriol Naturwiss
+Zentralbl Bakteriol Parasitenkd Infektionskr Hyg
+Zentralbl Bakteriol [B]
+Zentralbl Bakteriol [Orig A]
+Zentralbl Bakteriol [Orig B]
+Zentralbl Bakteriol [Orig]
+Zentralbl Biol Aerosolforsch
+Zentralbl Chir
+Zentralbl Gynakol
+Zentralbl Haut Geschlechtskr Grenzgeb
+Zentralbl Haut Und Geschlechtskr
+Zentralbl Hyg Umweltmed
+Zentralbl Mikrobiol
+Zentralbl Neurochir
+Zentralbl Pathol
+Zentralbl Phlebol
+Zentralbl Verkehrsmed Verkehrspsychol Luft Raumfahrtmed
+Zentralbl Veterinarmed A
+Zentralbl Veterinarmed B
+Zentralbl Veterinarmed [C]
+Zentralblatt Arbeitsmedizin Arbeitsschutz Erogon
+Zero Popul Growth Natl Rep
+Zest Hist
+Zh Eksp Klin Med
+Zh Evol Biokhim Fiziol
+Zh Mikrobiol Epidemiol Immunobiol
+Zh Nevrol Psikhiatr Im S S Korsakova
+Zh Nevropatol Psikhiatr Im S S Korsakova
+Zh Obshch Biol
+Zh Ushn Nos Gorl Bolezn
+Zh Vopr Neirokhir Im N N Burdenko
+Zh Vyssh Nerv Deiat Im I P Pavlova
+Zhe Jiang Yi Ke Da Xue Xue Bao
+Zhejiang Da Xue Xue Bao Yi Xue Ban
+Zhen Ci Yan Jiu
+Zhi Wu Sheng Li Yu Fen Zi Sheng Wu Xue Xue Bao
+Zhong Nan Da Xue Xue Bao Yi Xue Ban
+Zhong Xi Yi Jie He Xue Bao
+Zhong Xi Yi Jie He Za Zhi
+Zhong Yao Cai
+Zhong Yao Tong Bao
+Zhongguo Dang Dai Er Ke Za Zhi
+Zhongguo Ji Sheng Chong Xue Yu Ji Sheng Chong Bing Za Zhi
+Zhongguo Shi Yan Xue Ye Xue Za Zhi
+Zhongguo Wei Zhong Bing Ji Jiu Yi Xue
+Zhongguo Xiu Fu Chong Jian Wai Ke Za Zhi
+Zhongguo Yao Li Xue Bao
+Zhongguo Yi Liao Qi Xie Za Zhi
+Zhongguo Yi Xue Ke Xue Yuan Xue Bao
+Zhongguo Ying Yong Sheng Li Xue Za Zhi
+Zhongguo Zhen Jiu
+Zhongguo Zhong Xi Yi Jie He Za Zhi
+Zhongguo Zhong Yao Za Zhi
+Zhonghua Bing Li Xue Za Zhi
+Zhonghua Er Bi Yan Hou Ke Za Zhi
+Zhonghua Er Bi Yan Hou Tou Jing Wai Ke Za Zhi
+Zhonghua Er Ke Za Zhi
+Zhonghua Fang She Xian Yi Xue Za Zhi
+Zhonghua Fang She Xue Za Zhi
+Zhonghua Fu Chan Ke Za Zhi
+Zhonghua Gan Zang Bing Za Zhi
+Zhonghua Hu Li Za Zhi
+Zhonghua Jie He He Hu Xi Xi Ji Bing Za Zhi
+Zhonghua Jie He He Hu Xi Za Zhi
+Zhonghua Kou Qiang Ke Za Zhi
+Zhonghua Kou Qiang Yi Xue Za Zhi
+Zhonghua Lao Dong Wei Sheng Zhi Ye Bing Za Zhi
+Zhonghua Liu Xing Bing Xue Za Zhi
+Zhonghua Min Guo Wei Sheng Wu Ji Mian Yi Xue Za Zhi
+Zhonghua Min Guo Wei Sheng Wu Xue Za Zhi
+Zhonghua Min Guo Xiao Er Ke Yi Xue Hui Za Zhi
+Zhonghua Nan Ke Xue
+Zhonghua Nei Ke Za Zhi
+Zhonghua Shao Shang Za Zhi
+Zhonghua Shen Jing Ge Za Zhi
+Zhonghua Shen Jing Jing Shen Ke Za Zhi
+Zhonghua Shi Yan He Lin Chuang Bing Du Xue Za Zhi
+Zhonghua Wai Ke Za Zhi
+Zhonghua Wei Chang Wai Ke Za Zhi
+Zhonghua Xin Xue Guan Bing Za Zhi
+Zhonghua Xue Ye Xue Za Zhi
+Zhonghua Ya Yi Xue Hui Za Zhi
+Zhonghua Yan Ke Za Zhi
+Zhonghua Yi Shi Za Zhi
+Zhonghua Yi Xue Yi Chuan Xue Za Zhi
+Zhonghua Yi Xue Za Zhi
+Zhonghua Yi Xue Za Zhi (Taipei)
+Zhonghua Yu Fang Yi Xue Za Zhi
+Zhonghua Zheng Xing Shao Shang Wai Ke Za Zhi
+Zhonghua Zheng Xing Wai Ke Za Zhi
+Zhonghua Zhong Liu Za Zhi
+Zidonghua Xuebao
+Ziekenhuiswezen
+Zimb J Econ
+Zimbabwe Nurse
+Zimbabwe Rhod Nurse
+Zinbun
+Zion
+Ziva Antika
+Zobozdrav Vestn
+Zool Anz
+Zool Jahrb Abt Anat Ontogenie Tiere
+Zool Pol
+Zool Sci Contrib N Y Zool Soc
+Zool Scr
+Zool Zhurnal
+Zoolog Sci
+Zoology (Jena)
+Zoomorphology
+Zoonoses Res
+Zooprofilassi
+Zpr Geogr Ust CSAV
+Zu Guo
+Zuchthygiene
+Zur Medizingesch Abh
+Zur Taschenb
+Zwingliana
+Zygon
+Zygote
+[Rep No] NAMC ACEL United States Air Crew Equip Lab Phila
+eHealth Int
diff --git a/lib/ArrayExpress/Curator/ExperimentChecker.pm b/lib/ArrayExpress/Curator/ExperimentChecker.pm
new file mode 100644
index 0000000..0088c02
--- /dev/null
+++ b/lib/ArrayExpress/Curator/ExperimentChecker.pm
@@ -0,0 +1,2428 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the expt_check script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: ExperimentChecker.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::ExperimentChecker - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use base qw/ArrayExpress::Curator::ExperimentChecker/;
+
+=head1 DESCRIPTION
+
+This module represents an abstract parent class providing methods for
+data file and experiment annotation checks to its child classes. See
+the following documentation for concrete checker classes:
+L<ArrayExpress::Curator::Validate> (Tab2MAGE),
+L<ArrayExpress::MAGETAB::Checker> (MAGE-TAB),
+L<ArrayExpress::Curator::MIAMExpress> (MIAMExpress) and
+L<ArrayExpress::Curator::Standalone> (standalone).
+
+=head1 OPTIONS
+
+The objects created by this module can be instantiated with the
+following options, common to all submission routes.
+
+=over 2
+
+=item C<log_to_current_dir>
+
+Write the log files to the current working directory, rather than in
+the submissions directory.
+
+=item C<is_standalone>
+
+Run checker in standalone mode.
+
+=item C<clobber>
+
+Overwrite existing log and graph files without asking for confirmation.
+
+=item C<adf_filename>
+
+The name of the file to use as ADF in feature/reporter checks. This
+overrides any array designs specified in the submission.
+
+=item C<array_accession>
+
+The ArrayExpress accession number of the array design to use for
+feature/reporter checks. This overrides any array accessions specified
+in the submission.
+
+=item C<qt_filename>
+
+The name of the file to be used for QT definitions. See
+L<ArrayExpress::Datafile::QT_list> for information on the format of
+this file. See also C<include_default_qts>.
+
+=item C<include_default_qts>
+
+Use the QT definitions supplied with these scripts alongside any new
+QT definitions provided by the C<qt_filename> option.
+
+=back
+
+=head1 METHODS
+
+=over 2
+
+=item C<check()>
+
+Starts the checks, based on the options specified in the constructor.
+
+=item C<get_miamexpress_software_type()>
+
+Returns the software term to use for a MIAMExpress export, if a
+unanimous verdict can be reached. Otherwise returns undef. This is
+also used for information purposes with Tab2MAGE and MAGE-TAB
+submissions, which is why it's in this superclass.
+
+=back
+
+=head1 FILES
+
+There are currently five log files written out by the script:
+
+=over 2
+
+=item C<expt_report.log>
+
+Contains some summary information and data quality statistics for each
+data file.
+
+=item C<expt_errors.log>
+
+Lists the errors that were encountered in parsing the data
+files. Details on Feature and QuantitationType errors are given in
+separate log files. Qualifier Value Source usage is also logged in
+this error log file.
+
+=item C<expt_biomaterials.log>
+
+Provides a basic sanity check of the flow of BioMaterials through the
+experiment (i.e. Sample -> Extract -> Labeled Extract ->
+Hybridization). For Tab2MAGE checking the majority of the information
+in this file has been moved to an output PNG file, since it is a much
+clearer and more flexible visualization format.
+
+=item C<expt_feature.log>
+
+Lists feature coordinates and/or reporter identifiers (FGEM only)
+missing from the array design(s) used in the experiment. Entries here
+will typically mean that a dummy array has to be used. Update: This
+file now also lists duplicate features in a separate list.
+
+=item C<expt_columnheadings.log>
+
+Lists unrecognized QuantitationTypes or hybridization IDs (FGEM only)
+appearing in the data column headings.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output
+printed to the error and/or report filehandles:
+
+=over 4
+
+=item B<File existence>
+
+Checks that the data files referred to in the Tab2MAGE
+spreadsheet/MAGE-TAB SDRF/MIAMExpress database actually exist on the
+filesystem (error log).
+
+=item B<Affymetrix CHP file as normalized data>
+
+Checks that CHP files have been submitted as normalized rather than
+raw data (error log).
+
+=item B<Text file check>
+
+Confirms that submitted data files are text, not binary. This test
+is not applied to Affymetrix CHP files.
+
+=item B<File line endings check>
+
+Checks for Unix/DOS/Mac line endings (report log).
+
+=item B<Affymetrix EXP file check>
+
+Checks that EXP files are submitted as raw data and that they all have
+Protocol, Station and Module information (error log).
+
+=item B<Basic data file summary>
+
+Prints out the file format (Affymetrix, GenePix or Generic), the type
+(raw, normalized or transformed), row and column counts (report log).
+
+=item B<Duplicate columns>
+
+Checks for repeated column headings in each data file (error log).
+
+=item B<Excel truncated files>
+
+Checks for possible data corruption by Excel truncation of the file
+(error log).
+
+=item B<QuantitationTypes>
+
+Checks column headings against a list of known QTs. Reports on
+unrecognized QTs (error log, column headings log). This is a work in
+progress.
+
+=item B<FGEM hybridization IDs>
+
+Checks FGEM column headings against the hybridization IDs for the
+submission, reports on those which are not recognized. This
+incorporates a check on the QTs for FGEM files (error log, column
+headings log).
+
+=item B<FGEM BioDataCube order>
+
+Checks that the included final data matrix is laid out in DBQ order,
+rather than DQB. The ArrayExpress MAGE-ML loader software does not
+support the DQB order.
+
+=item B<Data checks>
+
+Data checks are only performed on recognized QT columns. Checks are
+for:
+
+ Text in numeric columns
+ Null values in numeric columns
+ Floats in integer columns
+ Inappropriate boolean values (i.e., not 0 or 1)
+ Log ratios outside reasonable range
+ Basic check on saturation indicators (primarily GenePix files)
+
+(error log).
+
+=item B<Benford's law>
+
+Calculates Benford's law across dimensioned float data. In theory
+this should be approximately 30% for good data (report log).
+
+=item B<Percent null>
+
+Calculates overall percent null across the whole data set. Zero values
+are also counted as null. In practice under 10% null values seems to
+be a reasonable expectation (report log).
+
+=item B<Feature/Reporter check vs. array design>
+
+Checks the feature coordinates (raw, normalized data) or the reporter
+identifiers (FGEM) against either the array designs linked to the
+hybridization in MIAMExpress, or against a user-supplied ADF. Prints
+out a list of features not found in the array design (error log,
+features log). Also alerts the curator when significantly fewer
+features are found in the data file compared to the array design
+(error log). Note that this will give false errors on array designs
+associated with dummy array designs (e.g., some Affy arrays).
+
+=item B<Duplicate Features/Reporters>
+
+Checks that Features or Reporter identifiers are not repeated within
+the same file. Prints out a list of duplicate features/reporters
+(error log, features log).
+
+=item B<Duplicate filenames>
+
+Checks that there are no duplicate files associated with the
+submission (error log).
+
+=item B<Row/column count consistency>
+
+Checks that all the files of a given type (raw, normalized)
+have consistent numbers of rows and columns (error log).
+
+=item B<Affy EXP file consistency>
+
+Checks that the parameters in each EXP file which should be the same
+are the same (error log).
+
+=item B<Potentially duplicated files with different names>
+
+Checks a single line from each file against the same line in every
+other file, and reports on any matching pairs of files (error log).
+
+=back
+
+=head1 SEE ALSO
+
+=over 1
+
+=item L<ArrayExpress::Curator::Validate>
+
+=item L<ArrayExpress::MAGETAB::Checker>
+
+=item L<ArrayExpress::Curator::MIAMExpress>
+
+=item L<ArrayExpress::Curator::Standalone>
+
+=item L<ArrayExpress::Datafile::QT_list>
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::ExperimentChecker;
+
+use strict;
+use warnings;
+
+use Carp;
+use File::Spec;
+use Scalar::Util qw(looks_like_number); # Used for checking data values
+use English qw( -no_match_vars );
+use List::Util qw(max);
+use List::MoreUtils qw(any none notall);
+use Readonly;
+use Class::Std;
+use IO::Handle;
+use Digest::MD5 qw( md5 ); # Used for tracking missing/duplicated features
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::Datafile::DataMatrix;
+
+use ArrayExpress::Datafile::QT_list qw(get_QTs);
+
+require ArrayExpress::Datafile;
+require ArrayExpress::Datafile::Affymetrix;
+
+use ArrayExpress::Datafile::Metrics qw(
+ check_data_metrics
+ calculate_benford
+ pearson_correlation
+);
+
+use ArrayExpress::Curator::Database qw(
+ retrieve_AE_adf
+ parse_adf
+ arrayaccession_in_aedw
+ map_affy_accno_to_name
+);
+
+use ArrayExpress::Curator::Report qw(report_accumulated_errors);
+
+use ArrayExpress::Curator::Logger;
+use base qw(ArrayExpress::Curator::Logger);
+
+# Attributes available for initialization.
+my %is_standalone : ATTR( :name<is_standalone>, :default<undef> );
+my %adf_filename : ATTR( :get<adf_filename>, :init_arg<adf_filename>, :default<undef> );
+my %array_accession : ATTR( :get<array_accession>, :init_arg<array_accession>, :default<undef> );
+my %qt_filename : ATTR( :get<qt_filename>, :init_arg<qt_filename>, :default<undef> );
+my %include_default_qts : ATTR( :get<include_default_qts>, :init_arg<include_default_qts>, :default<undef> );
+my %skip_data_checks : ATTR( :get<skip_data_checks>, :init_arg<skip_data_checks>, :default<undef> );
+my %safe_filechecks : ATTR( :get<safe_filechecks>, :init_arg<safe_filechecks>, :default<undef> );
+my %source_directory : ATTR( :get<source_directory>, :init_arg<source_directory>, :default<undef> );
+my %reporter_prefix : ATTR( :get<reporter_prefix>, :init_arg<reporter_prefix>, :default<undef> );
+my %compseq_prefix : ATTR( :get<compseq_prefix>, :init_arg<compseq_prefix>, :default<undef> );
+my %ignore_size_limits : ATTR( :name<ignore_size_limits>, :default<undef> );
+
+# Accumulative accessors; each of these has an "add_" method defined below.
+my %miame : ATTR( :get<miame>, :default<0> );
+my %aedw_score : ATTR( :get<aedw_score>, :default<0> );
+my %software_types : ATTR( :get<software_types>, :default<[]> );
+my %missing_features : ATTR( :get<missing_features>, :default<{}> );
+my %duplicate_features : ATTR( :get<duplicate_features>, :default<{}> );
+my %missing_features_cache : ATTR( :get<missing_features_cache>, :default<{}> );
+my %duplicate_features_cache : ATTR( :get<duplicate_features_cache>, :default<{}> );
+my %duplicate_columns : ATTR( :get<duplicate_columns>, :default<{}> );
+my %unknown_qts : ATTR( :get<unknown_qts>, :default<{}> );
+my %unknown_hybs : ATTR( :get<unknown_hybs>, :default<{}> );
+
+# These accessors simply accumulate unique hash keys; get_* returns an
+# arrayref of those keys.
+my %arrayexpress_arrays : ATTR( :get<arrayexpress_arrays>,:default<[]> );
+my %expt_designs : ATTR( :get<expt_designs>, :default<[]> );
+my %expt_organisms : ATTR( :get<expt_organisms>, :default<[]> );
+
+# This is a cache of array designs keyed by id.
+my %array_design_cache : ATTR( :default<{}> );
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_progname( $CONFIG->get_EXPTCHECK_PROGNAME() );
+ $self->set_version( $CONFIG->get_EXPTCHECK_VERSION() );
+
+ return;
+}
+
+sub get_files_and_annotation : PRIVATE {
+
+ # This is an abstract class: MIAMExpress, Tab2MAGE, and standalone
+ # checking are handled by overriding this method in their
+ # respective subclasses.
+
+ my ( $self ) = @_;
+
+ confess("Error: Stub method called in abstract class " . __PACKAGE__);
+
+}
+
+sub add_miame : RESTRICTED {
+
+ # Takes an integer, bitwise ORs it with $miame{ident $self},
+ # stores and returns the result. Used to store the MIAME scores
+ # for compliance reporting.
+
+ my ( $self, $err ) = @_;
+
+ $miame{ident $self} ||= 0;
+ if ($err) { $miame{ident $self} |= $err }
+
+ return $miame{ident $self};
+}
+
+sub add_aedw_score : RESTRICTED {
+
+ # Takes an integer, bitwise ORs it with $aedw_score{ident $self},
+ # stores and returns the result. Used to store the AEDW scores
+ # for compliance reporting.
+
+ my ( $self, $err ) = @_;
+
+ $aedw_score{ident $self} ||= 0;
+ if ($err) { $aedw_score{ident $self} |= $err }
+
+ return $aedw_score{ident $self};
+}
+
+sub _check_aedw_and_miame : PRIVATE {
+
+ # Run some general tests on aedw suitability and MIAME compliance.
+ my ($self, $hyb_ids) = @_;
+
+ # Initialize some logs which we will always want written to. Note
+ # that this must be done _after_ connecting to the database, or
+ # the names don't get localized properly:
+ $self->logprint('aedw', q{});
+ $self->logprint('miame', q{});
+
+ #######################################
+ # Top-level tests on AEDW suitability #
+ #######################################
+
+ # Report on the arrays used, and check against the DW. Each array
+ # must pass MIAME (in the autosubmissions DB, calculated by Anna
+ # Farne's scripts) for the score to be added to the total.
+ my @ae_arrays = @{ $self->get_arrayexpress_arrays() };
+ my $array_miame = $CONFIG->get_MIAME_ARRAYSEQ();
+ my $all_arrays_loaded = 1;
+
+ my %miame_compliant_pipeline
+ = map { $_ => 1 } @{ $CONFIG->get_MIAME_COMPLIANT_ARRAY_PIPELINES() || [] };
+
+ # If the array is not in AE, we have a problem.
+ unless (@ae_arrays) {
+ $self->logprint(
+ 'aedw',
+ "Problem: no public AE arrays associated with submission.\n"
+ );
+ $array_miame = 0;
+ $all_arrays_loaded = 0;
+ }
+
+ # Check on known AE-loaded arrays.
+ foreach my $accno ( @ae_arrays ) {
+ $self->logprint(
+ 'report',
+ "ArrayExpress array used: $accno\n",
+ );
+ unless ( arrayaccession_in_aedw($accno) ){
+ $all_arrays_loaded = 0;
+ $self->logprint(
+ 'aedw',
+ "Problem: Array $accno is not in the data warehouse.\n"
+ );
+ }
+
+ # Check that this isn't from a pipeline with special MIAME
+ # dispensation, then check that it's compliant in the
+ # autosubmissions database. Accession numbers are assumed to
+ # be in the form \D{n}\d{n}.
+ my ($acc_prefix) = ($accno =~ m/\A (\D*?) \d+ \z/xms);
+ unless ( $miame_compliant_pipeline{$acc_prefix} ) {
+ if ( $CONFIG->get_AUTOSUBS_DSN() ) {
+ require ArrayExpress::AutoSubmission::DB::ArrayDesign;
+ my $array = ArrayExpress::AutoSubmission::DB::ArrayDesign->retrieve(
+ accession => $accno,
+ );
+
+ # At the moment, array MIAME is yes or no.
+ unless ( $array && $array->miame_score() ) {
+ $self->logprint(
+ 'miame',
+ "Problem: Array $accno is not MIAME compliant.\n"
+ );
+ $array_miame = 0;
+ }
+ }
+ else {
+ $self->logprint(
+ 'miame',
+ "Problem: Unable to check array MIAME compliance in subs database.\n"
+ );
+ }
+ }
+ }
+
+ # Add score for the data warehouse if all loaded okay.
+ $self->add_aedw_score($CONFIG->get_AEDW_ARRAYLOADED()) if $all_arrays_loaded;
+
+ # Note the final score.
+ $self->add_miame($array_miame);
+
+ # Data warehouse only wants experiments with more than x no. of hybs.
+ if (scalar(grep { defined $_ } values %$hyb_ids)
+ >= $CONFIG->get_AEDW_MINIMUM_HYBS() ) {
+ $self->add_aedw_score($CONFIG->get_AEDW_HYBNUMBER());
+ }
+ else {
+ $self->logprint(
+ 'aedw',
+ "Problem: Experiment has fewer than "
+ . $CONFIG->get_AEDW_MINIMUM_HYBS()
+ . " hybridizations.\n"
+ );
+ }
+
+ # Only certain organisms and design types allowed in the DW. At
+ # least one design type must be in AEDW_DESIGN_TYPES; if not,
+ # check against AEDW_UNWANTED_DESIGN_TYPES and note if the
+ # experiment may be under-annotated.
+ my %allowed_design = map { $_ => 1 } @{ $CONFIG->get_AEDW_DESIGN_TYPES() || [] };
+ my %unallowed_design = map { $_ => 1 } @{ $CONFIG->get_AEDW_UNWANTED_DESIGN_TYPES() || [] };
+ my @design_types = @{ $self->get_expt_designs() };
+ my $is_bad_design_type;
+
+ # Check the positive stuff first.
+ if ( ! scalar @design_types ) {
+
+ # No design type
+ $is_bad_design_type++;
+ $self->logprint(
+ 'aedw',
+ "Problem: Experiment design is not given.\n"
+ );
+ } elsif ( any { $allowed_design{$_} } @design_types ) {
+
+ # Good design type
+ $self->add_aedw_score($CONFIG->get_AEDW_DESIGNTYPE());
+ }
+ else {
+
+ # Bad design type
+ $is_bad_design_type++;
+ $self->logprint(
+ 'aedw',
+ "Problem: Experiment design is not one of the required types:\n\t- ",
+ join("\n\t- ", sort keys %allowed_design),
+ "\n",
+ );
+ }
+
+ # Hold our noses and check the negative stuff.
+ if ( $is_bad_design_type
+ && ( ! scalar @design_types || none { $unallowed_design{$_} } @design_types ) ) {
+ $self->logprint(
+ 'aedw',
+ "Caveat: Experiment design is not one of the following unsuitable types,\n ",
+ "and may yet be suitable for the data warehouse:\n\t- ",
+ join("\n\t- ", sort keys %unallowed_design),
+ "\n",
+ );
+ }
+
+ return;
+}
+
+sub check {
+
+ # Takes a list of options (see perldocs above), checks the
+ # submission and returns the error code.
+
+ my $self = shift;
+
+ # Set STDOUT and STDERR to unbuffered, for live feedback to a
+ # command-line user.
+ STDOUT->autoflush(1);
+ STDERR->autoflush(1); # in case someone else has tampered...
+
+ # Connect to MX, or read in the spreadsheet; populate array design info.
+ my ( $filelist, $hyb_ids, $norm_ids )
+ = $self->get_files_and_annotation();
+
+ # Check the return values, just in case.
+ unless ( ( ref $filelist eq 'ARRAY' )
+ && ( ref $hyb_ids eq 'HASH' )
+ && ( ref $norm_ids eq 'HASH' ) ) {
+ $self->logprint(
+ 'error',
+ "FATAL ERROR: Unable to retrieve data file and/or annotation information\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ return $self->get_error();
+ }
+
+ # AEDW and MIAME checks (annotation only).
+ $self->_check_aedw_and_miame($hyb_ids);
+
+ ##############################
+ # Begin the datafile reports #
+ ##############################
+
+ # Sort out our QuantitationTypes
+
+ $self->logprint_line( 'error', 'QT file parsing START' );
+
+ # Get the QTs, either discarding or including known QTs
+ my $QTs = get_QTs( $self->get_qt_filename, $self->get_include_default_qts );
+
+ $self->logprint_line( 'error', 'QT file parsing END' );
+
+ # This is the heavyweight method that does all the work.
+ $self->check_file_list($filelist, $QTs, $hyb_ids, $norm_ids);
+
+ # Implementation of this method is left to the concrete subclasses.
+ $self->visualize_experiment($filelist);
+
+ return $self->get_error;
+
+}
+
+sub visualize_experiment : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ croak("ERROR: Abstract stub method called in ExperimentChecker superclass.");
+}
+
+sub check_file_list : PRIVATE {
+
+ my ($self, $filelist, $QTs, $hyb_ids, $norm_ids) = @_;
+
+ ######################
+ # START OF FILE LOOP #
+ ######################
+
+ # Start checking the files
+ $self->logprint_line( 'error', 'Data file parsing START' );
+
+ print STDOUT ( "\n" . scalar(@$filelist) . " files to process.\n\n" );
+
+ my (%file_name_count, %file_type_count);
+
+ # If the -x option was used, skip the heavyweight checks.
+ if ( $self->get_skip_data_checks() ) {
+ $self->logprint( 'error', "\nWARNING: SKIPPING data file checks.\n\n" );
+ $self->logprint( 'report', "** Data file checks were skipped. **\n\n" );
+ print STDOUT ( "** Skipping data file checks **.\n\n" );
+ }
+
+ # Run some checks on the files.
+ # Set FV "missing" flag to one if there are no files at all.
+ my $factorval_missing = scalar @$filelist ? 0 : 1;
+ foreach my $file (@$filelist) {
+
+ # Record some aggregate metadata.
+ $file_name_count{ $file->get_name() }++;
+ $file_type_count{ $file->get_data_type() }++;
+
+ # DW submissions must have factor values, attached here to the
+ # files (MX checking does not attach FVs to FGEM datafile object).
+ unless ( scalar( grep { defined $_ } values %{ $file->get_factor_value() } )
+ || ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+ || $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+ || $file->get_is_exp() ) ) {
+
+ $factorval_missing++;
+ }
+
+ # Check file, unless told not to (-x).
+ unless ( $self->get_skip_data_checks() ) {
+ $self->check_file($file, $QTs, $hyb_ids, $norm_ids);
+ }
+ }
+
+ # Note any FV problems.
+ if ( $factorval_missing ) {
+
+ # AEDW
+ $self->logprint(
+ 'aedw',
+ "Problem: Experiment does not have factor value for one or more hybs.\n"
+ );
+
+ # MIAME
+ $self->logprint(
+ 'miame',
+ "Problem: Experiment does not have factor value for one or more hybs.\n"
+ );
+ }
+ else {
+
+ # Record the AEDW and MIAME scores.
+ $self->add_aedw_score($CONFIG->get_AEDW_FACTORVALUES());
+ $self->add_miame($CONFIG->get_MIAME_FACTORVALUES());
+ }
+
+ ####################
+ # END OF FILE LOOP #
+ ####################
+
+ $self->logprint_line( 'report' );
+
+ $self->logprint_line( 'error', 'Data file parsing END' );
+
+ # Commence aggregate checks on the parsed files.
+ $self->logprint_line( 'error', 'Final consistency check START' );
+
+ # Check for duplicate file uploads (simply by name; no check of contents)
+ foreach my $filename ( keys %file_name_count ) {
+ if ( $file_name_count{$filename} > 1 ) {
+ $self->logprint( 'error',
+ "ERROR: Files named $filename have been uploaded multiple times.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ }
+
+ # Check that we have raw and (norm || fgem) files; this is a DW
+ # (and/or MIAME) requirement and may be elaborated upon later.
+ my $good_aedw_data = 1;
+ if ($file_type_count{'raw'}) {
+ $self->add_miame($CONFIG->get_MIAME_RAWDATA);
+ }
+ else {
+ $self->logprint(
+ 'miame',
+ "Problem: Raw data is absent.\n"
+ );
+ }
+
+ if ( $file_type_count{'normalized'}
+ || $file_type_count{$CONFIG->get_FGEM_FILE_TYPE()} ) {
+ $self->add_miame($CONFIG->get_MIAME_NORMDATA);
+ }
+ else {
+ $good_aedw_data = 0;
+ $self->logprint(
+ 'aedw',
+ "Problem: Normalized data is absent.\n"
+ );
+ $self->logprint(
+ 'miame',
+ "Problem: Normalized data is absent.\n"
+ );
+ }
+ $self->add_aedw_score($CONFIG->get_AEDW_GOODDATA()) if $good_aedw_data;
+
+ # Check row and column counts across files.
+ $self->_check_datafile_consistency($filelist);
+
+ # Report on QTs, FGEM hyb_ids and duplicate columns
+ report_accumulated_errors(
+ $self->get_unknown_qts(),
+ $self->log_fh('columns'),
+ 'Error: The following unknown QuantitationTypes:'
+ );
+ report_accumulated_errors(
+ $self->get_unknown_hybs(),
+ $self->log_fh('columns'),
+ 'Error: The following unknown Hyb/Norm IDs:'
+ );
+ report_accumulated_errors(
+ $self->get_duplicate_columns(),
+ $self->log_fh('columns'),
+ 'ERROR: The following duplicated column headings:'
+ );
+ report_accumulated_errors(
+ $self->get_missing_features(),
+ $self->log_fh('feature'),
+ 'ERROR: The following features or identifiers missing from the array:'
+ );
+ report_accumulated_errors(
+ $self->get_duplicate_features(),
+ $self->log_fh('feature'),
+ 'ERROR: The following duplicated feature coordinates / identifiers:'
+ );
+
+ # Sort out which are the EXP files
+ my $exp_filelist;
+ foreach my $file (@$filelist) {
+ push( @$exp_filelist, $file ) if $file->get_is_exp();
+ }
+
+ # Check the EXP files
+ my $exp_inconsistencies = $self->_check_exp_params($exp_filelist);
+
+ # Print out what we find
+ foreach my $section ( sort keys %$exp_inconsistencies ) {
+ foreach my $param ( sort keys %{ $exp_inconsistencies->{$section} } )
+ {
+ $self->logprint( 'error',
+ "Warning: parameter differs across the submitted EXP files: [$section] $param\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+
+ # Pairwise many-to-many comparison of a single selected data line
+ # from each file to catch renamed duplicates. This has been
+ # overloaded in the case of binary files to hold an md5 hash of
+ # the entire file.
+ for ( my $i = 0; $i <= $#$filelist; $i++ ) {
+ for ( my $j = $i + 1; $j <= $#$filelist; $j++ ) {
+ my $line_i = $filelist->[$i]->get_test_data_line();
+ my $line_j = $filelist->[$j]->get_test_data_line();
+
+ # Binary files may not have the test line.
+ if ( $line_i && $line_j && ( $line_i eq $line_j ) ) {
+ $self->logprint(
+ 'error',
+ "WARNING: Files ",
+ $filelist->[$i]->get_name(),
+ " and ",
+ $filelist->[$j]->get_name(),
+ " may be identical.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+ }
+
+ $self->logprint_line( 'error', 'Final consistency check END' );
+
+ print STDOUT ("\nDone processing files.\n");
+
+ return;
+}
+
+sub non_rowbased_data_test : PRIVATE {
+
+ # For data files which don't conform to the usual model of rows of
+ # data with headings, we run some basic tests here. This includes
+ # all Affy and binary file types.
+
+ # N.B. Affy CELv3 used to be treated as row-level data but when we
+ # export them as MAGE-ML we use a separate parser, so it would be
+ # better to invoke that during checking (FIXME?).
+
+ my ( $self, $file ) = @_;
+
+ # We can't sample the data, so we just take the MD5 hash.
+ $file->set_test_data_line( $file->get_md5_digest() );
+
+ # Check the file against those already loaded in AE.
+ if ( $CONFIG->get_AUTOSUBS_DSN() ) {
+ $self->check_file_against_loaded_data( $file );
+ }
+
+ $file->set_row_count(0); # reset the row count
+
+ return;
+}
+
+sub affymetrix_data_test : PRIVATE {
+
+ my ( $self, $file ) = @_;
+
+ # Affymetrix CHP or XDA CEL; record software for consensus
+ # calculation.
+ $file->set_format_type('Affymetrix');
+ $self->add_software_types('Affymetrix');
+
+ $self->confirm_affy_chip_type( $file );
+
+ # Very quick check to make sure CHP files are in the right place
+ if ( $file->get_path() =~ m/\.CHP$/i
+ && $file->get_data_type ne 'normalized' ) {
+
+ $self->logprint(
+ 'error',
+ "Error: CHP file ",
+ $file->get_name(), " submitted as raw data or FGEM.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Some generic tests we can run.
+ $self->non_rowbased_data_test( $file );
+
+ return;
+}
+
+sub affymetrix_expfile_test : PRIVATE {
+
+ # Check Affy EXP files are in the right place, and that they
+ # look okay.
+ my ( $self, $file ) = @_;
+
+ $file->set_format_type('Affymetrix');
+
+ # EXP files should be submitted as raw data (MX) or as a dedicated
+ # EXP file type (Tab2MAGE).
+ unless ( $file->get_data_type() eq 'raw'
+ || $file->get_data_type() eq 'EXP' ) {
+ $self->logprint(
+ 'error',
+ "Error: EXP file ",
+ $file->get_name(), " submitted as either normalized or FGEM.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Check on EXP files; populate the {exp_data} hash key,
+ # set format etc.
+ unless ( $file->parse_exp_file() ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ "ERROR parsing EXP file %s\n",
+ $file->get_name(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ my $exp_data = $file->get_exp_data();
+
+ unless ( $exp_data->{Fluidics}{Protocol}
+ && $exp_data->{'Sample Info'}{'Chip Lot'} ) {
+
+ $self->logprint(
+ 'error',
+ "Warning: EXP file ",
+ $file->get_name(),
+ " lacks either chip lot or protocol info.\n",
+ );
+
+ # Have relaxed this since we won't be using GDAC when
+ # automating anyway.
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+
+ return;
+}
+
+sub check_file : PRIVATE {
+
+ # Returns 1 on successful check (whether the file is good or not),
+ # undef if there was a problem checking.
+
+ my ( $self, $file, $QTs, $hyb_ids, $norm_ids ) = @_;
+
+ $self->logprint_line( 'report' );
+
+ my ($feature_coords);
+
+ # Set the data file error filehandle.
+ $file->set_error_fh( $self->log_fh('error') );
+
+ # just the filename
+ $file->set_name( ( File::Spec->splitpath( $file->get_path() ) )[2] );
+
+ my $hybdir =
+ ( ! $file->get_hyb_sysuid()
+ || ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) )
+ ? undef
+ : "hybrid" . $file->get_hyb_sysuid();
+ my $hyb_filename = $hybdir
+ ? File::Spec->catfile( $hybdir, $file->get_name() )
+ : $file->get_name();
+
+ print STDOUT ( "Checking file ", $file->get_name(), "... " );
+ $self->logprint_line('error', sprintf( " %s ", $file->get_name() ));
+
+ # Check that files are actually present and readable.
+ unless ( -r $file->get_path() ) {
+ $self->logprint( 'error', "FILE ERROR: File ",
+ $file->get_path(), " not found or unreadable.\n" );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ print STDOUT ("\n");
+ return; # Bail out with undef return
+ }
+
+ # Quick file size check - sometimes uploads result in
+ # zero-byte files.
+ if ( -s $file->get_path() == 0 ) {
+ $self->logprint(
+ 'error', "ERROR: File ",
+ $file->get_path(), " is empty (zero bytes).\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # If file exists, list it in the report log
+ $self->logprint( 'report', "Filename : ",
+ $file->get_path(), "\n" );
+ $self->logprint( 'report', "File type : ",
+ $file->get_data_type(), "\n" );
+
+ # Handle Affymetrix files here. In general we now don't bother
+ # running tests on these files. This at least confirms that the
+ # file header can be parsed (confirm_affy_chip_type).
+ if ( $file->get_path() =~ m/\. (CHP|CEL|EXP) \z/ixms ) {
+
+ if ( $file->get_path() =~ m/\. EXP \z/ixms ) {
+ $self->affymetrix_expfile_test( $file );
+ }
+ else {
+ $self->affymetrix_data_test( $file );
+ }
+
+ $self->logprint(
+ 'report', "File format : ", $file->get_format_type(), "\n",
+ );
+
+ # Finish the STDOUT row.
+ print STDOUT "done.\n";
+
+ return 1;
+ }
+
+ # Quick data type check here (text vs. binary).
+ if ( $file->get_is_binary() ) { # unless file is ASCII...
+
+ $self->logprint( 'report',
+ "File format : Binary (skipped further analysis)\n" );
+
+ # Warn on unrecognized binary file type. We've already handled
+ # Affy, so this file must not be recognized.
+ print STDOUT (
+ "Error: File ",
+ $file->get_name(), " is a binary file. Skipping.\n"
+ );
+ $self->logprint(
+ 'error',
+ "Error: File ", $file->get_name(),
+ " is a binary file; skipped further analysis.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+ # Some generic tests we can run.
+ $self->non_rowbased_data_test( $file );
+
+ # Finish the STDOUT row.
+ print STDOUT "done.\n";
+
+ # Bail out with undef return
+ return;
+ }
+
+ # File size check for safety's sake
+ unless ( ( -s $file->get_path() < $CONFIG->get_MAX_DATAFILE_SIZE() )
+ || $self->get_ignore_size_limits() ) {
+ print STDERR ( "Error: File ", $file->get_name(),
+ " is too large. Skipping.\n" );
+ $self->logprint(
+ 'report',
+ "File format : Unknown (file too large: skipped further analysis)\n"
+ );
+ $self->logprint( 'error', "ERROR: File ", $file->get_name(),
+ " is too large to be processed safely; further analysis skipped.\n"
+ );
+
+ # Likely these large files will break downstream processing, and
+ # we haven't checked them anyway.
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ $file->set_row_count(0); # reset the row count
+ return; # Bail out with undef return
+ }
+
+ # Now we've discarded the corner cases, get some actual
+ # information from the file. This also populates the $file object.
+ my $rc;
+ ( $feature_coords, $rc ) = $file->parse_datafile(
+ $QTs,
+ $hyb_ids,
+ $norm_ids,
+ );
+
+ # Note down the software type so we can derive a consensus later.
+ my $qt_type = $file->get_qt_type();
+ if ( $qt_type ) {
+
+ # Take everything up to the first square bracket.
+ $qt_type =~ s/^([^\[]*).*/$1/;
+ $self->add_software_types($qt_type);
+ }
+ if ($rc) {
+ $self->logprint(
+ 'error',
+ "PARSE ERROR: file ",
+ $file->get_name(), ": $rc",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Print out some basic information on the files
+ $self->logprint(
+ 'report',
+ "File format : ",
+ ( $file->get_qt_type() or 'Unknown' ), "\n"
+ );
+ $self->logprint( 'report', "Line endings : ",
+ $file->get_line_format(), "\n" );
+
+ $self->logprint(
+ 'report',
+ "Rows : ",
+ ( $file->get_row_count() || 'N/A' ), "\n"
+ );
+ my $column_count = scalar( @{ $file->get_column_headings() } );
+ $self->logprint(
+ 'report',
+ "Columns : ",
+ ( $column_count || 'N/A' ), "\n"
+ );
+
+ unless ($column_count) {
+ $self->logprint(
+ 'error',
+ "PARSE ERROR: Failed to parse column headings in file ",
+ $file->get_name(), "\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ $file->increment_parse_errors();
+ }
+
+ # Check for repeated column headings in each data file.
+ $self->_check_duplicate_columns( $file, $hyb_filename );
+
+ # Crappy Excel truncates files. Here we check for that.
+ if ( $file->get_row_count() == 65535 ) {
+ $self->logprint(
+ 'error',
+ "WARNING: Possible Excel-truncated file ",
+ $file->get_name(), "\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ # Report on bad column heading QTs
+ if ( scalar @{ $file->get_fail_columns() } ) {
+ $self->add_unknown_qts( $hyb_filename, $file->get_fail_columns() );
+ $self->logprint(
+ 'error',
+ "Warning: Possible QuantitationType problems in file ",
+ $file->get_name(), ". See column heading log file.\n",
+ );
+
+ # In the past this has been PARSEBAD for tab2mage,
+ # INNOCENT for MX. Whilst the autosubmissions system uses
+ # keep_all_qts, however, we can relax this.
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+
+ # This is for bad FGEM column heading hyb ids
+ if ( scalar @{ $file->get_fail_hybs() } ) {
+ $self->add_unknown_hybs( $hyb_filename, $file->get_fail_hybs() );
+ $self->logprint(
+ 'error',
+ "Error: Hyb/Norm ID problems in file ",
+ $file->get_name(), ". See column heading log file.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ if ( $file->get_data_type eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ my $datamatrix = ArrayExpress::Datafile::DataMatrix->new();
+
+ my ( $ba_list, $qt_list, $order ) =
+ $datamatrix->get_dimension_lists(
+ $file->get_heading_hybs(),
+ $file->get_heading_qts(),
+ );
+ if ( !$order ) {
+ $self->logprint(
+ 'error',
+ "PARSE ERROR: FGEM data cube order cannot be determined.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ elsif ( $order eq 'DQB' ) {
+ $self->logprint(
+ 'error',
+ qq{WARNING: FGEM data cube order "$order" }
+ . qq{is unsupported by ArrayExpress.\n}
+ );
+
+ # Not really a bad parsing, but we don't support
+ # this coding order in ArrayExpress yet.
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ # Call check_data_metrics() to check data for consistency, one
+ # column at a time
+ check_data_metrics( $file->get_data_metrics );
+
+ while ( my ( $header, $qt_metric ) = each %{ $file->get_data_metrics() } ) {
+ foreach my $error_message ( keys %{ $qt_metric->{errors} } ) {
+
+ # Filter out the singletons; they're not much of a
+ # problem, and all too common
+ if ( $qt_metric->{errors}{$error_message} > 10 ) {
+ $self->logprint(
+ 'error',
+ "Warning: $error_message (for ",
+ $qt_metric->{errors}{$error_message}
+ . " values) in column \'$header\', file ",
+ $file->get_name(),
+ "\n"
+ );
+
+ # We see these all the time; maybe make this stricter
+ # when QT processing is more finalized?
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+ }
+
+ # Print out the overall Benford's law value for each datafile (can
+ # do this column-wise if there's demand). N.B. we're only
+ # calculating from MeasuredSignal values at this time, because
+ # that's where the interesting data is.
+ my $benval = calculate_benford( $file->get_data_metrics() );
+ if ( looks_like_number($benval) ) {
+ $self->logprint(
+ 'report',
+ sprintf( "Benford's Law value: %.2f%%\n", $benval ),
+ );
+
+# Benford's law warnings deactivated for now (we can't do much about bad data).
+# if ($benval < 20 || $benval > 40){ # This value should be around 30%
+# $self->logprint('error', "Warning: Benford's Law violated by data in file ",$file->get_name(),". See report log.\n");
+# $self->add_error($CONFIG->get_ERROR_INNOCENT());
+# }
+
+ }
+ else {
+ $self->logprint(
+ 'report',
+ sprintf( "Benford's Law value: %s\n", $benval ),
+ );
+ }
+
+ # Print out percent null values for all the columns containing
+ # recognized QTs.
+ my $percent_null = $file->percent_null;
+ if ( looks_like_number($percent_null) ) {
+ $self->logprint(
+ 'report',
+ sprintf( "Percent null values: %.2f%%\n", $percent_null ),
+ );
+
+ # This value should be less than around 10-15%, based on what
+ # I've seen so far.
+ if ( $percent_null > 50 ) {
+ $self->logprint(
+ 'error',
+ "WARNING: Percent null data in ",
+ $file->get_name(), " > 50%. See report log.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+ else {
+ $self->logprint(
+ 'report',
+ sprintf( "Percent null values: %s\n", $percent_null ),
+ );
+ }
+
+ # We check raw and normalized files by feature coordinate,
+ # combined (transformed) data files by reporter identifier.
+ my $num_index_columns = scalar( @{ $file->get_index_columns() } );
+
+ # Skip files which weren't parsed correctly.
+ if ( $num_index_columns
+ && $feature_coords
+ && @{$feature_coords} ) {
+
+ $self->_compare_index_to_design(
+ $file,
+ $feature_coords,
+ $hyb_filename,
+ );
+
+ # Check for repeated features, reporters within a file. Note
+ # that repeated reporters or compseqs ($num_index_columns ==
+ # 1) are okay.
+ unless ( $num_index_columns == 1 ) {
+ $self->_check_duplicate_features(
+ $file,
+ $feature_coords,
+ $hyb_filename,
+ );
+ }
+ }
+
+ else {
+
+ # Bail if there are no recognizable features.
+ $self->logprint(
+ 'error',
+ "PARSE ERROR: Failed to parse features in file ",
+ $file->get_name(), ". Skipping feature/reporter check.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # And now we're done with this file
+ print STDOUT ("done.\n");
+
+ return 1;
+}
+
+sub get_miamexpress_software_type {
+
+ # Returns the software term to use for a MIAMExpress export, if a
+ # unanimous verdict can be reached. Otherwise returns undef.
+ my ( $self ) = @_;
+
+ my %consensus;
+
+ foreach my $software ( @{ $self->get_software_types() } ) {
+ $consensus{$software}++;
+ }
+
+ my $consensus_number = scalar( grep { defined $_ } values %consensus );
+
+ if ( $consensus_number == 1 ) {
+ return (keys %consensus)[0]; # Only one key.
+ }
+ else {
+ return; # Beware list context in caller.
+ }
+
+}
+
+#################################
+# Accumulative accessor methods #
+#################################
+
+sub add_missing_features : PRIVATE {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %tmp = @args;
+ while ( my ( $file, $features ) = each %tmp ) {
+
+ # We use an md5 hash string here rather than the full
+ # listing to save on memory; typically multiple files will
+ # share a list of missing features.
+ my $md5 = Digest::MD5->new();
+ foreach my $de_id (@$features) { $md5->add($de_id); }
+ my $ded_md5 = $md5->digest;
+
+ # Reuse the list generated from previous files. Note that
+ # we can also try writing the cache to disk and just index
+ # via $ded_md5 in future.
+ $missing_features_cache{ident $self}{ $ded_md5 } ||= $features;
+ $missing_features{ident $self}{ $file }
+ = $missing_features_cache{ident $self}{ $ded_md5 };
+ }
+ }
+
+ return;
+}
+
+sub add_duplicate_features : PRIVATE {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %tmp = @args;
+ while ( my ( $file, $features ) = each %tmp ) {
+
+ # We use an md5 hash string here rather than the full
+ # listing to save on memory; typically multiple files will
+ # share a list of duplicated features.
+ my $md5 = Digest::MD5->new();
+ foreach my $de_id (@$features) { $md5->add($de_id); }
+ my $ded_md5 = $md5->digest;
+
+ # Reuse the list generated from previous files. Note that
+ # we can also try writing the cache to disk and just index
+ # via $ded_md5 in future.
+ $duplicate_features_cache{ident $self}{ $ded_md5 } ||= $features;
+ $duplicate_features{ident $self}{ $file }
+ = $duplicate_features_cache{ident $self}{ $ded_md5 };
+ }
+ }
+
+ return;
+}
+
+sub add_duplicate_columns : PRIVATE {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %tmp = @args;
+ @{ $duplicate_columns{ident $self} }{ keys %tmp } = values %tmp;
+ }
+ return $duplicate_columns{ident $self};
+}
+
+sub add_unknown_qts : PRIVATE {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %tmp = @args;
+ @{ $unknown_qts{ident $self} }{ keys %tmp } = values %tmp;
+ }
+ return $unknown_qts{ident $self};
+}
+
+sub add_unknown_hybs : PRIVATE {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %tmp = @args;
+ @{ $unknown_hybs{ident $self} }{ keys %tmp } = values %tmp;
+ }
+ return $unknown_hybs{ident $self};
+}
+
+sub add_arrayexpress_arrays : PRIVATE {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %array = map { $_ => 1 }
+ @{ $arrayexpress_arrays{ident $self} };
+ foreach my $elem (@args) {
+ $array{$elem}++;
+ }
+ $arrayexpress_arrays{ident $self} = [ keys %array ];
+ }
+ return $arrayexpress_arrays{ident $self};
+}
+
+sub add_expt_designs : RESTRICTED {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %array = map { $_ => 1 }
+ @{ $expt_designs{ident $self} };
+ foreach my $elem (@args) {
+ $array{$elem}++;
+ }
+ $expt_designs{ident $self} = [ keys %array ];
+ }
+ return $expt_designs{ident $self};
+}
+
+sub add_expt_organisms : RESTRICTED {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ my %array = map { $_ => 1 }
+ @{ $expt_organisms{ident $self} };
+ foreach my $elem (@args) {
+ $array{$elem}++;
+ }
+ $expt_organisms{ident $self} = [ keys %array ];
+ }
+ return $expt_organisms{ident $self};
+}
+
+###################
+# Private methods #
+###################
+
+sub confirm_affy_chip_type : PRIVATE {
+
+ my ( $self, $file ) = @_;
+
+ # Takes an Affy Datafile (CHP or CEL), finds its chip_type (needs
+ # to parse the files, wrapped in an eval); compares this to the
+ # value returned from map_affy_accno_to_name for the
+ # ArrayDesign.accession (assuming both these values can be
+ # obtained), and alert the user if they differ.
+
+ my ( $chip_type, $is_gdac_chp );
+
+ local $EVAL_ERROR;
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ eval {
+ my $fac = ArrayExpress::Datafile::Affymetrix->new();
+ my $parser = $fac->make_parser( $file->get_path() );
+
+ # FIXME One day we hope to be able to eliminate this call.
+ $parser->parse_header();
+
+ $chip_type = $parser->get_chip_type();
+ $is_gdac_chp
+ = $parser->isa('ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP');
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+
+ if ( $EVAL_ERROR ) {
+
+ # Affy parser failed, we need to report that.
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Error: Unable to parse Affymetrix file "%s": %s\n},
+ $file->get_name(),
+ $EVAL_ERROR,
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ else {
+
+ # Check the chip type against the array accession.
+ my $accession;
+ if ( my $array = $file->get_array_design() ) {
+ $accession = $array->get_accession();
+ }
+ if ( $accession && $chip_type ) {
+ my $wanted = map_affy_accno_to_name( $accession );
+ if ( $wanted ) {
+ unless ( $wanted eq $chip_type ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: File "%s" [%s] assigned to incorrect array design (%s).\n},
+ $file->get_name(),
+ $chip_type,
+ $accession,
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ } else {
+ $self->logprint(
+ 'error',
+ "Warning: No [chip type] term in array design name for $accession\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+ elsif ( ! $is_gdac_chp ) {
+
+ # We skip this warning for old GDAC CHP files since we'd
+ # have to parse the file fully (not just the header) to
+ # get the chip_type.
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Warning: Either array accession or chip type unavailable for %s.\n",
+ $file->get_name(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+
+ return;
+}
+
+sub check_file_against_loaded_data : PRIVATE {
+
+ # Checks a binary (or ascii CELv3) file's md5 hash against those
+ # computed for AE loaded data.
+ my ( $self, $file ) = @_;
+
+ unless ( $CONFIG->get_AUTOSUBS_DSN() ) {
+ confess(
+ "Error: Cannot check file against loaded data: no database connection"
+ );
+ }
+
+ require ArrayExpress::AutoSubmission::DB::LoadedData;
+
+ my @previous = ArrayExpress::AutoSubmission::DB::LoadedData->search(
+ md5_hash => $file->get_md5_digest(),
+ );
+
+ if ( scalar @previous ) {
+ my @accessions;
+ foreach my $data ( @previous ) {
+ foreach my $expt ( $data->experiments() ) {
+ push @accessions, $expt->accession();
+ }
+ }
+ my $accs = join(", ", @accessions);
+ my $filename = $file->get_name();
+ $self->logprint(
+ 'error',
+ "Warning: File $filename has been previously loaded for experiments $accs\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+}
+
+sub add_software_types : PRIVATE {
+
+ my ( $self, @args ) = @_;
+
+ if (@args) {
+ push( @{ $software_types{ident $self} }, @args );
+ }
+
+ return $software_types{ident $self};
+
+}
+
+sub cache_user_supplied_arrays : RESTRICTED {
+
+ # If an ADF or AE accession has been specified, sort it out
+ # here. This method should not be called unless a user-supplied
+ # argument has been provided.
+ my ( $self, $filelist ) = @_;
+
+ ref $filelist eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $array_designs = {};
+
+ # Default supplied for cases where an accessionless ADF is used.
+ my $design_id = $self->get_array_accession() || 'user supplied';
+
+ # Set the design_ids so that all the files point to the user
+ # supplied design id.
+ foreach my $file (@$filelist) {
+ $file->set_array_design_id($design_id);
+ }
+
+ # Use the user-supplied ADF, overriding anything in the database.
+ if ( $self->get_adf_filename() ) {
+
+ $self->logprint( 'error', "ADF filename ", $self->get_adf_filename,
+ " supplied to script. Ignoring links to array designs experiment annotation.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+
+ # Get a reference to a hash with keys corresponding to
+ # period-delimited feature coordinates
+ if ( $self->get_skip_data_checks() ) {
+ print STDOUT ("Skipping ADF parsing.\n");
+ }
+ else {
+ my $adf_fh = IO::File->new( $self->get_adf_filename, '<' )
+ or croak( "Error: Unable to open ADF "
+ . $self->get_adf_filename
+ . ": $!\n" );
+
+ my $array = parse_adf(
+ $adf_fh,
+ undef,
+ $self->get_reporter_prefix(),
+ $self->get_compseq_prefix(),
+ );
+ $self->set_cached_array_design( $design_id, $array );
+ }
+ }
+
+ # Use a user-supplied ArrayExpress accession number, if available
+ elsif ( $self->get_array_accession() ) {
+
+ $self->logprint(
+ 'error',
+ "ArrayExpress accession number ",
+ $self->get_array_accession(),
+ " supplied to script. Ignoring links to array designs in MIAMExpress database.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+
+ # Parse the ADF
+ $self->get_ae_arraydesign({
+ accession => $self->get_array_accession,
+ });
+ }
+
+ else { warn("Warning: No array design associated with file.\n"); }
+
+ return;
+}
+
+sub set_cached_array_design : RESTRICTED {
+
+ my ( $self, $design_id, $array ) = @_;
+
+ $design_id or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ $array && $array->isa('ArrayExpress::Datafile::ArrayDesign')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Store the array design for later perusal.
+ $array_design_cache{ident $self}{$design_id} = $array;
+
+ return;
+}
+
+sub get_cached_array_design : RESTRICTED {
+
+ # Requires an array id
+ my ( $self, $design_id ) = @_;
+
+ $design_id or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Return undef if array not in cache.
+ return $array_design_cache{ident $self}{$design_id};
+}
+
+sub populate_file_arraydesigns : RESTRICTED {
+
+ my ( $self, $filelist ) = @_;
+
+ ref $filelist eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ DATAFILE:
+ foreach my $file (@$filelist) {
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Put the ADF features in the file hash. This links up two
+ # database queries: one datafile-based, the other array design
+ # id-based. The design_id is the key.
+ my $design_id = $file->get_array_design_id();
+
+ # Skip EXP files.
+ next DATAFILE if $file->get_is_exp();
+
+ if ( my $array = $self->get_cached_array_design( $design_id ) ) {
+ $file->set_array_design( $array );
+ }
+ }
+
+ return;
+}
+
+sub get_ae_arraydesign : RESTRICTED {
+
+ # Takes either an AE database id or an accession number (as part
+ # of the $args hashref), returns an ADF hashref as returned by
+ # Database::parse_adf().
+ my ($self, $args) = @_;
+
+ my ($temp_adf_fh, $accno);
+
+ # Attempt to get the ADF from AE, alongside the accession if only
+ # the database_id is known. We trap any errors here, and avoid
+ # them propagating further up the execution stack.
+ local $EVAL_ERROR;
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ eval {
+ ( $temp_adf_fh, $accno ) = retrieve_AE_adf(
+ $args->{database_id},
+ $args->{accession},
+ $self->log_fh('error'),
+ $self->get_skip_data_checks(),
+ );
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+
+ # Warn if the AE connection fails.
+ if ( $EVAL_ERROR ) {
+
+ print STDERR (
+ "Warning: Problem connecting to ArrayExpress:\n $EVAL_ERROR"
+ . " ...Attempting to continue...\n"
+ );
+
+ $self->logprint(
+ 'error',
+ "Warning: ADF retrieval from"
+ . " ArrayExpress failed: $EVAL_ERROR\n"
+ );
+
+ # Set an error, since now we can't check against the array
+ # design.
+ $self->add_error(
+ $CONFIG->get_ERROR_PARSEBAD()
+ );
+ }
+
+ # $temp_adf_fh is undef if we're skipping ADF checks.
+ elsif ( $temp_adf_fh ) {
+
+ my $array = parse_adf(
+ $temp_adf_fh,
+ $accno,
+ $self->get_reporter_prefix(),
+ $self->get_compseq_prefix(),
+ );
+
+ $self->set_cached_array_design( $accno, $array );
+ }
+ else {
+ print STDOUT ("Skipping ADF parsing.\n");
+ }
+
+ # Note the array accession for further checks later.
+ $self->add_arrayexpress_arrays($accno) if $accno;
+}
+
+sub _check_datafile_consistency : PRIVATE {
+
+ my ( $self, $filelist ) = @_;
+
+ # Sanity check of row and column counts across datafiles (raw, norm
+ # separately) Now checks for inconsistencies only within data files
+ # associated with a given array. Could check the column headings
+ # for consistency here also FIXME.
+ foreach my $type ( @{ $CONFIG->get_T2M_FILE_TYPES() } ) {
+ my ( $rowno, $colno );
+
+ DATA_FILE:
+ foreach my $file (@$filelist) {
+
+ # Don't bother comparing non-existent or read-protected
+ # files against everything else.
+ next DATA_FILE unless ( -r $file->get_path() );
+
+ # Only count file types from $CONFIG->get_T2M_FILE_TYPES().
+ next DATA_FILE
+ unless ( $file->get_data_type() eq $type );
+
+ # Leave out EXP files (these can still be raw, although
+ # they should be EXP).
+ next DATA_FILE if ( $file->get_is_exp() or $file->get_parse_errors() );
+
+ # Affy binary files are assumed okay; other binary file
+ # errors are reported elsewhere.
+ next DATA_FILE if ( $file->get_is_binary() );
+
+ # Count of data rows.
+ $rowno->{ $file->get_array_design_id() }{ $file->get_row_count() }++;
+
+ # Count of column headings.
+ my $column_count = scalar( @{ $file->get_column_headings } );
+ $colno->{ $file->get_array_design_id() }{$column_count}++;
+ }
+
+ # Count the number of different row and column counts across the
+ # data file set.
+ ROW_COUNT:
+ foreach my $array_rowno ( values %$rowno ) {
+ unless ($array_rowno) {
+ $self->logprint( 'error',
+ "Error: Data row counts not found in any files!\n" );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ next ROW_COUNT;
+ }
+ if ( scalar( grep { defined $_ } values %$array_rowno ) > 1 ) {
+ $self->logprint( 'error',
+ "Warning: Files of type \'$type\' have inconsistent numbers of data rows:\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ foreach my $rowcount ( keys %$array_rowno ) {
+ $self->logprint( 'error',
+ " $array_rowno->{$rowcount} files have $rowcount rows;\n"
+ );
+ }
+ }
+ }
+
+ COLUMN_COUNT:
+ foreach my $array_colno ( values %$colno ) {
+ unless ($array_colno) {
+ $self->logprint( 'error',
+ "Error: Data column counts not found in any files!\n" );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ next COLUMN_COUNT;
+ }
+ if ( scalar( grep { defined $_ } values %$array_colno ) > 1 ) {
+ $self->logprint( 'error',
+ "Warning: Files of type \'$type\' have inconsistent numbers of columns:\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ foreach my $colcount ( keys %$array_colno ) {
+ $self->logprint( 'error',
+ " $array_colno->{$colcount} files have $colcount columns;\n"
+ );
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+sub _check_exp_params : PRIVATE {
+
+ my ( $self, $exp_filelist ) = @_;
+
+ # List of parameters that can change
+ my @ignore_exp_params = (
+ 'Sample Type',
+ 'Description',
+ 'Chip Type',
+ 'Chip Lot',
+ 'Scan Date',
+ 'Hybridize Date',
+ 'Station',
+ 'Module',
+ 'Operator',
+ );
+
+ # Get a general list of params, divided by section, and an example
+ # value for each.
+ my %paramlist;
+ foreach my $file (@$exp_filelist) {
+ while ( my ($section, $data) = each %{ $file->get_exp_data() } ) {
+ while ( my ($param, $value) = each %{ $data } ) {
+
+ # We don't care which value ends up here, as long as it's in
+ # at least one of the files.
+ $paramlist{$section}{$param} = $value;
+ }
+ }
+ }
+
+ # Scan through the files looking for missing or different params
+ my %inconsistencies;
+ foreach my $file (@$exp_filelist) {
+ while ( my ($sectionname, $data) = each %paramlist ) {
+
+ my $expsection = $file->get_exp_data()->{$sectionname};
+
+ PARAM:
+ while ( my ($param, $value) = each %{ $data } ) {
+
+ next PARAM if (any { $param eq $_ } @ignore_exp_params);
+
+ unless ( $expsection && exists($expsection->{$param})
+ && ( $expsection->{$param} eq $value ) ) {
+ $inconsistencies{$sectionname}{$param}++;
+ }
+ elsif ( $expsection && exists($expsection->{$param}) ) {
+ $data->{$param} = $expsection->{$param};
+ }
+ }
+ }
+ }
+ return \%inconsistencies;
+}
+
+sub _check_features : PRIVATE {
+
+ # Accepts refs to array of data features and hash of ADF features
+ my ( $self, $data_features, $adf_features, $filename, $hyb_filename,
+ $type )
+ = @_;
+
+ # This subroutine only deals with feature coordinates in Generic
+ # MetaColumn/MetaRow format. GenePix Block/Column/Row formats are
+ # converted into this in Datafile->parse_datafile().
+
+ $type ||= 'Identifier';
+ $filename ||= q{};
+
+ my @missing_features;
+
+ foreach my $coord_key (@$data_features) {
+ push( @missing_features, $coord_key )
+ unless ( $adf_features->{$coord_key} );
+ }
+
+ # If there are any missing features, print them out
+ if ( scalar @missing_features ) {
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ $self->add_missing_features( $hyb_filename, \@missing_features );
+ $self->logprint( 'error',
+ "WARNING: ${type}s found in data file ${filename} which are missing from the ADF. See features log file.\n"
+ );
+ }
+
+ return scalar @missing_features; # Effectively an error code.
+}
+
+sub _check_duplicate_columns : PRIVATE {
+
+ my ( $self, $file, $hyb_filename ) = @_;
+
+ # Check for duplicate column headings
+ my $headings = $file->get_column_headings();
+ my $hyb_ids = $file->get_heading_hybs();
+ my %is_indexcol = map { $_ => 1 } @{ $file->get_index_columns() };
+ my %column_name_count;
+ foreach my $i ( 0..$#{ $headings } ) {
+ if ( $is_indexcol{$i} || ! defined $hyb_ids->[$i] ) {
+ $column_name_count{ $headings->[$i] }++;
+ }
+ else {
+ $column_name_count{ "$headings->[$i]($hyb_ids->[$i])" }++;
+ }
+ }
+ my @duplicates;
+ foreach my $heading ( keys %column_name_count ) {
+ if ( $column_name_count{$heading} > 1 ) {
+
+ push( @duplicates, $heading );
+
+ # We have to take an average of the nulls in the column to
+ # prevent negative % null values in our report
+ my $qt_metric = $file->get_data_metrics()->{$heading};
+ if ( $qt_metric && defined( $qt_metric->{notnull} ) ) {
+ $qt_metric->{notnull}
+ = $qt_metric->{notnull} / $column_name_count{$heading};
+ }
+ }
+ }
+ if (@duplicates) {
+ $self->add_duplicate_columns( $hyb_filename, \@duplicates );
+ $self->logprint(
+ 'error',
+ "ERROR: File ",
+ $file->get_name(),
+ " contains ",
+ scalar(@duplicates),
+ " duplicated columns. See column headings log file for details.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ return;
+}
+
+sub _check_duplicate_features : PRIVATE {
+
+ my ( $self, $file, $feature_coords, $hyb_filename ) = @_;
+
+ # Check for duplicate features in the data file
+ my $dupe_feature_check;
+ foreach my $feature (@$feature_coords) {
+ $dupe_feature_check->{$feature}++;
+ }
+
+ my @features;
+ if ( max( values %$dupe_feature_check ) > 1 ) { # We've got trouble
+
+ $self->logprint( 'error',
+ "ERROR: Duplicate features found in data file ",
+ $file->get_name(), ". See features log file.\n" );
+
+ # FIXME so that reporters/CSs are not flagged in this way?
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ foreach my $feature ( sort keys %$dupe_feature_check ) {
+ push( @features, $feature )
+ if ( $dupe_feature_check->{$feature} > 1 );
+ }
+ $self->add_duplicate_features( $hyb_filename, \@features );
+ }
+
+ return;
+}
+
+sub _compare_index_to_design : PRIVATE {
+
+ my ( $self, $file, $feature_coords, $hyb_filename ) = @_;
+
+ my $array_design = $file->get_array_design();
+
+ # Check that there's actually an array design for this file (not
+ # so in some Standalone mode checks).
+ unless ( $array_design ) {
+ $self->logprint(
+ 'error',
+ "Warning: Data file not associated with an array design: ",
+ $file->get_name(),
+ ". Skipping identifier check.\n"
+ );
+
+ # Block downstream processing on no check
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+
+ return;
+ }
+
+ # Feature coordinates. If only one index column, we assume it is an
+ # identifier column (Reporter or CS).
+ if ( scalar( @{ $file->get_index_columns() } ) == 1 ) {
+
+ # Double-check that we've got what we think we've got.
+ my $identifiers;
+ my $allowed = $CONFIG->get_T2M_INDICES();
+ my $identifier_heading
+ = $file->get_column_headings()->[ $file->get_index_columns()->[0] ];
+
+ if ( $identifier_heading =~ m/$allowed->{FGEM}[0]/i ) {
+ $identifiers = $array_design->get_reporters();
+ }
+ elsif ($identifier_heading =~ m/$allowed->{FGEM_CS}[0]/i
+ || $identifier_heading =~ m/$allowed->{AffyNorm}[0]/i
+ || $identifier_heading =~ m/$allowed->{GEO}[0]/i ) {
+ $identifiers = $array_design->get_compseqs();
+ }
+ else {
+ die("Internal script error: File ",
+ $file->get_name(),
+ " has unrecognized "
+ . $file->get_format_type()
+ . " column heading: $identifier_heading\n"
+ );
+ }
+
+ my $num_identifiers = scalar( grep { defined $_ } values %$identifiers );
+ if ($num_identifiers) {
+ $self->_check_features(
+ $feature_coords, $identifiers, $file->get_name(),
+ $hyb_filename, 'Identifier'
+ );
+
+ # Check that at least 95% of the array identifiers are present.
+ # We may want to relax this independently of Features, below.
+ if ( $#$feature_coords < ( $num_identifiers * 0.95 ) ) {
+ my $pc = ( $#$feature_coords / $num_identifiers ) * 100;
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Warning: Data file %s has fewer identifiers than expected from the array design (%d%%).\n",
+ $file->get_name(), $pc
+ )
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ }
+ else {
+ $self->logprint(
+ 'error',
+ "Warning: No suitable design element identifiers retrieved for file ",
+ $file->get_name(),
+ ". Skipping identifier check.\n"
+ );
+
+ # Block downstream processing on no check
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ elsif ( scalar( @{ $file->get_index_columns() } ) > 1 ) {
+
+ my $adf_features = $array_design->get_features();
+
+ my $num_adf_features
+ = scalar( grep { defined $_ } values %{ $adf_features } );
+
+ if ($num_adf_features) {
+ my $rc = $self->_check_features(
+ $feature_coords,
+ $adf_features,
+ $file->get_name(),
+ $hyb_filename,
+ 'Feature'
+ );
+
+ # Check that at least 95% of the array features are present
+ if ( $#$feature_coords < ( $num_adf_features * 0.95 ) ) {
+ my $pc = ( $#$feature_coords / $num_adf_features ) * 100;
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Warning: Data file %s has fewer features than expected from the array design (%d%%).\n",
+ $file->get_name(), $pc
+ )
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ }
+ else {
+ $self->logprint( 'error',
+ "Warning: No ADF features retrieved for file ",
+ $file->get_name(), ". Skipping feature check.\n" );
+
+ # No checks? not good to go, then
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ else { # This really, really should never happen
+ die( "Internal script error: file ",
+ $file->get_name(), " has a negative number of index columns!\n" );
+ }
+
+ return;
+}
+
+sub check_date_format : RESTRICTED {
+
+ # Check date against the AE required format, generate warnings if bad.
+ my ( $self, $date, $type ) = @_;
+
+ my $AE_dateformat = qr/\A \d{4}-\d{2}-\d{2} \z/xms;
+
+ my $datetype = defined ( $type )
+ ? "Experiment $type date"
+ : 'Date';
+
+ if ( $date ) {
+ if ( $date !~ $AE_dateformat ) {
+ $self->logprint(
+ 'error',
+ "Warning: $datetype ($date) not in ArrayExpress format (YYYY-MM-DD).\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+ else {
+ $self->logprint(
+ 'error',
+ "Warning: $datetype has not been set.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+
+ return;
+}
+
+sub check_mage_bmchars : RESTRICTED {
+
+ # Check a list of MAGE BioMaterials to assess whether they're
+ # sufficiently annotated.
+
+ my ( $self, $bm_list ) = @_;
+
+ foreach my $material ( @{ $bm_list || [] } ) {
+ my $bmc_list = $material->getCharacteristics();
+
+ # Less than 3 is the warning level (arbitrary)
+ if ( ! $bmc_list || ( scalar @$bmc_list < 3 ) ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Material "%s" may not be fully annotated.\n},
+ $material->getName(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+
+ return;
+}
+
+sub check_mage_labeledextracts : RESTRICTED {
+
+ # Check that all LabeledExtracts have an associated Label.
+
+ my ( $self, $les ) = @_;
+
+ foreach my $le ( @{ $les } ) {
+ my $labels = $le->getLabels() || [];
+
+ unless ( scalar @{ $labels } ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Labeled Extract "%s" is not associated with a Label.\n},
+ $le->getName(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ return;
+}
+
+sub generate_workflow_from_bags : RESTRICTED {
+
+ my ( $self, $bags, $classes ) = @_;
+
+ # Generate a very basic workflow from the mage bags.
+ # $classes is an arrayref of single-element hashrefs.
+
+ # Set a decent page margin at which to start.
+ my $margin = 1;
+
+ while ( my $instance = shift @{ $classes || [] } ) {
+
+ # NB one element only here!
+ my ( $thing, $name ) = each %$instance;
+
+ my $count = $#{ $bags->{$thing}->() } + 1;
+
+ if ($count) {
+ $self->logprint(
+ 'workflow',
+ ( ( " " x $margin ), "$count $name --> \n\n" ),
+ );
+ $margin++;
+ }
+ }
+
+ $self->logprint(
+ 'workflow',
+ ( ( " " x $margin ), "Done.\n" ),
+ );
+
+ return;
+}
+
+sub check_mage_treatments_protapps : RESTRICTED {
+
+ my ( $self, $materials, $type ) = @_;
+
+ foreach my $obj ( @{ $materials } ) {
+ foreach my $treatment ( @{ $obj->getTreatments() || [] } ) {
+
+ my $found;
+ foreach my $pa ( @{ $treatment->getProtocolApplications() || [] } ) {
+ $found++ if $pa->getProtocol();
+ }
+ unless ( $found ) {
+ my $actionvalue = $treatment->getAction()->getValue();
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: %s "%s" lacks a %s treatment protocol.\n},
+ $type,
+ $obj->getName(),
+ $actionvalue,
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+ }
+
+ return;
+}
+
+sub check_mage_hyb_protapps : RESTRICTED {
+
+ my ( $self, $pbas ) = @_;
+
+ foreach my $pba ( @{ $pbas } ) {
+
+ # Check hyb protocol.
+ if ( my $bac = $pba->getBioAssayCreation() ) {
+ my $found;
+ foreach my $pa ( @{ $bac->getProtocolApplications() || [] } ) {
+ $found++ if $pa->getProtocol();
+ }
+ unless ( $found ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Hybridization "%s" lacks a hybridization protocol.\n},
+ $pba->getName(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+ else {
+
+ # This is actually quite serious, as all hyb PBAs _must_ have a BAC.
+ $self->logprint(
+ 'error',
+ qq{Warning: Hybridization PBA lacks a BioAssayCreation object.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ return;
+}
+
+sub compare_fvs_to_bmcs : RESTRICTED {
+
+ my ( $self, $fvhashlist, $bmchashlist ) = @_;
+
+ # Takes lists of hashrefs, each hashref containing a single
+ # category => value pair, compares them and logs the results if
+ # there's a BMC variable not reflected in the FVs.
+
+ my %listed_as_variable;
+ foreach my $pair ( @{ $fvhashlist } ) {
+ while ( my ( $cat, $val ) = each %{ $pair } ) {
+ $listed_as_variable{$cat} = 1;
+ }
+ }
+
+ my %bmc_valuecount;
+ foreach my $pair ( @{ $bmchashlist } ) {
+ while ( my ( $cat, $val ) = each %{ $pair } ) {
+ $bmc_valuecount{$cat}{$val}++;
+ }
+ }
+
+ while ( my ( $cat, $valhash ) = each %bmc_valuecount ) {
+
+ # More than one value constitutes a variable.
+ if ( scalar(grep { defined $_ } values %{ $valhash }) > 1 ) {
+ unless ( $listed_as_variable{$cat} ) {
+ $self->logprint(
+ 'error',
+ qq{Warning: Experimental variable "$cat" is not listed as a FactorValue.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Logger.pm b/lib/ArrayExpress/Curator/Logger.pm
new file mode 100644
index 0000000..5c499cf
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Logger.pm
@@ -0,0 +1,409 @@
+#!/usr/bin/env perl
+#
+# Superclass to provide basic logging facilities to
+# ExperimentChecker.pm and Tab2MAGE.pm.
+#
+# Tim Rayner 2006, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id$
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Logger
+
+=head1 SYNOPSIS
+
+ use base qw/ArrayExpress::Curator::Logger/;
+
+=head1 DESCRIPTION
+
+This module is subclassed by ExperimentChecker and Tab2MAGE classes to
+provide them both with logging and error recording facilities.
+
+=head1 OPTIONS
+
+The objects created by this module can be instantiated with the
+following options.
+
+=over 2
+
+=item C<log_to_current_dir>
+
+A flag indicating whether to write log files to the current directory
+or not. Note that this has been overloaded such that any true value is
+now interpreted as the name of a directory into which to write.
+
+=item C<clobber>
+
+A flag indicating whether to silently overwrite old logs or not.
+
+=item C<progname>
+
+A program name string, used in log headers.
+
+=item C<version>
+
+A version string, used in log headers.
+
+=back
+
+=head1 PUBLIC METHODS
+
+=over 2
+
+=item C<add_error( $error )>
+
+Takes an integer, bitwise ORs it with the object's internally-stored
+error, records and returns the result. Used to store the overall error
+code determining suitability for MAGE-ML export. The final value is
+accessible using get_error().
+
+=item C<logfiles( $type )>
+
+Given the desired log file type, returns the desired filename
+template. Otherwise, returns a list of log file types.
+
+=item C<log_fh( $type )>
+
+Given the desired log file type, returns the opened filehandle.
+
+=item C<logprint( $type, $message )>
+
+Given the desired log file type and a message, prints the message to
+that log file. Linebreaks are not added to the output.
+
+=item C<logprint_line( $type, $message )>
+
+Given the desired log file type and a message, prints the message as
+part of a section-delimiting line in the log file. In this case
+linebreaks are added to the output.
+
+=item C<localize_logfiles( $args )>
+
+Given an optional hashref argument (keys: "volume", "directory",
+"name"), attempts to localize the logfiles to the directory structure
+given. Overridden by the log_to_current_dir attribute when setting log
+file location.
+
+=back
+
+=head1 AVAILABLE LOG TYPES AND TEMPLATES
+
+=over 2
+
+=item error => 'expt_error.log',
+
+=item report => 'expt_report.log',
+
+=item workflow => 'expt_biomaterials.log',
+
+=item feature => 'expt_feature.log',
+
+=item columns => 'expt_columnheadings.log',
+
+=item sample => 'expt_samples.log',
+
+=item protocol => 'expt_protocols.log',
+
+=item miame => 'expt_miame.log',
+
+=item aedw => 'expt_aedw.log',
+
+=item tab2mage => 'tab2mage.log',
+
+=item magetab => 'magetab.log',
+
+=back
+
+=head1 SEE ALSO
+
+=over 1
+
+=item L<ArrayExpress::Curator::ExperimentChecker>
+
+=item L<ArrayExpress::Curator::Tab2MAGE>
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Logger;
+
+use strict;
+use warnings;
+
+use Carp;
+use File::Spec;
+use File::Copy;
+use Class::Std;
+use Readonly;
+
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my (%logfiles, %log_fh) : ATTRS;
+
+# Attributes available for initialization.
+my %log_to_current_dir : ATTR( :get<log_to_current_dir>, :init_arg<log_to_current_dir>, :default<undef> );
+my %clobber : ATTR( :name<clobber>, :default<undef> );
+my %progname : ATTR( :name<progname>, :default<undef> );
+my %version : ATTR( :name<version>, :default<undef> );
+
+# Accumulative accessors; each of these has an "add_" method defined below.
+my %error : ATTR( :get<error>, :default<0> );
+
+Readonly my $REPORT_WIDTH => 80;
+
+sub add_error {
+
+ # Takes an integer, bitwise ORs it with $error{ident $self},
+ # stores and returns the result. Used to store the overall error
+ # code determining suitability for MAGE-ML export.
+
+ my ( $self, $err ) = @_;
+
+ $error{ident $self} ||= 0;
+ if ($err) { $error{ident $self} |= $err }
+
+ return $error{ident $self};
+}
+
+sub logfiles {
+
+ # Given an argument, returns the desired filename
+ # template. Otherwise, returns a list of log file types.
+
+ my ( $self, $wanted ) = @_;
+
+ # Log files. The following are just templates. The files will be
+ # written in the top-level directory for the submission unles the -p
+ # command-line option is supplied.
+ my %filenames = (
+ error => 'expt_error.log',
+ report => 'expt_report.log',
+ workflow => 'expt_biomaterials.log',
+ feature => 'expt_feature.log',
+ columns => 'expt_columnheadings.log',
+ sample => 'expt_samples.log',
+ protocol => 'expt_protocols.log',
+ miame => 'expt_miame.log',
+ aedw => 'expt_aedw.log',
+ tab2mage => 'tab2mage.log',
+ magetab => 'magetab.log',
+ );
+
+ # Fill in any blanks in our logfile list
+ @filenames{ keys %{ $logfiles{ident $self} } }
+ = values %{ $logfiles{ident $self} };
+
+ $logfiles{ident $self} = \%filenames;
+
+ if ($wanted) {
+ unless ( $logfiles{ident $self}{$wanted} ) {
+ croak(
+ qq{Error: Log file of type "$wanted" has no predefined filename.\n}
+ );
+ }
+ else {
+ return $logfiles{ident $self}{$wanted};
+ }
+ }
+ else {
+ return [ keys %{ $logfiles{ident $self} } ];
+ }
+}
+
+sub log_fh {
+
+ # Requires a log file type argument, returns the opened filehandle.
+
+ my ( $self, $wanted ) = @_;
+
+ $wanted or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ $log_fh{ident $self} ||= {};
+
+ unless ( $log_fh{ident $self}{$wanted} ) {
+
+ # Some of our log files we prefer without a header. This is
+ # especially true if the file is normally empty (file size 0) and
+ # only written to upon error.
+ my %no_headers = ( columns => 1, feature => 1 );
+
+ my @open_params =
+ $no_headers{$wanted}
+ ? ()
+ : ( $self->get_progname(), $self->get_version() );
+
+ my $filename = $self->logfiles($wanted)
+ or croak(qq{Error: Undefined log file type "$wanted".\n});
+
+ my $fh = $self->open_log_file( $filename, @open_params );
+
+ croak("Error: File $filename exists. User canceled script execution.")
+ unless $fh;
+
+ $log_fh{ident $self}{$wanted} = $fh;
+ }
+
+ return $log_fh{ident $self}{$wanted};
+
+}
+
+sub logprint {
+
+ my ( $self, $type, @messages ) = @_;
+
+ print { $self->log_fh($type) }(@messages);
+
+ return;
+}
+
+sub logprint_line {
+
+ my ( $self, $type, @messages ) = @_;
+
+ my $message = join(q{}, @messages);
+ my $line = $message
+ ? q{---} . " $message "
+ . (q{-} x ($REPORT_WIDTH - (length($message) + 5)))
+ : q{-} x $REPORT_WIDTH;
+ $self->logprint($type, "$line\n");
+
+ return;
+}
+
+sub change_logfilename : PRIVATE {
+
+ my ( $self, $type, $filename ) = @_;
+
+ $type or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ $filename or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ $logfiles{ident $self}{$type} = $filename;
+
+ return;
+}
+
+sub localize_logfiles {
+
+ my ( $self, $args ) = @_;
+
+ $args ||= {};
+
+ # Localize to current directory unless told otherwise.
+ $args->{directory} ||= q{.};
+
+ # Only change the logfile names (not the whole path) if we want
+ # them in the current working directory anyway.
+ if ( defined($self->get_log_to_current_dir) ) {
+ $args->{directory} = $self->get_log_to_current_dir || q{.};
+ }
+
+ foreach my $logtype ( @{ $self->logfiles } ) {
+
+ # Insert string into each logfile name.
+ my $filename = $self->logfiles($logtype);
+ if ( $args->{name} ) {
+ $filename =~ s/(expt\_)(.*.log)/$1$args->{name}\_$2/;
+ }
+ $self->change_logfilename(
+ $logtype,
+ File::Spec->catpath(
+ $args->{volume},
+ $args->{directory},
+ $filename
+ )
+ );
+ }
+
+ return;
+}
+
+sub open_log_file : PRIVATE {
+ my ( $self, $file, $programname, $version ) = @_;
+
+ # Test for pre-existing file, act as necessary.
+ if ( -e $file ) {
+
+ # Handle the clobber option.
+ unless ( $self->get_clobber() ) {
+ print STDERR ( "\n\nFile \'$file\' aleady exists. "
+ . "Overwrite? [Y(es)\/N(o)\/A(ll)]\n" );
+ chomp( my $answer = lc <STDIN> );
+
+ SWITCH:
+ {
+ $answer eq 'y' && do { last SWITCH; };
+ $answer eq 'a' && do { $self->set_clobber(1); last SWITCH; };
+ return;
+ }
+ }
+
+ # Create a backup of the previous file (delete old backup first).
+ if ( -e "$file.bak" ) {
+ unlink "$file.bak" or die(
+ "Error: Could not overwrite old backup log file $file.bak: $!\n"
+ );
+ }
+ copy( $file, "$file.bak" )
+ or die("Error: Could not create backup log file $file.bak: $!\n");
+ }
+ my $filehandle = IO::File->new( $file, '>' )
+ or croak("Error: Could not open file $file: $!\n");
+
+ # Set logs for unbuffered output (I'm tired of waiting...)
+ $filehandle->autoflush();
+
+ # Print a header
+ if ( defined($version) ) {
+ my $title = "$programname version $version log file output "
+ . date_now();
+ print $filehandle (
+ "$title\n" . ( "=" x ( length($title) ) ) . "\n\n" );
+ }
+
+ return ( $filehandle );
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/MAGE.pm b/lib/ArrayExpress/Curator/MAGE.pm
new file mode 100644
index 0000000..b3bea89
--- /dev/null
+++ b/lib/ArrayExpress/Curator/MAGE.pm
@@ -0,0 +1,2504 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic access to MAGE objects and MAGE-ML files
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: MAGE.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Curator::MAGE;
+
+use strict;
+use warnings;
+
+use Carp;
+use Bio::MAGE qw(:ALL);
+
+# Used for checking FVs (Value vs. Measurement)
+use Scalar::Util qw(looks_like_number);
+use List::Util qw(first);
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Common qw(decamelize);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $OE_CAT_ACTION
+ $OE_CAT_BIOSAMPLETYPE
+ $OE_CAT_MATERIALTYPE
+ $OE_CAT_PROTOCOLTYPE
+ $OE_CAT_SOFTWARETYPE
+ $OE_CAT_HARDWARETYPE
+ $OE_CAT_DERIVEDBIOASSAYTYPE
+ $OE_CAT_EXPERIMENTALFACTORCATEGORY
+ $OE_CAT_DATATYPE
+ $OE_CAT_SCALE
+ $OE_CAT_RELEASEDATE
+ $OE_CAT_SUBMISSIONDATE
+ $OE_CAT_ROLE
+ $OE_VAL_SYNTHETICDNA
+ $OE_VAL_TOTALRNA
+ $OE_VAL_GENOMICDNA
+ $OE_VAL_WHOLEORGANISM
+ $OE_VAL_ORGANISMPART
+ $OE_VAL_EXTRACT
+ $OE_VAL_NOTEXTRACT
+ $OE_VAL_POOL
+ $OE_VAL_SPECIFIEDBIOMATERIALACTION
+ $OE_VAL_NUCLEICACIDEXTRACTION
+ $OE_VAL_IMMUNOPRECIPITATE
+ $OE_VAL_LABELING
+ $OE_VAL_FLOAT
+ $OE_VAL_UNKNOWN
+ $OE_VAL_LINEARSCALE
+ $AE_LABELCOMPOUND_PREFIX
+ $AE_CHANNEL_PREFIX
+ $MAGE_UNITS
+);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ read_mage
+ write_mage
+ unique_identifier
+ postprocess_mage
+ make_container
+ new_ontologyentry
+ new_person
+ new_biosource
+ new_biosample
+ new_extract
+ new_immunoprecipitate
+ new_labeledextract
+ new_labelcompound
+ new_image
+ new_hybridization
+ new_scan
+ new_physicalbioassay
+ new_featureextraction
+ new_measuredbioassay
+ new_measuredbioassaydata
+ new_derivedbioassay
+ new_producertransformation
+ new_derivedbioassaydata
+ new_bioassaymap
+ new_quantitationtypemap
+ new_protocol
+ new_parameter
+ new_hardwareapplication
+ new_software
+ new_hardware
+ new_armanuf
+ new_array
+ new_factorvalue
+ new_experimentalfactor
+ new_quantitationtype
+ new_qtd
+ add_summary_stats
+ update_factorvalues
+);
+
+# Internal identifier counter
+my $ID_Count = 0;
+
+#
+# make_container
+#
+# Takes: coderef to a sub which returns a value to be entered uniquely in the closure hash
+# flag indicating whether unique identifiers should be passed to that coderef
+#
+# Returns: coderef to a wrapper sub incorporating a closure around the original coderef
+#
+
+# We *need* to postpone @_ unpacking here as part of the closure
+# creation. We therefore turn off Perl::Critic carping about this:
+
+## no critic RequireArgUnpacking
+sub make_container {
+
+ my ( $coderef, $pass_identifier ) = splice( @_, 0, 2 );
+
+ my $hash = {};
+
+ # It's actually important to use @_ in the following anon sub
+ # declaration, rather than some copy of whatever arguments were
+ # passed into this function at container creation time.
+ return
+ sub { _container_closure( $hash, $coderef, $pass_identifier, @_ ) };
+
+}
+## use critic RequireArgUnpacking
+
+#
+# _container_closure
+#
+# Takes: hashref for storing values
+# coderef to sub returning those values
+# flag indicating whether to pass the identifier through
+# identifier for the value returned by coderef
+# [list of arguments passed to coderef]
+#
+# Returns: A unique value returned by coderef and stored in hashref container
+#
+# * If no arguments are supplied to be passed to the coderef, *
+# * all the values (objects?) in the container are returned. *
+#
+sub _container_closure {
+
+ my ( $hash, $coderef, $pass_identifier, @args ) = @_;
+
+ # Return all the objects (unsorted) if no arguments.
+ return [ values %$hash ] unless scalar @args;
+
+ my $identifier = shift @args;
+
+ # Must have an identifier here. Confess for better debugging.
+ confess("Internal Error: Undefined local identifier passed to container function.\n")
+ unless defined($identifier);
+
+ # If identifier passed but no attributes hashref, just return the
+ # desired object without updating it.
+ if ( ! scalar @args || ( ref $args[0] ne 'HASH' ) ) {
+
+ if ( $args[0] && $args[0] eq 'delete' ) {
+
+ # Remove the object from the container.
+ my $rc = defined($hash->{$identifier});
+ delete $hash->{$identifier};
+
+ return $rc;
+ }
+ else {
+
+ # Query lookup for the identified object. This does not
+ # attempt to update the object (pass a hashref in @args to update).
+ return $hash->{$identifier};
+ }
+ }
+
+ # Add a pre-existing object to the passed arguments if found.
+ if ( $hash->{$identifier} ) {
+ push( @args, ( $hash->{$identifier} ) );
+ }
+
+ # Add back the identifier to @args if required
+ unshift( @args, $identifier ) if $pass_identifier;
+
+ # Get a new object, or an updated object if we found an old one
+ my @objects = ( $coderef->(@args) );
+
+ # Store the object in the container
+ $hash->{$identifier} = $objects[0];
+
+ return wantarray ? @objects : $objects[0];
+
+}
+
+sub unique_identifier {
+
+ return ++$ID_Count;
+
+}
+
+sub read_mage {
+
+ my ( $magepath, $dtd, $verbose ) = @_;
+
+ # Invoke our XML::Reader object
+ require Bio::MAGE::XML::Reader;
+ my $sax1 = 1;
+
+ # my $verbose = 5;
+ my $logfh = \*STDERR;
+ my $external_data = 1;
+ my $reader = Bio::MAGE::XML::Reader->new(
+ sax1 => $sax1,
+ verbose => $verbose,
+ log_file => $logfh,
+ external_data => $external_data,
+ );
+
+ my $dir = ( File::Spec->splitpath($magepath) )[1];
+ my $localdtd
+ = $dir ? File::Spec->catfile( $dir, "MAGE-ML.dtd" ) : "MAGE-ML.dtd";
+
+ my $dtd_delete_flag;
+ unless ( -e $localdtd ) {
+ print STDOUT ("Linking $dtd to $localdtd\n");
+ symlink( $dtd, $localdtd )
+ or croak("Error symlinking DTD file: $!\n");
+ $dtd_delete_flag++;
+ }
+
+ # Actually read in the file
+ my $mage = $reader->read($magepath);
+
+ # Remove the MAGE-ML.dtd symlink, if we created it
+ if ( $dtd_delete_flag and -l $localdtd ) {
+ print STDOUT ("Deleting $localdtd symlink.\n");
+ unlink($localdtd) or croak("Error removing $localdtd symlink: $!\n");
+ }
+ return $mage;
+}
+
+sub write_mage {
+
+ my ( $mage, $fh, $dataformat, $version ) = @_;
+
+ require Bio::MAGE::XML::Writer;
+ $version ||= "1.1";
+ $dataformat ||= "tab delimited";
+
+ my $writer = Bio::MAGE::XML::Writer->new(
+ fh => $fh,
+ external_data => 1,
+ data_format => $dataformat,
+ cube_holds_path => 1,
+ public_id => "-//OMG//DTD MAGE-ML $version//EN",
+ );
+ $writer->write($mage);
+
+ return;
+}
+
+sub postprocess_mage {
+ my ( $ded_fh, $old_fh, $new_fh, $dummy_id, $data_format ) = @_;
+
+ foreach my $filehandle ( $ded_fh, $old_fh, $new_fh ) {
+ if ($filehandle) {
+ seek( $filehandle, 0, 0 )
+ or croak("Error rewinding filehandle: $!\n");
+ }
+ }
+
+ my $error = 0;
+
+ MAGE_LINE:
+ while ( my $line = <$old_fh> ) {
+
+ # Skip over any dummy FeatureDimensions we've been told about
+ if ( $dummy_id
+ && $line =~ m!<FeatureDimension identifier="$dummy_id"! ) {
+ while ( my $skipped = <$old_fh> ) {
+ next MAGE_LINE if ( $skipped =~ m!</FeatureDimension>! );
+ }
+ }
+
+ # Insert our DEDs just before the close tag
+ if ( $ded_fh && $line =~ m!</DesignElementDimension_assnlist>! ) {
+ while ( my $fdline = <$ded_fh> ) {
+ print $new_fh $fdline or $error++;
+ }
+ }
+
+ if ( my ($filename)
+ = ( $line =~ m/DataExternal filenameURI="([^\"]+)"/ ) ) {
+ if ( $data_format->{$filename} ) {
+ until ( $line
+ =~ s/dataFormat="[^\"]+"/q{dataFormat="}.$data_format->{$filename}.q{"}/e
+ ) {
+ print $new_fh $line;
+ $line = <$old_fh>;
+ }
+ }
+ }
+
+ print $new_fh $line or $error++;
+ }
+
+ return !$error;
+
+}
+
+sub new_ontologyentry {
+
+ my ($args) = @_;
+
+ my $obj = Bio::MAGE::Description::OntologyEntry->new(
+ category => $args->{category},
+ value => $args->{value}
+ );
+
+ # OntologyReference
+ ( $args->{database_ref} && $args->{database_accession} ) && do {
+ my $databaseentry = Bio::MAGE::Description::DatabaseEntry->new(
+ database => $args->{database_ref},
+ accession => $args->{database_accession}
+ );
+ $obj->setOntologyReference($databaseentry);
+ };
+
+ return $obj;
+
+}
+
+sub _parse_person_name {
+
+ my $namestring = shift;
+
+ # Parse out the first and last names, and middle initials.
+ # N.B. this parsing isn't perfect, but will deal with things
+ # like "Mary Ann F.X. van Doone"
+
+ # Strip surrounding whitespace:
+ $namestring =~ s/\s*(.*?)\s*/$1/;
+
+ # Allow "Rayner, Tim F." form:
+ $namestring =~ s/(.*)\s*\,\s*(.*)/$2 $1/;
+ my @name_elements = split /[\.\s]+/, $namestring;
+
+ foreach my $part (@name_elements) { $part =~ s/[\. ]//g; }
+
+ my $last = pop @name_elements;
+ my $first = shift @name_elements;
+
+ NAME_PART:
+ while ( my $part = shift(@name_elements) ) {
+
+ # Hopefully this will take care of van Whatever
+ unless ( ( length($part) == 1 ) || ( $part eq lc($part) ) ) {
+ $first = "$first $part";
+ }
+ else {
+ unshift( @name_elements, $part );
+ last NAME_PART;
+ }
+ }
+
+ NAME_PART:
+ while ( my $part = pop(@name_elements) ) {
+ unless ( length($part) == 1 ) {
+ $last = "$part $last";
+ }
+ else {
+ push( @name_elements, $part );
+ last NAME_PART;
+ }
+ }
+
+ my $midinitials;
+ while ( my $part = shift(@name_elements) ) {
+ $midinitials .= "$part.";
+ }
+
+ return ( $first, $midinitials, $last );
+
+}
+
+sub new_person {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::AuditAndSecurity::Person->new unless $obj;
+
+ # Take the name if given, otherwise just the email address;
+ # otherwise croak.
+ my $name_id;
+ my ( $first, $midinitials, $last );
+ if ( $args->{name} ) {
+ ( $first, $midinitials, $last ) = _parse_person_name($args->{name});
+ $name_id = "$first";
+ $name_id .= " $midinitials" if $midinitials;
+ $name_id .= " $last";
+ }
+ elsif ( $args->{email} ) {
+ $name_id = $args->{email};
+ }
+ else {
+ croak("Error: Neither person name nor email address set.");
+ }
+
+ # Identify the Person object (limit name part of identifier to 50
+ # chars for AE database column safety).
+ my $identifier = sprintf(
+ "%s:%s.Person",
+ $args->{namespace},
+ substr($name_id, 0, 50),
+ );
+ $obj->setIdentifier($identifier)
+ unless $obj->getIdentifier;
+
+ # Sort out the name if available.
+ $last && do {
+ $obj->setLastName($last) unless $obj->getLastName;
+ };
+ $first && do {
+ $obj->setFirstName($first) unless $obj->getFirstName;
+ };
+ $midinitials && do {
+ $obj->setMidInitials($midinitials) unless $obj->getMidInitials;
+ };
+
+ # Email, role and organization.
+ $args->{email} && do {
+ $obj->setEmail( $args->{email} ) unless $obj->getEmail;
+ };
+ $args->{role} && do {
+ my $found;
+ if ( my $preexisting_roles = $obj->getRoles ) {
+ my $new_role = $args->{role};
+ $found = first { $_->getValue eq $new_role } @$preexisting_roles;
+ }
+ unless ($found) {
+ my $role = new_ontologyentry(
+ { category => $OE_CAT_ROLE,
+ value => $args->{role}
+ }
+ );
+ $obj->addRoles($role);
+ }
+ };
+ ( $args->{organization} || $args->{address} ) && do {
+
+ # Use just the first 50 characters in identifier creation to
+ # avoid AE database column limits.
+ my $identifier = sprintf(
+ "%s:%s.Organization",
+ $args->{namespace},
+ substr(( $args->{organization} || $args->{address} ), 0, 50),
+ );
+ my $organization = Bio::MAGE::AuditAndSecurity::Organization->new(
+ identifier => $identifier,
+ name => ( $args->{organization} || $args->{address} ),
+ address => $args->{address},
+ );
+ $obj->setAffiliation($organization) unless $obj->getAffiliation;
+ $obj->setAddress( $args->{address} ) unless $obj->getAddress;
+ };
+
+ return $obj;
+}
+
+sub _check_pooling {
+
+ my ( $obj, $protocol, $identifier_template ) = @_;
+
+ return unless ( $obj && $protocol );
+
+ # Count our biomaterials
+ my %bmm_hash;
+ foreach my $treatment ( @{ $obj->getTreatments() || [] } ) {
+
+ # Check the protocol hasn't already been applied in this treatment set
+ my $found;
+ if ( my $preexisting_protclapps
+ = $treatment->getProtocolApplications ) {
+ my $new_identifier = $protocol->getIdentifier;
+ $found
+ = first { $_->getProtocol()->getIdentifier eq $new_identifier }
+ @$preexisting_protclapps;
+ }
+ return if ( $found );
+
+ # Make a hash of the BioMaterialMeasurements
+ if ( my $source_bmms = $treatment->getSourceBioMaterialMeasurements )
+ {
+ foreach my $bmm (@$source_bmms) {
+ $bmm_hash{ $bmm->getBioMaterial()->getIdentifier } = $bmm;
+ }
+ }
+ }
+
+ # Add the new treatment (with each BMM) if more than one BMM
+ my $treatments;
+ if ( scalar( grep { defined $_ } values %bmm_hash ) > 1 ) { # More than one BMM
+ $treatments = new_treatments(
+ { protocols => [$protocol],
+ identifier_template => $identifier_template,
+ default_action => $OE_VAL_POOL,
+ }
+ );
+
+ # Correct the order of the treatment.
+ foreach my $treatment (@$treatments) {
+ $treatment->setOrder(0); # This appears to be legal
+ }
+
+ # Put every BMM in the pooling treatment
+ foreach my $bmm ( values %bmm_hash ) {
+ $treatments = _update_treatments( $treatments, $bmm );
+ }
+ }
+
+ return $treatments;
+}
+
+sub new_labelcompound {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioMaterial::Compound->new unless $obj;
+
+ $obj->setIdentifier( $AE_LABELCOMPOUND_PREFIX . $args->{dye} )
+ unless $obj->getIdentifier;
+
+ $obj->setName( $args->{dye} ) unless $obj->getName;
+
+ if ( $args->{indices} ) {
+ foreach my $new ( @{ $args->{indices} } ) {
+ my $found;
+ if ( my $preexisting = $obj->getCompoundIndices() ) {
+ my $new_cat = $new->getCategory();
+ my $new_val = $new->getValue();
+ $found = first {
+ $_->getCategory() eq $new_cat
+ && $_->getValue() eq $new_val
+ } @$preexisting;
+ }
+ $obj->addCompoundIndices( $new ) unless $found;
+ }
+ }
+
+ return ($obj);
+
+}
+
+sub new_labeledextract {
+
+ my ( $name, $args, $obj ) = @_;
+
+ # Set some default values.
+ $args->{material_type} ||= $OE_VAL_SYNTHETICDNA;
+
+ # Create a new object if we don't already have one
+ $obj = Bio::MAGE::BioMaterial::LabeledExtract->new unless $obj;
+
+ # Identifier, Name , MaterialType
+ my $identifier = "$args->{identifier_template}.LabeledExtract"
+ . ( $args->{dye} ? ".$args->{dye}" : q{} );
+ my $materialtype = &new_ontologyentry(
+ { category => $OE_CAT_MATERIALTYPE,
+ value => $args->{material_type},
+ }
+ );
+
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ $obj->setName($args->{name} || $name) unless $obj->getName;
+ $obj->setMaterialType($materialtype) unless $obj->getMaterialType;
+
+ # Treatments
+ if ( $args->{derived_from} ) {
+ my $source_bmm
+ = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+ bioMaterial => $args->{derived_from},
+ );
+
+ my $treatments = new_treatments(
+ { source_bmm => $source_bmm,
+ protocols => $args->{protocols},
+ parameters => $args->{parameters},
+ parameter_bag => $args->{parameter_bag},
+ identifier_template => "$args->{identifier_template}."
+ . ( $args->{dye} || q{} ),
+ default_action => $OE_VAL_LABELING,
+ }
+ );
+
+ if ( $obj->getTreatments ) {
+ $obj->setTreatments(
+ &_update_treatments( $obj->getTreatments, $source_bmm ) );
+ }
+ else {
+ $obj->setTreatments($treatments);
+ }
+ }
+
+ # Pooling
+ if ( $args->{pooling} ) {
+ my $pooling_treatments = _check_pooling(
+ $obj,
+ $args->{pooling},
+ $args->{identifier_template},
+ );
+ $obj->addTreatments(@$pooling_treatments) if $pooling_treatments;
+ }
+
+ # Labels
+ if ( $args->{dye} ) {
+
+ my $compound = $args->{compound_bag}
+ ->( $args->{dye}, { dye => $args->{dye} } );
+
+ my $found;
+ if ( my $preexisting_labels = $obj->getLabels ) {
+ my $new_identifier = $compound->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_labels;
+ }
+ $obj->addLabels($compound) unless $found;
+ }
+
+ return $obj;
+
+}
+
+sub new_treatments {
+
+ my ($args) = @_;
+
+ my @treatments;
+
+ # Protocols and parameters are both arrays which must be fed to this
+ # sub in synchronous order:
+ # $protocols = [procotol1, protocol2, protocol3]
+ # $parameters = [param set 1, param set 2, param set 3]
+ foreach my $protocol ( @{ $args->{protocols} } ) {
+
+ my $ptype = $protocol->getType;
+ my $actionvalue = $ptype->getValue;
+
+ my $action = &new_ontologyentry(
+ { category => $OE_CAT_ACTION,
+ value => $actionvalue,
+ }
+ );
+
+ my $protocolapplication
+ = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $protocol,
+ activityDate => 'n/a',
+
+ # FIXME, (also performers, hardware & software applications)
+ );
+
+ $args->{parameters} && do {
+
+ my $param_set = shift( @{ $args->{parameters} } )
+ or croak(
+ "Internal script error: Insufficient parameter sets for protocol "
+ . $protocol->getIdentifier
+ . ".\n" );
+
+ foreach my $param_id ( sort keys %{$param_set} ) {
+
+ my $parameter = $args->{parameter_bag}->( $param_id );
+
+ unless ( $parameter ) {
+ croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+ }
+
+ $protocolapplication->addParameterValues(
+ Bio::MAGE::Protocol::ParameterValue->new(
+ value => $param_set->{$param_id},
+ parameterType => $parameter
+ )
+ );
+
+ }
+ };
+
+ my $treatment = Bio::MAGE::BioMaterial::Treatment->new(
+ identifier =>
+ "$args->{identifier_template}.$actionvalue.Treatment",
+ action => $action,
+ protocolApplications => [$protocolapplication],
+ order => ( $#treatments + 2 ),
+ );
+
+ $treatment->setSourceBioMaterialMeasurements(
+ [ $args->{source_bmm} ] )
+ if $args->{source_bmm};
+
+ push( @treatments, $treatment );
+ }
+
+ unless ( scalar @treatments ) {
+
+ # We want at least one treatment, even if it doesn't have a
+ # protocolapplication, to hang our source_bmm off.
+
+ # Generic action.
+ my $action_val = $args->{default_action}
+ || $OE_VAL_SPECIFIEDBIOMATERIALACTION;
+ my $action = new_ontologyentry(
+ { category => $OE_CAT_ACTION,
+ value => $action_val,
+ }
+ );
+
+ # Make sure the identifier is truly unique; otherwise there is
+ # the risk of duplicate Treatment objects.
+ my $uniq_number = unique_identifier();
+ my $treatment = Bio::MAGE::BioMaterial::Treatment->new(
+ identifier => "$args->{identifier_template}.$uniq_number.Treatment",
+ action => $action,
+ order => ( $#treatments + 2 ),
+ );
+ $treatment->setSourceBioMaterialMeasurements(
+ [ $args->{source_bmm} ] )
+ if $args->{source_bmm};
+ push( @treatments, $treatment );
+ }
+
+ return \@treatments;
+
+}
+
+sub _update_treatments { # Used in e.g. pooling
+
+ my ( $treatments, $source_bmm ) = @_;
+
+ if ($source_bmm) {
+ foreach my $treatment (@$treatments) {
+
+ my $old_bmms = $treatment->getSourceBioMaterialMeasurements;
+ my $new_identifier = $source_bmm->getBioMaterial()->getIdentifier;
+ my $found = first {
+ $_->getBioMaterial()->getIdentifier eq $new_identifier;
+ }
+ @$old_bmms;
+
+ $treatment->addSourceBioMaterialMeasurements($source_bmm)
+ unless $found;
+ }
+ }
+
+ return $treatments;
+
+}
+
+sub _new_biomaterial_biosample {
+
+ my ( $args, $obj ) = @_;
+
+ # New object if necessary
+ $obj = Bio::MAGE::BioMaterial::BioSample->new unless $obj;
+
+ # Identifier, Name
+ $obj->setIdentifier( $args->{identifier} ) unless $obj->getIdentifier;
+ $obj->setName( $args->{name} ) unless $obj->getName;
+
+ # MaterialType
+ my $materialtype = &new_ontologyentry(
+ { category => $OE_CAT_MATERIALTYPE,
+ value => $args->{material_type},
+ }
+ );
+ $obj->setMaterialType($materialtype) unless $obj->getMaterialType;
+
+ # Type
+ my $type = &new_ontologyentry(
+ { category => $OE_CAT_BIOSAMPLETYPE,
+ value => $args->{biosample_type},
+ }
+ );
+ $obj->setType($type) unless $obj->getType;
+
+ # Treatments
+ if ( $args->{derived_from} ) {
+ my $source_bmm
+ = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+ bioMaterial => $args->{derived_from},
+ );
+
+ my $treatments = new_treatments(
+ { source_bmm => $source_bmm,
+ protocols => $args->{protocols},
+ parameters => $args->{parameters},
+ parameter_bag => $args->{parameter_bag},
+ identifier_template => $args->{identifier_template},
+ default_action => $args->{default_action},
+ }
+ );
+
+ if ( $obj->getTreatments ) {
+ $obj->setTreatments(
+ &_update_treatments( $obj->getTreatments, $source_bmm ) );
+ }
+ else {
+ $obj->setTreatments($treatments);
+ }
+ }
+
+ # Pooling
+ if ( $args->{pooling} ) {
+ my $pooling_treatments = _check_pooling(
+ $obj,
+ $args->{pooling},
+ $args->{identifier_template},
+ );
+ $obj->addTreatments(@$pooling_treatments) if $pooling_treatments;
+ }
+
+ return $obj;
+
+}
+
+sub new_biosample {
+
+ my ( $name, $args, $biosample ) = @_;
+
+ $args->{name} ||= $name;
+ $args->{material_type} ||= $OE_VAL_ORGANISMPART; # Set some default values
+ $args->{biosample_type} ||= $OE_VAL_NOTEXTRACT;
+ $args->{identifier} = "$args->{identifier_template}.Sample";
+
+ return &_new_biomaterial_biosample( $args, $biosample );
+
+}
+
+sub new_extract {
+
+ my ( $name, $args, $extract ) = @_;
+
+ $args->{name} ||= $name;
+ $args->{material_type} ||= $OE_VAL_TOTALRNA; # Set some default values
+ $args->{biosample_type} ||= $OE_VAL_EXTRACT;
+ $args->{default_action} ||= $OE_VAL_NUCLEICACIDEXTRACTION;
+ $args->{identifier} = "$args->{identifier_template}.Extract";
+
+ return &_new_biomaterial_biosample( $args, $extract );
+
+}
+
+sub new_immunoprecipitate {
+
+ my ( $name, $args, $ip ) = @_;
+
+ $args->{name} ||= $name;
+ $args->{material_type} ||= $OE_VAL_GENOMICDNA; # Set some default values
+ $args->{biosample_type} ||= $OE_VAL_EXTRACT;
+ $args->{default_action} ||= $OE_VAL_IMMUNOPRECIPITATE;
+ $args->{identifier} = "$args->{identifier_template}.Immunoprecipitate";
+
+ return &_new_biomaterial_biosample( $args, $ip );
+
+}
+
+sub new_protocol {
+
+ my ( $identifier, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Protocol::Protocol->new unless $obj;
+
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ $args->{protocol_type} && do {
+ my $type = &new_ontologyentry(
+ { category => $OE_CAT_PROTOCOLTYPE,
+ value => $args->{protocol_type},
+ }
+ );
+ $obj->setType($type) unless $obj->getType;
+ };
+
+ $args->{name}
+ && do { $obj->setName( $args->{name} ) unless $obj->getName; };
+ $args->{text}
+ && do { $obj->setText( $args->{text} ) unless $obj->getText; };
+
+ if ( $args->{parameters} ) {
+
+ # Sort out the parameters
+ my @parameters;
+
+ while ( my ( $param_name, $param_hash )
+ = each %{ $args->{parameters} } ) {
+
+ push(
+ @parameters,
+ $args->{parameter_bag}->(
+ $param_hash->{identifier},
+ { name => $param_name,
+ unit => $param_hash->{unit},
+ }
+ )
+ );
+ }
+
+ $obj->setParameterTypes( \@parameters )
+ unless $obj->getParameterTypes; # FIXME more granular update?
+
+ }
+
+ if ( $args->{software} ) {
+ _update_protocol_software( $obj, $args->{software} );
+ }
+ if ( $args->{hardware} ) {
+ _update_protocol_hardware( $obj, $args->{hardware} );
+ }
+
+ return $obj;
+
+}
+
+sub new_array {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Array::Array->new unless $obj;
+
+ # Identifier
+ my $identifier = "$args->{identifier_template}.Array";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier();
+
+ # PhysicalArrayDesign
+ my $accession;
+ if ( my $ad = $args->{arraydesign} ) {
+ $obj->setArrayDesign($ad) unless $obj->getArrayDesign();
+ $accession = $ad->getIdentifier();
+ }
+ elsif ( defined( $args->{arrayaccession} ) ) {
+ my $pad = Bio::MAGE::ArrayDesign::PhysicalArrayDesign->new(
+ identifier => $args->{arrayaccession},
+ );
+ $obj->setArrayDesign($pad) unless $obj->getArrayDesign();
+ $accession = $args->{arrayaccession};
+ }
+ else {
+ croak("Error: No array design information provided to new_array.");
+ }
+
+ # Array serial
+ $args->{serial} && do {
+ $obj->setArrayIdentifier( $args->{serial} )
+ unless $obj->getArrayIdentifier;
+ };
+
+ if ( my $get_armanuf = $args->{armanuf_bag} ) {
+
+ # ArrayManufacture - unique for each accession/serial no. combination
+ my $armanuf_key = $accession . ( $args->{serial} || q{} );
+ my $armanuf = $get_armanuf->(
+ $armanuf_key,
+ { array => $obj,
+ identifier_template => $args->{identifier_template},
+ }
+ );
+ $obj->setInformation($armanuf) unless $obj->getInformation;
+ }
+
+ return $obj;
+
+}
+
+sub new_armanuf {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Array::ArrayManufacture->new unless $obj;
+
+ # Identifier
+ my $pad = $args->{array}->getArrayDesign;
+ my $identifier = $pad->getIdentifier;
+ $obj->setIdentifier(
+ "$args->{identifier_template}.$identifier.ArrayManufacture")
+ unless $obj->getIdentifier;
+
+ # Arrays
+ my $found;
+ if ( my $preexisting_arrays = $obj->getArrays ) {
+ my $new_identifier = $args->{array}->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_arrays;
+ }
+ $obj->addArrays( $args->{array} ) unless $found;
+
+ return $obj;
+
+}
+
+sub new_factorvalue {
+
+ my ( $args, $obj ) = @_;
+
+ my ( $ef, $new );
+
+ # New object if needed; add to the ExperimentalFactor
+ unless ( $obj ) {
+ $obj = Bio::MAGE::Experiment::FactorValue->new();
+ $new++; # Flag so we know to add new FVs to the EF - see end of sub
+ }
+
+ # Identifier
+ unless ( $obj->getIdentifier() ) {
+ my $identifier = sprintf(
+ "%s:%s.%s.%s.FactorValue",
+ $args->{namespace},
+ $args->{exptaccession},
+ $args->{category},
+ $args->{value},
+ );
+ $obj->setIdentifier($identifier);
+ }
+
+ # Name
+ $obj->setName( $args->{value} ) unless $obj->getName;
+
+ # Value or Measurement (OE)
+ ( ( $args->{value} || $args->{value} eq '0' ) && $args->{category} )
+ && do {
+
+ # All numbers treated as measurements, unit class taken from
+ # category until we figure out something better
+ if ( looks_like_number( $args->{value} ) ) {
+
+ my $measurement;
+
+ # Unit is within parens.
+ if ( my ($unitname)
+ = ( $args->{category} =~ m/\(\s*(.*?)\s*\)/ ) ) {
+
+ $measurement = _measurement_from_unit_name($unitname);
+
+ # Strip any units from the category so the
+ # ExperimentalFactorCategory is correctly parsed. Unit class
+ # is everything before the first parenthesis.
+ $args->{category} = ( split /\s*\(/, $args->{category} )[0];
+ }
+
+ else {
+ $measurement = Bio::MAGE::Measurement::Measurement->new;
+ }
+
+ $measurement->setValue( $args->{value} );
+ $obj->setMeasurement($measurement) unless $obj->getMeasurement;
+
+ }
+ else {
+
+ my $value = &new_ontologyentry(
+ { category => $args->{category},
+ value => $args->{value}
+ }
+ );
+ $obj->setValue($value) unless $obj->getValue;
+
+ }
+ };
+
+ # Get a new ExperimentalFactor, by category
+ if ( my $get_ef = $args->{expt_factor_bag} ) {
+ $ef = $get_ef->(
+
+ # Category used as internal ID for tracking
+ $args->{category},
+ $args,
+ );
+ }
+ $ef->addFactorValues($obj) if $new;
+
+ return ( $obj, $ef );
+
+}
+
+sub new_experimentalfactor {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Experiment::ExperimentalFactor->new unless $obj;
+
+ # Identifier
+ my $identifier = sprintf(
+ "%s:%s.%s.ExperimentalFactor",
+ $args->{namespace},
+ $args->{exptaccession},
+ $args->{category},
+ );
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ # Name
+ $obj->setName( $args->{category} ) unless $obj->getName;
+
+ # Category (OE)
+ my $category_value = decamelize($args->{category});
+
+ my $category = new_ontologyentry(
+ { category => $OE_CAT_EXPERIMENTALFACTORCATEGORY,
+ value => $category_value
+ }
+ );
+ $obj->setCategory($category) unless $obj->getCategory;
+
+ return $obj;
+
+}
+
+sub new_quantitationtype {
+
+ my ( $args, $obj ) = @_;
+
+ my $qt_metrics = $args->{data_metrics}{ $args->{name} };
+
+ unless ($obj) {
+
+ # Check for subclass defined in QT data. Avoid autovivification
+ # here.
+ if ( $qt_metrics && $qt_metrics->{subclass} ) {
+
+ eval {
+ $obj
+ = ("Bio::MAGE::QuantitationType::$qt_metrics->{subclass}")
+ ->new;
+ }
+ or do {
+ print STDERR (
+ "Warning: Invalid QuantitationType subclass specified: "
+ . "$qt_metrics->{subclass}. Creating SpecalizedQuantitationType object.\n"
+ );
+ $obj
+ = Bio::MAGE::QuantitationType::SpecializedQuantitationType
+ ->new;
+ };
+
+ # $qt_metrics->{confmap} identifies to which QT a confidence
+ # indicator maps.
+ if ( $qt_metrics->{confmap} && $args->{container} ) {
+
+ my $target_identifier
+ = $args->{prefix} . $qt_metrics->{confmap};
+
+ # Recurse down one level only (container is not passed).
+ my $target_qt = $args->{container}->(
+ $target_identifier,
+ { identifier => $target_identifier,
+ name => $qt_metrics->{confmap},
+ prefix => $args->{prefix},
+ data_metrics => $args->{data_metrics},
+ }
+ );
+
+ croak(
+ "Internal MAGE.pm error: unable to identify target QT $qt_metrics->{confmap}\n"
+ )
+ unless $target_qt;
+
+ if ( ( $qt_metrics->{subclass} eq 'PValue' )
+ || ( $qt_metrics->{subclass} eq 'Error' )
+ || ( $qt_metrics->{subclass} eq 'ExpectedValue' ) ) {
+
+ # Set our target.
+ $obj->setTargetQuantitationType($target_qt);
+
+ # Update the target to point to us, if not already done.
+ my $found;
+ foreach my $confind (
+ @{ $target_qt->getConfidenceIndicators || [] } ) {
+ $found++ if ($confind->getIdentifier() eq $args->{identifier} );
+ }
+ $target_qt->addConfidenceIndicators($obj) unless $found;
+ }
+ }
+
+ }
+ else {
+
+ # No subclass given; fall back to specialized QT
+ $obj = Bio::MAGE::QuantitationType::SpecializedQuantitationType
+ ->new;
+ }
+ }
+
+ # Identifier, Name
+ $obj->setIdentifier( $args->{identifier} ) unless $obj->getIdentifier();
+ unless ( $obj->getName() ) {
+ my $name = $args->{name};
+ if ( $args->{software} ) {
+ $name = "$args->{software}:$name";
+ }
+ $obj->setName( $name );
+ }
+
+ $qt_metrics ||= {};
+
+ # DataType
+ my $datatypevalue = $qt_metrics->{datatype} || $OE_VAL_UNKNOWN;
+ my $datatype = &new_ontologyentry(
+ { category => $OE_CAT_DATATYPE,
+ value => $datatypevalue
+ }
+ );
+ $obj->setDataType($datatype) unless $obj->getDataType;
+
+ # Scale
+ my $scalevalue = $qt_metrics->{scale} || $OE_VAL_UNKNOWN;
+ my $scale = &new_ontologyentry(
+ { category => $OE_CAT_SCALE,
+ value => $scalevalue
+ }
+ );
+ $obj->setScale($scale) unless $obj->getScale;
+
+ # IsBackground
+ my $isbackground = $qt_metrics->{is_background} || 0;
+ $obj->setIsBackground($isbackground) unless $obj->getIsBackground;
+
+ # Channel
+ $qt_metrics->{channel} && do {
+ my $channel = Bio::MAGE::BioAssay::Channel->new(
+ identifier => $AE_CHANNEL_PREFIX . $qt_metrics->{channel},
+ name => $qt_metrics->{channel},
+ );
+ $obj->setChannel($channel);
+ };
+
+ # Description
+ $qt_metrics->{description} && do {
+ my $description = Bio::MAGE::Description::Description->new(
+ text => $qt_metrics->{description} );
+ $obj->setDescriptions( [$description] );
+ };
+
+ return $obj;
+
+}
+
+sub new_qtd {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssayData::QuantitationTypeDimension->new
+ unless $obj;
+
+ # Identifier
+ unless ( $obj->getIdentifier ) {
+ my $identifier
+ = "$args->{identifier_template}.QuantitationTypeDimension";
+ $obj->setIdentifier($identifier);
+ }
+
+ # QuantitationTypes. FIXME to add? - maybe rewrite QTD/QT handling
+ $obj->setQuantitationTypes( $args->{qt_list} )
+ unless $obj->getQuantitationTypes;
+
+ return $obj;
+
+}
+
+sub new_biosource {
+
+ my ( $args, $obj ) = @_;
+
+ # Defaults
+ $args->{material_type} ||= $OE_VAL_WHOLEORGANISM;
+
+ # New object if necessary
+ $obj = Bio::MAGE::BioMaterial::BioSource->new unless $obj;
+
+ # Identifier, Name
+ my $identifier = "$args->{identifier_template}.BioSource";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ $obj->setName( $args->{name} ) unless $obj->getName;
+
+ # MaterialType
+ my $materialtype = &new_ontologyentry(
+ { category => $OE_CAT_MATERIALTYPE,
+ value => $args->{material_type}
+ }
+ );
+ $obj->setMaterialType($materialtype) unless $obj->getMaterialType;
+
+ # BioMaterialCharacteristics
+ foreach my $char ( @{ $args->{characteristics} } ) {
+
+ my $found;
+ if ( my $preexisting_chars = $obj->getCharacteristics ) {
+ my $new_category = $char->getCategory;
+ my $new_value = $char->getValue;
+ $found = first {
+ $_->getCategory eq $new_category
+ && $_->getValue eq $new_value;
+ }
+ @$preexisting_chars;
+ }
+ $obj->addCharacteristics($char) unless $found;
+
+ }
+
+ # Add a free-text description, if included.
+ if ($args->{description}){
+ my $description = Bio::MAGE::Description::Description->new(
+ text => $args->{description},
+ );
+ $obj->setDescriptions([$description]) unless $obj->getDescriptions;
+ }
+
+ return $obj;
+
+}
+
+sub update_factorvalues {
+
+ my ( $obj, $newfvlist ) = @_;
+
+ my $oldfvlist = $obj->getBioAssayFactorValues;
+
+ my %fvhash = map { $_->getIdentifier => $_ } @$oldfvlist, @$newfvlist;
+
+ # Note that we're using set rather than add here because apparently
+ # addBioAssayFactorValues has some really bizarre behaviour
+ # (i.e. updating FVs on a related PBA when DBA is passed).
+ $obj->setBioAssayFactorValues( [ values %fvhash ] );
+
+ return;
+}
+
+sub new_hybridization {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::Hybridization->new unless $obj;
+
+ if ( $args->{identifier_template} ) {
+ $obj->setIdentifier("$args->{identifier_template}.Hybridization")
+ unless $obj->getIdentifier();
+ }
+ if ( $args->{name} ) {
+ $obj->setName($args->{name}) unless $obj->getName();
+ }
+ if ( $args->{physical_bioassay} ) {
+ $obj->setPhysicalBioAssayTarget( $args->{physical_bioassay} )
+ unless $obj->getPhysicalBioAssayTarget();
+ }
+
+ $obj->setArray( $args->{array} ) if $args->{array};
+
+ my $hyb_protocolapplication;
+ if ( $args->{hybridization_protocol} ) {
+
+ $hyb_protocolapplication
+ = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $args->{hybridization_protocol},
+ activityDate => 'n/a',
+
+ # FIXME, (also performers, hardware)
+ );
+
+ if ( $args->{hybridization_parameters} ) {
+
+ foreach my $param_id (
+ sort keys %{ $args->{hybridization_parameters} } ) {
+
+ my $parameter = $args->{parameter_bag}->( $param_id );
+
+ unless ( $parameter ) {
+ croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+ }
+
+ $hyb_protocolapplication->addParameterValues(
+ Bio::MAGE::Protocol::ParameterValue->new(
+ value => $args->{hybridization_parameters}{$param_id},
+ parameterType => $parameter
+ )
+ );
+
+ }
+ }
+
+ if ( $args->{hyb_hardware} ) {
+
+ my $hardwareapplication = &new_hardwareapplication(
+ { hardware => $args->{hyb_hardware},
+ parameters => $args->{hyb_hardware_params}
+ }
+ );
+ $hyb_protocolapplication->addHardwareApplications(
+ $hardwareapplication);
+
+ }
+
+ $obj->setProtocolApplications( [$hyb_protocolapplication] )
+ if $hyb_protocolapplication;
+
+ }
+
+ # We can also accept a straight arrayref of ProtocolApplications
+ # (e.g. as in MAGE-TAB implementation).
+ if ( $args->{hyb_protocolapps} ) {
+ $obj->setProtocolApplications( $args->{hyb_protocolapps} );
+ }
+
+ return $obj;
+
+}
+
+sub _update_protocol_software {
+
+ my ( $protocol, $software ) = @_;
+
+ my $found;
+ if ( my $preexisting_softwares = $protocol->getSoftwares || [] ) {
+ my $new_identifier = $software->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_softwares;
+ }
+ $protocol->addSoftwares($software) unless $found;
+
+ return;
+}
+
+sub _update_protocol_hardware {
+
+ my ( $protocol, $hardware ) = @_;
+
+ my $found;
+ if ( my $preexisting_hardwares = $protocol->getHardwares || [] ) {
+ my $new_identifier = $hardware->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_hardwares;
+ }
+ $protocol->addHardwares($hardware) unless $found;
+
+ return;
+}
+
+sub new_scan_protoapp {
+
+ my ( $args, $obj ) = @_;
+
+ unless ( $obj ) {
+ $obj = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $args->{scanning_protocol},
+ activityDate => 'n/a',
+
+ # FIXME (also performers, hardware)
+ );
+ }
+
+ if ( $args->{software} ) {
+ my $softwareapplication
+ = Bio::MAGE::Protocol::SoftwareApplication->new(
+ software => $args->{software},
+ );
+ $softwareapplication->setVersion( $args->{software_version} )
+ if $args->{software_version};
+
+ $obj->setSoftwareApplications(
+ [$softwareapplication],
+ );
+
+ _update_protocol_software(
+ $args->{scanning_protocol},
+ $args->{software},
+ );
+ }
+
+ if ( $args->{scanning_parameters} ) {
+
+ foreach my $param_id (
+ sort keys %{ $args->{scanning_parameters} } ) {
+
+ my $parameter = $args->{parameter_bag}->( $param_id );
+
+ unless ( $parameter ) {
+ croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+ }
+
+ $obj->addParameterValues(
+ Bio::MAGE::Protocol::ParameterValue->new(
+ value => $args->{scanning_parameters}{$param_id},
+ parameterType => $parameter
+ )
+ );
+ }
+ }
+
+ if ( $args->{scan_hardware} ) {
+
+ my $hardwareapplication = new_hardwareapplication(
+ { hardware => $args->{scan_hardware},
+ parameters => $args->{scan_hardware_params}
+ }
+ );
+ $obj->addHardwareApplications(
+ $hardwareapplication,
+ );
+ }
+
+ return $obj;
+}
+
+sub new_scan {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::ImageAcquisition->new unless $obj;
+
+ $args->{identifier_template} && do {
+ $obj->setIdentifier("$args->{identifier_template}.ImageAcquisition")
+ unless $obj->getIdentifier;
+ };
+ $args->{physical_bioassay} && do {
+ $obj->setPhysicalBioAssay( $args->{physical_bioassay} )
+ unless $obj->getPhysicalBioAssay;
+ $obj->setTarget( $args->{physical_bioassay} ) unless $obj->getTarget;
+ };
+
+ # ProtocolApplication and Software
+ if ( $args->{scanning_protocol} ) {
+ $obj->setProtocolApplications( [ new_scan_protoapp( $args ) ] );
+ }
+
+ # We can also accept a straight arrayref of ProtocolApplications
+ # (e.g. as in MAGE-TAB implementation).
+ if ( $args->{scan_protocolapps} ) {
+ $obj->setProtocolApplications( $args->{scan_protocolapps} );
+ }
+
+ # Image URIs (NB currently assumes ony one image per hyb FIXME)
+ if ( $args->{image} ) {
+ my $pba = $args->{physical_bioassay};
+ $args->{image}->setChannels( $pba->getChannels ) if $pba->getChannels;
+ $obj->setImages( [ $args->{image} ] );
+ $pba->setPhysicalBioAssayData( [ $args->{image} ] );
+ }
+
+ return $obj;
+
+}
+
+{
+ # Local cache for PBA data (FVs, labels attached to a given hyb).
+ my %pba_fv_cache;
+
+sub new_physicalbioassay {
+
+ my ( $name, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::PhysicalBioAssay->new unless $obj;
+
+ # Identifier
+ $args->{identifier_template} && do {
+ my $identifier = "$args->{identifier_template}.PhysicalBioAssay";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ };
+
+ # Name; store this also in $args for passing to new_hybridization
+ unless ( defined( $args->{name} ) ) {
+ $args->{name} = $name;
+ }
+ $obj->setName($args->{name}) unless $obj->getName();
+
+ # Channels
+ # Construct hash listing of old label ids
+ my $old_label_identifiers;
+ if ( $obj->getChannels ) {
+ foreach my $old_channel ( @{ $obj->getChannels } ) {
+ foreach my $old_label ( @{ $old_channel->getLabels } ) {
+ $old_label_identifiers->{ $old_label->getIdentifier }++;
+ }
+ }
+ }
+
+ # N.B. only one label per LE supported here (although LEs can have
+ # more than one label, and new_labelledextract does support
+ # this). This is not likely to be a problem for tab2mage or
+ # mage-tab though, as the LEs are internally uniqued by dye name.
+
+ my $dyename;
+ if ( $args->{label} ) {
+
+ # Convert LabelCompound into Channel
+ my $dye_id = $args->{label}->getIdentifier();
+ unless ( ( $dyename )
+ = ( $dye_id =~ m/^$AE_LABELCOMPOUND_PREFIX(.*)$/ ) ) {
+ $dyename = 'Unknown';
+ }
+
+ # Don't add the channel if it's already present.
+ unless ( $old_label_identifiers->{ $dye_id } ) {
+ my $channel = Bio::MAGE::BioAssay::Channel->new(
+ identifier => $AE_CHANNEL_PREFIX . $dyename,
+ name => $dyename,
+ labels => [ $args->{label} ],
+ );
+ $obj->addChannels($channel);
+
+ # Cache the label object for later.
+ $pba_fv_cache{$name}{$dyename}{label} = $args->{label};
+ }
+
+ }
+ else {
+
+ # We still need a default $dyename.
+ $dyename = 'Unknown';
+ }
+
+ my $merged_pba;
+ if ( $args->{derived_from} ) {
+
+ # i.e. this is a hyb-level PBA.
+
+ # Record the factor values in the cache. These will be used
+ # subsequently to rebuild two-colour hyb codings with the
+ # appropriate fvs and labels.
+ foreach my $fv ( @{ $args->{factor_values} } ) {
+ $pba_fv_cache{$name}{$dyename}{fv}{$fv->getIdentifier()} = $fv;
+ }
+
+ # BioAssayCreation
+ my $hyb_args = { %$args }; # Make a copy.
+ $hyb_args->{physical_bioassay} = $obj;
+ $obj->setBioAssayCreation( new_hybridization( $hyb_args ) )
+ unless $obj->getBioAssayCreation;
+
+ # SourceBioMaterialMeasurements - added where the BioMaterials
+ # differ (e.g. Cy3, Cy5 labeled extracts).
+ my $new_bm = $args->{derived_from};
+
+ my $preexisting_bmms
+ = $obj->getBioAssayCreation()->getSourceBioMaterialMeasurements;
+
+ my $new_identifier = $new_bm->getIdentifier;
+
+ my $found
+ = first { $_->getBioMaterial()->getIdentifier eq $new_identifier }
+ @$preexisting_bmms;
+
+ unless ($found) {
+ my $bmm = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+ bioMaterial => $new_bm );
+ $obj->getBioAssayCreation()
+ ->addSourceBioMaterialMeasurements($bmm);
+ }
+
+ # Best-practice PBA coding for two (or more) channels.
+ # Generate a channel PBA from the cached data for the original
+ # hyb-level PBA, and a merged PBA.
+ if ( scalar @{ $obj->getChannels() || [] } > 1 ) {
+
+ # Delete any preexisting BATs from single-channel coding.
+ $obj->setBioAssayTreatments([]);
+
+ my (@channel_specific_pbas, %merged_fvs, %merged_labels);
+ while ( my ( $dyename, $channeldata ) = each %{ $pba_fv_cache{$name} } ) {
+
+ my $new_id_template = "$args->{identifier_template}.$dyename";
+ my $label = $channeldata->{label};
+ my $factor_values = [ values %{ $channeldata->{fv} } ];
+
+ # We don't pass $args->{derived_from} down to the
+ # extended PBA generation; this is used as a flag to
+ # indicate hyb-level PBAs only.
+ my $channel_pba = $args->{extended_pba_bag}->(
+ "$name.$dyename",
+ {
+ identifier_template => $new_id_template,
+ label => $label,
+ factor_values => $factor_values,
+ },
+ );
+
+ # Add a BioAssayTreatment pointing back to $obj here.
+ my %preexisting;
+ if ( my $old_treats = $obj->getBioAssayTreatments() ) {
+ foreach my $treat ( @$old_treats ) {
+ if ( my $old_pba = $treat->getPhysicalBioAssay() ) {
+ $preexisting{ $old_pba->getIdentifier() }++;
+ }
+ }
+ }
+ unless ( $preexisting{ $channel_pba->getIdentifier() } ) {
+ $obj->addBioAssayTreatments(
+ new_scan(
+ {
+ identifier_template => $new_id_template,
+ physical_bioassay => $channel_pba,
+ }
+ )
+ );
+ }
+
+ # Cache these for addition to the merged PBA below.
+ foreach my $fv (@$factor_values) {
+ $merged_fvs{ $fv->getIdentifier() } = $fv;
+ }
+ $merged_labels{ $label->getIdentifier() } = $label;
+
+ push @channel_specific_pbas, $channel_pba;
+
+ }
+
+ # Create the merged PBA here. Note that the .merged suffix
+ # convention is assumed in other modules for mapping the
+ # hyb PBA to the merged PBA and vice versa.
+ my $merged_id_template = "$args->{identifier_template}.merged";
+
+ # This is a little clumsy; the alternative is proper
+ # support for multiple labels FIXME.
+ foreach my $label ( values %merged_labels ) {
+ $merged_pba = $args->{extended_pba_bag}->(
+ "$name.merged",
+ {
+ identifier_template => $merged_id_template,
+ label => $label,
+ factor_values => [ values %merged_fvs ],
+ },
+ );
+ }
+
+ # Add an empty BAT so we can attach image_acquisition protocol.
+ if ( $merged_pba ) {
+ my $merge_args = { %$args }; # Make a copy.
+ $merge_args->{identifier_template} = $merged_id_template;
+ $merge_args->{physical_bioassay} = $merged_pba;
+ $merged_pba->addBioAssayTreatments( new_scan( $merge_args ) );
+ }
+
+ # Link the merged and channel-specific PBAs.
+ foreach my $channel_pba ( @channel_specific_pbas ) {
+ my $scan_id_template
+ = "$args->{identifier_template}." . unique_identifier();
+ $channel_pba->setBioAssayTreatments( [ new_scan(
+ {
+ identifier_template => $scan_id_template,
+ physical_bioassay => $merged_pba,
+ },
+ ) ] );
+ }
+
+ # Repoint any MBAs pointing at the hyb-level PBA. This is
+ # particularly important for single-channel scans of
+ # two-colour hybs.
+ if ( $args->{mba_bag} ) {
+ foreach my $mba ( @{ $args->{mba_bag}->() } ) {
+ if ( my $fext = $mba->getFeatureExtraction() ) {
+ if ( my $source = $fext->getPhysicalBioAssaySource() ) {
+ if ( $obj->getIdentifier() eq $source->getIdentifier() ) {
+ $fext->setPhysicalBioAssaySource($merged_pba);
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+
+ # BioAssayTreatments
+ $obj->setBioAssayTreatments( [ new_scan( $hyb_args ) ] )
+ unless $obj->getBioAssayTreatments;
+ }
+ }
+ else {
+
+ # This is a channel-specific or merging PBA (do nothing).
+ }
+
+ # BioAssayFactorValues
+ # Update if FVs already present
+ $args->{factor_values}
+ && do { update_factorvalues( $obj, $args->{factor_values} ) };
+
+ return $obj;
+
+}
+
+}
+
+sub new_image {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::Image->new unless $obj;
+
+ my $identifier = "$args->{identifier_template}.Image";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ $obj->setURI( $args->{uri} ) unless $obj->getURI;
+
+ # We assume the default image format is TIFF
+ my $format = new_ontologyentry(
+ { category => "ImageFormat",
+ value => ( $args->{format} || "TIFF" )
+ }
+ );
+
+ $obj->setFormat($format) unless $obj->getFormat;
+
+ return $obj;
+}
+
+sub new_featureextraction {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::FeatureExtraction->new(
+ identifier => "$args->{identifier_template}.FeatureExtraction",
+ measuredBioAssayTarget => $args->{measured_bioassay},
+ )
+ unless $obj;
+ if ( $args->{physical_bioassay} ) {
+ $obj->setPhysicalBioAssaySource( $args->{physical_bioassay} )
+ unless $obj->getPhysicalBioAssaySource;
+ }
+
+ if ( $args->{protocol} ) {
+
+ my $protocolapplication
+ = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $args->{protocol},
+ activityDate => 'n/a',
+
+ # FIXME (also performers, hardware)
+ );
+ $args->{software} && do {
+ my $softwareapplication
+ = Bio::MAGE::Protocol::SoftwareApplication->new(
+ software => $args->{software} );
+ $softwareapplication->setVersion( $args->{software_version} )
+ if $args->{software_version};
+ $protocolapplication->setSoftwareApplications(
+ [$softwareapplication] )
+ if $softwareapplication;
+
+ _update_protocol_software( $args->{protocol}, $args->{software} );
+ };
+
+ $args->{parameters} && do {
+
+ foreach my $param_id ( sort keys %{ $args->{parameters} } ) {
+
+ my $parameter = $args->{parameter_bag}->( $param_id );
+
+ unless ( $parameter ) {
+ croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+ }
+
+ $protocolapplication->addParameterValues(
+ Bio::MAGE::Protocol::ParameterValue->new(
+ value => $args->{parameters}{$param_id},
+ parameterType => $parameter
+ )
+ );
+ }
+ };
+ $obj->setProtocolApplications( [$protocolapplication] );
+ }
+
+ # For MAGE-TAB.
+ if ( $args->{protocolapps} ) {
+ $obj->setProtocolApplications( $args->{protocolapps} );
+ }
+
+ return $obj;
+
+}
+
+sub new_measuredbioassay {
+
+ my ( $name, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::MeasuredBioAssay->new unless $obj;
+
+ # Identifier
+ if ( $args->{identifier_template} ) {
+ my $identifier = "$args->{identifier_template}.MeasuredBioAssay";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ }
+
+ # Name
+ $obj->setName($args->{name} || $name) unless $obj->getName;
+
+ # MBAD
+ if ( $args->{measured_bioassay_data} ) {
+
+ my $found;
+ if ( my $preexisting_mbads = $obj->getMeasuredBioAssayData ) {
+ my $new_identifier
+ = $args->{measured_bioassay_data}->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_mbads;
+ }
+ $obj->addMeasuredBioAssayData( $args->{measured_bioassay_data} )
+ unless $found;
+ };
+
+ # Channels
+ if ( $args->{channelname} ) {
+
+ # Construct hash listing of old label ids
+ my $found;
+ if ( my $oldchannels = $obj->getChannels() ) {
+ foreach my $old_ch ( @$oldchannels ) {
+ foreach my $old_label ( @{ $old_ch->getLabels() || [] } ) {
+ $found++ if ( $old_label->getName() eq $args->{channelname} );
+ }
+ }
+ }
+ unless ( $found ) {
+ my $label = new_labelcompound({dye => $args->{channelname}});
+ my $channel = Bio::MAGE::BioAssay::Channel->new(
+ identifier => $AE_CHANNEL_PREFIX . $args->{channelname},
+ name => $args->{channelname},
+ labels => [ $label ],
+ );
+ $obj->addChannels($channel);
+ }
+ }
+
+ # BioAssayFactorValues
+ # Update if FVs already present
+ if ( $args->{factor_values} ) {
+ update_factorvalues( $obj, $args->{factor_values} )
+ }
+
+ # FeatureExtraction
+ my %fext_args = %$args;
+ $fext_args{measured_bioassay} = $obj;
+
+ # Note that this needs to be able to overwrite old FEs, e.g. when
+ # switching to two-colour hyb PBA coding.
+ $obj->setFeatureExtraction( new_featureextraction( \%fext_args ) );
+
+ return $obj;
+
+}
+
+sub new_derivedbioassay {
+
+ my ( $name, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::DerivedBioAssay->new unless $obj;
+
+ # Identifier
+ $args->{identifier_template} && do {
+ my $identifier = "$args->{identifier_template}.DerivedBioAssay";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ };
+
+ # Name
+ $obj->setName($args->{name} || $name) unless $obj->getName;
+
+ # DBAD
+ $args->{derived_bioassay_data} && do {
+
+ my $found;
+ if ( my $preexisting_dbads = $obj->getDerivedBioAssayData ) {
+ my $new_identifier
+ = $args->{derived_bioassay_data}->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_dbads;
+ }
+ $obj->addDerivedBioAssayData( $args->{derived_bioassay_data} )
+ unless $found;
+ };
+
+ # BioAssayFactorValues
+ # Update if FVs already present
+ $args->{factor_values}
+ && do { update_factorvalues( $obj, $args->{factor_values} ) };
+
+ # DerivedBioAssayType
+ $args->{normalization_type} && do {
+ my $dbatype = &new_ontologyentry(
+ { category => $OE_CAT_DERIVEDBIOASSAYTYPE,
+ value => $args->{normalization_type},
+ }
+ );
+ $obj->setType($dbatype) unless $obj->getType;
+ };
+
+ # DerivedBioAssayMap
+ $args->{source_bioassays} && do {
+
+ unless ( $args->{bioassaymap_bag} ) {
+ croak("No BioAssayMap container passed to new_derivedbioassay.");
+ }
+
+ # BioAssayMap and DBA both have the same $name internal identifier.
+ # new_bioassaymap updates source_bioassays.
+ my $bioassaymap = $args->{bioassaymap_bag}->(
+ $name,
+ { identifier_template => $args->{identifier_template},
+ source_bioassays => $args->{source_bioassays},
+ target_bioassay => $obj
+ }
+ );
+ $obj->setDerivedBioAssayMap( [$bioassaymap] );
+ };
+
+ return $obj;
+
+}
+
+sub add_summary_stats {
+
+ my ( $obj, $stat_hash ) = @_;
+
+ if ($stat_hash) {
+ my @statistics;
+ foreach my $name ( sort keys %{$stat_hash} ) {
+ push(
+ @statistics,
+ Bio::MAGE::NameValueType->new(
+ name => $name,
+ value => $stat_hash->{$name},
+ )
+ );
+ }
+ $obj->setSummaryStatistics( \@statistics )
+ unless $obj->getSummaryStatistics;
+
+ }
+ else {
+
+ return 0;
+
+ }
+
+ return 1;
+}
+
+sub new_measuredbioassaydata {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssayData::MeasuredBioAssayData->new unless $obj;
+
+ # Identifier
+ my $identifier = "$args->{identifier_template}.MeasuredBioAssayData";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ # Name
+ unless ( defined( $args->{name} ) ) {
+ $args->{name} = $args->{filename};
+ }
+ $obj->setName( $args->{name} ) unless $obj->getName();
+
+ # DED, QTD (BAD added separately at the moment FIXME)
+ $args->{feature_dimension} && do {
+ $obj->setDesignElementDimension( $args->{feature_dimension} )
+ unless $obj->getDesignElementDimension;
+ };
+ $args->{quantitation_type_dimension} && do {
+ $obj->setQuantitationTypeDimension( $args->{quantitation_type_dimension} )
+ unless $obj->getQuantitationTypeDimension;
+ };
+
+ # BioDataCube
+ my $biodatacube = Bio::MAGE::BioAssayData::BioDataCube->new(
+ order => $args->{order} || 'BDQ',
+ cube => $args->{filename}
+ );
+ $obj->setBioDataValues($biodatacube) unless $obj->getBioDataValues;
+
+ # Summary Statistics
+ &add_summary_stats( $obj, $args->{summary_statistics} )
+ if $args->{summary_statistics};
+
+ return $obj;
+
+}
+
+sub new_producertransformation {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssayData::Transformation->new(
+ identifier => "$args->{identifier_template}.Transformation",
+ derivedBioAssayDataTarget => $args->{derived_bioassay_data},
+
+ # FIXME QTM
+ ) unless $obj;
+
+ if ( $args->{bioassay_map} && scalar @{ $args->{bioassay_map} } ) {
+ my $bioassay_mapping = Bio::MAGE::BioAssayData::BioAssayMapping->new(
+ bioAssayMaps => $args->{bioassay_map} );
+ $obj->setBioAssayMapping($bioassay_mapping)
+ unless $obj->getBioAssayMapping;
+ }
+
+ my $protocolapplication;
+ if ( $args->{protocol} ) {
+ $protocolapplication = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $args->{protocol},
+ activityDate => 'n/a',
+
+ # FIXME (also performers, hardware applications)
+ );
+ $args->{software} && do {
+ my $softwareapplication
+ = Bio::MAGE::Protocol::SoftwareApplication->new(
+ software => $args->{software} );
+ $softwareapplication->setVersion( $args->{software_version} )
+ if $args->{software_version};
+
+ $protocolapplication->setSoftwareApplications(
+ [$softwareapplication] );
+
+ &_update_protocol_software( $args->{protocol},
+ $args->{software} );
+ };
+
+ $args->{parameters} && do {
+
+ foreach my $param_id ( sort keys %{ $args->{parameters} } ) {
+
+ my $parameter = $args->{parameter_bag}->( $param_id );
+
+ unless ( $parameter ) {
+ croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+ }
+
+ $protocolapplication->addParameterValues(
+ Bio::MAGE::Protocol::ParameterValue->new(
+ value => $args->{parameters}{$param_id},
+ parameterType => $parameter
+ )
+ );
+
+ }
+ };
+ $obj->setProtocolApplications( [$protocolapplication] );
+ }
+
+ # MAGE-TAB support.
+ if ( $args->{protocolapps} ) {
+ $obj->setProtocolApplications( $args->{protocolapps} );
+ }
+
+ $obj->setBioAssayDataSources( $args->{source_bioassay_dataset} )
+ if ($args->{source_bioassay_dataset}
+ && scalar @{$args->{source_bioassay_dataset}});
+
+ $obj->setQuantitationTypeMapping( $args->{quantitationtype_mapping} )
+ if $args->{quantitationtype_mapping};
+
+ return $obj;
+
+}
+
+sub new_quantitationtypemap {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssayData::QuantitationTypeMap->new unless $obj;
+
+ $args->{target_qt} && do {
+ my $identifier = "$args->{identifier_template}."
+ . $args->{target_qt}->getName
+ . '.QuantitationTypeMap';
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ $obj->setTargetQuantitationType( $args->{target_qt} )
+ unless $obj->getTargetQuantitationType;
+ };
+
+ $args->{source_qts} && do {
+ if ( my $old_source_qts = $obj->getSourcesQuantitationType ) {
+
+ # update a previous list
+ foreach my $new_qt ( @{ $args->{source_qts} } ) {
+ my $new_identifier = $new_qt->getIdentifier;
+ my $found = first { $_->getIdentifier eq $new_identifier }
+ @$old_source_qts;
+ $obj->addSourcesQuantitationType($new_qt) unless $found;
+ }
+ }
+ else {
+ $obj->setSourcesQuantitationType( $args->{source_qts} );
+ }
+ };
+
+ return $obj;
+
+}
+
+sub new_derivedbioassaydata {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssayData::DerivedBioAssayData->new unless $obj;
+
+ # Identifier
+ my $identifier = "$args->{identifier_template}.DerivedBioAssayData";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ # Name
+ unless ( defined( $args->{name} ) ) {
+ $args->{name} = $args->{filename};
+ }
+ $obj->setName( $args->{name} ) unless $obj->getName();
+
+ # DED, QTD, BAD
+ $args->{de_dimension} && do {
+ $obj->setDesignElementDimension( $args->{de_dimension} )
+ unless $obj->getDesignElementDimension;
+ };
+ $args->{ba_dimension} && do {
+ $obj->setBioAssayDimension( $args->{ba_dimension} )
+ unless $obj->getBioAssayDimension;
+ };
+
+ my @qt_maps;
+ if ( my $qtd = $args->{qt_dimension} ) {
+ $obj->setQuantitationTypeDimension($qtd)
+ unless $obj->getQuantitationTypeDimension;
+ if ( $args->{source_qts}
+ && $args->{qtm_bag}
+ && ( my $qts = $qtd->getQuantitationTypes ) ) {
+ foreach my $qt (@$qts) {
+
+ # Note - we may need a proper container for QTMs FIXME
+ my %map_args = %$args;
+ $map_args{target_qt} = $qt;
+ my $map = $args->{qtm_bag}->( $qt->getName, \%map_args );
+ $qt->setQuantitationTypeMaps( [$map] );
+ push( @qt_maps, $map );
+ }
+ }
+ }
+ my $qt_mapping;
+ if (@qt_maps) {
+ $qt_mapping = Bio::MAGE::BioAssayData::QuantitationTypeMapping->new(
+ quantitationTypeMaps => \@qt_maps );
+ }
+
+ # BioDataCube
+ my $biodatacube = Bio::MAGE::BioAssayData::BioDataCube->new(
+ order => $args->{order} || 'DBQ',
+ cube => $args->{filename}
+ );
+ $obj->setBioDataValues($biodatacube) unless $obj->getBioDataValues;
+
+ # ProducerTransformation
+ my %txn_args = %$args;
+ $txn_args{derived_bioassay_data} = $obj;
+ $txn_args{quantitationtype_mapping} = $qt_mapping;
+ $obj->setProducerTransformation(
+ new_producertransformation( \%txn_args ) )
+ unless $obj->getProducerTransformation;
+
+ # Summary Statistics
+ &add_summary_stats( $obj, $args->{summary_statistics} )
+ if $args->{summary_statistics};
+
+ return $obj;
+
+}
+
+sub new_measurement {
+
+ my ($args) = @_;
+
+ my $unit;
+
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+
+ eval {
+ $unit = ("Bio::MAGE::Measurement::$args->{class}")->new;
+
+ # unitNameCV is required, so we need some defaults here
+ if ( $args->{class} eq 'TemperatureUnit' ) {
+
+ # The overwhelmingly likely case.
+ $unit->setUnitNameCV( $args->{unit} || 'degree_C' );
+ }
+ else {
+
+ # all other classes have 'other'
+ $unit->setUnitNameCV( $args->{unit} || 'other' );
+ }
+ };
+
+ $SIG{__DIE__} = $sighandler if $sighandler;
+
+ if ($EVAL_ERROR) {
+
+ # Fall-through default is generic QuantityUnit
+ print STDERR (
+ qq{Warning: Unit name "$args->{unit}" not in MAGE Unit }
+ . qq{controlled vocabulary. Creating generic QuantityUnit.\n}
+ );
+ $unit = Bio::MAGE::Measurement::QuantityUnit->new(
+ unitName => ( $args->{unit} || 'n/a' ),
+ unitNameCV => 'other',
+ );
+ }
+
+ my $measurement
+ = Bio::MAGE::Measurement::Measurement->new( unit => $unit );
+
+ $args->{kindcv} && $measurement->setKindCV( $args->{kindcv} );
+
+ $measurement->setValue( $args->{value} )
+ if ( $args->{value}
+ || ( defined( $args->{value} ) && $args->{value} eq '0' ) );
+
+ return $measurement;
+
+}
+
+sub _measurement_from_unit_name {
+
+ my ($unitname) = @_;
+
+ my $unitclass = q{};
+
+ # Populate a HoH with MAGE UnitNameCVs
+ my $mage_units;
+ foreach my $subclass ( keys %$MAGE_UNITS ) {
+ foreach my $unitNameCV ( @{ $MAGE_UNITS->{$subclass} } ) {
+ $mage_units->{$subclass}{$unitNameCV}++;
+ }
+ }
+
+ # Find our subclass
+ UNIT_SUBCLASS:
+ foreach my $subclass ( keys %$mage_units ) {
+ if ( $mage_units->{$subclass}{$unitname} ) {
+ $unitclass = $subclass;
+ last UNIT_SUBCLASS;
+ }
+ }
+
+ my ($kindCV) = ( $unitclass =~ m/^(.*)Unit$/ );
+ $kindCV = lc($kindCV);
+
+ my $measurement = &new_measurement(
+ { class => $unitclass,
+ unit => $unitname,
+ kindcv => $kindCV,
+ }
+ );
+
+ return $measurement;
+}
+
+sub new_parameter {
+
+ my ( $identifier, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Protocol::Parameter->new unless $obj;
+
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ $args->{name}
+ && do { $obj->setName( $args->{name} ) unless $obj->getName; };
+
+ ( defined( $args->{unit} ) && ( $args->{unit} ne q{} ) ) && do {
+
+ my $measurement = _measurement_from_unit_name( $args->{unit} );
+ $obj->setDefaultValue($measurement) unless $obj->getDefaultValue;
+
+ };
+
+ return $obj;
+
+}
+
+sub new_hardwareapplication {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Protocol::HardwareApplication->new unless $obj;
+
+ my $hardware = Bio::MAGE::Protocol::Hardware->new(
+ identifier => $args->{hardware} );
+ $obj->setHardware($hardware) unless $obj->getHardware;
+
+ foreach my $param_id ( sort keys %{ $args->{parameters} } ) {
+
+ # No need to keep track of these yet; Affy only FIXME.
+ my $parameter = new_parameter( $param_id );
+
+ unless ( $parameter ) {
+ croak(qq{Internal error: Parameter "$param_id" not defined.\n});
+ }
+
+ $obj->addParameterValues(
+ Bio::MAGE::Protocol::ParameterValue->new(
+ value => $args->{parameters}{$param_id},
+ parameterType => $parameter
+ )
+ );
+ }
+
+ return $obj;
+
+}
+
+sub new_bioassaymap {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssayData::BioAssayMap->new unless $obj;
+
+ # Identifier
+ $args->{identifier_template} && do {
+ my $identifier = "$args->{identifier_template}.BioAssayMap";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ };
+
+ # TargetBioAssay
+ $args->{target_bioassay} && do {
+ $obj->setBioAssayMapTarget( $args->{target_bioassay} )
+ unless $obj->getBioAssayMapTarget;
+ };
+
+ # SourceBioAssays
+ $args->{source_bioassays} && do {
+ if ( my $preexisting_bioassays = $obj->getSourceBioAssays ) {
+ foreach my $bioassay ( @{ $args->{source_bioassays} } ) {
+ my $new_identifier = $bioassay->getIdentifier;
+ my $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_bioassays;
+ $obj->addSourceBioAssays($bioassay) unless $found;
+ }
+ }
+ else {
+ $obj->setSourceBioAssays( $args->{source_bioassays} );
+ }
+ };
+
+ return $obj;
+
+}
+
+sub new_software {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Protocol::Software->new unless $obj;
+
+ # Identifier
+ my $identifier = "$args->{identifier_template}.Software";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ # Type
+ if ( $args->{type} ) {
+ my $type = new_ontologyentry(
+ { category => $OE_CAT_SOFTWARETYPE,
+ value => $args->{type}
+ }
+ );
+ $obj->setType($type) unless $obj->getType;
+ }
+
+ # Name
+ $obj->setName( $args->{name} ) unless $obj->getName;
+
+ return $obj;
+
+}
+
+sub new_hardware {
+
+ my ( $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Protocol::Hardware->new unless $obj;
+
+ # Identifier
+ my $identifier = "$args->{identifier_template}.Hardware";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+
+ # Type
+ if ( $args->{type} ) {
+ my $type = new_ontologyentry(
+ { category => $OE_CAT_HARDWARETYPE,
+ value => $args->{type}
+ }
+ );
+ $obj->setType($type) unless $obj->getType;
+ }
+
+ # Name
+ $obj->setName( $args->{name} ) unless $obj->getName;
+
+ return $obj;
+
+}
+
+1;
+
diff --git a/lib/ArrayExpress/Curator/MAGE/Definitions.pm b/lib/ArrayExpress/Curator/MAGE/Definitions.pm
new file mode 100644
index 0000000..3c02bc4
--- /dev/null
+++ b/lib/ArrayExpress/Curator/MAGE/Definitions.pm
@@ -0,0 +1,1181 @@
+#!/usr/bin/env perl
+#
+# Module to provide constant values for a local installation of the
+# ArrayExpress::Curator::MAGE module.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Definitions.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Definitions.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::MAGE::Definitions.pm - a module providing a
+central location for managing EDF column names, OntologyEntry
+categories and values.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::MAGE::Definitions
+ qw($EDF_EXPTACCESSION
+ validate_hybridization_section
+ $OE_CAT_PROTOCOLTYPE
+ $OE_VAL_GROW);
+
+=head1 DESCRIPTION
+
+This module provides a central location for various ontology terms and
+column heading definitions. In other words, if you want to change the
+parsing of your EDF or the output OntologyEntries, edit this
+module. Also provided is a series of optional validation subroutines for
+checking column or row names against the expected EDF headers.
+
+=head1 SUPPORTED HEADINGS
+
+=cut
+
+package ArrayExpress::Curator::MAGE::Definitions;
+
+use strict;
+use warnings;
+
+use Readonly;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+
+our @EXPORT_OK = qw(
+ validate_experiment_section
+ validate_protocol_section
+ validate_hybridization_section
+
+ $EDF_EXPTACCESSION
+ $EDF_EXPTDOMAIN
+ $EDF_EXPTNAME
+ $EDF_EXPTDESCRIPTION
+ $EDF_EXPTRELEASEDATE
+ $EDF_EXPTSUBMITTER
+ $EDF_EXPTDATACODER
+ $EDF_EXPTCURATOR
+ $EDF_EXPTINVESTIGATOR
+ $EDF_EXPTORGANIZATION
+ $EDF_EXPTADDRESS
+ $EDF_EXPTSUBMITTER_EMAIL
+ $EDF_EXPTCURATOR_EMAIL
+ $EDF_EXPTINVESTIGATOR_EMAIL
+ $EDF_EXPTDATACODER_EMAIL
+ $EDF_EXPTSUBMISSIONDATE
+ $EDF_EXPTDESIGNTYPE
+ $EDF_EXPTQUALITYCONTROL
+ $EDF_EXPTCURATEDNAME
+ $EDF_EXPTURI
+ $EDF_EXPTGEORELEASEDATE
+ $EDF_EXPTSECONDARYACCESSION
+
+ $EDF_PUBTITLE
+ $EDF_PUBPAGES
+ $EDF_PUBJOURNAL
+ $EDF_PUBVOLUME
+ $EDF_PUBISSUE
+ $EDF_PUBYEAR
+ $EDF_PUBAUTHORS
+ $EDF_PUBURI
+ $EDF_PUBMEDID
+
+ $EDF_PROTOCOLACCESSION
+ $EDF_PROTOCOLNAME
+ $EDF_PROTOCOLTYPE
+ $EDF_PROTOCOLTEXT
+ $EDF_PROTOCOLPARAMS
+
+ $EDF_BIOSOURCENAME
+ $EDF_BIOSOURCETYPE
+ $EDF_BIOSOURCEDESCRIPTION
+ $EDF_BIOSAMPLENAME
+ $EDF_BIOSAMPLETYPE
+ $EDF_EXTRACTNAME
+ $EDF_EXTRACTTYPE
+ $EDF_IMMUNOPRECIPITATENAME
+ $EDF_IMMUNOPRECIPITATETYPE
+ $EDF_LABELEDEXTRACTNAME
+ $EDF_LABELEDEXTRACTTYPE
+ $EDF_HYBRIDIZATIONNAME
+ $EDF_SCANNAME
+ $EDF_NORMALIZATIONNAME
+ $EDF_NORMALIZATIONTYPE
+ $EDF_TRANSFORMATIONNAME
+ $EDF_TRANSFORMATIONTYPE
+ $EDF_DYE
+
+ $EDF_GROW_PROTOCOL
+ $EDF_TREAT_PROTOCOL
+ $EDF_EXTRACT_PROTOCOL
+ $EDF_POOL_PROTOCOL
+ $EDF_LABEL_PROTOCOL
+ $EDF_IP_PROTOCOL
+ $EDF_HYB_PROTOCOL
+ $EDF_SCAN_PROTOCOL
+ $EDF_FEXT_PROTOCOL
+ $EDF_NORM_PROTOCOL
+ $EDF_TRXN_PROTOCOL
+
+ $EDF_SCAN_SOFTWARE
+ $EDF_FEXT_SOFTWARE
+ $EDF_NORM_SOFTWARE
+ $EDF_TRXN_SOFTWARE
+
+ $EDF_IMAGE_FORMAT
+
+ $EDF_ARRAYACCESSION
+ $EDF_ARRAYSERIAL
+
+ $EDF_FILE_PREFIX
+ $EDF_FILE_SUFFIX
+ $EDF_BMC_PREFIX
+ $EDF_BMC_SUFFIX
+ $EDF_FV_PREFIX
+ $EDF_FV_SUFFIX
+ $EDF_PARAM_PREFIX
+ $EDF_PARAM_SUFFIX
+
+ $EDF_SAMPLE_PARAMS
+ $EDF_TREAT_PARAMS
+ $EDF_EXTRACT_PARAMS
+ $EDF_IP_PARAMS
+ $EDF_LABEL_PARAMS
+ $EDF_HYB_PARAMS
+ $EDF_SCAN_PARAMS
+ $EDF_FEXT_PARAMS
+ $EDF_NORM_PARAMS
+ $EDF_TRXN_PARAMS
+
+ $EDF_NORM_STATS
+ $EDF_FEXT_STATS
+ $EDF_TRXN_STATS
+
+ $EDF_HYB_HARDWARE
+ $EDF_SCAN_HARDWARE
+ $EDF_HYB_HW_PARAMS
+ $EDF_SCAN_HW_PARAMS
+
+ $OE_CAT_ACTION
+ $OE_CAT_BIOSAMPLETYPE
+ $OE_CAT_MATERIALTYPE
+ $OE_CAT_PROTOCOLTYPE
+ $OE_CAT_SOFTWARETYPE
+ $OE_CAT_HARDWARETYPE
+ $OE_CAT_DERIVEDBIOASSAYTYPE
+ $OE_CAT_EXPERIMENTALFACTORCATEGORY
+ $OE_CAT_DATATYPE
+ $OE_CAT_SCALE
+ $OE_CAT_EXPERIMENTDESIGNTYPE
+ $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE
+ $OE_CAT_PUBLICATIONTYPE
+ $OE_CAT_RELEASEDATE
+ $OE_CAT_SUBMISSIONDATE
+ $OE_CAT_ROLE
+
+ $OE_VAL_SYNTHETICDNA
+ $OE_VAL_SYNTHETICRNA
+ $OE_VAL_TOTALRNA
+ $OE_VAL_GENOMICDNA
+ $OE_VAL_WHOLEORGANISM
+ $OE_VAL_ORGANISMPART
+ $OE_VAL_EXTRACT
+ $OE_VAL_NOTEXTRACT
+ $OE_VAL_FLOAT
+ $OE_VAL_UNKNOWN
+ $OE_VAL_LINEARSCALE
+ $OE_VAL_JOURNALARTICLE
+ $OE_VAL_GROW
+ $OE_VAL_POOL
+ $OE_VAL_SPECIFIEDBIOMATERIALACTION
+ $OE_VAL_NUCLEICACIDEXTRACTION
+ $OE_VAL_IMMUNOPRECIPITATE
+ $OE_VAL_LABELING
+ $OE_VAL_NORMALIZATION
+ $OE_VAL_HYBRIDIZATION
+ $OE_VAL_SCANNING
+ $OE_VAL_FEATUREEXTRACTION
+ $OE_VAL_SCANNING_SOFTWARE
+ $OE_VAL_ANALYSIS_SOFTWARE
+ $OE_VAL_TRANSFORMATION_SOFTWARE
+ $OE_VAL_SUBMITTER
+ $OE_VAL_DATA_CODER
+ $OE_VAL_CURATOR
+ $OE_VAL_INVESTIGATOR
+
+ $OE_VAL_CHIP_CHIP
+
+ $AE_LABELCOMPOUND_PREFIX
+ $AE_CHANNEL_PREFIX
+
+ $MAGE_UNITS
+);
+
+#####################################################################
+# NOTE: all column headers are lowercased during parsing; therefore #
+# only lowercase tags can be used here. #
+#####################################################################
+
+#######################
+# EDF column headings #
+#######################
+
+# Top-level experiment information
+Readonly our $EDF_EXPTACCESSION => 'accession';
+Readonly our $EDF_EXPTDOMAIN => 'domain';
+Readonly our $EDF_EXPTNAME => 'name';
+Readonly our $EDF_EXPTDESCRIPTION => 'description';
+Readonly our $EDF_EXPTRELEASEDATE => 'release_date';
+Readonly our $EDF_EXPTSUBMITTER => 'submitter';
+Readonly our $EDF_EXPTDATACODER => 'data_coder';
+Readonly our $EDF_EXPTCURATOR => 'curator';
+Readonly our $EDF_EXPTINVESTIGATOR => 'investigator';
+Readonly our $EDF_EXPTORGANIZATION => 'organization';
+Readonly our $EDF_EXPTADDRESS => 'address';
+Readonly our $EDF_EXPTSUBMITTER_EMAIL => 'submitter_email';
+Readonly our $EDF_EXPTDATACODER_EMAIL => 'data_coder_email';
+Readonly our $EDF_EXPTCURATOR_EMAIL => 'curator_email';
+Readonly our $EDF_EXPTINVESTIGATOR_EMAIL => 'investigator_email';
+Readonly our $EDF_EXPTSUBMISSIONDATE => 'submission_date';
+Readonly our $EDF_EXPTDESIGNTYPE => 'experiment_design_type';
+Readonly our $EDF_EXPTQUALITYCONTROL => 'quality_control';
+Readonly our $EDF_EXPTCURATEDNAME => 'ae_display_name';
+Readonly our $EDF_EXPTURI => 'uri';
+Readonly our $EDF_EXPTGEORELEASEDATE => 'geo_release_date';
+Readonly our $EDF_EXPTSECONDARYACCESSION => 'secondary_accession';
+
+=pod
+
+=head2 Experiment section: row names
+
+=over 1
+
+=item accession
+
+A string unique to the experiment. Used in all identifiers, and as a
+top-level Experiment identifier. Synonymous with the ArrayExpress
+accession number for an experiment. Example: E-MEXP-100
+
+=item domain
+
+A string identifying the origin of the information provided. Typically
+this will be an internet domain name such as "ebi.ac.uk". This string
+is used to define the namespace of the MAGE identifiers created.
+
+=item name
+
+The name of the experiment. The forms the name attribute of the
+top-level Experiment object.
+
+=item description
+
+A short description of the experiment. This is inserted into a
+Description text attribute attached to the top-level Experiment
+object.
+
+=item release_date
+
+In the form YYYY-MM-DDB<T>hh:mm:ssB<Z>. This is currently used in a
+NameValueType (name ArrayExpressReleaseDate) in the top-level
+Experiment object.
+
+=item submission_date
+
+In the form YYYY-MM-DDB<T>hh:mm:ssB<Z>. This is currently used in a
+NameValueType (name ArrayExpressSubmissionDate) in the top-level
+Experiment object.
+
+=item experiment_design_type
+
+A comma-separated list of MO ExperimentDesignType terms used in the
+top-level ExperimentDesign object.
+
+=item quality_control
+
+A comma-separated list of MO QualityControlDescriptionType used in the
+top-level ExperimentDesign object.
+
+=item submitter
+
+The name of the person submitting the experiment. This is used to
+create a Person object in the AuditAndSecurity package with a
+Roles:submitter OntologyEntry. The Person object is referenced in the
+Experiment package.
+
+=item submitter_email
+
+The email address of the person submitting the experiment.
+
+=item data_coder
+
+The name of the person responsible for coding the experiment into
+MAGE-ML. This is used to create a Person object in the
+AuditAndSecurity package with a Roles:data_coder OntologyEntry. The
+Person object is referenced in the Experiment package.
+
+=item data_coder_email
+
+The email address of the person who coded the experiment in MAGE-ML.
+
+=item curator
+
+The name of the person who curated the experiment. This is used to
+create a Person object in the AuditAndSecurity package with a
+Roles:curator OntologyEntry. The Person object is referenced in the
+Experiment package.
+
+=item curator_email
+
+The email address of the person who curated the experiment.
+
+=item investigator
+
+The name of the primary investigator on the experiment. This is used
+to create a Person object in the AuditAndSecurity package with a
+Roles:curator OntologyEntry. The Person object is referenced in the
+Experiment package.
+
+=item investigator_email
+
+The email address of the primary investigator.
+
+=item organization
+
+The name of the organisation to which the submitter is
+affiliated. This is used to create an Organization MAGE object, which
+is then associated with each Person object.
+
+=item address
+
+The address of the organisation to which the submitter is
+affiliated.
+
+=item ae_display_name
+
+The name of the experiment to be displayed in the ArrayExpress
+repository interface. This will typically be added by the ArrayExpress
+curators after data submission.
+
+=item URI
+
+A URI pointing to an alternative location for the experimental data,
+e.g. in a public repository database.
+
+=item geo_release_date
+
+For processing of GEO experiments; this value is used to create a
+"GEOReleaseDate" NameValueType object associated with the top-level
+Experiment; this is used internally by the ArrayExpress database.
+
+=item secondary_accession
+
+This value is used to populate the "SecondaryAcession" NameValueType
+object associated with the top-level Experiment. This is used
+internally by ArrayExpress to represent e.g. GEO accession numbers.
+
+=back
+
+=cut
+
+# Publication info
+Readonly our $EDF_PUBTITLE => 'publication_title';
+Readonly our $EDF_PUBPAGES => 'pages';
+Readonly our $EDF_PUBJOURNAL => 'journal';
+Readonly our $EDF_PUBVOLUME => 'volume';
+Readonly our $EDF_PUBISSUE => 'issue';
+Readonly our $EDF_PUBYEAR => 'year';
+Readonly our $EDF_PUBAUTHORS => 'authors';
+Readonly our $EDF_PUBURI => 'publication_uri';
+Readonly our $EDF_PUBMEDID => 'pubmed_id';
+
+=pod
+
+The following are used in a BibliographicReference associated with a
+second Description object in the top-level Experiment object:
+
+=over 1
+
+=item publication_title
+
+The free-text title of any associated publication. This is currently
+assumed to have PublicationType journal_article (MO), although more
+control over this may be added in future. Used in
+
+=item authors
+
+A free-text list of publication authors.
+
+=item journal
+
+The name of the journal. This should be a standard Pubmed abbreviation.
+
+=item year
+
+Year of publication.
+
+=item volume
+
+Journal volume.
+
+=item issue
+
+Journal issue.
+
+=item pages
+
+Page range of journal article.
+
+=item publication_URI
+
+Any URI associated with the publication.
+
+=item pubmed_id
+
+The Pubmed ID associated with the publication.
+
+=back
+
+=cut
+
+# Protocol information
+Readonly our $EDF_PROTOCOLACCESSION => 'accession';
+Readonly our $EDF_PROTOCOLNAME => 'name';
+Readonly our $EDF_PROTOCOLTYPE => 'type';
+Readonly our $EDF_PROTOCOLTEXT => 'text';
+Readonly our $EDF_PROTOCOLPARAMS => 'parameters';
+
+=pod
+
+=head2 Protocol section: column names
+
+The following are all attached to individual protocols defined by
+successive lines in this section:
+
+=over 1
+
+=item accession
+
+Database (e.g. ArrayExpress) accession no.
+
+=item name
+
+Protocol name.
+
+=item type
+
+MO ProtocolType term; this is an optional field. In its absence,
+Tab2MAGE will use default ProtocolType terms based on how the protocol
+is used within the Hybridization section.
+
+=item text
+
+Protocol text.
+
+=item parameters
+
+Protocol parameters, listed in the following form:
+name1(unit1);name2(unit2);...
+
+=back
+
+=cut
+
+# BioMaterials
+Readonly our $EDF_BIOSOURCENAME => 'biosource';
+Readonly our $EDF_BIOSOURCETYPE => 'biosourcematerial';
+Readonly our $EDF_BIOSOURCEDESCRIPTION => 'biosourcedescription';
+Readonly our $EDF_BIOSAMPLENAME => 'sample';
+Readonly our $EDF_BIOSAMPLETYPE => 'samplematerial';
+Readonly our $EDF_EXTRACTNAME => 'extract';
+Readonly our $EDF_EXTRACTTYPE => 'extractmaterial';
+Readonly our $EDF_IMMUNOPRECIPITATENAME => 'immunoprecipitate';
+Readonly our $EDF_IMMUNOPRECIPITATETYPE => 'immunoprecipitatematerial';
+Readonly our $EDF_LABELEDEXTRACTNAME => 'labeledextract';
+Readonly our $EDF_LABELEDEXTRACTTYPE => 'labeledextractmaterial';
+Readonly our $EDF_HYBRIDIZATIONNAME => 'hybridization';
+Readonly our $EDF_SCANNAME => 'scan';
+Readonly our $EDF_NORMALIZATIONNAME => 'normalization';
+Readonly our $EDF_NORMALIZATIONTYPE => 'normalizationtype';
+Readonly our $EDF_TRANSFORMATIONNAME => 'transformation';
+Readonly our $EDF_TRANSFORMATIONTYPE => 'transformationtype';
+Readonly our $EDF_DYE => 'dye';
+Readonly our $EDF_IMAGE_FORMAT => 'ImageFormat';
+
+=pod
+
+=head2 Hybridization section: column names
+
+=over 1
+
+=item BioSource
+
+Arbitrary name for a BioSource. This term is used as a unique
+identifier within a Tab2MAGE run to determine correct linking between
+objects. It may however be omitted, in favour of using the set of
+BioMaterialCharacteristics associated with a BioMaterial as the sole
+indicator of BioSource identity.
+
+=item Sample
+
+Arbitrary name for a BioSample (associated with a
+BioSampleType:not_extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a BioSample
+name constructed from the raw data filename is used instead.
+
+=item Extract
+
+Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking
+between objects; may be omitted if desired, in which case a Extract
+name constructed from the raw data filename is used instead.
+
+=item Immunoprecipitate
+
+Arbitrary name for a BioSample (associated with a
+BioSampleType:extract OntologyEntry). Used to control linking between
+objects; may be omitted if desired, in which case an Immunoprecipitate
+name constructed from the raw data filename is used instead. These
+objects are used in ChIP experiments and may be ignored for expression
+or CGH studies.
+
+=item LabeledExtract
+
+Arbitrary name for a LabeledExtract. Used to control linking between
+objects; may be omitted if desired, in which case a LabeledExtract name
+constructed from the raw data filename and the label dye name is used
+instead.
+
+=item Dye
+
+Name of the dye linked to the labeled extract (e.g., Cy3, Cy5, biotin).
+
+=item BioSourceMaterial
+
+MO MaterialType term to be attached to the BioSource. Default: whole_organism
+
+=item SampleMaterial
+
+MO MaterialType term to be attached to the BioSample. Default: organism_part
+
+=item ExtractMaterial
+
+MO MaterialType term to be attached to the Extract. Default: total_RNA
+
+=item ImmunoprecipitateMaterial
+
+MO MaterialType term to be attached to the Immunoprecipitate. Default: genomic_DNA
+
+=item LabeledExtractMaterial
+
+MO MaterialType term to be attached to the LabeledExtract. Default: synthetic_DNA
+
+=item BioSourceDescription
+
+Free-text description to attached to the BioSource (this should be
+used sparingly, if at all).
+
+=item Hybridization
+
+Arbitrary name for a hybridization. Used to control linking between
+objects; may be omitted if desired, in which case a Hybridization name
+constructed from the raw data filename is used instead.
+
+=item Scan
+
+Arbitrary name for a scanning event. Used to control linking between
+objects; may be omitted if desired, in which case a Scan name
+constructed from the raw data filename is used instead.
+
+=item Normalization
+
+Arbitrary name for a normalization procedure. Used to control linking
+between objects; may be omitted if desired, in which case a
+Normalization name constructed from the normalized data filename is
+used instead.
+
+=item NormalizationType
+
+MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.
+
+=item Transformation
+
+Arbitrary name for a data transformation procedure. Used to control linking
+between objects; may be omitted if desired.
+
+=item TransformationType
+
+MO DerivedBioAssayType term, attached to the relevant
+DerivedBioAssayData object.
+
+=item ImageFormat
+
+MO ImageFormat term. Only used if File[image] columns have been
+included in the spreadsheet. Used to create Image objects.
+
+=back
+
+=cut
+
+# Protocol accessions
+Readonly our $EDF_GROW_PROTOCOL => 'protocol[grow]';
+Readonly our $EDF_TREAT_PROTOCOL => 'protocol[treatment]';
+Readonly our $EDF_EXTRACT_PROTOCOL => 'protocol[extraction]';
+Readonly our $EDF_POOL_PROTOCOL => 'protocol[pool]';
+Readonly our $EDF_LABEL_PROTOCOL => 'protocol[labeling]';
+Readonly our $EDF_IP_PROTOCOL => 'protocol[immunoprecipitate]';
+Readonly our $EDF_HYB_PROTOCOL => 'protocol[hybridization]';
+Readonly our $EDF_SCAN_PROTOCOL => 'protocol[scanning]';
+Readonly our $EDF_FEXT_PROTOCOL => 'protocol[image_analysis]';
+Readonly our $EDF_NORM_PROTOCOL => 'protocol[normalization]';
+Readonly our $EDF_TRXN_PROTOCOL => 'protocol[transformation]';
+
+=pod
+
+=over 1
+
+=item Protocol[grow]
+
+Accession number for the "growth" protocol (BioSource->BioSample
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: grow
+
+=item Protocol[treatment]
+
+Accession number for the "treatment" protocol (BioSource->BioSample
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: specified_biomaterial_action
+
+=item Protocol[extraction]
+
+Accession number for the "extraction" protocol (BioSample->Extract
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: nucleic_acid_extraction
+
+=item Protocol[pool]
+
+Accession number for the "pooling" protocol (BioSample->Extract
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: pool
+
+=item Protocol[labeling]
+
+Accession number for the "labeling" protocol (Extract->LabeledExtract
+Treatment). The accession number should either be present in the
+protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: labeling
+
+=item Protocol[immunoprecipitate]
+
+Accession number for the "immunoprecipitation" protocol
+(Extract->Immunoprecipitate Treatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Omit if Immunoprecipitates are not used
+in the experiment. Default ProtocolType: immunoprecipitate
+
+=item Protocol[hybridization]
+
+Accession number for the "hybridization" protocol (PhysicalBioAssay
+BioAssayCreation). The accession number should either be present in
+the protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: hybridization
+
+=item Protocol[scanning]
+
+Accession number for the "image acquisition" protocol
+(PhysicalBioAssay BioAssayTreatment). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: image_acquisition;
+note however that if Protocol[image_analysis] is not specified then
+the scanning protocol defaults to feature_extraction. This is an
+ArrayExpress-specific behaviour and relates to the appearance of the
+experiment in the ArrayExpress web interface.
+
+=item Protocol[image_analysis]
+
+Accession number for the "feature extraction" protocol
+(MeasuredBioAssay FeatureExtraction). The accession number should
+either be present in the protocol section of the spreadsheet, or
+pre-existing in ArrayExpress. Default ProtocolType: feature_extraction
+
+=item Protocol[normalization]
+
+Accession number for the "normalization" protocol (DerivedBioAssayData
+ProducerTransformation). The accession number should either be present
+in the protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: bioassay_data_transformation
+
+=item Protocol[transformation]
+
+Accession number for the "transformation" protocol (DerivedBioAssayData
+ProducerTransformation). The accession number should either be present
+in the protocol section of the spreadsheet, or pre-existing in
+ArrayExpress. Default ProtocolType: bioassay_data_transformation
+
+=back
+
+=cut
+
+# Software
+
+Readonly our $EDF_SCAN_SOFTWARE => 'software[scanning]';
+Readonly our $EDF_FEXT_SOFTWARE => 'software[image_analysis]';
+Readonly our $EDF_NORM_SOFTWARE => 'software[normalization]';
+Readonly our $EDF_TRXN_SOFTWARE => 'software[transformation]';
+
+=pod
+
+Software is created in the Protocol package, and referenced as
+SoftwareApplication in the appropriate ProtocolApplication (see
+above).
+
+=over 1
+
+=item Software[scanning]
+
+Name of the scanning software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=item Software[image_analysis]
+
+Name of the feature extraction software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=item Software[normalization]
+
+Name of the normalization software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=item Software[transformation]
+
+Name of the data transformation software, followed by its version in
+parentheses. The Software name is used to create a Software object in
+the Protocol package, and the version is inserted into the relevant
+SoftwareApplication objects.
+
+=back
+
+=cut
+
+# Array information
+Readonly our $EDF_ARRAYACCESSION => 'array[accession]';
+Readonly our $EDF_ARRAYSERIAL => 'array[serial]';
+
+=pod
+
+=over 1
+
+=item Array[accession]
+
+ArrayExpress array accession number (e.g., A-MEXP-1). This is used in
+the Array package, and also to create Feature and Reporter identifiers
+for the DesignElementDimensions.
+
+=item Array[serial]
+
+Serial or lot number of the array. This is used to define
+ArrayManufacture objects within the Array package.
+
+=back
+
+=cut
+
+# Compound headings
+Readonly our $EDF_FILE_PREFIX => 'file[';
+Readonly our $EDF_FILE_SUFFIX => ']';
+Readonly our $EDF_BMC_PREFIX => 'biomaterialcharacteristics[';
+Readonly our $EDF_BMC_SUFFIX => ']';
+Readonly our $EDF_FV_PREFIX => 'factorvalue[';
+Readonly our $EDF_FV_SUFFIX => ']';
+Readonly our $EDF_PARAM_PREFIX => 'parameter[';
+Readonly our $EDF_PARAM_SUFFIX => ']';
+
+=pod
+
+=over 1
+
+=item File[raw]
+
+Name of the raw data file for a given hybridization.
+
+=item File[normalized]
+
+Name of the normalized data file for a given hybridization/normalization.
+
+=item File[transformed]
+
+Name of the transformed final data matrix file for the experiment.
+
+=item File[image]
+
+URI locating the Image object associated with an ImageAcquisition
+(scanning) event. ArrayExpress does not store images, although we can
+link to images stored on external web sites, where desired.
+
+=item File[cdf]
+
+Affymetrix array library file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data files,
+although for standard Affymetrix arrays the actual CDF file does not
+need to be supplied.
+
+=item File[exp]
+
+Affymetrix experiment description file pertaining to a given
+hybridization. Required for correct parsing of Affymetrix data files.
+
+=item BioMaterialCharacteristics[<I<category>>]
+
+MO term describing some feature of the BioSource (e.g., Genotype, Sex,
+DiseaseState, etc.). As many BioMaterialCharacteristics columns may be
+used as are desired. Each column should contain values from a
+different B<category> within the MGED Ontology.
+
+=item FactorValue[<I<category>>]
+
+MO term describing a FactorValue associated with a given hybridization
+(e.g., Genotype, Sex, DiseaseState, etc.). As many FactorValue columns
+may be used as are desired. Each column should contain values from a
+different B<category> within the MGED Ontology.
+
+=item Parameter[<I<parameter name>>]
+
+Parameter values for the parameters declared in the Protocol section
+of the spreadsheet. Note that a protocol must be declared in the
+spreadsheet in order to be able to use parameters with it.
+
+=back
+
+=cut
+
+# These are for internal use only. These are name=>value hashes thoughout.
+Readonly our $EDF_SAMPLE_PARAMS => 'TMparameters[sample]';
+Readonly our $EDF_TREAT_PARAMS => 'TMparameters[treat]';
+Readonly our $EDF_EXTRACT_PARAMS => 'TMparameters[extract]';
+Readonly our $EDF_IP_PARAMS => 'TMparameters[ip]';
+Readonly our $EDF_LABEL_PARAMS => 'TMparameters[label]';
+Readonly our $EDF_HYB_PARAMS => 'TMparameters[hybridization]';
+Readonly our $EDF_SCAN_PARAMS => 'TMparameters[scan]';
+Readonly our $EDF_FEXT_PARAMS => 'TMparameters[fext]';
+Readonly our $EDF_NORM_PARAMS => 'TMparameters[norm]';
+Readonly our $EDF_TRXN_PARAMS => 'TMparameters[trxn]';
+Readonly our $EDF_FEXT_STATS => 'TMstatistics[fext]';
+Readonly our $EDF_NORM_STATS => 'TMstatistics[norm]';
+Readonly our $EDF_TRXN_STATS => 'TMstatistics[trxn]';
+
+# Internal use only - Affymetrix. These will be linked to fully-formed objects
+Readonly our $EDF_HYB_HARDWARE => 'TMhardware[hybridization]';
+Readonly our $EDF_SCAN_HARDWARE => 'TMhardware[scan]';
+Readonly our $EDF_HYB_HW_PARAMS => 'TMhwparams[hybridization]';
+Readonly our $EDF_SCAN_HW_PARAMS => 'TMhwparams[scan]';
+
+##################
+# Ontology terms #
+##################
+
+# Categories
+Readonly our $OE_CAT_ACTION => 'Action';
+Readonly our $OE_CAT_BIOSAMPLETYPE => 'BioSampleType';
+Readonly our $OE_CAT_MATERIALTYPE => 'MaterialType';
+Readonly our $OE_CAT_PROTOCOLTYPE => 'ProtocolType';
+Readonly our $OE_CAT_SOFTWARETYPE => 'SoftwareType';
+Readonly our $OE_CAT_HARDWARETYPE => 'HardwareType';
+Readonly our $OE_CAT_DERIVEDBIOASSAYTYPE => 'DerivedBioAssayType';
+Readonly our $OE_CAT_EXPERIMENTALFACTORCATEGORY =>
+ 'ExperimentalFactorCategory';
+Readonly our $OE_CAT_DATATYPE => 'DataType';
+Readonly our $OE_CAT_SCALE => 'Scale';
+Readonly our $OE_CAT_EXPERIMENTDESIGNTYPE => 'ExperimentDesignType';
+Readonly our $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE =>
+ 'QualityControlDescriptionType';
+Readonly our $OE_CAT_PUBLICATIONTYPE => 'PublicationType';
+Readonly our $OE_CAT_RELEASEDATE => 'ReleaseDate';
+Readonly our $OE_CAT_SUBMISSIONDATE => 'SubmissionDate';
+Readonly our $OE_CAT_ROLE => 'Roles';
+
+# Values
+Readonly our $OE_VAL_SYNTHETICDNA => 'synthetic_DNA';
+Readonly our $OE_VAL_SYNTHETICRNA => 'synthetic_RNA';
+Readonly our $OE_VAL_TOTALRNA => 'total_RNA';
+Readonly our $OE_VAL_GENOMICDNA => 'genomic_DNA';
+Readonly our $OE_VAL_WHOLEORGANISM => 'whole_organism';
+Readonly our $OE_VAL_ORGANISMPART => 'organism_part';
+Readonly our $OE_VAL_EXTRACT => 'extract';
+Readonly our $OE_VAL_NOTEXTRACT => 'not_extract';
+Readonly our $OE_VAL_FLOAT => 'float';
+Readonly our $OE_VAL_UNKNOWN => 'unknown';
+Readonly our $OE_VAL_LINEARSCALE => 'linear_scale';
+Readonly our $OE_VAL_JOURNALARTICLE => 'journal_article';
+Readonly our $OE_VAL_GROW => 'grow';
+Readonly our $OE_VAL_POOL => 'pool';
+Readonly our $OE_VAL_SPECIFIEDBIOMATERIALACTION =>
+ 'specified_biomaterial_action';
+Readonly our $OE_VAL_NUCLEICACIDEXTRACTION => 'nucleic_acid_extraction';
+Readonly our $OE_VAL_IMMUNOPRECIPITATE => 'immunoprecipitate';
+Readonly our $OE_VAL_LABELING => 'labeling';
+Readonly our $OE_VAL_NORMALIZATION => 'bioassay_data_transformation';
+Readonly our $OE_VAL_HYBRIDIZATION => 'hybridization';
+Readonly our $OE_VAL_SCANNING => 'image_acquisition';
+Readonly our $OE_VAL_FEATUREEXTRACTION => 'feature_extraction';
+Readonly our $OE_VAL_SCANNING_SOFTWARE => 'image_acquisition_software';
+Readonly our $OE_VAL_ANALYSIS_SOFTWARE => 'feature_extraction_software';
+Readonly our $OE_VAL_TRANSFORMATION_SOFTWARE =>
+ 'bioassay_data_transformation_software';
+Readonly our $OE_VAL_SUBMITTER => 'submitter';
+Readonly our $OE_VAL_CURATOR => 'curator';
+Readonly our $OE_VAL_DATA_CODER => 'data_coder';
+Readonly our $OE_VAL_INVESTIGATOR => 'investigator';
+
+Readonly our $OE_VAL_CHIP_CHIP => 'binding_site_identification_design';
+
+###############################
+# MAGE identifier conventions #
+###############################
+Readonly our $AE_LABELCOMPOUND_PREFIX => 'ebi.ac.uk:LabelCompound:';
+Readonly our $AE_CHANNEL_PREFIX => 'ebi.ac.uk:Channel:';
+
+########################
+# MAGE Unit subclasses #
+########################
+
+Readonly our $MAGE_UNITS => {
+ TemperatureUnit => [qw(degree_C degree_F K)],
+ MassUnit => [qw(kg g mg ug ng pg fg)],
+ VolumeUnit => [qw(mL cc dL L uL nL pL fL)],
+
+ # m (meters) not supported: it clashes with m (minutes).
+ DistanceUnit => [qw(fm pm nm um mm cm)],
+ TimeUnit => [qw(years months weeks d h m s us)],
+ QuantityUnit => [qw(mol amol fmol pmol nmol umol mmol molecules)],
+ ConcentrationUnit => [
+ qw(M mM uM nM pM fM mg_per_mL mL_per_L g_per_L gram_percent mass_per_volume_percent mass_per_mass_percent)
+ ],
+};
+
+##########################
+# Validation subroutines #
+##########################
+
+=pod
+
+=head1 VALIDATION SUBROUTINES
+
+=over 1
+
+=item validate_expriment_section($test_arrayref, $error_fh)
+
+=item validate_protocol_section($test_arrayref, $error_fh)
+
+=item validate_hybridization_section($test_arrayref, $error_fh)
+
+Each of these subroutines simply takes a reference to an array
+containing the list of headings to be checked, and prints warnings on
+STDERR (and on the optional error filehandle) if it finds unrecognized
+headings. No further action is taken; these subroutines merely serve
+as a warning to the user.
+
+=back
+
+=cut
+
+sub _check_section {
+
+ my ( $testarray, $matcharray, $section, $error_fh ) = @_;
+
+ my $error = 0;
+
+ # Quote brackets and parentheses in each pattern to be matched
+ foreach my $entry (@$matcharray) {
+ $entry =~ s/([\[\]\(\)])/\\$1/g;
+ }
+
+ # Check each heading in the array to be tested
+ foreach my $header (@$testarray) {
+
+ # Print to STDERR and to the logfile
+ unless ( map { $header =~ m/^$_$/ } @$matcharray ) {
+ my $error_str
+ = qq{Warning: Unknown column/row name in $section section will be ignored: "$header"\n};
+ print STDERR $error_str;
+ print $error_fh ($error_str) if $error_fh;
+ $error |= $CONFIG->get_ERROR_PARSEBAD();
+ }
+ }
+
+ return $error;
+}
+
+sub validate_experiment_section {
+
+ my ( $testarray, $error_fh ) = @_;
+
+ my $matcharray = [
+ $EDF_EXPTACCESSION, $EDF_EXPTDOMAIN,
+ $EDF_EXPTNAME, $EDF_EXPTDESCRIPTION,
+ $EDF_EXPTRELEASEDATE, $EDF_EXPTSUBMISSIONDATE,
+ $EDF_EXPTSUBMITTER, $EDF_EXPTCURATOR,
+ $EDF_EXPTDATACODER, $EDF_EXPTINVESTIGATOR,
+ $EDF_EXPTORGANIZATION, $EDF_EXPTADDRESS,
+ $EDF_EXPTSUBMITTER_EMAIL, $EDF_EXPTCURATOR_EMAIL,
+ $EDF_EXPTDATACODER_EMAIL, $EDF_EXPTINVESTIGATOR_EMAIL,
+ $EDF_EXPTDESIGNTYPE, $EDF_EXPTQUALITYCONTROL,
+ $EDF_EXPTCURATEDNAME, $EDF_EXPTURI,
+ $EDF_EXPTGEORELEASEDATE, $EDF_EXPTSECONDARYACCESSION,
+ $EDF_PUBTITLE,
+ $EDF_PUBPAGES, $EDF_PUBJOURNAL,
+ $EDF_PUBVOLUME, $EDF_PUBISSUE,
+ $EDF_PUBYEAR, $EDF_PUBAUTHORS,
+ $EDF_PUBURI, $EDF_PUBMEDID,
+ ];
+
+ my $error |= _check_section(
+ $testarray,
+ $matcharray,
+ 'Experiment',
+ $error_fh,
+ );
+
+ return $error;
+
+}
+
+sub validate_protocol_section {
+
+ my ( $testarray, $error_fh ) = @_;
+
+ my $matcharray = [
+ $EDF_PROTOCOLACCESSION, $EDF_PROTOCOLNAME, $EDF_PROTOCOLTYPE,
+ $EDF_PROTOCOLTEXT, $EDF_PROTOCOLPARAMS,
+ ];
+
+ my $error |= _check_section(
+ $testarray,
+ $matcharray,
+ 'Protocol',
+ $error_fh,
+ );
+
+ return $error;
+
+}
+
+sub validate_hybridization_section {
+
+ my ( $testarray, $error_fh ) = @_;
+
+ my $matcharray = [
+ $EDF_BIOSOURCENAME,
+ $EDF_BIOSOURCETYPE,
+ $EDF_BIOSOURCEDESCRIPTION,
+ $EDF_BIOSAMPLENAME,
+ $EDF_BIOSAMPLETYPE,
+ $EDF_EXTRACTNAME,
+ $EDF_EXTRACTTYPE,
+ $EDF_IMMUNOPRECIPITATENAME,
+ $EDF_IMMUNOPRECIPITATETYPE,
+ $EDF_LABELEDEXTRACTNAME,
+ $EDF_LABELEDEXTRACTTYPE,
+ $EDF_HYBRIDIZATIONNAME,
+ $EDF_SCANNAME,
+ $EDF_NORMALIZATIONNAME,
+ $EDF_NORMALIZATIONTYPE,
+ $EDF_TRANSFORMATIONNAME,
+ $EDF_TRANSFORMATIONTYPE,
+ $EDF_DYE,
+
+ $EDF_GROW_PROTOCOL,
+ $EDF_TREAT_PROTOCOL,
+ $EDF_EXTRACT_PROTOCOL,
+ $EDF_POOL_PROTOCOL,
+ $EDF_LABEL_PROTOCOL,
+ $EDF_IP_PROTOCOL,
+ $EDF_HYB_PROTOCOL,
+ $EDF_SCAN_PROTOCOL,
+ $EDF_FEXT_PROTOCOL,
+ $EDF_NORM_PROTOCOL,
+ $EDF_TRXN_PROTOCOL,
+
+ $EDF_SCAN_SOFTWARE,
+ $EDF_FEXT_SOFTWARE,
+ $EDF_NORM_SOFTWARE,
+ $EDF_TRXN_SOFTWARE,
+
+ $EDF_IMAGE_FORMAT,
+
+ $EDF_ARRAYACCESSION,
+ $EDF_ARRAYSERIAL,
+
+ "$EDF_FILE_PREFIX.*$EDF_FILE_SUFFIX",
+ "$EDF_BMC_PREFIX.*$EDF_BMC_SUFFIX",
+ "$EDF_FV_PREFIX.*$EDF_FV_SUFFIX",
+ "$EDF_PARAM_PREFIX.*$EDF_PARAM_SUFFIX",
+ ];
+
+ my $error |= _check_section(
+ $testarray,
+ $matcharray,
+ 'Hybridization',
+ $error_fh,
+ );
+
+ return $error;
+
+}
+
+=pod
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/MIAMExpress.pm b/lib/ArrayExpress/Curator/MIAMExpress.pm
new file mode 100644
index 0000000..43e9157
--- /dev/null
+++ b/lib/ArrayExpress/Curator/MIAMExpress.pm
@@ -0,0 +1,2509 @@
+#!/usr/bin/env perl
+#
+# MIAMExpress.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2004 ArrayExpress team, EBI
+#
+# $Id: MIAMExpress.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Curator::MIAMExpress;
+
+use strict;
+use warnings;
+
+require DBI;
+require DBD::mysql;
+use Tie::IxHash;
+use Carp;
+use IO::File;
+use Class::Std;
+use Readonly;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Entrez_list qw(
+ parse_entrez_names
+);
+
+use ArrayExpress::Curator::Report qw(
+ datafile_consistency_table
+ biomaterials_report
+ format_description
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::Database qw(
+ parse_adf
+ get_ae_array_details
+);
+
+require ArrayExpress::Datafile;
+
+use base 'ArrayExpress::Curator::ExperimentChecker';
+
+my %mx_login : ATTR( :get<mx_login>, :init_arg<mx_login>, :default<undef> );
+my %mx_title : ATTR( :get<mx_title>, :init_arg<mx_title>, :default<undef> );
+my %tsubmis_sysuid : ATTR( :default<undef> );
+my %submission_filebase : ATTR( :default<undef> );
+my %mx_dbh : ATTR( :default<undef> );
+
+# Map each of the know experimental factors (TCTLVCB_DESCR values) to
+# the relevant column(s) in the TSAMPLE table. This is used in
+# reporting both fv->hyb relationships and the full sample annotation
+# report.
+Readonly my %CATEGORY_SQL_MAP => (
+
+ # These are the core factors currently supported by MX.
+ age => [
+ qw(TSAMPLE_AGERANGE_MIN TSAMPLE_AGERANGE_MAX TSAMPLE_TIME_UNIT TSAMPLE_TIME_POINT)
+ ],
+ compound => [],
+ disease_state => [qw(TSAMPLE_DISEASE_STATE)],
+ cell_line => [qw(TSAMPLE_CELL_LINE)],
+ cell_type => [qw(TSAMPLE_TARGET_CELL_TYPE)],
+ developmental_stage => [qw(TSAMPLE_DEV_STAGE)],
+ dose => [],
+ genotype => [qw(TSAMPLE_INDIVIDUAL_GEN)],
+ organism_part => [qw(TSAMPLE_ORGANISM_PART)],
+ sex => [qw(TSAMPLE_SEX)],
+ organism => [qw(TSAMPLE_TAXID)],
+ strain_or_line => [qw(TSAMPLE_STRAIN)],
+ temperature => [],
+ time => [],
+ other => [],
+ clinical_information => [qw(TSAMPLE_ADDITIONAL)],
+ individual => [qw(TSAMPLE_INDIVIDUAL)],
+ irradiate => [],
+ 'ChiP-antibody' => [], # typo in MIAMExpress;
+ 'ChIP-antibody' => [], # the correct version, in case they ever fix it.
+
+ # The following are not currently factors, the keys may therefore change.
+ provider => [qw(TSAMPLE_CELL_PROVIDER)],
+ type => [qw(TSAMPLE_SAMPLE_TYPE)],
+ genetic_modification => [qw(TSAMPLE_GENETIC_VARIATION)],
+ separation_technique => [qw(TSAMPLE_SEPARATION_TECH)],
+);
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $mx_login{$id} && $mx_title{$id} ) {
+ croak("Error: Both mx_title and mx_login must be set for MIAMExpress checking.");
+ }
+
+ # Set the locations for the logfiles. N.B. get_tsubmis_sysuid()
+ # connects to the MX database to retrieve TSUBMIS_SYSUID here.
+ my $logfile_str = sprintf(
+ "%s_sub%s",
+ $self->get_mx_login(),
+ $self->get_tsubmis_sysuid(),
+ );
+ $self->localize_logfiles({
+ name => $logfile_str,
+ directory => $self->get_submission_filebase(),
+ });
+
+ return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ # Quick check to see what we've been passed.
+ unless ( $self->get_mx_login() && $self->get_mx_title() ) {
+ croak(<<'END_ERROR');
+Error: MIAMExpress checker must be passed both "mx_login" and "mx_title" options.
+END_ERROR
+ }
+
+ my $filelist;
+ my $hyb_ids; # Used to map FGEM column headings to hybs.
+ my $norm_ids = {}; # Empty for MX; cannot map MX FGEM using Norm IDs.
+
+ print STDOUT ("Running in MIAMExpress mode...\n");
+
+ ( $filelist, $hyb_ids ) = $self->retrieve_MX_experiment();
+
+ return ( $filelist, $hyb_ids, $norm_ids );
+}
+
+sub get_mx_dbh : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Set up the DB connection, unless it's already been done.
+ unless ( $mx_dbh{ident $self} ) {
+
+ unless ( $CONFIG->get_MX_DSN() ) {
+ croak(
+ "Error: MIAMExpress connection parameters"
+ . " not correctly set in site config YAML file.\n"
+ );
+ }
+
+ print STDOUT ("Connecting to MIAMExpress database...\n");
+ my $dbh = DBI->connect(
+ $CONFIG->get_MX_DSN(), $CONFIG->get_MX_USERNAME(),
+ $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS()
+ )
+ or croak("$DBI::errstr\n");
+
+ $mx_dbh{ident $self} = $dbh;
+ }
+
+ return $mx_dbh{ident $self};
+}
+
+sub get_tsubmis_sysuid {
+
+ my ( $self ) = @_;
+
+ unless ( defined $tsubmis_sysuid{ident $self} ) {
+ my ( $sysuid, $filebase ) = $self->query_mx_for_subs_info();
+ $tsubmis_sysuid{ident $self} = $sysuid;
+ $submission_filebase{ident $self} = $filebase;
+ }
+
+ return $tsubmis_sysuid{ident $self};
+}
+
+sub get_submission_filebase {
+
+ my ( $self ) = @_;
+
+ unless ( defined $submission_filebase{ident $self} ) {
+ my ( $sysuid, $filebase ) = $self->query_mx_for_subs_info();
+ $tsubmis_sysuid{ident $self} = $sysuid;
+ $submission_filebase{ident $self} = $filebase;
+ }
+
+ return $submission_filebase{ident $self};
+}
+
+sub _get_db_adf_features : PRIVATE {
+
+ my ( $self, $hyb_ids ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # First, get a list of ARRAY_DESIGNIDs
+ my %array_designs;
+ foreach my $hyb_sysuid ( values %$hyb_ids ) {
+ my $design_id = $self->_get_array_designid( $hyb_sysuid );
+
+ # This rationalises the list so we only parse each ADF once
+ $array_designs{$design_id}++;
+ }
+
+ # Next, process that list. Table retrieved from ArrayExpress, but
+ # only if we need to:
+ foreach my $design_id ( keys %array_designs ) {
+
+ if ( $design_id > 0 ) {
+
+ # ADF comes from MIAMExpress
+ my ( $adf_filename, $arrayname )
+ = $self->_get_adf_filename( $design_id );
+
+ unless ( $adf_filename && $arrayname ) {
+ $self->logprint( 'error',
+ "DATABASE ERROR: Incomplete data returned by MIAMExpress for array design id $design_id.\n"
+ );
+
+ # This is an unlikely error; if this is tripped who knows how
+ # much else is broken?
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ else {
+ $self->logprint( 'error',
+ "Warning: Using ADF from MIAMExpress: $arrayname. Array not yet loaded and public in ArrayExpress.\n"
+ );
+
+ # Loading cannot occur if the array is not loaded.
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ if ( $self->get_skip_data_checks() ) {
+ print STDOUT ("Skipping ADF parsing.\n");
+ }
+ else {
+ my $db_adf_fh = IO::File->new( $adf_filename, '<' )
+ or croak("Error: Unable to open $adf_filename: $!\n");
+ my $array = parse_adf(
+ $db_adf_fh,
+ undef,
+ $self->get_reporter_prefix(),
+ $self->get_compseq_prefix(),
+ );
+ close($db_adf_fh);
+ $self->set_cached_array_design( $design_id, $array );
+ }
+ }
+
+ elsif ( $design_id < 0 ) {
+
+ # ADF comes from ArrayExpress.
+
+ # We actually want a positive database id.
+ my $arrayexpress_id = -$design_id;
+
+ $self->get_ae_arraydesign({
+ database_id => $arrayexpress_id,
+ });
+ }
+ }
+}
+
+sub _MX_populate_array_designs : PRIVATE {
+
+ my ( $self, $filelist, $hyb_ids ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ if ( $self->get_adf_filename || $self->get_array_accession ) {
+
+ $self->cache_user_supplied_arrays( $filelist );
+
+ }
+ else {
+
+ # Go to the database(s) and get as many ADFs as are linked to
+ # by the hybridizations in MIAMExpress.
+
+ # Get the associated ADF features from the database. These
+ # are stored in an ExperimentChecker method wrapping Storable
+ # caches on disk.
+
+ $self->_get_db_adf_features( $hyb_ids );
+
+ }
+
+ $self->populate_file_arraydesigns( $filelist );
+
+ return;
+}
+
+sub _check_db_publication : PRIVATE {
+
+ # Checks the associated publication details,prints warnings and/or
+ # errors to error log.
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TCTLVCB_VALUE, TPUBLIC_SYSUID from TPUBLIC, TCTLVCB
+where TPUBLIC_SUBID=?
+and TPUBLIC_PUBLICATION=TCTLVCB_SYSUID
+and TPUBLIC_DEL_STATUS='U' and TCTLVCB_DEL_STATUS='U'
+QUERY
+
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ my %publications;
+ while ( my $rowref = $sth->fetchrow_arrayref ) {
+
+ # publication sysuid => publication name
+ $publications{ $rowref->[1] } = $rowref->[0];
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Check through for others, get the appropriate name from TOTHERS
+ # and replace the value in the hash
+ foreach my $pub_sysuid ( keys %publications ) {
+
+ if ( $publications{$pub_sysuid} =~ m/other/i ) {
+
+ $self->logprint(
+ 'error',
+ qq{Warning: "Other" publication specified in submission.\n},
+ );
+ $self->add_error($CONFIG->get_ERROR_INNOCENT());
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_VALUE from TOTHERS
+where TOTHERS_PUBLICID=?
+and TOTHERS_DEL_STATUS='U'
+QUERY
+ $sth->execute($pub_sysuid) or croak($sth->errstr);
+
+ my $rowref = $sth->fetchrow_arrayref;
+ $publications{$pub_sysuid} = $rowref->[0];
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+ }
+ }
+
+ # Check through the publication names
+ my $entrez_approved = parse_entrez_names();
+ foreach my $name ( values %publications ) {
+ unless ( $entrez_approved->{$name} ) {
+ $self->logprint(
+ 'error',
+ "Warning: Non-standard publication name: $name\n",
+ );
+ $self->add_error($CONFIG->get_ERROR_INNOCENT());
+ }
+ }
+
+ return;
+}
+
+sub retrieve_MX_experiment : PRIVATE {
+
+ my ( $self ) = @_;
+
+ ###########################
+ # Connect to the database #
+ ###########################
+
+ print STDOUT ("Querying MIAMExpress for submission data... \n");
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ ##########################################
+ # Initialise top level dir and log files #
+ ##########################################
+
+ $self->logprint( 'error', "* MIAMExpress mode *\n\n" );
+ $self->logprint_line( 'error', 'DB query START' );
+
+ #######################
+ # Biomaterials report #
+ #######################
+
+ $self->_check_db_publication();
+
+ print STDOUT ("Writing BioMaterials report file... ");
+
+ # Get the associations between sample, extract, labeled extract, and
+ # hybs as a heirarchical hashref.
+ my ( $samples_to_hybs, $protocol_typemap, $protocol_others, $rc )
+ = $self->_get_db_objects();
+ $self->add_error($rc);
+
+ # Count the samples, extracts, LEs and hybs. FIXME to deal with NULLs
+ my %bm_summary;
+
+ # Sample
+ @{ $bm_summary{samples} }{ keys %$samples_to_hybs }
+ = values %$samples_to_hybs;
+ foreach my $sample ( values %$samples_to_hybs ) {
+
+ # Extract
+ @{ $bm_summary{extracts} }{ keys %$sample } = values %$sample;
+ foreach my $extract ( values %$sample ) {
+
+ # Label
+ @{ $bm_summary{labels} }{ keys %$extract } = values %$extract;
+ foreach my $label ( values %$extract ) {
+
+ # Hyb
+ @{ $bm_summary{hybs} }{ keys %$label } = values %$label;
+ }
+ }
+ }
+
+ # Count the contants of the hashrefs, print out the results.
+ my %bm_count;
+ foreach my $bm ( keys %bm_summary ) {
+ $bm_count{$bm} = scalar( grep { defined $_ } values %{ $bm_summary{$bm} } );
+ }
+
+ $self->logprint(
+ 'workflow',
+ "$bm_count{samples} samples -> ",
+ "$bm_count{extracts} extracts -> ",
+ "$bm_count{labels} labeled extracts -> ",
+ "$bm_count{hybs} hybridizations\n"
+ );
+
+ # Print out the trees, forward and reverse:
+ biomaterials_report(
+ $samples_to_hybs,
+ 'Samples to Hybridizations',
+ $self->log_fh('workflow')
+ );
+ my $hybs_to_samples = $self->_invert_heirarchy($samples_to_hybs);
+ biomaterials_report(
+ $hybs_to_samples,
+ 'Hybridizations to Samples',
+ $self->log_fh('workflow')
+ );
+
+ # Get relationships between FactorValues, Hybs and data files.
+ # $hyb_factors is hyb_id => {fv_string1 => 1, etc.} for reporting;
+ # $hyb_category_fvs is hyb_id => {category1 => {value_string1 => 1} }
+ # for mapping files to FVs.
+ my $hyb_category_fvs;
+ if ( $CONFIG->get_MX_EXTENDED_REPORT() ) {
+ my $hyb_factors;
+ ( $hyb_factors, $hyb_category_fvs ) = $self->_get_db_factor_assns();
+ $self->add_error($rc);
+ biomaterials_report(
+ $hyb_factors,
+ 'Hybridizations to FactorValues',
+ $self->log_fh('workflow')
+ );
+ my $reversed_factors = $self->_invert_heirarchy($hyb_factors);
+ biomaterials_report(
+ $reversed_factors,
+ 'FactorValues to Hybridizations',
+ $self->log_fh('workflow')
+ );
+ }
+
+ print STDOUT ("done.\n");
+
+ if ( $CONFIG->get_MX_EXTENDED_REPORT() ) {
+
+ print STDOUT ("Generating sample annotation report... ");
+
+ $self->_get_db_sample_annotation();
+
+ print STDOUT ("done.\n");
+ }
+
+ # Collect the design type info for later.
+ $self->add_expt_designs(
+ @{ $self->_get_db_expt_designs() }
+ );
+
+ #############################
+ # Get data file information #
+ #############################
+
+ # Get a list of files in the database submitted via MIAMExpress for
+ # this submission. This is an array of
+ # ArrayExpress::Datafile objects.
+ my $filelist = $self->get_db_files();
+
+ # Add factor information to the datafile objects (raw and norm only).
+ if ( $hyb_category_fvs ) {
+
+ FILE:
+ foreach my $file (@$filelist) {
+
+ # Skip FGEM - they have no hyb_identifier.
+ next FILE
+ if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() );
+
+ # We've mapped the FVs to HYB_ID, not HYB_SYSUID.
+ my $hyb_id = $file->get_hyb_identifier();
+
+ while ( my ($category, $values)
+ = each %{$hyb_category_fvs->{$hyb_id}} ) {
+
+ foreach my $value_string (keys %$values) {
+ $file->add_factor_value( $category, $value_string );
+ }
+ }
+ }
+ }
+
+ # get a list of hyb_ids pointing to a list of hyb_sysuids
+ my $hyb_ids = $self->_get_hyb_ids();
+
+ # Check for Qualifier Value Source entries
+ my $qvs = $self->_get_db_qvs();
+ if (@$qvs) {
+ $self->logprint( 'error', "Warning: QVS used in annotation:\n" );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ foreach my $entry (@$qvs) {
+
+ if ( $entry->{sample_id} ) { # Sample QVS
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Sample qualifier Name: %-22s Value: %s\n",
+ $entry->{qualifier}, $entry->{value}
+ )
+ );
+
+ }
+ else { # Experiment QVS
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Experiment qualifier Name: %-22s Value: %s\n",
+ $entry->{qualifier}, $entry->{value}
+ )
+ );
+ }
+ }
+ }
+
+ # Check for "Other" values
+ my $others = $self->_get_db_others();
+
+ # Skip empty values for deleted OTHERS - this seems to be a bug
+ # in MX (DEL_STATUS not set).
+ my @cleaned_others;
+ foreach my $other (@$others) {
+ if ( $other->{value} ) {
+ push( @cleaned_others, $other );
+ }
+ }
+
+ if (@cleaned_others) {
+ $self->logprint( 'error',
+ qq{Warning: "Other" values used in annotation:\n} );
+
+ OTHER_ENTRY:
+ foreach my $entry (@cleaned_others) {
+
+ if ( $entry->{sample_id} ) { # Sample OTHER
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Sample value Type: %-22s Value: %s\n",
+ $entry->{id}, $entry->{value}
+ )
+ );
+
+ }
+ else { # Experiment OTHER
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Experiment value Type: %-22s Value: %s\n",
+ $entry->{id}, $entry->{value}
+ )
+ );
+ }
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+
+ # Nested hash: {protocol_type => {protocol_sysuid => {others_id =>
+ # others_value}}}
+ while ( my ( $ptype, $others ) = each %$protocol_others ) {
+ foreach my $protocol ( values %$others ) {
+
+ OTHER_PROTOCOL:
+ while ( my ( $key, $value ) = each %$protocol ) {
+
+ # Skip empty values for deleted OTHERS - this seems to be a
+ # bug in MX (DEL_STATUS not set).
+ next OTHER_PROTOCOL unless ($value);
+
+ $self->logprint( 'error',
+ qq{Warning: "Other" values used in $ptype protocol: $key => $value\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+ }
+
+ $self->logprint_line( 'error', 'DB query END' );
+
+ $self->logprint_line( 'error', 'Protocol check START' );
+
+ ##################
+ # Protocol check #
+ ##################
+
+ print STDOUT ("Generating protocol report... ");
+
+ # Count the number of combined (transformed) and normalized data files
+ my ( $combined_count, $normalized_count );
+ foreach my $file (@$filelist) {
+ $combined_count++
+ if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() );
+
+ $normalized_count++
+ if ( $file->get_data_type() eq 'normalized' );
+ }
+
+ # Then we check the protocols reported by &_get_db_objects
+ my (%protocol_text, $norm_protocol_found);
+ foreach my $ptype ( keys %$protocol_typemap ) {
+
+ PROTOCOL_BY_TYPE:
+ foreach my $obj_id ( keys %{ $protocol_typemap->{$ptype} } ) {
+
+ # Count the protocols associated with a given object id
+ my $pcount = scalar @{ $protocol_typemap->{$ptype}{$obj_id} };
+
+ # Check for multiple protocols associated with an object
+ # (shouldn't happen).
+ if ( $pcount > 1 ) {
+ $self->logprint( 'error',
+ "Warning: Multiple protocols of type \'$ptype\' attached to object \'$obj_id\'\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ # Check protocol lengths, warn if too short (<50 chars at
+ # the moment)
+ PROTOCOL_LENGTH:
+ foreach my $protocol ( @{ $protocol_typemap->{$ptype}{$obj_id} } ) {
+
+ # GLOB indicates an AE public protocol (e.g. Affymetrix).
+ next PROTOCOL_LENGTH if defined($protocol->{glob});
+
+ if ( !defined($protocol->{text})
+ || ( length($protocol->{text}) < 50 ) ) {
+ $self->logprint( 'error',
+ qq{Warning: Description too short for $ptype protocol attached to '$obj_id'\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+
+ # Note the text for reporting below.
+ $protocol_text{$ptype}{$protocol->{id}} = $protocol;
+ }
+
+ # Check for pooling protocols
+ if ( $ptype eq 'pooling' ) {
+ if ( $pcount > 0 ) {
+ $self->logprint( 'error',
+ qq{Warning: Pooling protocol attached to extract '$obj_id'\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ next PROTOCOL_BY_TYPE;
+ }
+
+ # Check for normalization protocols
+ if ( $ptype eq 'normalization' ) {
+ $norm_protocol_found = 1 if $pcount;
+ if ( $normalized_count && ( $pcount <= 0 ) ) {
+ $self->logprint( 'error',
+ qq{Warning: Normalized data for hybridization '$obj_id'}
+ . qq{ supplied without normalization protocol.\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ next PROTOCOL_BY_TYPE;
+ }
+
+ # Check for transformation protocols
+ if ( $ptype eq 'transformation' ) {
+ $norm_protocol_found = 1 if $pcount;
+ if ( $combined_count && ( $pcount <= 0 ) ) {
+ $self->logprint( 'error',
+ qq{Warning: Final data matrix file supplied}
+ . qq{ without transformation protocol.\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ next PROTOCOL_BY_TYPE;
+ }
+
+ # Check for unbound protocols (pooling, normalization omitted at
+ # this stage).
+ if ( $pcount <= 0 ) {
+ $self->logprint( 'error',
+ "Warning: Object \'$obj_id\' not bound to $ptype protocol.\n" );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+ }
+
+ # Note the results for MIAME compliance tracking.
+ if ($norm_protocol_found) {
+ $self->add_miame($CONFIG->get_MIAME_NORMPROTOCOL);
+ }
+ else {
+ $self->logprint(
+ 'miame',
+ "Problem: Data transformation protocol not found.\n"
+ );
+ }
+
+ foreach my $ptype qw(
+ growth
+ treatment
+ pooling
+ extraction
+ labeling
+ hybridization
+ scanning
+ normalization
+ transformation
+ ) {
+ foreach my $protocol ( values %{$protocol_text{$ptype}} ) {
+ $self->logprint_line(
+ 'protocol',
+ "[$ptype protocol] $protocol->{name}");
+ $self->logprint('protocol', "$protocol->{text}\n\n");
+ }
+ }
+ $self->logprint_line( 'error', 'Protocol check END' );
+ print STDOUT ("done.\n");
+
+ # Start the report log.
+ $self->logprint( 'report', "* MIAMExpress mode *\n\n" );
+ $self->logprint(
+ 'report', " User login: ",
+ $self->get_mx_login, "\n", " Experiment: ",
+ $self->get_mx_title, "\n\n"
+ );
+
+ $self->_report_description();
+
+ ######################################
+ # Deal with Array design information #
+ ######################################
+
+ # Get the associated array designs on a per-hyb basis
+
+ $self->logprint_line( 'error', 'ADF parsing START' );
+
+ # Populate the $file->get_adf_features() hashrefs within @$filelist
+ $self->_MX_populate_array_designs( $filelist, $hyb_ids );
+
+ $self->logprint_line( 'error', 'ADF parsing END' );
+
+ # Data file check on consistency across hybs
+ $self->logprint( 'report',
+ "\nChecking for presence of raw and/or normalised data files:\n" );
+ $self->logprint( 'report', datafile_consistency_table($filelist) );
+
+ print STDOUT ("done.\n");
+
+ # We don't disconnect from the database, in case we're operating
+ # over a batch of experiments to be checked. The autosubs daemons
+ # fork before connecting, so we shouldn't have persistent
+ # connections to the database.
+ return ( $filelist, $hyb_ids );
+
+}
+
+sub query_mx_for_subs_info : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $login = $self->get_mx_login() or croak("Error: mx_login not set.");
+ my $title = $self->get_mx_title() or croak("Error: mx_title not set.");
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # Retrieve data from the table.
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSUBMIS_SYSUID from TSUBMIS where TSUBMIS_LOGIN=?
+and TSUBMIS_SUB_DESCR=? and TSUBMIS_DEL_STATUS='U'
+QUERY
+ $sth->execute( $login, $title ) or croak($sth->errstr);
+
+ # Get all the TSUBMIS_UID values returned as an array of arrays.
+ # (arrayref=[\@row1 \@row2 \@row3] where e.g. @row1=($col1,$col2))
+ my $arrayref = $sth->fetchall_arrayref;
+
+ # Count up the @rows.
+ my $rowcount = scalar @$arrayref;
+
+ # Check that we have one and only one row returned, assign the first
+ # and only column value (TSUBMIS_SYSUID) to $tsubmis_sysuid.
+ my $tsubmis_sysuid;
+ if ( $rowcount == 1 ) { # all is well
+ $tsubmis_sysuid = $arrayref->[0][0];
+
+ }
+ elsif ( $rowcount == 0 ) {
+
+ # Possible typo in login/title arguments
+ croak(
+ "Error: Login name \'$login\' with experiment title \'$title\' not found in MIAMExpress database.\n"
+ );
+ }
+ else {
+
+ # This should never happen
+ croak(
+ "ERROR: Login name \'$login\' with Experiment title \'$title\' has multiple MIAMExpress database entries.\n"
+ );
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Get the experiment filebase, while we have a connection.
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='experiment_datafiles_path'
+QUERY
+
+ $sth->execute() or die($sth->errstr);
+
+ # FIXME we're assuming the query worked here.
+ my $MX_submissions_filebase = File::Spec->catdir(
+ $sth->fetchrow_arrayref()->[0], $login, "submission$tsubmis_sysuid",
+ );
+
+ return ($tsubmis_sysuid, $MX_submissions_filebase);
+}
+
+sub get_db_files {
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # Retrieve data from the table.
+
+ my $FGEM_ardesin;
+
+ # Get Raw data filenames associated directly with hybs
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TARRAY_DESIGNID,THYBRID_SYSUID, THYBRID_ID, TFILE_PATH
+from TFILE,THYBRID,TLABHYB,TARRAY
+where TLABHYB_HYBRIDID=THYBRID_SYSUID and TFILE_HYBRIDID=THYBRID_SYSUID
+and THYBRID_ARRAYID=TARRAY_SYSUID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+and TFILE_DEL_STATUS='U' and TARRAY_DEL_STATUS='U'
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ # Get the relevant stuff out of $sth.
+ # Array of file hashes of attributes:
+ my @file_list;
+
+ # Hash of hyb_sysuids pointing to {raw} or {normalized} arrays of
+ # filenames:
+ my %hyb_list;
+
+ # Array of FGEM filenames:
+ my @FGEM_list;
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ my $path = File::Spec->catfile(
+ $self->get_submission_filebase(),
+ "hybrid$rowref->{'THYBRID_SYSUID'}",
+ $rowref->{'TFILE_PATH'}
+ );
+
+ # If we can, map the design ID to an accession.
+ my $design_id = $rowref->{'TARRAY_DESIGNID'};
+ if ( $design_id < 0 ) {
+ my $result = get_ae_array_details({database_id => -$design_id});
+ $design_id = $result if $result;
+ }
+
+ my $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => $rowref->{'TFILE_PATH'},
+ data_type => 'raw',
+ hyb_identifier => $rowref->{'THYBRID_ID'},
+ hyb_sysuid => $rowref->{'THYBRID_SYSUID'},
+ array_design_id => $design_id,
+ is_miamexpress => 1,
+ });
+
+ push( @file_list, $file );
+ push(
+ @{ $hyb_list{ $rowref->{'THYBRID_SYSUID'} }{raw} },
+ $file->get_path()
+ );
+ $FGEM_ardesin ||= $design_id;
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Get the normalized data file names
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TARRAY_DESIGNID,THYBRID_SYSUID,THYBRID_ID,TFILE_PATH
+from TFILE,THYBRID,TLABHYB,TARRAY
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_SYSUID=TFILE_TRANSHYBRIDID
+and TARRAY_SYSUID=THYBRID_ARRAYID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+and TFILE_DEL_STATUS='U' and TARRAY_DEL_STATUS='U'
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ # get the relevant stuff out of $sth
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ my $path = File::Spec->catfile(
+ $self->get_submission_filebase(),
+ "hybrid$rowref->{'THYBRID_SYSUID'}",
+ $rowref->{'TFILE_PATH'}
+ );
+
+ # If we can, map the design ID to an accession.
+ my $design_id = $rowref->{'TARRAY_DESIGNID'};
+ if ( $design_id < 0 ) {
+ my $result = get_ae_array_details({database_id => -$design_id});
+ $design_id = $result if $result;
+ }
+
+ my $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => $rowref->{'TFILE_PATH'},
+ data_type => 'normalized',
+ hyb_identifier => $rowref->{'THYBRID_ID'},
+ hyb_sysuid => $rowref->{'THYBRID_SYSUID'},
+ array_design_id => $design_id,
+ is_miamexpress => 1,
+ });
+
+ push( @file_list, $file );
+ push(
+ @{ $hyb_list{ $rowref->{'THYBRID_SYSUID'} }{normalized} },
+ $file->get_path()
+ );
+ $FGEM_ardesin ||= $design_id;
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Deal with FGEM files here
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TFILE_PATH from TFILE,TEXPRFNL
+where TFILE_EXPRFNLID=TEXPRFNL_SYSUID and TEXPRFNL_SUBID=?
+and TFILE_DEL_STATUS='U'
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ my $path = File::Spec->catfile(
+ $self->get_submission_filebase(),
+ $rowref->{'TFILE_PATH'},
+ );
+
+ # array_design_id here is a placeholder; MX does not associate
+ # FGEM with ARDESIN. We default to the first raw data ARDESIN
+ # here.
+ my $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => $rowref->{'TFILE_PATH'},
+ data_type => $CONFIG->get_FGEM_FILE_TYPE(),
+ is_miamexpress => 1,
+ array_design_id => ( $FGEM_ardesin || 'UNKNOWN' ),
+ });
+
+ push( @file_list, $file );
+ push( @FGEM_list, $file->get_path() );
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ return wantarray
+ ? ( \@file_list, \%hyb_list, \@FGEM_list )
+ : \@file_list;
+
+}
+
+sub _get_db_expt_designs : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TCTLVCB_DESCR from TCTLVCB, TEXPRTYP, TEXPRMNT
+where TEXPRMNT_EXPRID=TEXPRTYP_EXPRID
+and TEXPRTYP_ID=TCTLVCB_SYSUID
+and TEXPRMNT_SUBID=?
+and TCTLVCB_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U' and TEXPRTYP_DEL_STATUS='U'
+QUERY
+
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ my @design_types;
+ while ( my $termlist = $sth->fetchrow_arrayref() ) {
+ push(@design_types, $termlist->[0]);
+ }
+
+ return \@design_types;
+}
+
+sub _get_hyb_ids : PRIVATE {
+
+ # Returns a list of hyb ids and a second list of hyb sysuids
+ # associated with the submission.
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct THYBRID_ID,THYBRID_SYSUID from THYBRID,TLABHYB
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ my %hyb_list;
+
+ # get the relevant stuff out of $sth
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ my $hyb = $rowref->{'THYBRID_ID'};
+
+ # hash, {hyb_id => hyb_sysuid}
+ $hyb_list{$hyb} = $rowref->{'THYBRID_SYSUID'};
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ return ( \%hyb_list );
+}
+
+sub _protocol_length_and_others : PRIVATE {
+
+ my ( $self, $obj_id_protocol_text, $get_ptext, $get_others )
+ = @_;
+
+ my %others;
+
+ foreach my $obj_id ( keys %$obj_id_protocol_text ) {
+
+ $get_ptext->execute( $self->get_tsubmis_sysuid(), $obj_id )
+ or croak($get_ptext->errstr);
+
+ # DBI row arrayref points at array with protocol text,
+ # protocol sysuid.
+ foreach my $rowref ( @{ $get_ptext->fetchall_arrayref } ) {
+
+ # Add the protocol text to the %protocol_typemap leaf for
+ # this object.
+ push(
+ @{ $obj_id_protocol_text->{$obj_id} },
+ {
+ text => $rowref->[0],
+ id => $rowref->[1],
+ name => $rowref->[2],
+ glob => $rowref->[3],
+ },
+ );
+
+ # Use the protocol sysuid to pull out OTHERS info, while
+ # we're here.
+ my $tprotcls_sysuid = $rowref->[1];
+ $get_others->execute($tprotcls_sysuid)
+ or croak($get_others->errstr);
+ while ( my $rowref = $get_others->fetchrow_hashref ) {
+ $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+ $others{$tprotcls_sysuid}{ lc( $rowref->{'TOTHERS_ID'} ) }
+ = $rowref->{'TOTHERS_VALUE'};
+ }
+ }
+ }
+
+ return \%others;
+
+}
+
+sub _get_db_protocols : PRIVATE {
+
+ my ( $self, $protocol_typemap ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $protocol_others;
+
+ foreach my $type ( keys %$protocol_typemap ) {
+
+ my $get_ptext;
+
+ PROTOCOLTYPE:
+ {
+
+ ( $type eq 'transformation' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TPROTCLS, TEXPRFNL, TFILE
+where TEXPRFNL_PROTOCOLID = TPROTCLS_SYSUID
+and TFILE_EXPRFNLID = TEXPRFNL_SYSUID
+and TEXPRFNL_SUBID=? and TFILE_PATH=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TEXPRFNL_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U' and TFILE_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'normalization' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from THYBRID,TLABHYB,TPROTCLS
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_NORM_PROTOCOLID = TPROTCLS_SYSUID
+and TLABHYB_SUBID=? and THYBRID_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'hybridization' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from THYBRID,TLABHYB,TPROTCLS
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_PROTOCOLID = TPROTCLS_SYSUID
+and TLABHYB_SUBID=? and THYBRID_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'scanning' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from THYBRID,TLABHYB,TPROTCLS
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and THYBRID_SCAN_PROTOCOLID = TPROTCLS_SYSUID
+and TLABHYB_SUBID=? and THYBRID_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'labeling' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TLABEL,TEXTRACT,TPOOLED,TPROTCLS
+where TLABEL_PROTOCOLID=TPROTCLS_SYSUID and TEXTRACT_SYSUID=TLABEL_EXTRACTID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TPOOLED_SUBID=?
+and TLABEL_ID=? and TLABEL_DEL_STATUS='U' and TEXTRACT_DEL_STATUS='U'
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TPOOLED_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'extraction' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TEXTRACT,TPOOLED,TPROTCLS
+where TEXTRACT_PROTOCOLID=TPROTCLS_SYSUID and TEXTRACT_SYSUID=TPOOLED_EXTRACTID
+and TPOOLED_SUBID=? and TEXTRACT_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TEXTRACT_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'pooling' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TEXTRACT,TPOOLED,TPROTCLS
+where TEXTRACT_POOL_PROTOCOLID=TPROTCLS_SYSUID and TEXTRACT_SYSUID=TPOOLED_EXTRACTID
+and TPOOLED_SUBID=? and TEXTRACT_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TEXTRACT_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U' and TPROTCLS_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'treatment' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TPROTCLS,TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID and TSAMPLE_PROTOCOLID=TPROTCLS_SYSUID
+and TEXPRMNT_SUBID=? and TSAMPLE_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TPROTCLS_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ ( $type eq 'growth' ) && do {
+ $get_ptext = $dbh->prepare(<<'QUERY');
+select distinct TPROTCLS_DESCR, TPROTCLS_SYSUID, TPROTCLS_ID, TPROTCLS_GLOBID
+from TPROTCLS,TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID and TSAMPLE_GROWTH_PROTOCOLID=TPROTCLS_SYSUID
+and TEXPRMNT_SUBID=? and TSAMPLE_ID=?
+and (TPROTCLS_GLOBID is not NULL or (TPROTCLS_DESCR is not NULL and TPROTCLS_ID is not NULL))
+and TPROTCLS_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U'
+QUERY
+ last PROTOCOLTYPE;
+ };
+
+ croak("INTERNAL SCRIPT ERROR: Unknown protocol type.\n");
+
+ }
+
+ my $get_others = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID, TOTHERS_VALUE from TOTHERS, TPROTCLS
+where TPROTCLS_SYSUID=TOTHERS_DETAIL_PRTCLID and TPROTCLS_SYSUID=?
+QUERY
+ my $others = $self->_protocol_length_and_others(
+ $protocol_typemap->{$type},
+ $get_ptext,
+ $get_others,
+ );
+
+ $protocol_others->{$type} = $others;
+
+ $get_ptext->finish();
+ $get_others->finish();
+
+ }
+
+ return $protocol_others;
+
+}
+
+sub _tree_to_table : PRIVATE {
+
+ # Converts a hash heirarchy (key1 => {key2 => {key3 => 0}}, ...) into an
+ # array of arrays ([key1, key2, key3],[key1, key4, ...], ...); used to
+ # reverse a hash heirarchy.
+
+ my ( $self, $hash ) = @_;
+ my $AoA;
+
+ foreach my $key ( keys %$hash ) {
+ if ( ref( $hash->{$key} ) eq 'HASH' ) {
+ my $lower_AoA = $self->_tree_to_table( $hash->{$key} );
+ foreach my $row (@$lower_AoA) {
+ push( @$AoA, [ $key, @$row ] );
+ }
+ }
+ else {
+ push @$AoA, [$key];
+ }
+
+ }
+ return $AoA;
+}
+
+sub _invert_heirarchy : PRIVATE {
+
+ # Takes a hashref, inverts the heirarchy using an array of arrays,
+ # and returns a rebuilt inverted heirarchical hash.
+
+ my ( $self, $hash ) = @_;
+
+ my $AoA = $self->_tree_to_table($hash);
+
+ my $newhash;
+ foreach my $row (@$AoA) {
+ $newhash = $self->_build_heirarchy( $newhash, reverse @$row );
+ }
+
+ return $newhash;
+}
+
+sub _build_heirarchy : PRIVATE {
+
+ # Recurse through a passed list of terms, create a Tie::IxHash at
+ # every level.
+
+ # We tie these hashes to maintain database order (i.e. ordered by
+ # SYSUID). This order is then reflected in the biomaterials reports,
+ # which is extremely useful when hacking any output MAGE-ML.
+
+ my ( $self, $hash, @termlist ) = @_;
+
+ my $newterm = shift @termlist;
+
+ unless ($hash) {
+ tie( my %newhash, 'Tie::IxHash' );
+ $hash = \%newhash;
+ }
+
+ ref $hash eq 'HASH' or croak; # Just in case
+
+ if ( scalar( @termlist ) ) {
+ unless ( $hash->{$newterm} ) {
+ tie( my %newhash, 'Tie::IxHash' );
+ $hash->{$newterm} = \%newhash;
+ }
+ $self->_build_heirarchy( $hash->{$newterm}, @termlist );
+ }
+ else {
+ $hash->{$newterm} = 0;
+ }
+
+ return $hash;
+}
+
+sub _mx_tsample_tntxsyn_name : PRIVATE {
+
+ # Get the TNTXSYN_NAME_TXT value attached to a given sample.
+
+ my ( $self, $tsample_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<"QUERY");
+select distinct TNTXSYN_NAME_TXT
+from TSAMPLE, TNTXSYN
+where TSAMPLE_SYSUID=?
+and TSAMPLE_TAXID=TNTXSYN_TAX_ID
+and TNTXSYN_TAX_ID!='0'
+and TNTXSYN_NAME_TXT!='NULL'
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+ $sth->execute( $tsample_sysuid )
+ or croak($sth->errstr);
+ my $values = $sth->fetchall_arrayref;
+ $sth->finish();
+
+ return $values;
+}
+
+sub _mx_tsample_column_value : PRIVATE {
+
+ # Get the actual column value from a TSAMPLE table column.
+
+ my ( $self, $column, $tsample_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<"QUERY");
+select distinct $column
+from TSAMPLE
+where TSAMPLE_SYSUID=?
+and $column!='NULL'
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+ $sth->execute( $tsample_sysuid )
+ or croak($sth->errstr);
+ my $values = $sth->fetchall_arrayref;
+ $sth->finish();
+
+ return $values;
+}
+
+sub _mx_tsample_tctlvcb_descr : PRIVATE {
+
+ # Get the value in TCTLVCB mapped to by the id in a TSAMPLE table
+ # column.
+
+ my ( $self, $sql_column, $tsample_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<"QUERY");
+select distinct TCTLVCB_DESCR
+from TSAMPLE, TCTLVCB
+where TSAMPLE_SYSUID=?
+and $sql_column=TCTLVCB_SYSUID
+and TCTLVCB_SYSUID!='0'
+and TCTLVCB_DESCR!='NULL'
+and TSAMPLE_DEL_STATUS='U' and TCTLVCB_DEL_STATUS='U'
+QUERY
+ $sth->execute( $tsample_sysuid )
+ or croak($sth->errstr);
+ my $values = $sth->fetchall_arrayref;
+ $sth->finish();
+
+ return $values;
+}
+
+sub _mx_tsample_tothers_value : PRIVATE {
+
+ # Get the value in TOTHERS mapped to by the id in a TSAMPLE table
+ # column.
+
+ my ( $self, $mapped_other, $tsample_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_VALUE
+from TOTHERS, TSAMPLE
+where TOTHERS_SAMPLEID=?
+and TOTHERS_ID=?
+and TOTHERS_VALUE!='NULL'
+and TSAMPLE_DEL_STATUS='U' and TOTHERS_DEL_STATUS='U'
+QUERY
+ $sth->execute( $tsample_sysuid, $mapped_other )
+ or croak($sth->errstr);
+ my $value = [ "OTHER: " . $sth->fetchall_arrayref()->[0][0] ];
+ $sth->finish();
+
+ return $value;
+}
+
+sub _mx_tfctrval_text_and_unit : PRIVATE {
+
+ # Get the details from TFCTRVAL pertaining to a particular
+ # EXPFCTRID and linked to a given sample.
+
+ my ( $self, $tfctrval_expfctrid, $tsample_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TFCTRVAL_FREETEXT, TFCTRVAL_FREETEXTUNIT
+from TFCTRVAL
+where TFCTRVAL_SAMPLEID=? and TFCTRVAL_FREETEXT!='NULL'
+and TFCTRVAL_EXPFCTRID=?
+and TFCTRVAL_DEL_STATUS='U'
+QUERY
+ $sth->execute( $tsample_sysuid, $tfctrval_expfctrid )
+ or croak($sth->errstr);
+ my $values = $sth->fetchall_arrayref;
+ $sth->finish();
+
+ return $values;
+}
+
+sub _mx_tsample_annotation_values : PRIVATE {
+
+ # Retrieve a TSAMPLE annotation value based on the passed SQL
+ # column.
+
+ my ( $self, $sql_column, $tsample_sysuid ) = @_;
+
+ my $values;
+
+ # Special cases first.
+ if (grep {/^$sql_column$/i}
+ qw(TSAMPLE_AGERANGE_MIN
+ TSAMPLE_AGERANGE_MAX
+ TSAMPLE_DISEASE_STATE
+ TSAMPLE_CELL_LINE
+ TSAMPLE_TARGET_CELL_TYPE
+ TSAMPLE_INDIVIDUAL_GEN
+ TSAMPLE_STRAIN
+ TSAMPLE_CELL_PROVIDER
+ TSAMPLE_INDIVIDUAL
+ TSAMPLE_ADDITIONAL)
+ ) {
+
+ # AGERANGE columns just use the TSAMPLE table value.
+ # TSAMPLE_INDIVIDUAL, TSAMPLE_ADDITIONAL and several
+ # other columns (see above) also need this treatment.
+ $values = $self->_mx_tsample_column_value(
+ $sql_column,
+ $tsample_sysuid,
+ );
+
+ }
+ elsif ( $sql_column eq 'TSAMPLE_TAXID' ) {
+
+ # Organism is a special case, maps to TNXTSYN table.
+ $values = $self->_mx_tsample_tntxsyn_name($tsample_sysuid);
+
+ }
+ else {
+
+ # Next, check TCTLVCB via TSAMPLE; for "other" from a
+ # pulldown list add the value.
+ $values
+ = $self->_mx_tsample_tctlvcb_descr(
+ $sql_column,
+ $tsample_sysuid,
+ );
+
+ # If returned value contains 'other', map to
+ # TOTHERS. TSAMPLE_DEV_STAGE can be text, so we must allow
+ # this check in that case also.
+ foreach my $value (@$values) {
+
+ # Substitute "other" values.
+ if ( $value->[0] eq 'other' ) {
+ my $mapped_other = $self->_map_samplesql_to_othertag(
+ $sql_column,
+ );
+ $value = $self->_mx_tsample_tothers_value(
+ $mapped_other,
+ $tsample_sysuid,
+ );
+ }
+
+ # Handle the 'not applicable' pulldown menu item by nulling it.
+ elsif ( $value->[0] eq 'not applicable' ) {
+ $value = [];
+ }
+ }
+ unless ( scalar(@$values) ) {
+
+ # Fall back to the actual values in TSAMPLE.
+ $values = $self->_mx_tsample_column_value(
+ $sql_column,
+ $tsample_sysuid,
+ );
+ }
+ }
+ return $values;
+}
+
+sub _mx_tsample_annotation_report : PRIVATE {
+
+ # Print out a report on annotation for a given sample using an
+ # ExperimentChecker object logprint method.
+
+ my ( $self, $tsample_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID
+from TSAMPLE
+where TSAMPLE_SYSUID=?
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+
+ $sth->execute( $tsample_sysuid )
+ or croak($sth->errstr);
+
+ # This should only ever return one value (check FIXME)
+ my $tsample_id = $sth->fetchall_arrayref()->[0][0];
+ $sth->finish();
+
+ my @categories = sort keys %CATEGORY_SQL_MAP;
+ my $name_heading = 'Sample Name';
+ my $max_category_length = 0;
+ foreach my $category ( @categories, $name_heading ) {
+ my $length = length($category);
+ $max_category_length = $length if ( $length > $max_category_length );
+ }
+
+ # Sample name (TSAMPLE_ID) heading.
+ my $printable = sprintf( "%${max_category_length}s: %s\n",
+ $name_heading, $tsample_id );
+ $self->logprint( 'sample', $printable );
+
+ foreach my $category (@categories) {
+ my $factorstring
+ = $self->_mx_tsample_annotation_string(
+ $category,
+ $tsample_sysuid,
+ );
+
+ # Only print the fields containing meaningful information.
+ if ( defined($factorstring) && ( $factorstring ne q{} ) ) {
+ my $printable = sprintf( "%${max_category_length}s: %s\n",
+ $category, $factorstring );
+ $self->logprint( 'sample', $printable );
+
+ if (lc($category) eq 'organism') {
+ $self->add_expt_organisms($factorstring);
+ }
+ }
+ }
+
+ $self->logprint( 'sample', q{-} x $max_category_length, "-\n" );
+
+ return;
+}
+
+sub _mx_tsample_annotation_string : PRIVATE {
+
+ # For a given category in %CATEGORY_SQL_MAP, generate the
+ # sample-specific string appropriate to that factor.
+
+ my ( $self, $category, $tsample_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my @factor_values; # This will get stringified later
+
+ # Get an arrayref of places to look for controlled vocabulary terms.
+ my $mapped_columns = $self->_map_efcategorytag_to_samplesql($category);
+
+ # Unless $mapped_columns is empty (get FREETEXT, FREETEXTUNIT),
+ # check for match between each mapped column and TCTLVCB; fall
+ # back to TOTHERS; then just use the TSAMPLE values.
+ foreach my $sql_column (@$mapped_columns) {
+
+ my $values
+ = $self->_mx_tsample_annotation_values(
+ $sql_column,
+ $tsample_sysuid,
+ );
+
+ # Dump the returned values into our array of FVs.
+ if (@$values) {
+ push( @factor_values, map { @{$_} } @$values );
+ }
+ else {
+
+ # This is needed for correct ordering of complex results,
+ # e.g. age.
+ push( @factor_values, q{} );
+ }
+ }
+
+ # Final fallback to TFCTRVAL_FREETEXT ("other" measurement FVs)
+ unless ( scalar(@factor_values) ) {
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TFCTRVAL_EXPFCTRID
+from TFCTRVAL, TSAMPLE, TCTLVCB
+where TFCTRVAL_SAMPLEID=TSAMPLE_SYSUID
+and TFCTRVAL_EXPFCTRID=TCTLVCB_SYSUID
+and TSAMPLE_SYSUID=?
+and TCTLVCB_DESCR=?
+and TFCTRVAL_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U'
+and TCTLVCB_DEL_STATUS='U'
+QUERY
+
+ $sth->execute( $tsample_sysuid, $category )
+ or croak($sth->errstr);
+
+ # This should only ever return one value (check FIXME)
+ my $tfctrval_expfctrid = $sth->fetchall_arrayref()->[0][0];
+ $sth->finish();
+
+ my $values = $self->_mx_tfctrval_text_and_unit(
+ $tfctrval_expfctrid,
+ $tsample_sysuid,
+ );
+
+ if (@$values) {
+
+ # Create a FREETEXT FREETEXTUNIT string (omit
+ # FREETEXTUNIT if undef).
+ push( @factor_values,
+ map { $_->[0] . ( defined( $_->[1] ) ? " $_->[1]" : q{} ) }
+ @$values );
+ }
+ }
+
+ # Build a human-readable string from the factorvalue result.
+ my $factorstring;
+ if ( scalar @factor_values ) {
+ if ( $category eq 'age' ) { # Age is a special case
+ $factorstring = $self->_mx_factorvalue_agestring(\@factor_values);
+ }
+ else {
+ $factorstring = join( ', ', @factor_values );
+ }
+ }
+ return $factorstring;
+}
+
+sub _get_db_factor_assns : PRIVATE {
+
+ # Get all the factorvalues for an experiment, return them in a
+ # simple {hyb=>fv} heirarchical hashref.
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # Get all the categories. This is big because we want to map from
+ # sample to hyb.
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct THYBRID_ID, TSAMPLE_SYSUID, TCTLVCB_DESCR, TFCTRVAL_EXPFCTRID
+from THYBRID,TLABHYB,TLABEL,TEXTRACT,TSAMPLE,TPOOLED,TFCTRVAL,TCTLVCB
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and TLABEL_SYSUID=TLABHYB_LABELID
+and TEXTRACT_SYSUID=TLABEL_EXTRACTID and TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TFCTRVAL_SAMPLEID=TSAMPLE_SYSUID
+and TFCTRVAL_EXPFCTRID=TCTLVCB_SYSUID
+and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TLABEL_DEL_STATUS='U'
+and TEXTRACT_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U'
+and TFCTRVAL_DEL_STATUS='U' and TCTLVCB_DEL_STATUS='U'
+order by TCTLVCB_DESCR, THYBRID_ID, TSAMPLE_SYSUID
+QUERY
+
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ # Array of hashrefs, one per experimental factor category.
+ my @retrieved_factors;
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ push @retrieved_factors, $rowref;
+ }
+ $sth->finish(); # Overlapping prepares not good for portability
+
+ my ($hyb_factors, %hyb_category_fvs);
+
+ foreach my $factor (@retrieved_factors) {
+
+ my $factorstring =
+ $self->_mx_tsample_annotation_string(
+ $factor->{'TCTLVCB_DESCR'},
+ $factor->{'TSAMPLE_SYSUID'},
+ );
+
+ # Construct the category-FV hash for mapping to files later.
+ $hyb_category_fvs{ $factor->{'THYBRID_ID'} }
+ { $factor->{'TCTLVCB_DESCR'} }{$factorstring}++;
+
+ $factorstring .= " ($factor->{'TCTLVCB_DESCR'})";
+
+ # Add the factor string to the $hyb_factors heirarchy.
+ $hyb_factors = $self->_build_heirarchy(
+ $hyb_factors,
+ $factor->{'THYBRID_ID'},
+ $factorstring,
+ );
+ }
+ return ($hyb_factors, \%hyb_category_fvs);
+}
+
+sub _mx_factorvalue_agestring : PRIVATE {
+
+ # Sub takes an age fv arrayref as defined by
+ # _map_efcategorytag_to_samplesql, and formats it nicely for
+ # printing.
+
+ my ( $self, $factor_values ) = @_;
+
+ # Sort out the age (and optional range) to start with.
+ my $factorstring = $factor_values->[0];
+ $factorstring .= "-$factor_values->[1]"
+ if $factor_values->[1];
+
+ # Don't bother with units if there's no
+ # age or age range.
+ if ($factorstring) {
+ $factorstring .= " $factor_values->[2]"
+ if $factor_values->[2];
+ }
+
+ # We keep the initial time point regardless, as it may indicate a
+ # submitter error.
+ $factorstring .= " since $factor_values->[3]"
+ if $factor_values->[3];
+
+ return $factorstring;
+}
+
+sub _map_efcategorytag_to_samplesql : PRIVATE {
+
+ my ( $self, $category ) = @_;
+
+ # Empty arrayrefs typically signify that these are 'other' FVs, held
+ # in TFCTRVAL_FREETEXT and TFCTRVAL_FREETEXTUNIT. This is an
+ # egregious hack made necessary by the MIAMExpress schema.
+ unless ( $CATEGORY_SQL_MAP{$category} ) {
+ croak(
+ qq{ERROR: Unmapped experimental factor category "$category".\n});
+ }
+
+ return $CATEGORY_SQL_MAP{$category};
+}
+
+sub _map_samplesql_to_othertag : PRIVATE {
+
+ my ( $self, $sql_colname ) = @_;
+
+ # These map the TCTLVCB-linked TSAMPLE columns which also have
+ # "other" as an option to the relevant TOTHERS_ID. This is an
+ # egregious hack made necessary by the MIAMExpress schema.
+ my %mapping = (
+ TSAMPLE_SAMPLE_TYPE => 'SAMPLE_TYPE',
+ TSAMPLE_DEV_STAGE => 'DEVELOPMENTAL_STAGE',
+ TSAMPLE_TIME_POINT => 'AGE_TIME_POINTS',
+ TSAMPLE_ORGANISM_PART => 'ORGANISM_PART',
+ TSAMPLE_SEX => 'SEX',
+ TSAMPLE_GENETIC_VARIATION => 'GENETIC_VARIATION',
+ TSAMPLE_TARGET_CELL_TYPE => 'CELL_TYPE',
+ TSAMPLE_TREATMENT_TYPE => 'TREATMENT_TYPE',
+ TSAMPLE_SEPARATION_TECH => 'SEPARATION_TECHNIQUE',
+ );
+
+ unless ( $mapping{$sql_colname} ) {
+ croak(qq{ERROR: Unmapped TSAMPLE SQL column name "$sql_colname".\n});
+ }
+
+ return $mapping{$sql_colname};
+}
+
+sub _get_db_objects : PRIVATE {
+
+ # Gets samples, extracts, labelled extracts, hybs, and returns
+ # hashes describing how they map to each other. See
+ # &heirarchy_report for how these are used.
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my ( $samples_to_hybs, %protocol_typemap );
+
+ my $error = 0;
+
+ # Get a table with sample, extract, labeled extract and hyb
+ # columns. We retrieve SYSUIDs as well, to guard against duplicate
+ # names. SYSUIDs are also used to sort the tied hashes.
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TEXTRACT_ID,TLABEL_ID,THYBRID_ID,TSAMPLE_SYSUID,TEXTRACT_SYSUID,TLABEL_SYSUID,THYBRID_SYSUID
+from THYBRID,TLABHYB,TLABEL,TEXTRACT,TSAMPLE,TPOOLED
+where THYBRID_SYSUID=TLABHYB_HYBRIDID and TLABEL_SYSUID=TLABHYB_LABELID
+and TEXTRACT_SYSUID=TLABEL_EXTRACTID and TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TLABHYB_SUBID=?
+and TLABHYB_DEL_STATUS='U' and THYBRID_DEL_STATUS='U' and TLABEL_DEL_STATUS='U'
+and TEXTRACT_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U'
+order by TSAMPLE_SYSUID, TEXTRACT_SYSUID, TLABEL_SYSUID, THYBRID_SYSUID
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ # Use of THYBRID_ID instead of THYBRID_SYSUID here is a bug
+ # FIXME. See also below for other protocol types. Note that a
+ # check should now have been added to the MIAMExpress codebase to
+ # prevent this bug ever occurring.
+ $protocol_typemap{hybridization}{ $rowref->{'THYBRID_ID'} } = [];
+ $protocol_typemap{normalization}{ $rowref->{'THYBRID_ID'} } = [];
+ $protocol_typemap{scanning}{ $rowref->{'THYBRID_ID'} } = [];
+
+ my @terms = (
+ $rowref->{'TSAMPLE_ID'}, $rowref->{'TEXTRACT_ID'},
+ $rowref->{'TLABEL_ID'}, $rowref->{'THYBRID_ID'}
+ );
+ $samples_to_hybs = $self->_build_heirarchy(
+ $samples_to_hybs,
+ @terms,
+ );
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # We need to check for unused labels, extracts, samples here.
+ # Set up our nulls:
+ my $null = '**{NULL}**';
+
+ # Labeled Extract check
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TEXTRACT_ID,TLABEL_ID,TSAMPLE_SYSUID,TEXTRACT_SYSUID,TLABEL_SYSUID
+from TLABEL,TEXTRACT,TSAMPLE,TPOOLED
+where TEXTRACT_SYSUID=TLABEL_EXTRACTID and TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TPOOLED_SUBID=?
+and TLABEL_DEL_STATUS='U' and TEXTRACT_DEL_STATUS='U'
+and TPOOLED_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U'
+order by TSAMPLE_SYSUID, TEXTRACT_SYSUID, TLABEL_SYSUID
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ QUERY_RESULT:
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ $protocol_typemap{labeling}{ $rowref->{'TLABEL_ID'} } = [];
+
+ next QUERY_RESULT
+ if $samples_to_hybs->{ $rowref->{'TSAMPLE_ID'} }
+ { $rowref->{'TEXTRACT_ID'} }{ $rowref->{'TLABEL_ID'} };
+
+ my @terms = (
+ $rowref->{'TSAMPLE_ID'},
+ $rowref->{'TEXTRACT_ID'},
+ $rowref->{'TLABEL_ID'}, $null,
+ );
+ $samples_to_hybs = $self->_build_heirarchy(
+ $samples_to_hybs,
+ @terms,
+ );
+ $error |= $CONFIG->get_ERROR_PARSEFAIL();
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Extract check
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TEXTRACT_ID,TSAMPLE_SYSUID,TEXTRACT_SYSUID
+from TEXTRACT,TSAMPLE,TPOOLED
+where TSAMPLE_SYSUID=TPOOLED_SAMPLEID
+and TEXTRACT_SYSUID=TPOOLED_EXTRACTID and TPOOLED_SUBID=?
+and TEXTRACT_DEL_STATUS='U' and TPOOLED_DEL_STATUS='U' and TSAMPLE_DEL_STATUS='U'
+order by TSAMPLE_SYSUID, TEXTRACT_SYSUID
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ QUERY_RESULT:
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ $protocol_typemap{extraction}{ $rowref->{'TEXTRACT_ID'} } = [];
+ $protocol_typemap{pooling}{ $rowref->{'TEXTRACT_ID'} } = [];
+
+ next QUERY_RESULT
+ if $samples_to_hybs->{ $rowref->{'TSAMPLE_ID'} }
+ { $rowref->{'TEXTRACT_ID'} };
+
+ my @terms = (
+ $rowref->{'TSAMPLE_ID'},
+ $rowref->{'TEXTRACT_ID'},
+ $null, $null,
+ );
+ $samples_to_hybs = $self->_build_heirarchy(
+ $samples_to_hybs,
+ @terms,
+ );
+ $error |= $CONFIG->get_ERROR_PARSEFAIL();
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Sample check (we have to go in though TEXPRMNT here)
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_ID,TSAMPLE_SYSUID
+from TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID
+and TEXPRMNT_SUBID=?
+and TSAMPLE_DEL_STATUS='U' and TEXPRMNT_DEL_STATUS='U'
+order by TSAMPLE_SYSUID
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ QUERY_RESULT:
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ $protocol_typemap{treatment}{ $rowref->{'TSAMPLE_ID'} } = [];
+ $protocol_typemap{growth}{ $rowref->{'TSAMPLE_ID'} } = [];
+
+ next QUERY_RESULT
+ if $samples_to_hybs->{ $rowref->{'TSAMPLE_ID'} };
+
+ my @terms = ( $rowref->{'TSAMPLE_ID'}, $null, $null, $null, );
+ $samples_to_hybs = $self->_build_heirarchy(
+ $samples_to_hybs,
+ @terms,
+ );
+ $error |= $CONFIG->get_ERROR_PARSEFAIL();
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Deal with FGEM files here
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TFILE_PATH from TFILE,TEXPRFNL
+where TFILE_EXPRFNLID=TEXPRFNL_SYSUID and TEXPRFNL_SUBID=?
+and TFILE_DEL_STATUS='U'
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ $protocol_typemap{transformation}{ $rowref->{'TFILE_PATH'} } = [];
+ }
+ $sth->finish();
+
+ # Further populate our protocol hash
+ my $protocol_others = $self->_get_db_protocols( \%protocol_typemap );
+
+ # Return HoHoHoH for the associations between sample, extract,
+ # labeled extract and hyb (in both directions).
+ return ( $samples_to_hybs, \%protocol_typemap, $protocol_others, $error );
+}
+
+sub _get_db_expr_ids : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # Use a hash to get a uniqued list of expr_ids
+ my %expr_ids;
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TEXPRMNT_EXPRID
+from TEXPRMNT
+where TEXPRMNT_SUBID=?
+and TEXPRMNT_DEL_STATUS='U'
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ $expr_ids{ $rowref->{'TEXPRMNT_EXPRID'} }++;
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ return \%expr_ids;
+
+}
+
+sub _get_db_sample_annotation : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # Find all the TSAMPLE_SYSUIDs for the experiment.
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TSAMPLE_SYSUID
+from TSAMPLE,TEXPRMNT
+where TSAMPLE_EXPRID=TEXPRMNT_EXPRID
+and TEXPRMNT_SUBID=?
+and TSAMPLE_DEL_STATUS='U'
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ my @sample_sysuids;
+ while ( my $sample_ref = $sth->fetchrow_arrayref() ) {
+ push( @sample_sysuids, @$sample_ref );
+ }
+ $sth->finish();
+
+ # Print a report on each sample.
+ foreach my $tsample_sysuid (@sample_sysuids) {
+ $self->_mx_tsample_annotation_report($tsample_sysuid);
+ }
+
+ return;
+}
+
+sub _get_db_others : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my @others; #AoH
+
+ my $expr_ids = $self->_get_db_expr_ids();
+
+ # Top-level experiment OTHERSs
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID,TOTHERS_VALUE,TEXPRMNT_EXPRID
+from TOTHERS, TEXPRMNT, TEXPRQC
+where TEXPRQC_EXPRID=TOTHERS_EXPRID
+and TOTHERS_EXPRID=TEXPRMNT_EXPRID
+and TOTHERS_DEL_STATUS='U'
+and TEXPRQC_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+ push(
+ @others,
+ { id => lc( $rowref->{'TOTHERS_ID'} ),
+ value => $rowref->{'TOTHERS_VALUE'},
+ expr_id => $rowref->{'TEXPRMNT_EXPRID'},
+ }
+ );
+ }
+
+ # If there are no QC others, we need to try again to get top-level others
+ unless (@others) {
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID,TOTHERS_VALUE,TEXPRMNT_EXPRID
+from TOTHERS, TEXPRMNT
+where TOTHERS_EXPRID=TEXPRMNT_EXPRID
+and TOTHERS_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+ push(
+ @others,
+ { id => lc( $rowref->{'TOTHERS_ID'} ),
+ value => $rowref->{'TOTHERS_VALUE'},
+ expr_id => $rowref->{'TEXPRMNT_EXPRID'},
+ }
+ );
+ }
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Get the OTHERs for samples associated with the experiment(s) - of
+ # course, there should be only one experiment, but we're making this
+ # extensible (maybe).
+ foreach my $expr_id ( keys %$expr_ids ) {
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TOTHERS_ID,TOTHERS_VALUE,TSAMPLE_ID
+from TOTHERS,TSAMPLE
+where TSAMPLE_SYSUID = TOTHERS_SAMPLEID
+and TOTHERS_DEL_STATUS='U'
+and TSAMPLE_DEL_STATUS='U'
+and TSAMPLE_EXPRID=?
+QUERY
+ $sth->execute($expr_id) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ $rowref->{'TOTHERS_ID'} =~ s/_/ /g;
+ push(
+ @others,
+ { id => lc( $rowref->{'TOTHERS_ID'} ),
+ value => $rowref->{'TOTHERS_VALUE'},
+ sample_id => $rowref->{'TSAMPLE_ID'},
+ expr_id => $expr_id,
+ }
+ );
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+ }
+ return \@others;
+
+}
+
+sub _get_db_qvs : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my @qvs; #AoH
+
+ my $expr_ids = $self->_get_db_expr_ids();
+
+ # Top-level experiment QVSs
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TQUALIF_NAME,TQUALIF_VALUE,TQUALIF_SOURCE,TEXPRMNT_EXPRID
+from TQUALIF,TEXPRMNT
+where TEXPRMNT_EXPRID = TQUALIF_EXPRID
+and TQUALIF_DEL_STATUS='U'
+and TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ push(
+ @qvs,
+ { qualifier => $rowref->{'TQUALIF_NAME'},
+ value => $rowref->{'TQUALIF_VALUE'},
+ source => $rowref->{'TQUALIF_SOURCE'},
+ expr_id => $rowref->{'TEXPRMNT_EXPRID'},
+ }
+ );
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Get the QVSs for samples associated with the experiment(s) - of
+ # course, there should be only one experiment, but we're making this
+ # extensible (maybe).
+ foreach my $expr_id ( keys %$expr_ids ) {
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TQUALIF_NAME,TQUALIF_VALUE,TQUALIF_SOURCE,TSAMPLE_ID
+from TQUALIF,TSAMPLE
+where TSAMPLE_SYSUID = TQUALIF_SAMPLEID
+and TQUALIF_DEL_STATUS='U'
+and TSAMPLE_DEL_STATUS='U'
+and TSAMPLE_EXPRID=?
+QUERY
+ $sth->execute($expr_id) or croak($sth->errstr);
+
+ while ( my $rowref = $sth->fetchrow_hashref ) {
+ push(
+ @qvs,
+ { qualifier => $rowref->{'TQUALIF_NAME'},
+ value => $rowref->{'TQUALIF_VALUE'},
+ source => $rowref->{'TQUALIF_SOURCE'},
+ sample_id => $rowref->{'TSAMPLE_ID'},
+ expr_id => $expr_id,
+ }
+ );
+ }
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+ }
+ return \@qvs;
+}
+
+sub _get_array_designid : PRIVATE {
+
+ my ( $self, $hyb_sysuid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # Get a single cell containing the array design id, hopefully.
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TARRAY_DESIGNID from TARRAY,THYBRID
+where TARRAY_SYSUID = THYBRID_ARRAYID
+and TARRAY_DEL_STATUS='U' and THYBRID_DEL_STATUS='U'
+and THYBRID_SYSUID=?
+QUERY
+ $sth->execute($hyb_sysuid) or croak($sth->errstr);
+
+ my $arrayref = $sth->fetchall_arrayref;
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # One entry only
+ if ( $#{$arrayref} > 0 ) {
+ croak(
+ "Error: MIAMExpress returns too many arrays for hyb sysuid $hyb_sysuid.\n"
+ );
+ }
+
+ my $array_designid = $arrayref->[0][0];
+
+ # No zeros allowed.
+ unless ($array_designid) {
+ croak(
+ "Error: MIAMExpress returned array design id of zero for hyb sysuid $hyb_sysuid.\n"
+ );
+ }
+
+ return ($array_designid);
+}
+
+sub _get_adf_filename : PRIVATE {
+
+ my ( $self, $array_designid ) = @_;
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ # Get a single row containing the ADF filename, submission id and
+ # array submitter login, hopefully.
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TARDESIN_SYSUID,TFILE_PATH,TARDESIN_SUBID,TSUBMIS_LOGIN,TARDESIN_DESIGN_NAME
+from TARDESIN, TFILE, TSUBMIS
+where TFILE_ARDESINID=TARDESIN_SYSUID and TSUBMIS_SYSUID=TARDESIN_SUBID
+and TARDESIN_DEL_STATUS='U' and TFILE_DEL_STATUS='U' and TSUBMIS_DEL_STATUS='U'
+and TARDESIN_SYSUID=?
+QUERY
+ $sth->execute($array_designid) or croak($sth->errstr);
+
+ my $results = $sth->fetchall_hashref('TARDESIN_SYSUID')
+ or croak($sth->errstr);
+
+ my $arrayinfo = $results->{$array_designid}
+ or croak("Error retrieving MIAMExpress ADF filename for $array_designid.\n");
+
+ # dump the statement handler now that we are finished with it
+ $sth->finish();
+
+ # Get the array filebase, while we have a connection.
+ $sth = $dbh->prepare(<<'QUERY');
+select distinct TSYSOPT_VALUE from TSYSOPT
+where TSYSOPT_CODE='array_datafiles_path'
+QUERY
+
+ $sth->execute() or die($sth->errstr);
+
+ # FIXME we're assuming the query worked here.
+ my $adf_filename
+ = File::Spec->catfile(
+ $sth->fetchrow_arrayref()->[0],
+ $arrayinfo->{'TSUBMIS_LOGIN'},
+ 'array' . $arrayinfo->{'TARDESIN_SUBID'},
+ $arrayinfo->{'TFILE_PATH'},
+ );
+
+ return (
+ $adf_filename,
+ $arrayinfo->{'TARDESIN_DESIGN_NAME'},
+ );
+}
+
+sub _report_description : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $report_fh = $self->log_fh('report');
+
+ # Connect to MX.
+ my $dbh = $self->get_mx_dbh();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select distinct TEXPRMNT_DESCR
+from TEXPRMNT
+where TEXPRMNT_DEL_STATUS='U'
+and TEXPRMNT_SUBID=?
+QUERY
+ $sth->execute( $self->get_tsubmis_sysuid() ) or croak($sth->errstr);
+
+ my $arrayref = $sth->fetchall_arrayref or croak($sth->errstr);
+
+ foreach my $description (@$arrayref) {
+ format_description( $description->[0], $report_fh );
+ print $report_fh "\n";
+ }
+
+ return;
+}
+
+sub visualize_experiment : RESTRICTED {
+
+ my ( $self, $filelist ) = @_;
+
+ warn("Graph visualization not supported for MIAMExpress experiments.\n");
+
+ return;
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: MIAMExpress.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::MIAMExpress - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::MIAMExpress;
+
+ my $checker = ArrayExpress::Curator::MIAMExpress->new({
+ mx_login => $login,
+ mx_title => $title,
+ });
+
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to interact with a local MIAMExpress installation. It
+optionally exports just two (fairly heavyweight) subroutines. Please
+see the end of this document for the tests carried out by this module.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<mx_login>
+
+The submitter login name associated with a MIAMExpress submission. See
+C<mx_title>.
+
+=item C<mx_title>
+
+The title of a MIAMExpress submission. See C<mx_login>.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output printed
+to the error and/or report filehandles (see
+L<ArrayExpress::Curator::ExperimentChecker> for a list of more general
+tests which are also run):
+
+=over 4
+
+=item B<BioMaterials>
+
+Checks the linking between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file which attempts to
+portray these links in ASCII. Highlights BioMaterials which have not
+been properly linked through to hybridizations (biomaterials log).
+
+=item B<Experimental factor values>
+
+Generates a report showing how hybridizations have been linked to
+experimental factor values, and vice versa (biomaterials log).
+
+=item B<Sample annotation>
+
+Generates a report on all the annotation attached to the samples in
+the experiment (sample log).
+
+=item B<Publication>
+
+Checks whether the publication details include standard Pubmed
+abbreviations; also alerts the curator if a new publication ("other")
+has been indicated (error log).
+
+=item B<QualifierValueSource>
+
+Alerts the curator if QVS entries have been used under Experiment or
+Sample annotation (error log).
+
+=item B<Other>
+
+Alerts the curator if "Other" entries have been used under Experiment, Protocol or
+Sample annotation (error log).
+
+=item B<Transformation protocol>
+
+Alerts the curator if a final data matrix file has been supplied
+without a transformation protocol (error log).
+
+=item B<Protocol description length>
+
+Alerts the curator if protocols with fewer than 50 characters are
+submitted (error log).
+
+=item B<Pooling protocol>
+
+Alerts the curator if a pooling protocol has been linked to a
+submission (error log).
+
+=item B<Normalization protocol>
+
+Alerts the curator if normalized data files have been supplied
+without a normalization protocol (error log).
+
+=item B<Other unbound protocols>
+
+Alerts the curator if protocols other than pooling, normalization or
+transformation have not been bound to their appropriate objects (error
+log).
+
+=item B<Number of files submitted per hybridization>
+
+Prints a table indicating which kinds of raw and normalized data file
+have been submitted for each hybridization (report log).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/Report.pm b/lib/ArrayExpress/Curator/Report.pm
new file mode 100644
index 0000000..b2f07d7
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Report.pm
@@ -0,0 +1,438 @@
+#!/usr/bin/env perl
+#
+# Report.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2004 ArrayExpress team, EBI
+#
+# $Id: Report.pm 1939 2008-02-10 23:29:47Z tfrayner $
+#
+
+package ArrayExpress::Curator::Report;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../index.html">
+ <img src="../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Reporter.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Reporter.pm - a module providing some
+functions useful in generating reports.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Report qw(datafile_consistency_table);
+
+ print datafile_consistency_table( \@filelist );
+
+=head1 DESCRIPTION
+
+This is a simple module providing utility functions and regexp
+patterns used elsewhere in the Tab2MAGE package.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<datafile_consistency_table( \@filelist )>
+
+Given an arrayref of Datafile objects (see L<ArrayExpress::Datafile>),
+returns a string representing a summary of files tabulated by
+hyb_identifier, reporting on presence or absence in the filesystem.
+
+=item C<biomaterials_report( $HoH, $title, $fh )>
+
+Given a nested hashref of hashrefs (of indeterminate depth), a report
+title and an output filehandle, prints a report giving links between
+nodes in the hashref structure (recursively).
+
+=item C<report_accumulated_errors( $HoH, $fh, $string )>
+
+Given a hashref of hashrefs keyed by filename and then by elements
+(e.g. duplicate feature identifiers), an output filehandle and an
+explanatory string, print a report.
+
+=item C<format_description( $text, $fh )>
+
+Takes a text string and an output filehandle, and prints the string in
+lines of less than 80 chars, splitting words on whitespace.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ datafile_consistency_table
+ biomaterials_report
+ report_accumulated_errors
+ format_description
+);
+
+sub get_max_colwidth {
+
+ my ( $hashref, $previousmax, $lookup ) = @_;
+
+ my $col_width = 0;
+ foreach my $key ( keys %$hashref ) {
+ my $keylength = length( $lookup->{$key} );
+ $col_width = $keylength if ( $keylength > $col_width );
+ }
+
+ $col_width = $previousmax
+ if ( $previousmax && $previousmax > $col_width );
+
+ return ($col_width);
+
+}
+
+sub _recurse_heirarchy {
+
+ # Prints a report given a ref to a HoHoHoH of indeterminate
+ # depth. Deals only with the hash keys. The terminal hash values
+ # _must_ be zero or undef to finish the recursion properly. Note
+ # that this function is recursive, and as such is a little hairy in
+ # places.
+
+ my ( $HoH, $widths, $depth ) = @_;
+
+ # Start at depth 0, recurse to depth $#$widths.
+ $depth ||= 0;
+
+ my $col_width = $widths->[$depth];
+
+ # Define the column format.
+ my $col_format = "%" . "${col_width}.${col_width}s";
+
+ # Initialise $lines (must not be undefined).
+ my $lines = q{};
+
+ my @termlist = tied(%$HoH) ? keys %$HoH : sort keys %$HoH;
+
+ foreach my $term (@termlist) {
+
+ my $lines_ending = substr( $lines, length($lines) - 1 );
+
+ if ( $depth && $lines_ending eq "\n" ) {
+
+ # If we're at depth, and we've just finished a line;
+ # -- insert some spacers,
+ for ( my $i = 0; $i < $depth - 1; $i++ ) {
+ my $col_format
+ = "%" . $widths->[$i] . "." . $widths->[$i] . "s";
+ my $spacer = sprintf( "$col_format ", q{} );
+ $lines .= $spacer;
+ }
+
+ # -- and an arrow,
+ my $i = $depth - 1;
+ my $col_format = "%" . $widths->[$i] . "." . $widths->[$i] . "s";
+ my $arrow = sprintf( "$col_format \\-> ", q{} );
+ $lines .= $arrow;
+ }
+
+ $lines .= sprintf( $col_format, $term );
+
+ if ( $HoH->{$term} ) {
+
+ # If we haven't reached the bottom of the HoHoHoH...
+ # -- append a mapping arrow.
+ $lines .= " --> ";
+
+ # Recurse to the next depth in the HoHoHoH
+ $lines
+ .= _recurse_heirarchy( $HoH->{$term}, $widths, $depth + 1 );
+
+ }
+ else {
+
+ $lines .= "\n";
+
+ }
+ }
+ return ($lines);
+}
+
+sub _collect_widths {
+
+ my ( $HoH, $depth ) = @_;
+
+ $depth ||= 0;
+ my @width;
+
+ # Figure out column width at this depth
+ $width[$depth] ||= 0;
+ foreach my $key ( keys %$HoH ) {
+ my $keylength = length($key);
+ $width[$depth] = $keylength if ( $keylength > $width[$depth] );
+ }
+
+ foreach my $term ( keys %$HoH ) {
+ if ( $HoH->{$term} ) {
+
+ # If we haven't reached the bottom of the HoHoHoH, recurse to
+ # the next level down.
+ my $subwidth = _collect_widths( $HoH->{$term}, $depth + 1 );
+
+ RECURSION_DEPTH:
+ for ( my $i = 0; $i <= $#$subwidth; $i++ ) {
+
+ next RECURSION_DEPTH unless defined( $subwidth->[$i] );
+
+ # If this width is greater than that in the top-level array,
+ # modify the top-level array accordingly.
+ if ( !defined( $width[$i] )
+ || $subwidth->[$i] > $width[$i] ) {
+ $width[$i] = $subwidth->[$i];
+ }
+ }
+ }
+ }
+ return \@width;
+
+}
+
+sub format_report {
+
+ # NB. Normally this sub is passed a hashref tied to Tie::IxHash to
+ # preserve creation order (ordered by SYSUID). Untied hashes are
+ # sorted by their keys (typically ID). This is implemented in
+ # _recurse_heirarchy.
+
+ my ($HoH) = @_;
+
+ # Populate an arrayref:
+ # $widths->[$depth] = $column_width where $depth is (0..$levels-1)
+ my $widths = _collect_widths($HoH);
+
+ # Use the widths information to format a report string
+ my $lines = _recurse_heirarchy( $HoH, $widths );
+
+ return $lines;
+
+}
+
+sub biomaterials_report {
+
+ # Prints the workflow reports
+
+ my ( $report_data, $title, $workflow_fh ) = @_;
+
+ # if filehandle unset, use STDOUT
+ $workflow_fh =
+ $workflow_fh
+ ? $workflow_fh
+ : \*STDOUT;
+
+ print $workflow_fh ("\n");
+ print $workflow_fh ( "#" x ( length($title) + 4 ) . "\n" );
+ print $workflow_fh ("# $title #\n");
+ print $workflow_fh ( "#" x ( length($title) + 4 ) . "\n" );
+ print $workflow_fh ("\n");
+
+ print $workflow_fh ( format_report($report_data) );
+
+ print $workflow_fh ("\n");
+
+ return;
+}
+
+sub draw_table_line {
+
+ # Draws a horizontal table line given the width of the first column,
+ # the number of other columns, and their width.
+
+ my ( $first_colwidth, $colwidth, $datacolno ) = @_;
+ my $lines = q{};
+
+ $lines .= "+" . ( "-" x ( $first_colwidth + 2 ) ) . "+";
+ $lines .= ( ( "-" x ( $colwidth + 2 ) ) . "+" ) x $datacolno;
+ $lines .= "\n";
+
+ return $lines;
+}
+
+sub datafile_consistency_table {
+
+ # Takes an array of ArrayExpress::Datafile objects.
+ # Returns a string containing a table ready to print, listing the
+ # files associated with the experiment and any missing files.
+ my $filelist = shift;
+
+ my $lines = q{};
+
+ # Skip if no files in the list.
+ return $lines unless ( scalar @$filelist );
+
+ # Get a list of hybs mapped to file types;
+ # get a list of extensions.
+ my $filetypes;
+ my $extensions;
+
+ DATA_FILE:
+ foreach my $file (@$filelist) {
+
+ next DATA_FILE
+ if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() );
+
+ ( my $extension ) = ( $file->get_path() =~ m/\.([^\.]*)$/ );
+ $extension ||= 'UNKNOWN';
+ $extension =~ tr/a-z/A-Z/;
+ if ( -f $file->get_path() ) {
+
+ # everything's rosy
+ $filetypes->{ $file->get_hyb_identifier() }{$extension} = 1;
+ }
+ else {
+
+ # in the DB, not found on disk
+ $filetypes->{ $file->get_hyb_identifier() }{$extension} = -1;
+ }
+ $extensions->{$extension} = 1;
+ }
+
+ # Figure out our column widths
+ my $colwidth = 12;
+ my $colformat = " %" . $colwidth . "." . $colwidth . "s |";
+
+ my $hybwidth = 0;
+ foreach my $hyb_id ( keys %$filetypes ) {
+ my $hyblength = length($hyb_id);
+ $hybwidth = $hyblength if ( $hyblength > $hybwidth );
+ }
+ my $hybformat = " %" . $hybwidth . "." . $hybwidth . "s |";
+
+ my $num_exts = scalar( grep { defined $_ } values %$extensions );
+
+ # Start drawing the table;
+ # header lines...
+ $lines .= &draw_table_line( $hybwidth, $colwidth, $num_exts );
+ $lines .= sprintf( "|$hybformat", "hyb \\ file extension" );
+ foreach my $ext ( sort keys %$extensions ) {
+ $lines .= sprintf( $colformat, $ext );
+ }
+ $lines .= "\n";
+ $lines .= &draw_table_line( $hybwidth, $colwidth, $num_exts );
+
+ # table content...
+ foreach my $hyb_id ( sort keys %$filetypes ) {
+ $lines .= "|" . sprintf( $hybformat, $hyb_id );
+ foreach my $extension ( sort keys %$extensions ) {
+
+ SWITCH_BY_FILE_EXTENSION:
+ {
+ !$filetypes->{$hyb_id}{$extension} && do {
+ $lines .= sprintf( $colformat, "NOT UPLOADED" );
+ last SWITCH_BY_FILE_EXTENSION;
+ };
+ $filetypes->{$hyb_id}{$extension} == 1 && do {
+ $lines .= sprintf( $colformat, "ok" );
+ last SWITCH_BY_FILE_EXTENSION;
+ };
+ $filetypes->{$hyb_id}{$extension} == -1 && do {
+ $lines .= sprintf( $colformat, "FILE LOST" );
+ last SWITCH_BY_FILE_EXTENSION;
+ };
+ }
+ }
+ $lines .= "\n";
+ }
+
+ # final line...
+ $lines .= &draw_table_line( $hybwidth, $colwidth, $num_exts );
+ $lines .= "\n";
+
+ return $lines;
+}
+
+sub report_accumulated_errors {
+
+ my ( $bad, $output_fh, $error_string ) = @_;
+ my $errorcols;
+
+ foreach my $filename ( keys %$bad ) {
+
+ my $arraystring = join( "\n", sort @{ $bad->{$filename} } );
+ push( @{ $errorcols->{$arraystring} }, $filename );
+
+ }
+
+ foreach my $arraystring ( keys %$errorcols ) {
+ print $output_fh (
+ "$error_string\n\n",
+ $arraystring,
+ "\n\n were found in files:\n\n\t",
+ join( "\n\t", @{ $errorcols->{$arraystring} } ),
+ "\n\n-----------------------------------------------\n\n"
+ );
+ }
+
+ return;
+}
+
+sub format_description {
+
+ # Takes a text string and a filehandle, and outputs the string in
+ # lines of less than 80 chars, splitting words on whitespace.
+
+ my ( $text, $fh ) = @_;
+
+ my $pagewidth = 80;
+
+ # Remove newlines and tabs, and split on spaces
+ $text =~ s/\s+/ /g;
+ my @words = split /[ ]+/, $text;
+
+ # Build lines of <= $pagewidth (80) chars and print them.
+ while (@words) {
+ my $line = shift(@words);
+ while ( @words && ( length( $line . $words[0] ) < $pagewidth ) ) {
+ $line .= " " . shift(@words);
+ }
+ print $fh "$line\n";
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Standalone.pm b/lib/ArrayExpress/Curator/Standalone.pm
new file mode 100644
index 0000000..3d83ed5
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Standalone.pm
@@ -0,0 +1,167 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the expt_check script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Standalone.pm 1987 2008-03-06 11:29:22Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Standalone.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Standalone - Standalone experiment checking.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Standalone;
+
+ my $checker =
+ ArrayExpress::Curator::Standalone->new({
+ data_files => \@ARGV,
+ });
+
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides the basic operations needed for checking a set of
+data files in full standalone mode. For MIAMExpress and Tab2MAGE
+experiment checking, see L<ArrayExpress::Curator::MIAMExpress> and
+L<ArrayExpress::Curator::Validate>, respectively.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<data_files>
+
+An array reference containing a list of data filenames to be checked.
+
+=back
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2007.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Curator::Standalone;
+
+use strict;
+use warnings;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+require ArrayExpress::Datafile;
+
+use base qw(ArrayExpress::Curator::ExperimentChecker);
+
+my %data_files : ATTR( :get<data_files>, :init_arg<data_files>, :default<[]> );
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ unless ( scalar @{ $data_files{$id} } ) {
+ croak("Error: No data files passed to standalone checker.");
+ }
+
+ $self->set_is_standalone(1);
+
+ # Localise to CWD, or value of get_log_to_current_dir.
+ $self->localize_logfiles();
+
+ return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ # Full standalone mode; MIAMExpress and Tab2MAGE checking are
+ # handled by overriding this method in their respective
+ # subclasses.
+
+ my $filelist = [];
+ my $hyb_ids = {}; # Used to map FGEM column headings to hybs.
+ my $norm_ids = {}; # Empty for MX; cannot map MX FGEM using Norm IDs.
+
+ print STDOUT ("Running in Standalone mode...\n");
+
+ $self->logprint( 'error', "* Standalone mode *\n\n" );
+ $self->logprint( 'report', "* Standalone mode *\n\n" );
+
+ # Set up our list of Datafile objects
+ foreach my $filename ( @{ $self->get_data_files() } ) {
+
+ # We have to set a default file type and array design
+ # id here.
+ my $file = ArrayExpress::Datafile->new({
+ name => $filename,
+ data_type => 'raw',
+ array_design_id => $self->get_array_accession() || 'UNKNOWN',
+ });
+
+ push( @$filelist, $file );
+ }
+
+ $self->logprint_line( 'error', 'ADF parsing START' );
+
+ # Populate the $file->get_adf_features() hashrefs within @$filelist
+ $self->cache_user_supplied_arrays( $filelist );
+ $self->populate_file_arraydesigns( $filelist );
+
+ $self->logprint_line( 'error', 'ADF parsing END' );
+
+ return ( $filelist, $hyb_ids, $norm_ids );
+}
+
+sub visualize_experiment : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ # Empty method; we can't visualize in such cases.
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Tab2MAGE.pm b/lib/ArrayExpress/Curator/Tab2MAGE.pm
new file mode 100644
index 0000000..68782e0
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Tab2MAGE.pm
@@ -0,0 +1,2602 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the tab2mage script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Tab2MAGE.pm 2086 2008-06-23 10:48:42Z tfrayner $
+#
+
+package ArrayExpress::Curator::Tab2MAGE;
+
+use strict;
+use warnings;
+
+use Carp;
+use File::Spec;
+use File::Path;
+use File::Basename;
+use IO::File;
+use Text::CSV_XS;
+use List::Util qw(first);
+use Readonly;
+use Bio::MAGE qw(:ALL);
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ date_now
+ find_cdf
+ get_filepath_from_uri
+);
+
+require ArrayExpress::Datafile;
+require ArrayExpress::Datafile::Parser;
+require ArrayExpress::Datafile::DataMatrix;
+
+use ArrayExpress::Curator::Common qw(
+ check_linebreaks
+ $RE_EMPTY_STRING
+ $RE_COMMENTED_STRING
+ $RE_SURROUNDED_BY_WHITESPACE
+ $RE_WITHIN_PARENTHESES
+ $RE_SQUARE_BRACKETS
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::MAGE qw(
+ write_mage
+ postprocess_mage
+ unique_identifier
+ make_container
+ new_biosource
+ new_biosample
+ new_extract
+ new_immunoprecipitate
+ new_labeledextract
+ new_labelcompound
+ new_image
+ new_physicalbioassay
+ new_measuredbioassay
+ new_measuredbioassaydata
+ new_derivedbioassay
+ new_derivedbioassaydata
+ new_bioassaymap
+ new_quantitationtypemap
+ new_protocol
+ new_parameter
+ new_software
+ new_armanuf
+ new_array
+ new_factorvalue
+ new_experimentalfactor
+ new_quantitationtype
+ new_qtd
+ new_ontologyentry
+ new_person
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ validate_experiment_section
+ validate_protocol_section
+ validate_hybridization_section
+
+ $EDF_EXPTACCESSION
+ $EDF_EXPTDOMAIN
+ $EDF_EXPTNAME
+ $EDF_EXPTSUBMITTER
+ $EDF_EXPTDATACODER
+ $EDF_EXPTCURATOR
+ $EDF_EXPTINVESTIGATOR
+ $EDF_EXPTORGANIZATION
+ $EDF_EXPTADDRESS
+ $EDF_PUBAUTHORS
+ $EDF_EXPTSUBMITTER_EMAIL
+ $EDF_EXPTCURATOR_EMAIL
+ $EDF_EXPTDATACODER_EMAIL
+ $EDF_EXPTINVESTIGATOR_EMAIL
+ $EDF_EXPTSECONDARYACCESSION
+ $EDF_EXPTDESCRIPTION
+ $EDF_EXPTRELEASEDATE
+ $EDF_EXPTSUBMISSIONDATE
+ $EDF_EXPTGEORELEASEDATE
+ $EDF_EXPTDESIGNTYPE
+ $EDF_EXPTQUALITYCONTROL
+ $EDF_EXPTCURATEDNAME
+ $EDF_EXPTURI
+
+ $EDF_PUBTITLE
+ $EDF_PUBPAGES
+ $EDF_PUBJOURNAL
+ $EDF_PUBVOLUME
+ $EDF_PUBISSUE
+ $EDF_PUBYEAR
+ $EDF_PUBAUTHORS
+ $EDF_PUBURI
+ $EDF_PUBMEDID
+
+ $EDF_PROTOCOLACCESSION
+ $EDF_PROTOCOLNAME
+ $EDF_PROTOCOLTYPE
+ $EDF_PROTOCOLTEXT
+ $EDF_PROTOCOLPARAMS
+
+ $EDF_PROTOCOLACCESSION
+ $EDF_PROTOCOLPARAMS
+ $EDF_BIOSOURCENAME
+ $EDF_BIOSOURCETYPE
+ $EDF_BIOSOURCEDESCRIPTION
+ $EDF_BIOSAMPLENAME
+ $EDF_BIOSAMPLETYPE
+ $EDF_EXTRACTNAME
+ $EDF_EXTRACTTYPE
+ $EDF_IMMUNOPRECIPITATENAME
+ $EDF_IMMUNOPRECIPITATETYPE
+ $EDF_LABELEDEXTRACTNAME
+ $EDF_LABELEDEXTRACTTYPE
+ $EDF_HYBRIDIZATIONNAME
+ $EDF_SCANNAME
+ $EDF_NORMALIZATIONNAME
+ $EDF_NORMALIZATIONTYPE
+ $EDF_DYE
+
+ $EDF_GROW_PROTOCOL
+ $EDF_TREAT_PROTOCOL
+ $EDF_EXTRACT_PROTOCOL
+ $EDF_POOL_PROTOCOL
+ $EDF_LABEL_PROTOCOL
+ $EDF_IP_PROTOCOL
+ $EDF_HYB_PROTOCOL
+ $EDF_SCAN_PROTOCOL
+ $EDF_FEXT_PROTOCOL
+ $EDF_NORM_PROTOCOL
+
+ $EDF_SCAN_SOFTWARE
+ $EDF_FEXT_SOFTWARE
+ $EDF_NORM_SOFTWARE
+
+ $EDF_IMAGE_FORMAT
+
+ $EDF_ARRAYACCESSION
+ $EDF_ARRAYSERIAL
+
+ $EDF_FILE_PREFIX
+ $EDF_FILE_SUFFIX
+ $EDF_PARAM_PREFIX
+ $EDF_PARAM_SUFFIX
+ $EDF_BMC_PREFIX
+ $EDF_BMC_SUFFIX
+ $EDF_FV_PREFIX
+ $EDF_FV_SUFFIX
+
+ $EDF_SAMPLE_PARAMS
+ $EDF_TREAT_PARAMS
+ $EDF_EXTRACT_PARAMS
+ $EDF_IP_PARAMS
+ $EDF_LABEL_PARAMS
+ $EDF_HYB_PARAMS
+ $EDF_SCAN_PARAMS
+ $EDF_FEXT_PARAMS
+ $EDF_NORM_PARAMS
+
+ $EDF_FEXT_STATS
+ $EDF_NORM_STATS
+
+ $EDF_HYB_HARDWARE
+ $EDF_SCAN_HARDWARE
+ $EDF_HYB_HW_PARAMS
+ $EDF_SCAN_HW_PARAMS
+
+ $OE_CAT_EXPERIMENTDESIGNTYPE
+ $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE
+ $OE_CAT_PUBLICATIONTYPE
+ $OE_VAL_JOURNALARTICLE
+
+ $OE_VAL_SUBMITTER
+ $OE_VAL_DATA_CODER
+ $OE_VAL_CURATOR
+ $OE_VAL_INVESTIGATOR
+
+ $OE_VAL_GROW
+ $OE_VAL_POOL
+ $OE_VAL_SPECIFIEDBIOMATERIALACTION
+ $OE_VAL_NUCLEICACIDEXTRACTION
+ $OE_VAL_IMMUNOPRECIPITATE
+ $OE_VAL_LABELING
+ $OE_VAL_NORMALIZATION
+ $OE_VAL_HYBRIDIZATION
+ $OE_VAL_SCANNING
+ $OE_VAL_FEATUREEXTRACTION
+ $OE_VAL_SCANNING_SOFTWARE
+ $OE_VAL_ANALYSIS_SOFTWARE
+ $OE_VAL_TRANSFORMATION_SOFTWARE
+
+ $OE_VAL_SYNTHETICRNA
+ $OE_VAL_TOTALRNA
+);
+
+use ArrayExpress::Curator::Logger;
+use base qw(ArrayExpress::Curator::Logger);
+
+# Class::Std attributes
+my %spreadsheet_filename : ATTR( :get<spreadsheet_filename>, :init_arg<spreadsheet_filename> );
+my %target_directory : ATTR( :name<target_directory>, :init_arg<target_directory> );
+my %source_directory : ATTR( :get<source_directory>, :init_arg<source_directory> :default<undef> );
+my %is_standalone : ATTR( :get<is_standalone>, :init_arg<is_standalone>, :default<0> );
+my %qt_filename : ATTR( :get<qt_filename>, :init_arg<qt_filename> :default<undef> );
+my %keep_all_qts : ATTR( :get<keep_all_qts>, :init_arg<keep_all_qts> :default<undef> );
+my %keep_protocol_accns : ATTR( :get<keep_protocol_accns>, :init_arg<keep_protocol_accns> :default<undef> );
+my %include_default_qts : ATTR( :get<include_default_qts>, :init_arg<include_default_qts> :default<undef> );
+my %use_plain_text : ATTR( :get<use_plain_text>, :init_arg<use_plain_text> :default<undef> );
+my %reporter_prefix : ATTR( :get<reporter_prefix>, :init_arg<reporter_prefix>, :default<undef> );
+my %compseq_prefix : ATTR( :get<compseq_prefix>, :init_arg<compseq_prefix>, :default<undef> );
+my %external_accession : ATTR( :get<external_accession>, :init_arg<external_accession> :default<undef> );
+my %external_domain : ATTR( :get<external_domain>, :init_arg<external_domain> :default<undef> );
+my %bags : ATTR( :name<bags>, :default<{}> );
+my %namespace : ATTR( :name<namespace>, :default<q{}> );
+my %expt_section : ATTR( :name<expt_section>, :default<{}> );
+my %prot_section : ATTR( :name<prot_section>, :default<[]> );
+my %hyb_section : ATTR( :name<hyb_section>, :default<[]> );
+my %pool_protocol_assns : ATTR( :default<{}> );
+my %pool_protocol_usage : ATTR( :default<{}> );
+my %ignore_size_limits : ATTR( :name<ignore_size_limits>, :default<undef> );
+
+###########
+# METHODS #
+###########
+
+sub BUILD {
+ my ( $self, $id, $args ) = @_;
+
+ # Set up the MAGE container hash
+ $self->initialize_bags();
+
+ return;
+}
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ croak("Error: no target_directory set")
+ unless ( $target_directory{$id} );
+
+ croak("Error: no spreadsheet_filename set")
+ unless ( $spreadsheet_filename{$id} );
+
+ $self->set_progname( $CONFIG->get_TAB2MAGE_PROGNAME() );
+ $self->set_version( $CONFIG->get_TAB2MAGE_VERSION() );
+
+ # Sort out our log files. N.B. the relevant directory may not have
+ # been created yet, so beware of opening filehandle from these.
+ my $logfile_string = File::Spec->catfile(
+ $self->get_target_directory(),
+ basename($self->get_spreadsheet_filename()),
+ );
+ $logfile_string =~ s/\.\w{3,4}$//; # strip off the extension
+ my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+ $self->localize_logfiles({
+ directory => $dir,
+ volume => $vol,
+ });
+
+ return;
+}
+
+sub get_readablename {
+
+ my ($self, $datarow) = @_;
+
+ my $readablename = q{};
+
+ FILE_TYPE:
+ foreach my $filetype ( @{ $CONFIG->get_T2M_FILE_TYPES() } )
+ { # order is important
+ my $key = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+ if ( $datarow->{$key} ) {
+ ($readablename)
+ = ( $datarow->{$key} =~ m{([^/]*?) (?: [.] \w{3})* \z}xms );
+ last FILE_TYPE; # raw is the default
+ }
+ }
+
+ # Fall back to hyb id if there's no raw or normalized
+ # files. Return value must be defined but can be false.
+ return ($readablename || $datarow->{$EDF_HYBRIDIZATIONNAME} || q{});
+}
+
+sub initialize_bags : PRIVATE {
+ my $self = shift;
+
+ my %bag_of;
+
+ $bag_of{protocol} = make_container( \&new_protocol, 1 );
+
+ # 1 here indicates that the &new_protocol sub requires pass-through
+ # of the identifier
+
+ $bag_of{parameter} = make_container( \&new_parameter, 1 );
+ $bag_of{software} = make_container( \&new_software );
+
+ $bag_of{armanuf} = make_container( \&new_armanuf );
+ $bag_of{array} = make_container( \&new_array );
+
+ $bag_of{factorvalue} = make_container( \&new_factorvalue );
+ $bag_of{experimentalfactor} = make_container( \&new_experimentalfactor );
+
+ $bag_of{biosource} = make_container( \&new_biosource );
+ $bag_of{biosample} = make_container( \&new_biosample, 1 );
+ $bag_of{extract} = make_container( \&new_extract, 1 );
+ $bag_of{immunoprecipitate} = make_container( \&new_immunoprecipitate, 1 );
+ $bag_of{labeledextract} = make_container( \&new_labeledextract, 1 );
+ $bag_of{labelcompound} = make_container( \&new_labelcompound );
+ $bag_of{image} = make_container( \&new_image );
+
+ $bag_of{pba} = make_container( \&new_physicalbioassay, 1 );
+ $bag_of{mba} = make_container( \&new_measuredbioassay, 1 );
+ $bag_of{dba} = make_container( \&new_derivedbioassay, 1 );
+ $bag_of{extended_pba} = make_container( \&new_physicalbioassay, 1 );
+ $bag_of{datamatrix_mba} = make_container( \&new_measuredbioassay, 1 );
+ $bag_of{datamatrix_dba} = make_container( \&new_derivedbioassay, 1 );
+
+ $bag_of{mbad} = make_container( \&new_measuredbioassaydata );
+ $bag_of{dbad} = make_container( \&new_derivedbioassaydata );
+ $bag_of{datamatrix_mbad} = make_container( \&new_measuredbioassaydata );
+ $bag_of{datamatrix_dbad} = make_container( \&new_derivedbioassaydata );
+
+ $bag_of{bam} = make_container( \&new_bioassaymap );
+ $bag_of{qtm} = make_container( \&new_quantitationtypemap );
+
+ $bag_of{quantitationtype} = make_container( \&new_quantitationtype );
+ $bag_of{qtd} = make_container( \&new_qtd );
+
+ $bag_of{people} = make_container( \&new_person );
+
+ $self->set_bags( \%bag_of );
+
+ return \%bag_of;
+}
+
+sub write_mageml {
+ my ($self) = @_;
+
+ # Get a hashref of containers for MAGE objects.
+ my $bag_of = $self->get_bags();
+
+ # Create the target directory.
+ mkpath( [ $self->get_target_directory() ], 0, oct(775) );
+
+ $self->logprint(
+ 'tab2mage',
+ sprintf("Parsing spreadsheet %s...\n\n", $self->get_spreadsheet_filename() ),
+ );
+
+ # Make a note if standalone mode was used
+ if ( $self->get_is_standalone() ) {
+ $self->logprint(
+ 'tab2mage',
+ "Standalone mode. Feature identifiers will be formatted without reference to ArrayExpress.\n"
+ );
+
+ # Potentially catastrophic - not allowed in automated pipelines
+ $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+ }
+
+ # Parse the EDF
+ $self->read_edf();
+
+ #########################
+ # Add the new protocols #
+ #########################
+ $self->create_protocols();
+
+ # Set default experiment accession and name. This must be done after
+ # the protocol accessions have been reassigned, above, and before
+ # further processing.
+ my $exptinfo = $self->get_expt_section();
+ my $namespace = $self->get_namespace();
+ $exptinfo->{$EDF_EXPTACCESSION} ||= '{UNASSIGNED}';
+ $exptinfo->{$EDF_EXPTNAME} ||= '{NONE}';
+
+ # This sets up a handler object for DED and QTD-related processing
+ my $parser = ArrayExpress::Datafile::Parser->new({
+ namespace => "$namespace:$exptinfo->{$EDF_EXPTACCESSION}",
+ include_known_qts => $self->get_include_default_qts(),
+ quantitation_types => $self->get_qt_filename(),
+ use_binary_datafiles => ! $self->get_use_plain_text(),
+ is_standalone => $self->get_is_standalone(),
+ output_directory => $self->get_target_directory(),
+ source_directory => $self->get_source_directory(),
+ error_fh => $self->log_fh('tab2mage'),
+ allow_undef_qts => $self->get_keep_all_qts(),
+ protocol_bag => $bag_of->{protocol},
+ parameter_bag => $bag_of->{parameter},
+ reporter_prefix => $self->get_reporter_prefix(),
+ compseq_prefix => $self->get_compseq_prefix(),
+ ignore_size_limits => $self->get_ignore_size_limits(),
+ });
+
+ # Initial file and sanity check
+ $self->_file_sanity_check();
+
+ $self->set_target_directory(
+ File::Spec->rel2abs( $self->get_target_directory() )
+ );
+ my $output_file = File::Spec->catfile(
+ $self->get_target_directory(),
+ "$exptinfo->{$EDF_EXPTACCESSION}.xml"
+ );
+
+ ###################################################
+ # Top-level MAGE object and the Experiment object #
+ ###################################################
+ my $mage = Bio::MAGE->new();
+
+ my ( $experiment, $experimentdesign )
+ = $self->top_level_experiment();
+
+ ###########################
+ # Start Main Datarow Loop #
+ ###########################
+
+ # Hashref for tracking processed files
+ my $file_seen = {};
+
+ # Array of raw/norm data matrices (e.g. Illumina)
+ my @rewritten_data_matrices;
+
+ foreach my $datarow ( @{ $self->get_hyb_section() } ) {
+
+ ##############
+ # Data Files #
+ ##############
+
+ my @datarow_files;
+
+ # START FILE LOOP
+ # NB. the values determined here will be used in BioAssayData later.
+ FILETYPE:
+ foreach my $filetype ( @{ $CONFIG->get_T2M_FILE_TYPES() } ) {
+
+ # Skip this file type if not found in data row. Set the
+ # file name and path.
+ my $filename
+ = $datarow->{ $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX }
+ or next FILETYPE;
+
+ my $path = get_filepath_from_uri(
+ $filename,
+ $self->get_source_directory(),
+ );
+
+ my $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => basename( $path ),
+ data_type => $filetype,
+ array_design_id => $datarow->{$EDF_ARRAYACCESSION},
+ });
+
+ # Process each file just once
+ next FILETYPE if $file_seen->{ $file->get_name() }++;
+
+ # Sort out our DesignElementDimension and get some column heading
+ # info; the $file hashref is populated. This also generates the
+ # stripped file for loading
+ $self->add_error($parser->parse( $file, $datarow ));
+
+ # Keep non-transformed data matrices for later
+ if ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+ || $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ # Process this later, after all the PBA objects have been created.
+ push( @rewritten_data_matrices, [ $file, $datarow ] );
+
+ }
+ else {
+
+ # QuantitationType and QuantitationTypeDimension for
+ # per-hyb data.
+ my ( $qtlist, $qtd_key ) = $self->qts_from_names( $file );
+
+ my $qtd = $bag_of->{qtd}->(
+ $qtd_key,
+ { qt_list => $qtlist,
+ identifier_template =>
+ "$namespace.$exptinfo->{$EDF_EXPTACCESSION}."
+ . unique_identifier
+ }
+ );
+ $file->set_mage_qtd($qtd);
+
+ # Add to the list of per-hyb data files for later
+ # BioAssay creation.
+ push( @datarow_files, $file );
+ }
+
+ }
+
+ # END FILE LOOP
+
+ my $identifier_template
+ = "$namespace:$exptinfo->{$EDF_EXPTACCESSION}." . unique_identifier;
+
+ $self->create_mage_objects(
+ $datarow, $identifier_template, \@datarow_files
+ );
+
+ }
+
+ # Make sure all pooling protocols are linked to something.
+ my $identifier_template
+ = "$namespace:$exptinfo->{$EDF_EXPTACCESSION}";
+ $self->link_unused_pool_protocols($identifier_template);
+
+ # END MAIN DATAROW LOOP
+
+ ######################
+ # DataMatrix parsing #
+ ######################
+
+ # Measured DMs first.
+ foreach my $matrix (@rewritten_data_matrices) {
+
+ my ($file, $datarow) = @$matrix;
+
+ # Some raw files may be DataMatrix-style, e.g. Illumina.
+ my $identifier_template
+ = "$namespace:$exptinfo->{$EDF_EXPTACCESSION}";
+
+ # FIXME add support for MeasuredBioAssay creation here.
+ my $dm = ArrayExpress::Datafile::DataMatrix->new();
+ $dm->create_mage( $file, $datarow, $self, $identifier_template );
+ }
+
+ # Loop over the Hybridization section again, mapping DataMatrix bioassays to
+ # those already created in $bag_of.
+ DATAROW:
+ foreach my $datarow ( @{ $self->get_hyb_section() } ) {
+
+ my $filename = $datarow->{
+ $EDF_FILE_PREFIX
+ . $CONFIG->get_FGEM_FILE_TYPE()
+ . $EDF_FILE_SUFFIX
+ }
+ or next DATAROW;
+
+ my $path;
+ if ( my $dir = $self->get_source_directory() ) {
+ $path = File::Spec->catfile( $dir, $filename );
+ }
+ else {
+ $path = File::Spec->rel2abs($filename);
+ }
+
+ my $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => ( File::Spec->splitpath($path) )[2],
+ data_type => $CONFIG->get_FGEM_FILE_TYPE(),
+ array_design_id => $datarow->{$EDF_ARRAYACCESSION},
+ });
+
+ # Do each file just once
+ next DATAROW if $file_seen->{ $file->get_name() }++;
+
+ # Sort out our DesignElementDimension and get some column heading
+ # info; the $file hashref is populated. This also generates the
+ # stripped file for loading.
+
+ $self->add_error($parser->parse( $file, $datarow ));
+
+ my $identifier_template = "$namespace:$exptinfo->{$EDF_EXPTACCESSION}";
+
+ my $dm = ArrayExpress::Datafile::DataMatrix->new();
+ $dm->create_mage( $file, $datarow, $self, $identifier_template );
+
+ }
+
+ # END DataMatrix parsing
+
+ # Add references to all BioAssays and BioAssayData to the Experiment object
+ foreach my $ba_type qw(pba mba dba extended_pba datamatrix_mba datamatrix_dba) {
+ if ( @{ $bag_of->{$ba_type}->() } ) {
+ $experiment->addBioAssays( @{ $bag_of->{$ba_type}->() } );
+ }
+ }
+ foreach my $ba_data_type qw(mbad dbad datamatrix_mbad datamatrix_dbad) {
+ if ( @{ $bag_of->{$ba_data_type}->() } ) {
+ $experiment->addBioAssayData( @{ $bag_of->{$ba_data_type}->() } );
+ }
+ }
+
+ # Insert the FactorValues (may be redundant FIXME)
+ $experimentdesign->setExperimentalFactors( $bag_of->{experimentalfactor}->() );
+
+ # This next line uses MAGE1.1 features if available, otherwise 1.0
+ my $mage_version; # used to set the correct PUBLIC ID in the output
+ if ( eval { $experiment->setExperimentDesigns( [$experimentdesign] ) } ) {
+ $mage_version = "1.1";
+ }
+ else {
+ $experiment->setExperimentDesign($experimentdesign);
+ $mage_version = "1.0";
+ }
+
+ # Insert the top-level Experiment object into our MAGE container
+ $mage->add_objects( [$experiment] );
+
+ # Insert all the objects stored in our containers
+ foreach my $bag ( values %$bag_of ) {
+ $mage->add_objects( $bag->() );
+ }
+
+ my ( $ded_fh, $dummy_id );
+ if ( $parser->get_ded_count() ) {
+
+ $ded_fh = $parser->get_feature_fh();
+
+ # Create a placeholder for the insertion of DEDs
+ $dummy_id = 'DUMMY';
+ my $dummy_FD = Bio::MAGE::BioAssayData::FeatureDimension->new(
+ identifier => $dummy_id,
+ name => $dummy_id,
+ );
+ $mage->add_objects( [$dummy_FD] );
+ }
+ else {
+ print STDOUT ("No DesignElementDimension found.\n");
+ }
+
+ # Construct the MAGE structure
+ my $mage_identifier = sprintf(
+ "MAGE:%s:%s v%s:%s:%s",
+ $exptinfo->{$EDF_EXPTDOMAIN},
+ $CONFIG->get_TAB2MAGE_PROGNAME(),
+ $CONFIG->get_TAB2MAGE_VERSION(),
+ $exptinfo->{$EDF_EXPTACCESSION},
+ $exptinfo->{$EDF_EXPTNAME}
+ );
+ $mage->identifier( $mage_identifier );
+
+ # Write out everything but the DED
+ print STDOUT ("Writing out initial MAGE-ML...\n");
+ my $mage_fh = IO::File->new_tmpfile;
+ write_mage( $mage, $mage_fh, 'tab delimited', $mage_version );
+
+ # Rewind the temporary filehandle
+ seek( $mage_fh, 0, 0 );
+
+ # Fix the DED using classical text processing (lower memory overhead)
+ print STDOUT ("Inserting DesignElementDimension where found...\n");
+ my $output_fh = IO::File->new( $output_file, '>' )
+ or die("Error: cannot open output file $output_file: $!\n");
+
+ postprocess_mage(
+ $ded_fh,
+ $mage_fh,
+ $output_fh,
+ $dummy_id,
+ $parser->get_cel_types(),
+ );
+
+ return wantarray
+ ? ( $mage, $exptinfo->{$EDF_EXPTACCESSION}, $self->get_error() )
+ : $mage ;
+}
+
+sub create_mage_objects {
+
+ my ( $self, $datarow, $identifier_template, $datarow_files )
+ = @_;
+
+ my $bag_of = $self->get_bags();
+ my $namespace = $self->get_namespace();
+ my $exptinfo = $self->get_expt_section();
+
+ # To be used in names. Raw filename minus extensions used as default
+ my $readablename = $self->get_readablename($datarow);
+
+ ################
+ # FactorValues #
+ ################
+ my @fvlist;
+
+ # Sort here is important for $readablename consistency
+ DATAROW_COLUMN:
+ foreach my $key ( sort keys %$datarow ) {
+
+ # Skip empty values (i.e. null factorvalues are not created;
+ # zeroes are allowed).
+ next DATAROW_COLUMN
+ if ( !defined( $datarow->{$key} )
+ || $datarow->{$key} =~ $RE_EMPTY_STRING );
+
+ my $regexp_str = "$EDF_FV_PREFIX(.*)$EDF_FV_SUFFIX";
+ $regexp_str =~ s{$RE_SQUARE_BRACKETS}{\\$1}gxms;
+
+ # Strict matching should be okay here; we've processed the
+ # headings in read_edf.
+ if ( ( my $category ) = ( $key =~ m/^$regexp_str$/ ) ) {
+
+ # Most of this next line is now non-functional, as we're no
+ # longer implicitly creating null FVs. Kept for reference
+ # purposes.
+ my $value =
+ ( defined( $datarow->{$key} ) && $datarow->{$key} ne q{} )
+ ? $datarow->{$key}
+ : 'null'; # This should take care of null factor values
+
+ my ( $fv, $ef ) = $bag_of->{factorvalue}->(
+
+ # This is used for tracking FVs internally.
+ "$category.$value",
+ { category => $category,
+ value => $value,
+ namespace => $namespace,
+ exptaccession => $exptinfo->{$EDF_EXPTACCESSION},
+ expt_factor_bag => $bag_of->{experimentalfactor},
+ },
+ );
+
+ push( @fvlist, $fv );
+ }
+ }
+
+ ###############################
+ # Set up the pooling protocol #
+ ###############################
+ my $pooling_protocol;
+ if ( $datarow->{$EDF_POOL_PROTOCOL} ) {
+ $pooling_protocol = $bag_of->{protocol}->(
+ $datarow->{$EDF_POOL_PROTOCOL},
+ { protocol_type => $OE_VAL_POOL }
+ );
+ }
+
+ ################
+ ################
+ # BioMaterials #
+ ################
+ ################
+
+ #############
+ # BioSource #
+ #############
+ my ( $biosource, @characteristics, $tracker_key, $biosource_name );
+
+ # Sort here is important for BioSource tracking.
+ DATAROW_COLUMN:
+ foreach my $key ( sort keys %$datarow ) {
+
+ # Skip empty columns. Note that a zero is not empty.
+ next DATAROW_COLUMN
+ unless ( defined( $datarow->{$key} )
+ && ( $datarow->{$key} ne q{} ) );
+
+ my $regexp_str = "$EDF_BMC_PREFIX(.*)$EDF_BMC_SUFFIX";
+ $regexp_str =~ s{$RE_SQUARE_BRACKETS}{\\$1}gxms;
+
+ if ( ( my $category ) = ( $key =~ m/^$regexp_str$/ ) ) {
+
+ my $bmc = new_ontologyentry(
+ { category => $category,
+ value => $datarow->{$key}
+ }
+ );
+ push( @characteristics, $bmc );
+ $tracker_key .= $datarow->{$key};
+ }
+ }
+
+ if ( $datarow->{$EDF_BIOSOURCENAME} ) { # If we're given a name, we use it
+
+ $biosource_name = $tracker_key = $datarow->{$EDF_BIOSOURCENAME};
+
+ }
+ else { # Otherwise we assume that the combination of
+ # BioMaterialCharacteristics defines a unique BioSource,
+ # and make up a name for it.
+
+ $biosource_name = "BioSource: " . ( $readablename || q{} );
+
+ }
+
+ # Only actually define a biosource if it's given in this datarow
+ if ( $tracker_key
+ || $datarow->{$EDF_BIOSOURCETYPE} ) {
+ $biosource = $bag_of->{biosource}->(
+ ( $tracker_key || $biosource_name ), # Fall back to name if no BMC
+ { material_type => $datarow->{$EDF_BIOSOURCETYPE},
+ characteristics => \@characteristics,
+ identifier_template => $identifier_template,
+ name => $biosource_name,
+ description => $datarow->{$EDF_BIOSOURCEDESCRIPTION},
+ }
+ );
+ }
+
+ ##########
+ # Sample #
+ ##########
+ my ( $biosample, $biosample_name );
+ if ( $datarow->{$EDF_BIOSAMPLENAME}
+ || $datarow->{$EDF_GROW_PROTOCOL}
+ || $datarow->{$EDF_TREAT_PROTOCOL}
+ || $datarow->{$EDF_POOL_PROTOCOL}
+ || $datarow->{$EDF_BIOSAMPLETYPE} ) {
+
+ # Protocol and Parameter arrays have to be kept in sync.
+ my $sample_protocols = [];
+ my $sample_parameters = [];
+ $datarow->{$EDF_GROW_PROTOCOL} && do {
+ push(
+ @$sample_protocols,
+ $bag_of->{protocol}->(
+ $datarow->{$EDF_GROW_PROTOCOL},
+ { protocol_type => $OE_VAL_GROW }
+ )
+ );
+ push( @$sample_parameters,
+ ( $datarow->{$EDF_SAMPLE_PARAMS} || {} ) );
+ };
+ $datarow->{$EDF_TREAT_PROTOCOL} && do {
+ push(
+ @$sample_protocols,
+ $bag_of->{protocol}->(
+ $datarow->{$EDF_TREAT_PROTOCOL},
+ { protocol_type => $OE_VAL_SPECIFIEDBIOMATERIALACTION }
+ )
+ );
+ push( @$sample_parameters,
+ ( $datarow->{$EDF_TREAT_PARAMS} || {} ) );
+ };
+
+ $biosample_name = $datarow->{$EDF_BIOSAMPLENAME}
+ || $biosource_name
+ || "Sample: $readablename";
+ $biosample = $bag_of->{biosample}->(
+ $biosample_name,
+ { derived_from => $biosource,
+ material_type => $datarow->{$EDF_BIOSAMPLETYPE},
+ protocols => $sample_protocols,
+ parameters => $sample_parameters,
+ parameter_bag => $bag_of->{parameter},
+ identifier_template => $identifier_template,
+ pooling => $pooling_protocol,
+ }
+ );
+
+ # Check whether we need to cache the pooling protocol.
+ if ( $pooling_protocol ) {
+ $self->check_pool_protocol_usage(
+ $pooling_protocol,
+ $biosample,
+ );
+ }
+ }
+
+ ###########
+ # Extract #
+ ###########
+ my ( $extract, $extract_name );
+ if ( $datarow->{$EDF_EXTRACTNAME}
+ || $datarow->{$EDF_EXTRACT_PROTOCOL}
+ || $datarow->{$EDF_EXTRACTTYPE} ) {
+ my $extract_protocols = [];
+ $datarow->{$EDF_EXTRACT_PROTOCOL} && do {
+ push(
+ @$extract_protocols,
+ $bag_of->{protocol}->(
+ $datarow->{$EDF_EXTRACT_PROTOCOL},
+ { protocol_type => $OE_VAL_NUCLEICACIDEXTRACTION }
+ )
+ );
+ };
+ $extract_name = $datarow->{$EDF_EXTRACTNAME}
+ || $biosample_name
+ || $biosource_name
+ || "Extract: $readablename";
+ $extract = $bag_of->{extract}->(
+ $extract_name,
+
+ # Degrade gracefully...
+ { derived_from => ( $biosample || $biosource ),
+ material_type => $datarow->{$EDF_EXTRACTTYPE},
+ protocols => $extract_protocols,
+ parameters => [ $datarow->{$EDF_EXTRACT_PARAMS} || {} ],
+ parameter_bag => $bag_of->{parameter},
+ identifier_template => $identifier_template,
+ pooling => $pooling_protocol
+ }
+ );
+
+ # Check whether we need to cache the pooling protocol.
+ if ( $pooling_protocol ) {
+ $self->record_pool_protocol_assn(
+ $pooling_protocol,
+ $extract,
+ );
+ $self->check_pool_protocol_usage(
+ $pooling_protocol,
+ $extract,
+ );
+ }
+ }
+
+ #####################################
+ # Immunoprecipitate (ChIP-specific) #
+ #####################################
+ my ( $labeled_extract, $ip );
+ if ( $datarow->{$EDF_IMMUNOPRECIPITATENAME}
+ || $datarow->{$EDF_IP_PROTOCOL}
+ || $datarow->{$EDF_IMMUNOPRECIPITATETYPE} ) {
+ my $ip_protocols = [];
+ $datarow->{$EDF_IP_PROTOCOL} && do {
+ push(
+ @$ip_protocols,
+ $bag_of->{protocol}->(
+ $datarow->{$EDF_IP_PROTOCOL},
+ { protocol_type => $OE_VAL_IMMUNOPRECIPITATE }
+ )
+ );
+ };
+
+ my $ip_name;
+ unless ( $ip_name = $datarow->{$EDF_IMMUNOPRECIPITATENAME} ) {
+ $ip_name = $extract_name
+ || $biosample_name
+ || $biosource_name
+ || $readablename;
+ $ip_name .= ' IP';
+ }
+
+ $ip = $bag_of->{immunoprecipitate}->(
+ $ip_name,
+
+ # Degrade gracefully...
+ { derived_from => ( $extract || $biosample || $biosource ),
+ material_type => $datarow->{$EDF_IMMUNOPRECIPITATETYPE},
+ protocols => $ip_protocols,
+ parameters => [ $datarow->{$EDF_IP_PARAMS} || {} ],
+ parameter_bag => $bag_of->{parameter},
+ identifier_template => $identifier_template,
+ pooling => $pooling_protocol
+ }
+ );
+
+ # Check whether we need to cache the pooling protocol.
+ if ( $pooling_protocol ) {
+ $self->check_pool_protocol_usage(
+ $pooling_protocol,
+ $ip,
+ );
+ }
+
+ ########################
+ # LabeledExtracts (IP) #
+ ########################
+ if ( $datarow->{$EDF_LABELEDEXTRACTNAME}
+ || $datarow->{$EDF_LABEL_PROTOCOL}
+ || $datarow->{$EDF_LABELEDEXTRACTTYPE} ) {
+ my $labeled_extract_protocols = [];
+ $datarow->{$EDF_LABEL_PROTOCOL} && do {
+ push(
+ @$labeled_extract_protocols,
+ $bag_of->{protocol}->(
+ $datarow->{$EDF_LABEL_PROTOCOL},
+ { protocol_type => $OE_VAL_LABELING }
+ )
+ );
+ };
+ my $labeled_extract_name = $datarow->{$EDF_LABELEDEXTRACTNAME}
+ || "$ip_name $datarow->{$EDF_DYE}";
+ $labeled_extract = $bag_of->{labeledextract}->(
+ $labeled_extract_name,
+
+ # We know we have $ip...
+ { derived_from => $ip,
+ material_type => $datarow->{$EDF_LABELEDEXTRACTTYPE},
+ protocols => $labeled_extract_protocols,
+ parameters => [ $datarow->{$EDF_LABEL_PARAMS} || {} ],
+ parameter_bag => $bag_of->{parameter},
+ compound_bag => $bag_of->{labelcompound},
+ identifier_template => $identifier_template,
+ dye => $datarow->{$EDF_DYE},
+ pooling => $pooling_protocol
+ }
+ );
+ }
+
+ }
+ else {
+
+ ############################
+ # LabeledExtracts (normal) #
+ ############################
+ if ( $datarow->{$EDF_LABELEDEXTRACTNAME}
+ || $datarow->{$EDF_LABEL_PROTOCOL}
+ || $datarow->{$EDF_LABELEDEXTRACTTYPE} ) {
+ my $labeled_extract_protocols = [];
+ $datarow->{$EDF_LABEL_PROTOCOL} && do {
+ push(
+ @$labeled_extract_protocols,
+ $bag_of->{protocol}->(
+ $datarow->{$EDF_LABEL_PROTOCOL},
+ { protocol_type => $OE_VAL_LABELING }
+ )
+ );
+ };
+
+ my $labeled_extract_name;
+ unless ( $labeled_extract_name = $datarow->{$EDF_LABELEDEXTRACTNAME} ) {
+ $labeled_extract_name = $extract_name
+ || $biosample_name
+ || $biosource_name
+ || "Label: $readablename";
+ $labeled_extract_name .= " $datarow->{$EDF_DYE}"
+ if $datarow->{$EDF_DYE};
+ }
+
+ $labeled_extract = $bag_of->{labeledextract}->(
+ $labeled_extract_name,
+ { derived_from => ( $extract || $biosample || $biosource ),
+ material_type => $datarow->{$EDF_LABELEDEXTRACTTYPE},
+ protocols => $labeled_extract_protocols,
+ parameters => [ $datarow->{$EDF_LABEL_PARAMS} || {} ],
+ parameter_bag => $bag_of->{parameter},
+ compound_bag => $bag_of->{labelcompound},
+ identifier_template => $identifier_template,
+ dye => $datarow->{$EDF_DYE},
+ pooling => $pooling_protocol
+ }
+ );
+ }
+ }
+
+ # Check whether we need to cache the pooling protocol.
+ if ( $pooling_protocol ) {
+ $self->check_pool_protocol_usage(
+ $pooling_protocol,
+ $labeled_extract,
+ );
+ }
+
+ #########
+ # Array #
+ #########
+ my $array;
+ if ( $datarow->{$EDF_ARRAYACCESSION} ) {
+
+ $array = $bag_of->{array}->(
+ ($datarow->{$EDF_HYBRIDIZATIONNAME} || $readablename),
+ { identifier_template => $identifier_template,
+ arrayaccession => $datarow->{$EDF_ARRAYACCESSION},
+ serial => $datarow->{$EDF_ARRAYSERIAL},
+ armanuf_bag => $bag_of->{armanuf},
+ },
+ );
+ }
+
+ # END Array
+
+ #############################
+ #############################
+ # BioAssayData and BioAssay #
+ #############################
+ #############################
+
+ # NB uses information from the datafile parsing at the start of the
+ # datarow loop. Declared here so DBA[D] can map to MBA[D]
+ my $mbad;
+
+ # These default to raw filename if available. $hyb_name is used as
+ # both internal PBA id and PBA name attribute.
+ my $hyb_name = $datarow->{$EDF_HYBRIDIZATIONNAME} || $readablename;
+
+ # $scan_name is used as the internal id of the MBA object.
+ my $scan_name;
+ if ( $datarow->{$EDF_SCANNAME} ) {
+ $scan_name = $datarow->{$EDF_SCANNAME};
+ }
+ elsif ( my $hname = $datarow->{$EDF_HYBRIDIZATIONNAME} ) {
+
+ # These fallbacks are required for accurate raw datamatrix
+ # (e.g. Illumina) processing.
+ if ( my $fname
+ = $datarow->{ $EDF_FILE_PREFIX . 'raw' . $EDF_FILE_SUFFIX } ) {
+ $scan_name = "$hname $fname";
+ }
+ else {
+ $scan_name = $hname;
+ }
+ }
+ else {
+ $scan_name = $datarow->{ $EDF_FILE_PREFIX . 'raw' . $EDF_FILE_SUFFIX };
+ }
+
+ # $mba_name is used separately as the MBA name attribute
+ # (displayed in AE web interface, so needs to be kept simple).
+ my $mba_name = $datarow->{$EDF_SCANNAME} || $hyb_name;
+
+ # $norm_name is both the internal ID of the DBA and the DBA name attribute.
+ my $norm_name = $datarow->{$EDF_NORMALIZATIONNAME}
+ || $datarow->{ $EDF_FILE_PREFIX . 'normalized' . $EDF_FILE_SUFFIX };
+
+ ####################################
+ # PhysicalBioAssay (Hybridization) #
+ ####################################
+
+ my $pba;
+
+ # Only do this if hybname available, otherwise we risk null
+ # container identifiers.
+ if ($hyb_name) {
+ my ( $hyb_protocol, $scan_protocol );
+ $datarow->{$EDF_HYB_PROTOCOL} && do {
+ $hyb_protocol = $bag_of->{protocol}->(
+ $datarow->{$EDF_HYB_PROTOCOL},
+ { protocol_type => $OE_VAL_HYBRIDIZATION }
+ );
+ };
+
+ # If both scan and fext protocols available, add scanning
+ # here. Otherwise attach scanning to feature_extraction below.
+ ( $datarow->{$EDF_SCAN_PROTOCOL} && $datarow->{$EDF_FEXT_PROTOCOL} )
+ && do {
+ my $ptype = $OE_VAL_SCANNING;
+ $scan_protocol = $bag_of->{protocol}->(
+ $datarow->{$EDF_SCAN_PROTOCOL},
+ { protocol_type => $ptype }
+ );
+ };
+ my $scan_software = q{};
+ my $scan_software_version = q{};
+
+ # If we've got a FEXT protocol then all well and good. If not, we
+ # don't need a image_acquisition_software here since it will be
+ # added as feature_extraction_software below.
+ ( $datarow->{$EDF_SCAN_SOFTWARE} && $datarow->{$EDF_FEXT_PROTOCOL} )
+ && do {
+ my $name;
+ ( $name, $scan_software_version )
+ = $self->parse_software( $datarow->{$EDF_SCAN_SOFTWARE} );
+ $scan_software = $bag_of->{software}->(
+ "SCAN:$name"
+ . (
+ $scan_software_version ? ":$scan_software_version" : q{}
+ ),
+ { identifier_template =>
+ "$identifier_template.ImageAcquisition",
+ name => $name,
+ type => $OE_VAL_SCANNING_SOFTWARE
+ }
+ );
+ };
+ my $image;
+ my $image_data
+ = $datarow->{ $EDF_FILE_PREFIX . 'image' . $EDF_FILE_SUFFIX };
+ if ($image_data) {
+ $image = $bag_of->{image}->(
+ $image_data,
+ { identifier_template => $identifier_template,
+ uri => $image_data,
+ format => $datarow->{$EDF_IMAGE_FORMAT}
+ }
+ );
+ }
+
+ my $label;
+ if ( $labeled_extract && $labeled_extract->getLabels() ) {
+
+ # Assume only one dye per label (works for Tab2MAGE; not
+ # so for MAGE-TAB).
+ $label = $labeled_extract->getLabels()->[0];
+ }
+
+ $pba = $bag_of->{pba}->(
+ $hyb_name,
+ { derived_from =>
+ ( $labeled_extract || $ip || $extract || $biosample || $biosource ),
+ label => $label,
+ array => $array,
+ identifier_template => $identifier_template,
+ factor_values => \@fvlist,
+ hybridization_protocol => $hyb_protocol,
+ hybridization_parameters => $datarow->{$EDF_HYB_PARAMS},
+ scanning_protocol => $scan_protocol,
+ scanning_parameters => $datarow->{$EDF_SCAN_PARAMS},
+ parameter_bag => $bag_of->{parameter},
+ software => $scan_software,
+ software_version => $scan_software_version,
+ image => $image,
+ hyb_hardware => $datarow->{$EDF_HYB_HARDWARE},
+ hyb_hardware_params => $datarow->{$EDF_HYB_HW_PARAMS},
+ scan_hardware => $datarow->{$EDF_SCAN_HARDWARE},
+ scan_hardware_params => $datarow->{$EDF_SCAN_HW_PARAMS},
+ extended_pba_bag => $bag_of->{extended_pba},
+ mba_bag => $bag_of->{mba},
+ }
+ );
+ } # end if $hybname
+
+ # Handle the two-colour hyb coding. Assumes that merged PBAs have
+ # a .merged name suffix.
+ if ( $pba && ( scalar @{ $pba->getChannels() || [] } > 1 ) ) {
+ my $pba_name = $pba->getName();
+ $pba = $bag_of->{extended_pba}->(
+ "$hyb_name.merged"
+ );
+ unless ( $pba ) {
+ die(sprintf(
+ "Internal error: Merged PBA undefined for two-color coding (%s).",
+ $pba_name)
+ );
+ }
+ }
+
+ ####################
+ # MeasuredBioAssay #
+ ####################
+ my $mba;
+ if ($scan_name) {
+
+ my $fext_protocol;
+ my $fext_protocol_accession;
+ ( $fext_protocol_accession = $datarow->{$EDF_FEXT_PROTOCOL}
+ || $datarow->{$EDF_SCAN_PROTOCOL} )
+ && do {
+ $fext_protocol = $bag_of->{protocol}->(
+ $fext_protocol_accession,
+ { protocol_type => $OE_VAL_FEATUREEXTRACTION }
+ );
+ };
+ my $fext_software = q{};
+ my $fext_software_version = q{};
+ my $fext_sw_string = q{};
+
+ # If FEXT software not available, we use SCAN software (see above
+ # for scanning software treatment).
+ ( $fext_sw_string = $datarow->{$EDF_FEXT_SOFTWARE}
+ || $datarow->{$EDF_SCAN_SOFTWARE} )
+ && do {
+ my $name;
+ ( $name, $fext_software_version )
+ = $self->parse_software($fext_sw_string);
+ $fext_software = $bag_of->{software}->(
+ "FEXT:$name"
+ . (
+ $fext_software_version ? ":$fext_software_version" : q{}
+ ),
+ { identifier_template =>
+ "$identifier_template.FeatureExtraction",
+ name => $name,
+ type => $OE_VAL_ANALYSIS_SOFTWARE
+ }
+ );
+ };
+ $mba = $bag_of->{mba}->(
+ $scan_name,
+ { name => $mba_name,
+ physical_bioassay => $pba,
+ factor_values => \@fvlist,
+ identifier_template => $identifier_template,
+ protocol => $fext_protocol,
+ parameters => $datarow->{$EDF_FEXT_PARAMS},
+ parameter_bag => $bag_of->{parameter},
+ software => $fext_software,
+ software_version => $fext_software_version
+ }
+ );
+
+ } # end if $scan_name
+
+ ###################
+ # DerivedBioAssay #
+ ###################
+ my $dba;
+ if ($norm_name) {
+
+ # Allow norm without raw bioassay.
+ my $source_bioassays = $mba ? [$mba] : undef;
+
+ # BioAssayMap (below) and DBA both have the same $norm_name
+ # internal identifier.
+ $dba = $bag_of->{dba}->(
+ $norm_name,
+ { factor_values => \@fvlist,
+ identifier_template => $identifier_template,
+ normalization_type => $datarow->{$EDF_NORMALIZATIONTYPE},
+ source_bioassays => $source_bioassays,
+ bioassaymap_bag => $bag_of->{bam},
+ },
+ );
+ } # end if $norm_name (dbad added below)
+
+ ################
+ # BioAssayData #
+ ################
+
+ DATAROW_FILE:
+ foreach my $file ( @{ $datarow_files || [] } ) {
+
+ # Skip if there's no filename.
+ next DATAROW_FILE unless $file->get_name();
+
+ my $de_dimension;
+
+ DESIGN_ELEMENT_DIMENSION:
+ {
+
+ last DESIGN_ELEMENT_DIMENSION unless ( $file->get_ded_type() );
+
+ my $dim_class
+ = 'Bio::MAGE::BioAssayData::'
+ . $file->get_ded_type()
+ . 'Dimension';
+
+ eval {
+ $de_dimension = $dim_class->new(
+ identifier => $file->get_ded_identifier() );
+ }
+ or croak(
+ "Error: Unrecognized DED type: "
+ . $file->get_ded_type()
+ . "\n" );
+
+ # If we've already seen this file once, $de_dimension will end
+ # up empty and ignored.
+
+ }
+
+ if ( $file->get_data_type() eq 'raw' ) {
+
+ ########################
+ # MeasuredBioAssayData #
+ ########################
+ $mbad = $bag_of->{mbad}->(
+ $file->get_name(),
+ { filename => $file->get_target_filename(),
+ feature_dimension => $de_dimension,
+ summary_statistics => $datarow->{$EDF_FEXT_STATS},
+ quantitation_type_dimension => $file->get_mage_qtd(),
+ identifier_template => $identifier_template
+ }
+ );
+
+ # Add mbad to the mba we created earlier
+ my $found;
+ if ( my $preexisting_mbads = $mba->getMeasuredBioAssayData ) {
+ my $new_identifier = $mbad->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_mbads;
+ }
+ $mba->addMeasuredBioAssayData($mbad) unless $found;
+
+ #####################
+ # BioAssayDimension #
+ #####################
+ my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+ identifier =>
+ "$identifier_template.MeasuredBioAssayDimension",
+ bioAssays => [$mba],
+ );
+ $mbad->setBioAssayDimension($bad)
+ unless $mbad->getBioAssayDimension();
+
+ }
+ elsif ( $file->get_data_type() eq 'normalized' ) {
+
+ #######################
+ # DerivedBioAssayData #
+ #######################
+ my $protocol;
+ $datarow->{$EDF_NORM_PROTOCOL} && do {
+ $protocol = $bag_of->{protocol}->(
+ $datarow->{$EDF_NORM_PROTOCOL},
+ { protocol_type => $OE_VAL_NORMALIZATION }
+ );
+ };
+
+ my $norm_software = q{};
+ my $software_version = q{};
+ $datarow->{$EDF_NORM_SOFTWARE} && do {
+ my $name;
+ ( $name, $software_version )
+ = $self->parse_software( $datarow->{$EDF_NORM_SOFTWARE} );
+ $norm_software = $bag_of->{software}->(
+ "NORM:$name"
+ . ( $software_version ? ":$software_version" : q{} ),
+ { identifier_template =>
+ "$identifier_template.Normalization",
+ name => $name,
+ type => $OE_VAL_TRANSFORMATION_SOFTWARE
+ }
+ );
+ };
+
+ # BioAssayMap and DBA both have the same $norm_name
+ # internal identifier. Omit this if there are no MBAs (no
+ # mapping necessary, and the BAMs will be empty and
+ # dropped from the output MAGE-ML).
+ my @bams;
+ my $bam = $bag_of->{bam}->($norm_name);
+ if ( $mba && $bam ) {
+
+ # same name used here and for DBA above
+ push @bams, $bam;
+ }
+
+ # BioAssayDimension
+ my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+ identifier => "$identifier_template.DerivedBioAssayDimension",
+ bioAssays => [$dba],
+ );
+
+ # Source QTs for QTMapping
+ my %source_qts;
+ if ( $mbad
+ && ( my $source_qtd = $mbad->getQuantitationTypeDimension ) )
+ {
+ foreach my $qt ( @{ $source_qtd->getQuantitationTypes } ) {
+ $source_qts{ $qt->getIdentifier } = $qt;
+ }
+ }
+
+ my $source_bioassays
+ = $mbad ? [$mbad] : undef; # Allow norm data with no raw
+ my $dbad = $bag_of->{dbad}->(
+ $file->get_name(),
+ { filename => $file->get_target_filename(),
+ de_dimension => $de_dimension,
+ qt_dimension => $file->get_mage_qtd(),
+ ba_dimension => $bad,
+ identifier_template => $identifier_template,
+ protocol => $protocol,
+ parameters => $datarow->{$EDF_NORM_PARAMS},
+ parameter_bag => $bag_of->{parameter},
+ summary_statistics => $datarow->{$EDF_NORM_STATS},
+ source_bioassay_dataset => $source_bioassays,
+ bioassay_map => \@bams,
+ software => $norm_software,
+ software_version => $software_version,
+ source_qts => [ sort values %source_qts ],
+ qtm_bag => $bag_of->{qtm},
+ }
+ );
+
+ # Add dbad to the dba we created earlier
+ $self->dba_add_data( $dba, $dbad );
+
+ }
+ else {
+
+ croak( "Error: Unsupported file type: "
+ . $file->get_data_type()
+ . "\n" );
+
+ }
+ }
+
+ return $bag_of;
+}
+
+sub dba_add_data {
+
+ my ( $self, $dba, $dbad ) = @_;
+
+ # Check that the DBAD is not already present, add if not.
+ my $found;
+ if ( my $preexisting_dbads = $dba->getDerivedBioAssayData ) {
+ my $new_identifier = $dbad->getIdentifier;
+ $found = first { $_->getIdentifier eq $new_identifier }
+ @$preexisting_dbads;
+ }
+ $dba->addDerivedBioAssayData($dbad) unless $found;
+
+ return;
+}
+
+sub parse_software {
+ my ($self, $software_string) = @_;
+
+ # Software name is everthing up to the first parenthesis
+ my $name = ( split /\s*\(/, $software_string )[0];
+
+ # Software version is within parens
+ my ($software_version) = ( $software_string =~ $RE_WITHIN_PARENTHESES );
+
+ return ( $name, $software_version );
+}
+
+sub qts_from_names {
+
+ my ( $self, $file, $qt_names ) = @_;
+
+ my $bag_of = $self->get_bags();
+
+ $qt_names ||= $file->get_column_headings();
+
+ my ( @qtd_qtlist, $qtd_key );
+ my ( $QT_prefix, $software );
+
+ if ( $file->get_format_type() eq 'Affymetrix' ) {
+ $QT_prefix = 'Affymetrix:QuantitationType:';
+ $software = 'Affymetrix';
+ }
+ elsif ( my ( $type, $qt_namespace )
+ = ( $file->get_qt_type =~ m{\A (.*?) \[ (.*) \] \z}xms ) ) {
+ $QT_prefix = "${qt_namespace}:${type}_QuantitationType:";
+ $software = $type;
+ }
+ else {
+ $QT_prefix = sprintf('Unknown:%s_QuantitationType:', $file->get_qt_type());
+ $software = $file->get_qt_type() eq 'Ambiguous'
+ ? 'Unknown'
+ : $file->get_qt_type();
+ }
+
+ foreach my $heading (@$qt_names) {
+
+ my $identifier = $QT_prefix . $heading;
+ my $quantitationtype = $bag_of->{quantitationtype}->(
+ $identifier,
+ { identifier => $identifier,
+ prefix => $QT_prefix,
+ name => $heading,
+ software => $software,
+ data_metrics => $file->get_data_metrics(),
+ container => $bag_of->{quantitationtype},
+ }
+ );
+ push( @qtd_qtlist, $quantitationtype );
+
+ $qtd_key .= $heading;
+
+ }
+
+ # Quick check for sane QTs (This error should no longer be triggered
+ # by missing confidence indicator targets - see MAGE.pm).
+ foreach my $qt ( @{ $bag_of->{quantitationtype}->() } ) {
+ unless ( $qt->getIdentifier ) {
+ print STDERR (<<'END_ERROR');
+ERROR: At least one QuantitationType found lacking an identifier.
+Please check that the confidence indicator targets in your QT file
+are represented in the data files.
+
+END_ERROR
+
+ croak( qq{Error occurred at data file }
+ . $file->get_name()
+ . q{QT name: "}
+ . $qt->getName()
+ . q{"} );
+ }
+ }
+
+ $qtd_key ||= 'NULL'; # In case of no QTs, e.g. missing file
+
+ return ( \@qtd_qtlist, $qtd_key );
+}
+
+sub top_level_experiment : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $people_carrier = $self->get_bags()->{people};
+ my $namespace = $self->get_namespace();
+ my $exptinfo = $self->get_expt_section();
+
+ my $experiment = $self->new_experiment();
+ my $experimentdesign = $self->new_experimentdesign();
+
+ # Just create objects for the submitter, data coder and/or curator.
+ my $submitter = $exptinfo->{$EDF_EXPTSUBMITTER}
+ || $exptinfo->{$EDF_EXPTSUBMITTER_EMAIL};
+ $submitter && do {
+ $people_carrier->(
+ $submitter,
+ { namespace => $namespace,
+ name => $exptinfo->{$EDF_EXPTSUBMITTER},
+ organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+ address => $exptinfo->{$EDF_EXPTADDRESS},
+ email => $exptinfo->{$EDF_EXPTSUBMITTER_EMAIL},
+ role => $OE_VAL_SUBMITTER,
+ }
+ );
+ };
+ my $curator = $exptinfo->{$EDF_EXPTCURATOR}
+ || $exptinfo->{$EDF_EXPTCURATOR_EMAIL};
+ $curator && do {
+ $people_carrier->(
+ $curator,
+ { namespace => $namespace,
+ name => $exptinfo->{$EDF_EXPTCURATOR},
+ organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+ address => $exptinfo->{$EDF_EXPTADDRESS},
+ email => $exptinfo->{$EDF_EXPTCURATOR_EMAIL},
+ role => $OE_VAL_CURATOR,
+ }
+ );
+ };
+ my $datacoder = $exptinfo->{$EDF_EXPTDATACODER}
+ || $exptinfo->{$EDF_EXPTDATACODER_EMAIL};
+ $datacoder && do {
+ $people_carrier->(
+ $datacoder,
+ { namespace => $namespace,
+ name => $exptinfo->{$EDF_EXPTDATACODER},
+ organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+ address => $exptinfo->{$EDF_EXPTADDRESS},
+ email => $exptinfo->{$EDF_EXPTDATACODER_EMAIL},
+ role => $OE_VAL_DATA_CODER,
+ }
+ );
+ };
+ my $investigator = $exptinfo->{$EDF_EXPTINVESTIGATOR}
+ || $exptinfo->{$EDF_EXPTINVESTIGATOR_EMAIL};
+ $investigator && do {
+ $people_carrier->(
+ $investigator,
+ { namespace => $namespace,
+ name => $exptinfo->{$EDF_EXPTINVESTIGATOR},
+ organization => $exptinfo->{$EDF_EXPTORGANIZATION},
+ address => $exptinfo->{$EDF_EXPTADDRESS},
+ email => $exptinfo->{$EDF_EXPTINVESTIGATOR_EMAIL},
+ role => $OE_VAL_INVESTIGATOR,
+ }
+ );
+ };
+
+ # Everyone goes on the Providers list
+ $experiment->setProviders( $people_carrier->() );
+
+ return ( $experiment, $experimentdesign );
+}
+
+sub new_experiment : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $exptinfo = $self->get_expt_section();
+
+ my $experiment = Bio::MAGE::Experiment::Experiment->new(
+ identifier => $exptinfo->{$EDF_EXPTACCESSION} );
+
+ $exptinfo->{$EDF_EXPTNAME}
+ && do { $experiment->setName( $exptinfo->{$EDF_EXPTNAME} ); };
+
+ my $description = Bio::MAGE::Description::Description->new;
+
+ # make sure we get some kind of BQS object
+ $exptinfo->{$EDF_PUBPAGES} ||= "-";
+
+ my $bqs = Bio::MAGE::BQS::BibliographicReference->new(
+ title => $exptinfo->{$EDF_PUBTITLE},
+ pages => $exptinfo->{$EDF_PUBPAGES},
+ publication => $exptinfo->{$EDF_PUBJOURNAL},
+ volume => $exptinfo->{$EDF_PUBVOLUME},
+ issue => $exptinfo->{$EDF_PUBISSUE},
+ year => $exptinfo->{$EDF_PUBYEAR},
+ authors => $exptinfo->{$EDF_PUBAUTHORS},
+ URI => $exptinfo->{$EDF_PUBURI},
+ );
+ $bqs->addParameters(
+ &new_ontologyentry(
+ { category => $OE_CAT_PUBLICATIONTYPE,
+ value => $OE_VAL_JOURNALARTICLE,
+ }
+ )
+ );
+ if ($exptinfo->{$EDF_PUBMEDID}) {
+ my $db = Bio::MAGE::Description::Database->new(
+ identifier => 'ebi.ac.uk:Database:pubmed',
+ );
+ my $pubmed = Bio::MAGE::Description::DatabaseEntry->new(
+ accession => $exptinfo->{$EDF_PUBMEDID},
+ database => $db,
+ );
+ $bqs->addAccessions($pubmed);
+ }
+ $description->addBibliographicReferences($bqs);
+
+ $exptinfo->{$EDF_EXPTDESCRIPTION}
+ && do { $description->setText( $exptinfo->{$EDF_EXPTDESCRIPTION} ); };
+ $exptinfo->{$EDF_EXPTURI}
+ && do { $description->setURI( $exptinfo->{$EDF_EXPTURI} ); };
+
+ $experiment->addDescriptions($description);
+
+ $exptinfo->{$EDF_EXPTRELEASEDATE} && do {
+ my $date = Bio::MAGE::NameValueType->new(
+ name => 'ArrayExpressReleaseDate',
+ value => $exptinfo->{$EDF_EXPTRELEASEDATE},
+ );
+ $experiment->addPropertySets($date);
+ };
+ $exptinfo->{$EDF_EXPTSUBMISSIONDATE} && do {
+ my $date = Bio::MAGE::NameValueType->new(
+ name => 'ArrayExpressSubmissionDate',
+ value => $exptinfo->{$EDF_EXPTSUBMISSIONDATE},
+ );
+ $experiment->addPropertySets($date);
+ };
+ $exptinfo->{$EDF_EXPTGEORELEASEDATE} && do {
+ my $date = Bio::MAGE::NameValueType->new(
+ name => 'GEOReleaseDate',
+ value => $exptinfo->{$EDF_EXPTGEORELEASEDATE},
+ );
+ $experiment->addPropertySets($date);
+ };
+
+ # Either add the curated name, or a placeholder in MAGE.
+ my $arrayexpress_curated_name = Bio::MAGE::NameValueType->new(
+ name => 'AEExperimentDisplayName',
+ value => ( $exptinfo->{$EDF_EXPTCURATEDNAME} || q{} ),
+ );
+
+ # We support multiple secondary accessions. This may need more
+ # finesse in future.
+ my @secondary_accessions;
+ my @accnos = split /\s*;\s*/,
+ ( $exptinfo->{$EDF_EXPTSECONDARYACCESSION} || q{} );
+
+ # At least one secondary accession NVT is required.
+ $accnos[0] = q{} unless scalar @accnos;
+ foreach my $acc ( @accnos ) {
+ my $accession = Bio::MAGE::NameValueType->new(
+ name => 'SecondaryAccession',
+ value => $acc,
+ );
+ push @secondary_accessions, $accession;
+ }
+ $experiment->addPropertySets(
+ $arrayexpress_curated_name,
+ @secondary_accessions,
+ );
+
+ return $experiment;
+
+}
+
+sub new_experimentdesign {
+
+ my ( $self ) = @_;
+
+ my $exptinfo = $self->get_expt_section();
+
+ # Sort out ExperimentDesign
+ my $experimentdesign = Bio::MAGE::Experiment::ExperimentDesign->new;
+
+ $exptinfo->{$EDF_EXPTDESIGNTYPE} && do {
+
+ # Split the string on commas, semicolons
+ foreach
+ my $type ( split /\s*[,;]\s*/, $exptinfo->{$EDF_EXPTDESIGNTYPE} )
+ {
+ my $experimentdesigntype = &new_ontologyentry(
+ { category => $OE_CAT_EXPERIMENTDESIGNTYPE,
+ value => $type
+ }
+ );
+ $experimentdesign->addTypes($experimentdesigntype);
+ }
+ };
+ $exptinfo->{$EDF_EXPTQUALITYCONTROL} && do {
+
+ # Split the string on commas, semicolons
+ my @qualitycontrol;
+ foreach my $type ( split /\s*[,;]\s*/,
+ $exptinfo->{$EDF_EXPTQUALITYCONTROL} ) {
+ push(
+ @qualitycontrol,
+ &new_ontologyentry(
+ { category => $OE_CAT_QUALITYCONTROLDESCRIPTIONTYPE,
+ value => $type
+ }
+ )
+ );
+ }
+ my $qcd = Bio::MAGE::Description::Description->new(
+ annotations => \@qualitycontrol );
+ $experimentdesign->setQualityControlDescription($qcd);
+ };
+
+ return $experimentdesign;
+
+}
+
+sub htmlify_protocoltext {
+
+ # Make some simple substitutions to correctly format text loaded
+ # into ArrayExpress. This subroutine is likely to be extended as
+ # more substitutions are found.
+
+ my ($self, $text) = @_;
+
+ # The order of these substitutions is not currently important.
+
+ # 1. Lone ampersands should be &
+ $text =~ s/\&(?!#?\w+;)/\&/g;
+
+ # 2. Angle brackets (that are not part of HTML tags).
+ # $text =~ s/<(?!\s*[a-z]+\s*\/?>)/\</gi;
+ # Negative look-behind is fixed width, this is not trivial to implement.
+ # $text =~ s/>/\>/gi;
+
+ # 3. Newlines become <br>
+ $text =~ s/(\r\n?|\n)/<br>$1/g;
+
+ # 4. Various common non-ASCII characters:
+ # a. degrees
+ $text =~ s/�/\°/g;
+ $text =~ s/�/\°/g;
+ $text =~ s/�X/\°/g;
+
+ # b. mu
+ $text =~ s/�/\μ/g;
+ $text =~ s/��/\μ/g;
+
+ # c. apostrophes, quotes
+ $text =~ s/�/\'/g;
+ $text =~ s/�/\"/g;
+ $text =~ s/�/\"/g;
+
+ return $text;
+
+}
+
+sub create_protocols {
+
+ my ( $self ) = @_;
+
+ my $bag_of = $self->get_bags();
+ foreach my $element (@{ $self->get_prot_section() } ) {
+
+ # Quick check before we try and generate an object without an
+ # internal identifier.
+ unless ( defined( $element->{$EDF_PROTOCOLACCESSION} ) ) {
+ croak("Error: Protocol found without an accession");
+ }
+
+ $element->{$EDF_PROTOCOLTEXT}
+ = $self->htmlify_protocoltext(
+ $element->{$EDF_PROTOCOLTEXT}
+ );
+
+ # Generate the MAGE protocol objects
+ my $protocol = $bag_of->{protocol}->(
+ $element->{$EDF_PROTOCOLACCESSION},
+ { protocol_type => $element->{$EDF_PROTOCOLTYPE},
+ name => $element->{$EDF_PROTOCOLNAME},
+ text => $element->{$EDF_PROTOCOLTEXT},
+ parameters => $element->{$EDF_PROTOCOLPARAMS},
+ parameter_bag => $bag_of->{parameter},
+ }
+ );
+ }
+
+ # Reassign protocol accessions, if cache file location is set in
+ # Config.yml. This works, because the protocols are still
+ # referenced by their original accession in the $bag_of
+ # container. Using the -K option to the script deactivates this so
+ # that other features can be tested using dummy submissions.
+ if ( $CONFIG->get_AUTOSUBS_DSN()
+ && ! $self->get_keep_protocol_accns() ) {
+
+ print STDOUT ("Reassigning protocol accessions as necessary...\n");
+
+ require ArrayExpress::AutoSubmission::DB::Protocol;
+
+ # We require the experiment to have an accession number for
+ # protocol accession reassignment.
+ my $expt_accession;
+ unless ( $expt_accession = $self->get_expt_section()->{$EDF_EXPTACCESSION} ) {
+ croak("Error: Experiment accession number not defined.\n");
+ }
+
+ # Experiment accessions need to be in a defined format. We
+ # constrain that here.
+ my $t2m_expt_prefix = $CONFIG->get_T2M_EXPERIMENT_PREFIX();
+ unless ( $expt_accession
+ =~ m/\A $t2m_expt_prefix \d+ \z/xms ) {
+ croak( qq{Error: Experiment accession not in the form }
+ . qq{"${t2m_expt_prefix}N" (where N is an integer).\n} );
+ }
+
+ # Set the accession prefix.
+ ArrayExpress::AutoSubmission::DB::Protocol->accession_prefix(
+ $CONFIG->get_T2M_PROTOCOL_PREFIX(),
+ );
+
+ # Schwarzian transform to sort by user-supplied accession
+ my @sorted = map { $_->[0] }
+ sort { $a->[1] cmp $b->[1] }
+ map { [ $_, $_->getIdentifier ] } @{ $bag_of->{protocol}->() };
+ foreach my $protocol (@sorted) {
+
+ my $user_accession = $protocol->getIdentifier;
+
+ my $prot_accession
+ = ArrayExpress::AutoSubmission::DB::Protocol->reassign_protocol(
+ $user_accession,
+ $expt_accession,
+ $protocol->getName(),
+ );
+
+ $protocol->setIdentifier($prot_accession);
+ if ( my $paramlist = $protocol->getParameterTypes ) {
+ foreach my $parameter (@$paramlist) {
+ my $param_id = $parameter->getIdentifier;
+
+ # Note that this substitution will break if the
+ # parameter id format is ever changed FIXME
+ my $namespace = $self->get_namespace();
+ $param_id
+ =~ s/$namespace:$user_accession/"$namespace:$prot_accession"/e;
+ $parameter->setIdentifier($param_id);
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+sub _file_sanity_check : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # NB FIXME so that predeclared files are not croaked upon when lacking
+ # required info.
+
+ my $datafile_rowlist = $self->get_hyb_section();
+
+ # Give up if we've go no hybs. Maybe FIXME later so we can have
+ # protocol-only subs?
+ croak("Error: No data in Hybridization section.\n")
+ unless ( scalar @$datafile_rowlist );
+
+ # Check for column dependencies. Data files all need an array
+ # accession number. This is used in both the DesignElementDimension
+ # and in creating the correct Array package, so we need it for Affy
+ # submissions as well.
+ foreach my $datarow (@$datafile_rowlist) {
+ foreach my $filetype (
+ @{ $CONFIG->get_T2M_FILE_TYPES() },
+ $CONFIG->get_FGEM_FILE_TYPE()
+ ) {
+ my $heading = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+ if ( $datarow->{$heading} && !$datarow->{$EDF_ARRAYACCESSION} ) {
+ croak(
+ "Error: Data file $datarow->{$heading} provided without Array accession number\n"
+ );
+ }
+ }
+ }
+
+ # Check that all the files are present
+ my $errorcount;
+ foreach my $datarow (@$datafile_rowlist) {
+
+ DATAROW_COLUMN:
+ foreach my $column ( keys %$datarow ) {
+
+ # General form File[type] e.g. File[raw],
+ # File[normalized]; skip blanks
+
+ my $regexp_str = "$EDF_FILE_PREFIX(.*)$EDF_FILE_SUFFIX";
+ $regexp_str =~ s{$RE_SQUARE_BRACKETS}{\\$1}gxms;
+
+ if ( ( my ($filetype) = ( $column =~ m/\A$regexp_str\z/ ) )
+ && $datarow->{$column} ) {
+
+ next DATAROW_COLUMN
+ if ( $filetype eq 'image' ); # Skip image files.
+
+ # CDF files can be found in the directory defined in
+ # Config.pm
+ if ( $filetype eq 'cdf' ) {
+
+ # Correct the CDF name on the fly.
+ my $path;
+ my $cdffile = $datarow->{$column};
+ ($datarow->{$column}, $path) = find_cdf(
+ $datarow->{$column},
+ $self->get_source_directory(),
+ );
+
+ if ( !-e $path ) {
+ print STDERR ("Error: CDF file not found: $cdffile\n");
+ $self->logprint(
+ 'tab2mage',
+ "Error: CDF file not found: $cdffile\n"
+ );
+ $errorcount++;
+ }
+ }
+
+ # All other files must be in the current working
+ # directory or the source dir set using the -d option.
+ else {
+
+ my $dir = $self->get_source_directory();
+ my $path = $dir
+ ? File::Spec->catfile( $dir, $datarow->{$column} )
+ : $datarow->{$column};
+
+ if ( !-e $path ) {
+ print STDERR (
+ "Error: Missing file of type $filetype: $datarow->{$column}\n"
+ );
+ $self->logprint(
+ 'tab2mage',
+ "Error: Missing file of type $filetype: $datarow->{$column}\n"
+ );
+ $errorcount++;
+ }
+ }
+ }
+ }
+ }
+
+ if ( $errorcount ) {
+ croak(
+ "Files missing. See log file for details. This script has terminated.\n");
+ }
+
+ return;
+}
+
+sub read_edf {
+
+ my ( $self, $error_fh ) = @_;
+
+ $error_fh ||= $self->log_fh('tab2mage');
+
+ my $exptinfo = {};
+ my $datafile_rowlist = [];
+ my $protocollist = [];
+
+ my $spreadsheet = $self->get_spreadsheet_filename();
+ if ( -B $spreadsheet ) {
+ croak(
+ sprintf("Error: Spreadsheet file %s is a binary file format."
+ . " This script supports plain text only.",
+ $spreadsheet));
+ }
+
+ my $eol_char = check_linebreaks($spreadsheet)
+ or croak(
+ sprintf("Error: Cannot correctly parse linebreaks in file %s",
+ $spreadsheet));
+ if ( ( $eol_char eq "\015" )
+ && ( $Text::CSV_XS::VERSION < 0.27 ) ) {
+
+ # Mac linebreaks not supported by older versions of Text::CSV_XS.
+ croak("Error: Mac linebreaks not supported by this version"
+ . " of Text::CSV_XS. Please upgrade to version 0.27 or higher.\n");
+ }
+ local $/ = $eol_char;
+
+ my $fh = IO::File->new( $spreadsheet, '<' )
+ or croak(
+ sprintf("Error opening spreadsheet file %s: %s",
+ $spreadsheet, $!));
+
+ # Namespace:Authority string:
+ my ( $namespace, $domain );
+
+ EDF_LINE:
+ while ( my $line = <$fh> ) {
+
+ next EDF_LINE
+ if ( $line =~ $RE_COMMENTED_STRING ); # Allow hash comments
+
+ # Some default values, just in case.
+ # We assign these between each section, as they are used in
+ # Protocol and Hybridization.
+
+ # This is the default as assigned by ArrayExpress:
+ $domain ||= 'ebi.ac.uk';
+ $namespace = "$domain:" . $CONFIG->get_TAB2MAGE_PROGNAME();
+
+ if ( $line =~ m/Experiment [ ]? section/ixms ) {
+ ( $exptinfo, $fh )
+ = $self->_parse_columnwise( $fh, $eol_char );
+
+ $self->add_error(
+ validate_experiment_section(
+ [ sort keys %$exptinfo ],
+ $error_fh,
+ )
+ );
+
+ # If the constructor was passed an accession number and/or
+ # domain, use it/them in preference to the one(s) in the
+ # spreadsheet.
+ if ( my $accession = $self->get_external_accession() ) {
+ $exptinfo->{$EDF_EXPTACCESSION} = $accession;
+ }
+ if ( my $domain = $self->get_external_domain() ) {
+ $exptinfo->{$EDF_EXPTDOMAIN} = $domain;
+ }
+
+ $domain = $exptinfo->{$EDF_EXPTDOMAIN} || $domain;
+
+ next EDF_LINE;
+ }
+
+ elsif ( $line =~ m/Protocol [ ]? section/ixms ) {
+ ( $protocollist, $fh )
+ = $self->_parse_rowwise( $fh, $eol_char );
+
+ # Check first line of section only; it is representative.
+ if ( scalar @{ $protocollist || [] } ) {
+ $self->add_error(
+ validate_protocol_section(
+ [ sort keys %{ $protocollist->[0] } ],
+ $error_fh,
+ )
+ );
+ }
+
+ # Fix parameters so they're a proper hash {name => unit}
+
+ PROTOCOL_LINE:
+ foreach my $row (@$protocollist) {
+ next PROTOCOL_LINE unless $row->{$EDF_PROTOCOLPARAMS};
+
+ # HoH: name => {identifier => q{}, unit => q{}}
+ my %param2unit;
+
+ # Split on semicolon; discard surrounding whitespace
+ my @parameters = split /\s*;\s*/, $row->{$EDF_PROTOCOLPARAMS};
+
+ foreach my $param (@parameters) {
+
+ # Parameter name is everything before the first parenthesis.
+ my $name = ( split /\s*\(/, $param )[0];
+
+ # Unit is within parens.
+ my ($unit) = ( $param =~ $RE_WITHIN_PARENTHESES );
+ $unit ||= q{}; # In case the pattern match failed.
+
+ my $param_id
+ = "$namespace:$row->{$EDF_PROTOCOLACCESSION}.$name.Parameter";
+ $param2unit{$name} = {
+ identifier => $param_id,
+ unit => $unit,
+ name => $name,
+ };
+ }
+
+ # Replace the original string with the new hash.
+ $row->{$EDF_PROTOCOLPARAMS} = \%param2unit if @parameters;
+ }
+ next EDF_LINE;
+ }
+
+ elsif ( $line =~ m/Hybridi[sz]ation [ ]? section/ixms ) {
+ ( $datafile_rowlist, $fh )
+ = $self->_parse_rowwise( $fh, $eol_char );
+
+ # Check first line of section only; it is representative.
+ if ( scalar @{ $datafile_rowlist || [] } ) {
+ $self->add_error(
+ validate_hybridization_section(
+ [ sort keys %{ $datafile_rowlist->[0] } ],
+ $error_fh,
+ )
+ );
+ }
+
+ # Parameter support - reorganize our parameters method-wise.
+ my %proto2param = (
+ $EDF_GROW_PROTOCOL => $EDF_SAMPLE_PARAMS,
+ $EDF_TREAT_PROTOCOL => $EDF_TREAT_PARAMS,
+ $EDF_EXTRACT_PROTOCOL => $EDF_EXTRACT_PARAMS,
+ $EDF_LABEL_PROTOCOL => $EDF_LABEL_PARAMS,
+ $EDF_IP_PROTOCOL => $EDF_IP_PARAMS,
+ $EDF_HYB_PROTOCOL => $EDF_HYB_PARAMS,
+ $EDF_SCAN_PROTOCOL => $EDF_SCAN_PARAMS,
+ $EDF_FEXT_PROTOCOL => $EDF_FEXT_PARAMS,
+ $EDF_NORM_PROTOCOL => $EDF_NORM_PARAMS,
+ );
+
+ foreach my $row (@$datafile_rowlist) {
+
+ # Correct common casing error for Cy-dyes
+ $row->{$EDF_DYE}
+ && do { $row->{$EDF_DYE} =~ s{\A cy(\d+) \z}{Cy$1}ixms; };
+
+ # Map parameters to protocol applications. Scan through the
+ # protocoltype:
+
+ PARAMETER_MAPPING:
+ while ( my ( $protocoltype, $methodparams )
+ = each %proto2param ) {
+
+ next PARAMETER_MAPPING unless $row->{$protocoltype};
+
+ # Scan through our list of defined protocols
+ foreach my $protocol (@$protocollist) {
+
+ if ( $protocol->{$EDF_PROTOCOLACCESSION} eq
+ $row->{$protocoltype}
+ && $protocol->{$EDF_PROTOCOLPARAMS} ) {
+
+ # For each defined parameter:
+ while ( my ( $param_name, $param_hash )
+ = each %{ $protocol->{$EDF_PROTOCOLPARAMS} } )
+ {
+ my $value_column = $EDF_PARAM_PREFIX
+ . $param_name
+ . $EDF_PARAM_SUFFIX;
+
+ # Incorporate the parameter into the
+ # method-specific parameter hash.
+ if ( defined( $row->{$value_column} )
+ && $row->{$value_column} ne q{} ) {
+ $row->{$methodparams}
+ { $param_hash->{identifier} }
+ = $row->{$value_column};
+ }
+ }
+ }
+ }
+ }
+ }
+ next EDF_LINE;
+ }
+
+ print STDERR (
+ "Warning: Line below is not associated with any section:\n\t$line\n"
+ )
+ if ( $line =~ m/\S/ );
+ $self->add_error($CONFIG->get_ERROR_INNOCENT());
+
+ }
+
+ # Check that we have some data parsed
+ unless ( scalar( grep { defined $_ } values %$exptinfo ) ) {
+ print STDERR "Warning: Experiment section not found.\n";
+ $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+ }
+ unless ( scalar @{ $protocollist || [] } ) {
+
+ # Use STDOUT here as this is a common use case.
+ print STDOUT "Warning: Protocol section not found.\n";
+ $self->add_error($CONFIG->get_ERROR_MIAME());
+ }
+ unless ( scalar @{ $datafile_rowlist || [] } ) {
+ print STDERR "Warning: Hybridization section not found.\n";
+ $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+ }
+
+ # In case the domain is not set in the experiment section, set it
+ # here.
+ $exptinfo->{$EDF_EXPTDOMAIN} ||= $domain;
+
+ $self->set_expt_section($exptinfo);
+ $self->set_hyb_section($datafile_rowlist);
+ $self->set_prot_section($protocollist);
+ $self->set_namespace($namespace);
+
+ return ( $self->get_error() );
+}
+
+sub _parse_columnwise : PRIVATE {
+
+ my ( $self, $fh, $eol_char ) = @_;
+
+ my $summary = {};
+
+ # Set up a CSV parser object
+ my $csv_format = Text::CSV_XS->new(
+ { sep_char => qq{\t},
+ quote_char => qq{"}, # default
+ escape_char => qq{"}, # default
+ binary => 1,
+ eol => ( $eol_char || "\n" ),
+ allow_loose_quotes => 1,
+ }
+ );
+
+ my $larry;
+ FILE_LINE:
+ while ( $larry = $csv_format->getline($fh) ) {
+
+ # Section ends on empty line.
+ my $line = join( q{}, @$larry );
+ last FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+ # Allow hash comments.
+ next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+ # Strip all whitespace from row headings.
+ $larry->[0] =~ s/\s*//g;
+
+ # lc to allow case-insensitivity.
+ $larry->[0] = lc( $larry->[0] );
+
+ # Raise error on non-unique row tag; pass the error back, but
+ # don't let our sig handler see it.
+ {
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ if ( $summary->{ $larry->[0] } ) {
+ croak("ERROR: Row names must be unique: $larry->[0]\n");
+ }
+ $SIG{__DIE__} = $sighandler;
+ }
+
+ # Strip surrounding whitespace from value.
+ $larry->[1] && $larry->[1] =~ s{$RE_SURROUNDED_BY_WHITESPACE}{$1}gxms;
+
+ # Don't record empty values
+ $summary->{ $larry->[0] } = $larry->[1]
+ if ( defined( $larry->[1] ) && $larry->[1] !~ $RE_EMPTY_STRING );
+
+ }
+
+ # Check we've parsed to the end of the file.
+ my ( $error, $mess ) = $csv_format->error_diag();
+ unless ( $larry || $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ croak(
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_format->error_input(),
+ ),
+ );
+ }
+
+ return ( $summary, $fh );
+
+}
+
+sub _parse_rowwise : PRIVATE {
+
+ my ( $self, $fh, $eol_char ) = @_;
+
+ my $tablelist = [];
+
+ # Set up a CSV parser object
+ my $csv_format = Text::CSV_XS->new(
+ { sep_char => qq{\t},
+ quote_char => qq{"}, # default
+ escape_char => qq{"}, # default
+ binary => 1,
+ eol => ( $eol_char || "\n" ),
+ allow_loose_quotes => 1,
+ }
+ );
+
+ # Scan down to the first non-comment line
+ my ( $firstline, $firstlarry );
+ until ( $firstline && $firstline !~ $RE_COMMENTED_STRING ) {
+ $firstlarry = $csv_format->getline($fh);
+ $firstline = join( '', @{ $firstlarry || [] } );
+ }
+
+ # Check we've parsed to the end of the file.
+ my ( $error, $mess ) = $csv_format->error_diag();
+ unless ( $firstlarry || $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ croak(
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_format->error_input(),
+ ),
+ );
+ }
+
+ # Handle the special case where there's a section heading but no
+ # section.
+ return ( $tablelist, $fh ) if ( $firstline =~ $RE_EMPTY_STRING );
+
+ # Use only the headings which have a text label, check for
+ # uniqueness.
+ my ( @headings, %already_seen );
+ foreach my $heading (@$firstlarry) {
+ push( @headings, $heading );
+ if ( $heading && $already_seen{$heading}++ ) {
+ croak("ERROR: Column headings must be unique: $heading\n");
+ }
+ }
+
+ # Strip whitespace from column headings, lc to allow case-insensitivity
+ foreach my $heading (@headings) {
+
+ # Strip whitespace surrounding term in brackets. NB. don't strip
+ # parameter name internal whitespace - needed for mapping.
+ if ( my ( $superclass, $category )
+ = ( $heading =~ m{(.* \[) \s* (.*?) \s* \]}xms ) ) {
+ $superclass =~ s/\s*//g;
+ $superclass = lc($superclass);
+
+ # Normalize the file type, array and protocol type terms, just in case.
+ if ( $superclass eq $EDF_FILE_PREFIX
+ || $superclass =~ m/\A Protocol/ixms
+ || $superclass =~ m/\A Array/ixms ) {
+ $category =~ s/\s*//g;
+ $category = lc($category);
+ }
+ $heading = "$superclass$category]";
+ }
+
+ else {
+ $heading = lc($heading);
+ $heading =~ s/\s*//g;
+ }
+ }
+
+ # Now process each row of the section
+ my $larry;
+ FILE_LINE:
+ while ( $larry = $csv_format->getline($fh) ) {
+
+ # Section ends on empty line.
+ my $line = join( q{}, @$larry );
+ last FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+ # Allow hash comments.
+ next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+ my $row = {};
+ my $last_heading = scalar @headings;
+
+ COLUMN_VALUE:
+ for ( my $i = 0; $i < $last_heading; $i++ ) {
+
+ # Skip empty values.
+ next COLUMN_VALUE unless defined( $larry->[$i] );
+
+ # Strip whitespace.
+ $larry->[$i] =~ s{$RE_SURROUNDED_BY_WHITESPACE}{$1}gxms;
+ $row->{ $headings[$i] } = $larry->[$i];
+ }
+ push @$tablelist, $row;
+ }
+
+ # Check we've parsed to the end of the file.
+ ( $error, $mess ) = $csv_format->error_diag();
+ unless ( $larry || $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ croak(
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_format->error_input(),
+ ),
+ );
+ }
+
+ return ( $tablelist, $fh );
+}
+
+sub check_pool_protocol_usage : PRIVATE {
+
+ my ( $self, $protocol, $material ) = @_;
+
+ my $prot_id = $protocol->getIdentifier();
+ my $is_used;
+
+ TREATMENT:
+ foreach my $treatment ( @{ $material->getTreatments() || [] } ) {
+
+ # Pooling treatments are always order 0 for tab2mage.
+ next TREATMENT unless ( $treatment->getOrder() eq '0' );
+
+ foreach my $protapp (
+ @{ $treatment->getProtocolApplications() || [] } ) {
+
+ my $oldprot = $protapp->getProtocol();
+ $is_used++ if ( $prot_id eq $oldprot->getIdentifier() );
+ }
+ }
+
+ if ( $is_used ) {
+ $pool_protocol_usage{ident $self}{$prot_id}++;
+ }
+
+ return;
+}
+
+sub record_pool_protocol_assn : PRIVATE {
+
+ my ( $self, $protocol, $biosample ) = @_;
+
+ my $prot_id = $protocol->getIdentifier();
+ my $sample_id = $biosample->getIdentifier();
+
+ # This is overly complex because the protocol identifier could
+ # change, e.g. after protocol accession reassignment. So $prot_id
+ # may not correctly point to a protocol object in the protocol bag
+ # object.
+ $pool_protocol_assns{ident $self}{ $prot_id }{ $sample_id } = {
+ sample => $biosample,
+ protocol => $protocol,
+ };
+
+ return;
+}
+
+sub link_unused_pool_protocols : PRIVATE {
+
+ my ( $self, $identifier_template ) = @_;
+
+ PROTOCOL:
+ foreach my $poolinghash ( values %{ $pool_protocol_assns{ident $self} } ) {
+
+ foreach my $assn ( values %{ $poolinghash } ) {
+
+ my $protocol = $assn->{protocol};
+ my $extract = $assn->{sample};
+
+ my $prot_id = $protocol->getIdentifier();
+
+ next PROTOCOL if $pool_protocol_usage{ident $self}{$prot_id};
+
+ unless ( defined($protocol) && defined($extract) ) {
+ confess("Error: either protocol or extract undefined in pooling recheck.");
+ }
+
+ $self->link_pool_protocol( $protocol, $extract, $identifier_template );
+ }
+ }
+
+ return;
+}
+
+sub link_pool_protocol : PRIVATE {
+
+ my ( $self, $protocol, $extract, $identifier_template ) = @_;
+
+ my ( $treatment, $oldbmms );
+ TREATMENT:
+ foreach my $old ( @{ $extract->getTreatments() || [] } ) {
+
+ # Take the first set of BMMs as being representative.
+ $oldbmms ||= $old->getSourceBioMaterialMeasurements();
+
+ # Pooling is always order 0 for tab2mage.
+ if ( $old->getOrder() eq '0' ) {
+ $treatment = $old;
+ last TREATMENT;
+ }
+ }
+
+ my $action = Bio::MAGE::Description::OntologyEntry->new(
+ category => 'Action',
+ value => 'pool',
+ );
+
+ unless ( $treatment ) {
+
+ my $identifier = "$identifier_template.pool." . unique_identifier() . '.Treatment';
+ $treatment = Bio::MAGE::BioMaterial::Treatment->new(
+ identifier => $identifier,
+ order => 0,
+ action => $action,
+ sourceBioMaterialMeasurements => $oldbmms,
+ );
+ $extract->addTreatments( $treatment );
+ }
+
+ my $found;
+ foreach my $old ( @{ $treatment->getProtocolApplications || [] } ) {
+ my $oldprot = $old->getProtocol();
+ $found++ if ( $protocol->getIdentifier()
+ eq $oldprot->getIdentifier() );
+ }
+
+ unless ( $found ) {
+ my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $protocol,
+ activityDate => 'n/a',
+ );
+ $treatment->addProtocolApplications($pa);
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Curator/Validate.pm b/lib/ArrayExpress/Curator/Validate.pm
new file mode 100644
index 0000000..d68f9bf
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Validate.pm
@@ -0,0 +1,1084 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the tab2mage and experiment
+# checker scripts.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Validate.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Curator::Validate;
+
+use strict;
+use warnings;
+use Class::Std;
+use English qw( -no_match_vars );
+use File::Basename;
+
+use ArrayExpress::Curator::Entrez_list qw(
+ parse_entrez_names
+);
+
+require ArrayExpress::Curator::Tab2MAGE;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::MAGE qw(
+ unique_identifier
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $EDF_EXPTACCESSION
+ $EDF_EXPTDESCRIPTION
+ $EDF_EXPTRELEASEDATE
+ $EDF_EXPTSUBMISSIONDATE
+ $EDF_EXPTGEORELEASEDATE
+ $EDF_EXPTSUBMITTER
+ $EDF_EXPTSUBMITTER_EMAIL
+ $EDF_PUBJOURNAL
+ $EDF_PUBMEDID
+ $EDF_ARRAYACCESSION
+ $EDF_FILE_PREFIX
+ $EDF_FILE_SUFFIX
+ $EDF_HYBRIDIZATIONNAME
+ $EDF_FV_PREFIX
+ $EDF_FV_SUFFIX
+ $EDF_PROTOCOLACCESSION
+ $EDF_PROTOCOLNAME
+ $EDF_PROTOCOLTYPE
+ $EDF_PROTOCOLTEXT
+ $EDF_PROTOCOLPARAMS
+ $EDF_GROW_PROTOCOL
+ $EDF_TREAT_PROTOCOL
+ $EDF_EXTRACT_PROTOCOL
+ $EDF_POOL_PROTOCOL
+ $EDF_LABEL_PROTOCOL
+ $EDF_IP_PROTOCOL
+ $EDF_HYB_PROTOCOL
+ $EDF_SCAN_PROTOCOL
+ $EDF_FEXT_PROTOCOL
+ $EDF_NORM_PROTOCOL
+ $EDF_TRXN_PROTOCOL
+ $EDF_PARAM_PREFIX
+ $EDF_PARAM_SUFFIX
+);
+
+use ArrayExpress::Curator::Report qw(
+ datafile_consistency_table
+ format_description
+);
+
+use ArrayExpress::Curator::Common qw(
+ get_filepath_from_uri
+);
+
+require ArrayExpress::Datafile;
+
+use base 'ArrayExpress::Curator::ExperimentChecker';
+
+my %spreadsheet_filename : ATTR( :get<spreadsheet_filename>, :init_arg<spreadsheet_filename>, :default<undef> );
+my %mage_generator : ATTR( :get<mage_generator>, :set<mage_generator>, :default<undef> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $spreadsheet_filename{$id} ) {
+ croak("Error: spreadsheet_filename must be set for Tab2MAGE checking.");
+ }
+
+ my $logfile_string = File::Spec->rel2abs($self->get_spreadsheet_filename());
+ $logfile_string =~ s/\.\w{3,4}$//; # strip off the extension
+ my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+ $self->localize_logfiles({
+ directory => $dir,
+ volume => $vol,
+ name => $name,
+ });
+
+ return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ # Quick check to see what we've been passed.
+ unless ( $self->get_spreadsheet_filename() ) {
+ croak(<<'END_ERROR');
+Error: Tab2MAGE checker must be passed a spreadsheet_filename option.
+END_ERROR
+ }
+
+ my $filelist;
+ my $hyb_ids; # Used to map FGEM column headings to hybs.
+ my $norm_ids; # Empty for MX; cannot map MX FGEM using Norm IDs.
+ my $tab2mage; # FIXME factor this out of here at some point.
+
+ print STDOUT ("Running in Tab2MAGE mode...\n");
+
+ $self->logprint( 'error', "* Tab2MAGE mode *\n\n" );
+ $self->logprint( 'report', "* Tab2MAGE mode *\n\n" );
+
+ # Check the metadata from the spreadsheet, retrieve the
+ # hybridization section.
+ ( $filelist, $hyb_ids, $norm_ids ) = $self->validate_edf();
+
+ $self->logprint_line( 'error', 'ADF parsing START' );
+
+ # We need $file to have array info.
+ if ( $self->get_adf_filename || $self->get_array_accession ) {
+
+ $self->cache_user_supplied_arrays( $filelist || [] );
+
+ }
+ else {
+
+ # Just populate the keys for now.
+ my %array_designs;
+ foreach my $file ( @{ $filelist || [] } ) {
+ unless ( $file->get_is_exp() ) {
+ $array_designs{ $file->get_array_design_id() }++;
+ }
+ }
+
+ # populate the array designs hashref
+ unless ( $self->get_is_standalone() ) {
+
+ my @accno_table;
+ foreach my $accno ( keys %array_designs ) {
+
+ # ADF retrieval crashes when AE is down; we trap that here.
+ $self->get_ae_arraydesign({
+ accession => $accno,
+ });
+ }
+ }
+ }
+
+ $self->populate_file_arraydesigns( $filelist || [] );
+
+ $self->logprint_line( 'error', 'ADF parsing END' );
+
+ return ( $filelist, $hyb_ids, $norm_ids );
+}
+
+sub check_bmchars_against_fvs : PRIVATE {
+
+ my ( $self, $bags ) = @_;
+
+ # Generate a list of FV OE category => value pairs
+ my @fv_catvals;
+ foreach my $ef ( @{ $bags->{'experimentalfactor'}->() } ) {
+ foreach my $fv ( @{ $ef->getFactorValues() || [] } ) {
+ my ( $category, $value );
+ if ( my $fvvalue = $fv->getValue() ) {
+
+ # Regular FV Value OE.
+ $category = $fvvalue->getCategory();
+ $value = $fvvalue->getValue();
+ }
+ elsif ( my $measurement = $fv->getMeasurement() ) {
+
+ # FV Measurements.
+ my $efc = $ef->getCategory();
+ $category = $efc->getValue();
+
+ # CamelCase the term.
+ $category =~ s/^(.)/uc($1)/e;
+ $category =~ s/_(.)/uc($1)/ge;
+ $category =~ s/_//;
+
+ $value = $measurement->getValue();
+ }
+ push @fv_catvals, { $category => $value };
+ }
+ }
+
+ # Generate a list of BMC OE category => value pairs.
+ my @bmc_catvals;
+ foreach my $source ( @{ $bags->{'biosource'}->() || [] } ) {
+ foreach my $char ( @{ $source->getCharacteristics() || [] } ) {
+
+ # We assume regular BMC OEs here (no nesting).
+ push @bmc_catvals, { $char->getCategory => $char->getValue };
+ }
+ }
+
+ $self->compare_fvs_to_bmcs(\@fv_catvals, \@bmc_catvals);
+
+ return;
+}
+
+sub _check_hybridizations : PRIVATE {
+
+ my ( $self, $bag_of ) = @_;
+
+ my %pba_mapcount;
+ foreach my $mba ( @{ $bag_of->{mba}->() } ) {
+ my $pba = $mba->getFeatureExtraction()->getPhysicalBioAssaySource;
+ $pba_mapcount{ $pba->getName() }++;
+ }
+
+ foreach my $pba ( @{ $bag_of->{pba}->() } ) {
+
+ # FactorValues
+ my $fv_list = $pba->getBioAssayFactorValues();
+
+ # Must have at least one FV
+ unless ( $#$fv_list >= 0 ) {
+ $self->logprint( 'error',
+ q{Warning: no FactorValue found for hybridization "}
+ . $pba->getName()
+ . qq{".\n} );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+
+ # Hyb-level PBA checks.
+ if ( my $bac = $pba->getBioAssayCreation() ) {
+
+ # Arrays (required by the model)
+ unless ( $bac->getArray() ) {
+ $self->logprint(
+ 'error',
+ q{ERROR: Hybridization "}
+ . $pba->getName()
+ . qq{" has no associated array (needs Array[accession]).\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Labeled Extracts
+ my @labels;
+ my $source_bmms = $bac->getSourceBioMaterialMeasurements();
+ foreach my $bmm (@{ $source_bmms || [] } ) {
+ push( @labels, $bmm->getBioMaterial() );
+ }
+ unless (@labels) {
+ $self->logprint(
+ 'error',
+ q{Warning: no LabeledExtracts linked to hybridization "}
+ . $pba->getName()
+ . qq{".\n} );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ # The next couple of tests check for mappings to MBAs;
+ # obviously this is made more complex when using the
+ # two-colour codings. Here we query for the merged PBA.
+ my $mappable_pba;
+ if ( scalar @{ $pba->getChannels() || [] } > 1 ) {
+
+ # This assumes the convention established in the
+ # AE::Curator::MAGE module is adhered to.
+ $mappable_pba = $bag_of->{extended_pba}->(
+ $pba->getName() . ".merged"
+ );
+ unless ( $mappable_pba ) {
+ die(sprintf(
+ "Internal error: Merged PBA undefined for two-color coding (%s).",
+ $pba->getName())
+ );
+ }
+ }
+ else {
+
+ # Single-channel coding.
+ $mappable_pba = $pba;
+ }
+
+ # MeasuredBioAssays; we maintain a strict 1:n PBA:MBA mapping;
+ # having n PBA map to one MBA results in dangling PBAs, which we
+ # detect here.
+ my $mappable_pba_name = $mappable_pba->getName();
+ if ( $pba_mapcount{ $mappable_pba_name }
+ && ( $pba_mapcount{ $mappable_pba_name } > 1 ) ) {
+ $self->logprint( 'error',
+ q{Warning: Multiple scans map to hybridization "}
+ . $mappable_pba_name
+ . qq{".\n} );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ elsif ( !$pba_mapcount{ $mappable_pba_name }
+ || ( $pba_mapcount{ $mappable_pba_name } < 1 ) ) {
+ $self->logprint( 'error',
+ q{WARNING: Hybridization "}
+ . $mappable_pba_name
+ . q{" is not referenced by any MeasuredBioAssay: }
+ . qq{possible duplicate filename in spreadsheet?\n} );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ return;
+}
+
+sub _check_datatrans_protocol : PRIVATE {
+
+ # Returns true if any $protocoltype protocol is found attached to
+ # a normalized data file (this is used for MIAME compliance
+ # testing).
+ my ( $self,
+ $tab2mage,
+ $filetype,
+ $protocoltype,
+ $err_string,
+ ) = @_;
+
+ my $norm_data = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+ my ( %filenames, %has_protocol, $protocol_found );
+ foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+ if ( $datarow->{$norm_data} ) {
+ $filenames{ $datarow->{$norm_data} }++;
+ if ( $datarow->{$protocoltype} ) {
+ $has_protocol{ $datarow->{$norm_data} }++;
+ $protocol_found = 1;
+ }
+ }
+ }
+ foreach my $norm_file ( keys %filenames ) {
+
+ # Affy CHP files without protocols are okay (we derive the
+ # protocol from their metadata).
+ unless ( $has_protocol{$norm_file} || ( $norm_file =~ m/\.CHP$/i ) ) {
+ $self->logprint( 'error',
+ qq{Warning: $err_string "$norm_file"\n} );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+
+ return $protocol_found;
+}
+
+sub _check_protocols : PRIVATE {
+
+ my ( $self, $tab2mage ) = @_;
+
+ my @protocol_headings = (
+ $EDF_GROW_PROTOCOL, $EDF_TREAT_PROTOCOL, $EDF_EXTRACT_PROTOCOL,
+ $EDF_POOL_PROTOCOL, $EDF_LABEL_PROTOCOL, $EDF_IP_PROTOCOL,
+ $EDF_HYB_PROTOCOL, $EDF_SCAN_PROTOCOL, $EDF_FEXT_PROTOCOL,
+ $EDF_NORM_PROTOCOL, $EDF_TRXN_PROTOCOL,
+ );
+
+ # Check that all declared protocols are referenced
+ my ( %unreferenced, %undeclared );
+
+ PROTOCOL:
+ foreach my $protocol ( @{ $tab2mage->get_prot_section() || [] } ) {
+
+ # Confirm that the protocol does actually have an accession (required).
+ unless ( $protocol->{$EDF_PROTOCOLACCESSION} ) {
+ $self->logprint(
+ 'error',
+ "ERROR: Protocol found without an accession: ",
+ $protocol->{$EDF_PROTOCOLNAME}, "\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ next PROTOCOL;
+ }
+
+ # Check on protocol text length. Arbitrary limit; found to work
+ # quite well in practice, though.
+ unless ( length( $protocol->{$EDF_PROTOCOLTEXT} ) > 50 ) {
+ $self->logprint( 'error',
+ q{Warning: Description for }
+ . ( $protocol->{$EDF_PROTOCOLTYPE} || q{} )
+ . q{ protocol "}
+ . ( $protocol->{$EDF_PROTOCOLNAME} || q{} )
+ . qq{" is too short.\n} );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+
+ my $protocol_referenced;
+ foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+ foreach my $heading (@protocol_headings) {
+ $protocol_referenced++
+ if ( $datarow->{$heading}
+ && $datarow->{$heading} eq
+ $protocol->{$EDF_PROTOCOLACCESSION} );
+ }
+ }
+ $unreferenced{ $protocol->{$EDF_PROTOCOLACCESSION} }++
+ unless ($protocol_referenced);
+ }
+
+ # Check that all referenced protocols are declared
+ foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+
+ PROTOCOL_COLUMN:
+ foreach my $heading (@protocol_headings) {
+
+ # allow blanks in the spreadsheet
+ next PROTOCOL_COLUMN unless $datarow->{$heading};
+
+ my $protocol_declared;
+ foreach my $protocol ( @{ $tab2mage->get_prot_section() || [] } ) {
+ $protocol_declared++
+ if ( $datarow->{$heading} eq
+ $protocol->{$EDF_PROTOCOLACCESSION} );
+ }
+ $undeclared{ $datarow->{$heading} }++ unless ($protocol_declared);
+ }
+ }
+
+ while ( my ( $protocol, $count ) = each %unreferenced ) {
+ $self->logprint( 'error',
+ "Warning: Protocol $protocol is declared in the Protocol section but not referenced in the Hybridization section.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ while ( my ( $protocol, $count ) = each %undeclared ) {
+ $self->logprint( 'error',
+ "Warning: Protocol $protocol is referenced in the Hybridization section ($count times) but not declared in the Protocol section.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+
+ # Normalization and transformation protocols have to be checked in
+ # the $datafile_rowlist construct as they aren't created in the MAGE
+ # objects until the data files have been fully processed.
+
+ my $protocol_found = $self->_check_datatrans_protocol(
+ $tab2mage,
+ 'normalized',
+ $EDF_NORM_PROTOCOL,
+ 'No normalization protocol associated with normalized file',
+ );
+ $protocol_found ||= $self->_check_datatrans_protocol(
+ $tab2mage,
+ $CONFIG->get_FGEM_FILE_TYPE(),
+ $EDF_TRXN_PROTOCOL,
+ 'No transformation protocol associated with final data matrix file',
+ );
+
+ # Note the results for MIAME compliance tracking.
+ if ($protocol_found) {
+ $self->add_miame($CONFIG->get_MIAME_NORMPROTOCOL);
+ }
+ else {
+ $self->logprint(
+ 'miame',
+ "Problem: Data transformation protocol not found.\n"
+ );
+ }
+
+ return;
+}
+
+sub _check_parsed_protocols : PRIVATE {
+
+ my ( $self, $bag_of ) = @_;
+
+ # Check through for biomaterial treatments lacking protocols
+ foreach my $bm qw(biosample extract immunoprecipitate labeledextract) {
+ $self->check_mage_treatments_protapps(
+ $bag_of->{$bm}->(),
+ $bm,
+ );
+ }
+
+ # Hybs lacking protocols.
+ $self->check_mage_hyb_protapps( $bag_of->{pba}->() );
+
+ # Check scan protocol (now defaults to MBA FeatureExtraction).
+ foreach my $mba ( @{ $bag_of->{mba}->() } ) {
+ my $fext = $mba->getFeatureExtraction();
+ my $found;
+ foreach my $pa ( @{ $fext->getProtocolApplications() || [] } ) {
+ $found++ if $pa->getProtocol();
+ }
+ unless ( $found ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Hybridization "%s" lacks a scanning protocol.\n},
+ $mba->getName(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+
+ # Normalization protocol can't be checked here, as dbad/mbad
+ # creation is coupled to file processing. See _check_protocols()
+
+ return;
+}
+
+sub _check_parameters : PRIVATE {
+
+ my ( $self, $tab2mage ) = @_;
+
+ # Check that all referenced parameters are declared, and vice versa
+ my %known_params;
+ foreach my $protocol ( @{ $tab2mage->get_prot_section() || [] } ) {
+ if ( $protocol->{$EDF_PROTOCOLPARAMS} ) {
+ foreach my $name ( keys %{ $protocol->{$EDF_PROTOCOLPARAMS} } ) {
+ $known_params{$name}++;
+ }
+ }
+ }
+
+ # Find the problem parameters
+ my ( %referenced, %undeclared );
+
+ my $regexp_str = "$EDF_PARAM_PREFIX(.*)$EDF_PARAM_SUFFIX";
+ $regexp_str =~ s/([\[\]])/\\$1/g;
+
+ foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+ foreach my $column ( keys %$datarow ) {
+ if ( my ($name) = ( $column =~ m/$regexp_str$/ ) ) {
+ $referenced{$name}++;
+ unless ( $known_params{$name} ) { $undeclared{$name}++; }
+ }
+ }
+ }
+
+ # Print out the errors
+ foreach my $name ( keys %undeclared ) { # referenced but not declared
+ $self->logprint( 'error',
+ qq{Warning: Parameter "$name" is not declared in the Protocol spreadsheet section.\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ foreach my $name ( keys %known_params ) { # declared but not referenced
+ unless ( $referenced{$name} ) {
+ $self->logprint( 'error',
+ qq{Warning: Parameter "$name" is not referenced in the Hybridization spreadsheet section.\n}
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ return;
+}
+
+sub _datafile_to_filelist : PRIVATE {
+
+ my ( $self, $tab2mage ) = @_;
+
+ # Sort out our known file types
+ my $known_types;
+
+ # Standard file types
+ foreach my $filetype ( @{ $CONFIG->get_T2M_FILE_TYPES() || [] },
+ $CONFIG->get_FGEM_FILE_TYPE() ) {
+ $known_types->{$filetype}++;
+ }
+
+ # Other file types
+ my @other_known = qw(image cdf exp);
+ foreach my $type (@other_known) { $known_types->{$type}++; }
+
+ # hashref for tracking processed files.
+ my $filelist = {};
+ my $regexp_str = "$EDF_FILE_PREFIX(.*)$EDF_FILE_SUFFIX";
+ $regexp_str =~ s/([\[\]])/\\$1/g;
+
+ foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+
+ # Check there are no unrecognized file types in the data row
+ HYB_COLUMN:
+ foreach my $column ( keys %$datarow ) {
+ my $matched_type;
+
+ next HYB_COLUMN
+ unless ( ($matched_type) = ( $column =~ m/$regexp_str$/ ) );
+
+ unless ( $known_types->{$matched_type} ) {
+ $self->logprint( 'error',
+ "Warning: Unrecognized file type '$matched_type' will be ignored.\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ my $readablename = $tab2mage->get_readablename($datarow);
+
+ # defaults to raw filename if available
+ my $hyb_name = $datarow->{$EDF_HYBRIDIZATIONNAME} || $readablename;
+
+ # populate $file_list
+ FILE_TYPE:
+ foreach my $filetype (
+ @{ $CONFIG->get_T2M_FILE_TYPES() || [] },
+ $CONFIG->get_FGEM_FILE_TYPE()
+ ) {
+
+ my $heading = $EDF_FILE_PREFIX . $filetype . $EDF_FILE_SUFFIX;
+
+ # Skip rows lacking referenced files.
+ next FILE_TYPE unless ( $datarow->{$heading} );
+
+ # Get a Datafile object.
+ my $file;
+ unless ( $file = $filelist->{ $datarow->{$heading} } ) {
+ my $path;
+ if ( $self->get_skip_data_checks() ) {
+ $path = $datarow->{$heading};
+ }
+ else {
+ $path = get_filepath_from_uri(
+ $datarow->{$heading},
+ $self->get_source_directory(),
+ );
+ }
+ $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => basename( $path ),
+ data_type => $filetype,
+ hyb_identifier => $hyb_name,
+ array_design_id => $datarow->{$EDF_ARRAYACCESSION} || '{UNKNOWN}',
+ });
+ }
+
+ # Sort out the FactorValues, adding new ones where we've
+ # seen the file before.
+ my $regexp_str = "$EDF_FV_PREFIX(.*)$EDF_FV_SUFFIX";
+ $regexp_str =~ s/([\[\]])/\\$1/g;
+
+ # All files are labeled with their respective FVs. This is
+ # used in DW flagging and (one day) in Pearson analysis
+ # for QA purposes.
+ foreach my $fv_heading ( keys %$datarow ) {
+ if ( my ($category) = ( $fv_heading =~ m/$regexp_str$/ ) ) {
+ $file->add_factor_value( $category, $datarow->{$fv_heading} );
+ }
+ }
+
+ # Do the full processing on each file just once.
+ next FILE_TYPE if ( $filelist->{ $datarow->{$heading} } );
+
+ $filelist->{ $datarow->{$heading} } = $file;
+ }
+
+ # do EXP files here
+ my $exp_heading = $EDF_FILE_PREFIX . 'exp' . $EDF_FILE_SUFFIX;
+ if ( $datarow->{$exp_heading} ) {
+
+ my $file;
+ unless ( $file = $filelist->{ $datarow->{$exp_heading} } ) {
+ my $path;
+ if ( $self->get_skip_data_checks() ) {
+ $path = $datarow->{$exp_heading};
+ }
+ else {
+ $path = get_filepath_from_uri(
+ $datarow->{$exp_heading},
+ $self->get_source_directory(),
+ );
+ }
+
+ # N.B. we really don't care about the array_design_id
+ # for EXP files at this stage, but it's a required
+ # attribute.
+ $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => basename( $path ),
+ data_type => 'raw',
+ hyb_identifier => $hyb_name,
+ array_design_id => $datarow->{$EDF_ARRAYACCESSION} || '{UNKNOWN}',
+ is_exp => 1,
+ });
+ }
+
+ $filelist->{ $datarow->{$exp_heading} } = $file;
+ }
+ }
+
+ # Return the list of files in filename order
+ return [ map { $filelist->{$_} } sort keys %$filelist ];
+}
+
+sub _print_edf_workflow : PRIVATE {
+
+ my ( $self, $bag_of ) = @_;
+
+ # keys are from Tab2MAGE::create_bags()
+ my @classes = (
+ { biosource => 'BioSources' },
+ { biosample => 'Samples' },
+ { extract => 'Extracts' },
+ { immunoprecipitate => 'IPs' },
+ { labeledextract => 'Labeled extracts' },
+ { pba => 'Hybridizations' },
+ { mbad => 'Raw data files' },
+ { dbad => 'Normalized data files' },
+ );
+
+ $self->generate_workflow_from_bags( $bag_of, [ @classes ] );
+
+ return;
+}
+
+sub visualize_experiment : RESTRICTED {
+
+ my ( $self, $filelist ) = @_;
+
+ my $tab2mage = $self->get_mage_generator();
+
+ FILE:
+ foreach my $file ( @{ $filelist || [] } ) {
+ if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+ || $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+ # This can crash, e.g. if the data matrix is malformed.
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ local $EVAL_ERROR;
+ eval{
+ my $dm = ArrayExpress::Datafile::DataMatrix->new();
+ $dm->create_mage($file, {}, $tab2mage, q{});
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+ if ($EVAL_ERROR) {
+ warn(sprintf(
+ "Error parsing data matrix %s (%s); graph visualization will omit this file.",
+ $file->get_name(),
+ $EVAL_ERROR,
+ ));
+ last FILE;
+ }
+ }
+ }
+
+ # Create the dotfile (and PNG if GraphViz installed)
+ my $output_base = $self->get_spreadsheet_filename();
+ $output_base =~ s/\.\w{3}$//;
+ $output_base ||= 'tab2mage'; # Just in case
+
+ if ( defined($self->get_log_to_current_dir) ) {
+ my $output_dir = $self->get_log_to_current_dir || q{.};
+ my ($vol, $dir, $name) = File::Spec->splitpath($output_base);
+ $output_base = File::Spec->catfile($output_dir, $name);
+ }
+
+ # we'll just use the default font here.
+ require ArrayExpress::Curator::Visualize;
+ ArrayExpress::Curator::Visualize->import(
+ qw(dot_and_png)
+ );
+ $self->set_clobber(
+ dot_and_png(
+ $tab2mage->get_bags(),
+ $output_base,
+ undef,
+ $self->get_clobber(),
+ )
+ );
+
+ return;
+}
+
+sub validate_edf : PRIVATE {
+
+ # Parses MAGE objects out of a spreadsheet, reports on any problems
+ # found. This does not touch the data files (they are checked later
+ # in Datafile.pm).
+
+ my ( $self ) = @_;
+
+ # Use dummy accession, domain for internal MAGE generation. Don't
+ # reassign protocol accessions yet.
+ my $tab2mage = ArrayExpress::Curator::Tab2MAGE->new({
+ spreadsheet_filename => $self->get_spreadsheet_filename(),
+ target_directory => q{.},
+ external_accession => '{ACCESSION}',
+ external_domain => '{DOMAIN}',
+ keep_protocol_accns => 1,
+ });
+
+ $self->logprint_line( 'error', 'Spreadsheet parsing START' );
+
+ # Parse the EDF
+ my $rc = $tab2mage->read_edf( $self->log_fh('error') );
+ $self->add_error($rc);
+ my $exptinfo = $tab2mage->get_expt_section();
+ my $bag_of = $tab2mage->get_bags();
+
+ # Create and link the mage objects
+ $tab2mage->create_protocols();
+ foreach my $datarow ( @{ $tab2mage->get_hyb_section() || [] } ) {
+ $tab2mage->create_mage_objects(
+ $datarow,
+ unique_identifier(),
+ );
+ }
+
+ # Count BioMaterials -> Hybs
+ $self->_print_edf_workflow( $bag_of );
+
+ if ( $exptinfo->{$EDF_EXPTDESCRIPTION} ) {
+ format_description(
+ $exptinfo->{$EDF_EXPTDESCRIPTION},
+ $self->log_fh('report'),
+ );
+ }
+
+ # Check for standard journal name
+ if ( $exptinfo->{$EDF_PUBJOURNAL} ) {
+ my $entrez_approved = parse_entrez_names;
+ unless ( $entrez_approved->{ $exptinfo->{$EDF_PUBJOURNAL} } ) {
+ $self->logprint(
+ 'error',
+ "Warning: Non-standard publication name: ",
+ $exptinfo->{$EDF_PUBJOURNAL}, "\n"
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+
+ # Check for numeric-only pubmed ID.
+ if ( $exptinfo->{$EDF_PUBMEDID} ) {
+ if ( $exptinfo->{$EDF_PUBMEDID} =~ m/\D/ ) {
+ $self->logprint(
+ 'error',
+ "Warning: PubMed ID contains non-numeric characters: ",
+ $exptinfo->{$EDF_PUBMEDID}, "\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+
+ # Check that appropriate dates have been set, and are the correct format.
+ $self->check_date_format($exptinfo->{$EDF_EXPTRELEASEDATE}, 'release' );
+ $self->check_date_format($exptinfo->{$EDF_EXPTSUBMISSIONDATE}, 'submission' );
+ if ( $exptinfo->{$EDF_EXPTGEORELEASEDATE} ) {
+ $self->check_date_format($exptinfo->{$EDF_EXPTGEORELEASEDATE}, 'GEO release' );
+ }
+
+ # Check the submitter details.
+ if ( !$exptinfo->{$EDF_EXPTSUBMITTER} ) {
+ $self->logprint( 'error',
+ "Warning: Experiment submitter has no name.\n");
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ if ( !$exptinfo->{$EDF_EXPTSUBMITTER_EMAIL} ) {
+ $self->logprint( 'error',
+ "Warning: Submitter email address is missing.\n" );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ # Collect ExperimentDesignType terms for DW checking. We use the
+ # MAGE.pm subroutine for parsing the appropriate $exptinfo tag.
+ my $experimentdesign = $tab2mage->new_experimentdesign();
+ $self->add_expt_designs(
+ map { $_->getValue() }
+ @{ $experimentdesign->getTypes() || [] }
+ );
+
+ # Also collect Organism OEs.
+ foreach my $source ( @{ $bag_of->{biosource}->() } ) {
+ foreach my $char ( @{ $source->getCharacteristics() || [] } ) {
+ if ( $char->getCategory() eq 'Organism' ) {
+ $self->add_expt_organisms( $char->getValue() );
+ }
+ }
+ }
+
+ # Check that all variable BMCs are also declared as FVs.
+ $self->check_bmchars_against_fvs( $bag_of );
+
+ # Checks on protocols and parameters
+ $self->_check_protocols( $tab2mage );
+ $self->_check_parsed_protocols( $bag_of );
+ $self->_check_parameters( $tab2mage );
+
+ # all OEs (warn) FIXME;
+
+ # Missing columns or situations where defaults will be
+ # used. (e.g. MaterialType) FIXME this is actually non-trivial
+ # without re-writing the parser.
+
+ # Number of FVs / Arrays / BMCs per hyb/biosource
+ $self->_check_hybridizations( $bag_of );
+ $self->check_mage_bmchars( $bag_of->{'biosource'}->() );
+
+ # Check Labeled Extract - Label association is present.
+ $self->check_mage_labeledextracts( $bag_of->{'labeledextract'}->() );
+
+ # Insert files into @$filelist here. Note $file->get_is_exp() flag is
+ # populated. Also checks File[*] headings that will be ignored.
+ my $filelist = $self->_datafile_to_filelist( $tab2mage );
+
+ $self->logprint_line( 'error', 'Spreadsheet parsing END' );
+
+ # Table of files per hyb
+ $self->logprint( 'report',
+ "\nChecking for presence of raw and/or normalized data files:\n" );
+ $self->logprint( 'report', datafile_consistency_table($filelist) );
+
+ # List of PBA ids for FGEM checking
+ my $hyb_ids = {};
+ foreach my $pba ( @{ $bag_of->{pba}->() } ) {
+ $hyb_ids->{ $pba->getName }++;
+ }
+
+ # List of DBA ids for FGEM checking
+ my $norm_ids = {};
+ foreach my $dba ( @{ $bag_of->{dba}->() } ) {
+ $norm_ids->{ $dba->getName }++;
+ }
+
+ $self->set_mage_generator( $tab2mage );
+
+ return ( $filelist, $hyb_ids, $norm_ids );
+
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Validate.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Validate - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Validate;
+
+ my $checker = ArrayExpress::Curator::Validate->new({
+ spreadsheet_filename => $tab2mage_file,
+ });
+
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<spreadsheet_filename>
+
+The Tab2MAGE spreadsheet to check.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output
+printed to the error and/or report filehandles:
+
+=over 4
+
+=item B<Hybridizations>
+
+Confirms that each hybridization is associated with an array
+design (i.e., ArrayExpress accession number), and at least
+one experimental factor value.
+
+=item B<BioMaterialCharacteristics>
+
+Checks that each BioSource has at least some annotation.
+This is not, however, a full test of MIAME compliance.
+
+=item B<Undeclared Experimental Factors>
+
+Checks all material characteristics against the factors described by
+the document, alerting the user if any such characteristics vary
+during the experiment without having been declared as an experimental
+factor.
+
+=item B<Protocols>
+
+Checks that all protocols referenced in the Hybridization spreadsheet
+section are declared in the Protocol section (and vice versa).
+
+=item B<Protocol accessions>
+
+Confirms that all declared protocols in the Protocol section have accession numbers.
+
+=item B<Protocol text length>
+
+Warns if any of the protocol texts seem too brief.
+
+=item B<Protocol presence and usage>
+
+Checks that a protocol has been attached to each step of the
+experiment (biomaterial treatments, hybridization, scanning and normalization).
+
+=item B<Parameters>
+
+Checks that all parameters referenced in the Hybridization spreadsheet
+section are declared in the Protocol section (and vice versa).
+
+=item B<BioMaterials>
+
+Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the
+numbers of each. If the Graphviz software is installed
+(http://www.graphviz.org) then the script will use the 'dot'
+program to produce a PNG format graph showing how the
+various components relate to each other.
+
+=item B<Publication journal>
+
+Warns the user if the specified publication journal name is not
+in the included list of standard Entrez journal abbreviations.
+
+=item B<PubMed ID>
+
+Warns the user if a non-numeric PubMed ID has been entered.
+
+=item B<Submission and release dates>
+
+Warns the user if submission and/or release dates have not been
+specified.
+
+=item B<Submitter>
+
+Checks that both submitter name and email have been provided.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/Curator/Visualize.pm b/lib/ArrayExpress/Curator/Visualize.pm
new file mode 100644
index 0000000..aef1585
--- /dev/null
+++ b/lib/ArrayExpress/Curator/Visualize.pm
@@ -0,0 +1,356 @@
+#!/usr/bin/env perl
+#
+# Module to provide visualization subroutines for tab2mage spreadsheets.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Visualize.pm 1941 2008-02-11 12:27:15Z tfrayner $
+#
+
+package ArrayExpress::Curator::Visualize;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Visualize.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Curator::Visualize - a module used by expt_check.pl
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Curator::Visualize qw(dot_and_png);
+
+ dot_and_png( $mage_bags, 'my_expt', 'luxisr', 1 );
+
+=head1 DESCRIPTION
+
+This module provides a set of subroutines used by the experiment
+checker script to confirm the MIAME metadata supplied in a Tab2MAGE
+spreadsheet.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item C<dot_and_png( $mage_bags, $output_base, $font, $clobber, $classes )>
+
+Takes: a hashref of MAGE containers, as generated by Tab2MAGE, MAGE
+and MAGE-TAB classes; a string for the output filenames (minus
+extension, so e.g. "out" becomes "out.dot" and "out.png"); an optional
+font name; an optional flag indicating whether files should be
+silently overwritten; an optional hashref of classes to draw. This
+last argument will usually be omitted. Generates the dot file and
+passes it to the Graphviz dot for drawing. Returns a new $clobber
+argument.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Carp;
+use IO::File;
+use Readonly;
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $AE_LABELCOMPOUND_PREFIX
+);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ dot_and_png
+);
+
+Readonly my $LIGHT_YELLOW => '#f8ff98';
+Readonly my $RED => '#ff5454';
+Readonly my $GREEN => '#81ff6d';
+Readonly my $YELLOW => '#fff835';
+Readonly my $LIGHT_BLUE => '#add9e6';
+Readonly my $GREY => '#c5c5c5';
+Readonly my $MAUVE => '#9b7bff';
+
+sub _check_file {
+
+ my ( $file, $clobberref ) = @_;
+
+ if ( -e $file && !$$clobberref ) {
+ print STDERR ( "\n\nFile \'$file\' aleady exists. "
+ . "Overwrite? [Y(es)\/N(o)\/A(ll)]\n" );
+ chomp( my $answer = lc <STDIN> );
+
+ SWITCH:
+ {
+ $answer eq 'y' && do { last SWITCH; };
+ $answer eq 'a' && do { $$clobberref = 1; last SWITCH; };
+ return (0);
+ }
+ }
+
+ return (1);
+
+}
+
+sub _mysyscall {
+
+ # Wrapper sub for the system() call.
+
+ my $systemcall = shift();
+
+ my $rc = 0xffff & system($systemcall);
+ my $ok = ( $rc == 0 );
+ return $ok;
+}
+
+sub dot_and_png {
+
+ my ( $bag_of, $output_base, $font, $clobber, $classes ) = @_;
+
+ $font ||= $CONFIG->get_VISUALIZE_FONT();
+
+ my $dotfile = "$output_base.dot";
+
+ unless ( _check_file( $dotfile, \$clobber ) ) {
+ print STDERR ("Aborting creation of dot and png files.\n");
+ return ($clobber);
+ }
+
+ print STDOUT ("Creating 'dot' graph file...\n");
+
+ my $dot_fh = IO::File->new( $dotfile, '>' )
+ or croak("Unable to open $dotfile for writing.\n");
+
+ print $dot_fh (<<'END_HEADER');
+digraph "Experiment"{
+rankdir=LR;
+rankspace=200
+END_HEADER
+
+ # keys are from Tab2MAGE->initialize_bags()
+ $classes ||= {
+ biosource => 'BioSource',
+ biosample => 'Sample',
+ extract => 'Extract',
+ immunoprecipitate => 'IP',
+ labeledextract => 'Labeled Extract',
+ pba => 'Hybridization',
+ mba => 'Scan',
+ datamatrix_mba => 'Scan',
+ dba => 'Normalization',
+ datamatrix_dba => 'Transformation',
+ };
+
+ my %colors = (
+ Cy3 => $GREEN,
+ Cy5 => $RED,
+ biotin => $MAUVE,
+ );
+
+ CLASS:
+ foreach my $class ( keys %$classes ) {
+
+ unless ( ref($bag_of->{$class}) eq 'CODE' ) {
+ croak("ERROR: no CODE ref available for $class container during visualization.");
+ }
+
+ OBJECT:
+ foreach my $object ( @{ $bag_of->{$class}->() } ) {
+
+ my $identifier = $object->getIdentifier();
+
+ # Default color for nodes
+ my $color = $LIGHT_BLUE;
+ my $name = $object->getName();
+ my $type = $classes->{$class};
+
+ # Exceptions to the light_blue rule:
+ # 1. Labels
+ if ( $object->can('getLabels') ) {
+
+ my @labels = @{ $object->getLabels() || [] };
+
+ # More than one dye
+ if ( @labels > 1 ) { $color = $YELLOW; }
+
+ # No dye
+ elsif ( @labels < 1 ) { $color = $GREY; }
+
+ # Just right
+ else {
+
+ my $dye = $labels[0]->getIdentifier();
+ $dye =~ s/^$AE_LABELCOMPOUND_PREFIX//;
+
+ if ( $colors{$dye} ) { $color = $colors{$dye}; }
+
+ # if dye not recognized, color the same as default
+ }
+ }
+
+ # 2. Data files
+ elsif ( $object->can('getBioDataValues') ) {
+ $color = $LIGHT_YELLOW;
+ }
+
+ # 3. MeasuredBioAssays
+ elsif ( $object->can('getMeasuredBioAssayData') ) {
+ $color = $LIGHT_YELLOW;
+ }
+
+ # 4. DerivedBioAssays
+ elsif ( $object->can('getDerivedBioAssayData') ) {
+ $color = $LIGHT_YELLOW;
+ }
+
+ print $dot_fh ( qq{"$identifier" [label="$name\\n$type",}
+ . qq{ color=black, shape=box, style=filled,}
+ . qq{ color="$color", fontname=$font];\n} );
+
+ # Sort out our edges between nodes
+ my %sources;
+
+ # BioMaterials
+ if ( $object->can('getTreatments') ) {
+
+ my @treatments = @{ $object->getTreatments() || [] };
+
+ foreach my $treat (@treatments) {
+
+ if ( my $bmms
+ = $treat->getSourceBioMaterialMeasurements() ) {
+ foreach my $bmm ( @{$bmms} ) {
+ my $id = $bmm->getBioMaterial()->getIdentifier();
+ $sources{$id}++;
+ }
+ }
+ }
+ }
+
+ # Hybridization PBAs
+ if ( $object->can('getBioAssayCreation') ) {
+
+ if ( my $bac = $object->getBioAssayCreation() ) {
+
+ my $bmms = $bac->getSourceBioMaterialMeasurements();
+
+ foreach my $bmm ( @{ $bmms || [] } ) {
+ $sources{ $bmm->getBioMaterial()->getIdentifier() }++;
+ }
+ }
+ }
+
+ # MBAs
+ if ( $object->can('getFeatureExtraction') ) {
+
+ if ( my $fext = $object->getFeatureExtraction() ) {
+
+ my $source = $fext->getPhysicalBioAssaySource();
+
+ BIOASSAY:
+ foreach my $hyb_pba ( @{ $bag_of->{'pba'}->() } ) {
+
+ # Single channel is easy.
+ if ( $source->getIdentifier() eq $hyb_pba->getIdentifier() ) {
+ $sources{ $hyb_pba->getIdentifier() }++;
+ next BIOASSAY;
+ }
+
+ # Multi-channel is a bit harder. We assume a
+ # chain of three PBAs here: hyb, channel and
+ # merged. This is the standard two-colour
+ # coding supported by the Tab2MAGE and
+ # MAGE-TAB parsers.
+ foreach my $bat ( @{ $hyb_pba->getBioAssayTreatments() || [] } ) {
+ my $ch_pba = $bat->getTarget() or next BIOASSAY;
+ foreach my $ch_bat ( @{ $ch_pba->getBioAssayTreatments() || [] } ) {
+ my $target = $ch_bat->getTarget() or next BIOASSAY;
+ if ( $source->getIdentifier() eq $target->getIdentifier() ) {
+ $sources{ $hyb_pba->getIdentifier() }++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ # DBAs
+ if ( $object->can('getDerivedBioAssayMap') ) {
+
+ my @source_ba_lists = map { $_->getSourceBioAssays() }
+ @{ $object->getDerivedBioAssayMap() || [] };
+
+ foreach my $list (@source_ba_lists) {
+ foreach my $ba (@$list) {
+ $sources{ $ba->getIdentifier() }++;
+ }
+ }
+ }
+
+ # Write all the links to the dot file.
+ foreach my $source ( keys %sources ) {
+ print $dot_fh ( qq{"$source"->"$identifier"[color=black];\n});
+ }
+ }
+ }
+
+ print $dot_fh "}\n";
+
+ close($dot_fh) or croak("Error closing output filehandle: $!\n");
+
+ my $pngfile = "$output_base.png";
+
+ unless ( _check_file( $pngfile, \$clobber ) ) {
+ print STDERR ("Aborting creation of PNG file.\n");
+ return ($clobber);
+ }
+
+ print STDOUT ("Creating PNG graph file...\n");
+
+ _mysyscall(qq{dot -Tpng -o "$pngfile" "$dotfile"})
+ or print STDERR (
+ "Error: Graph drawing requires that the Graphviz 'dot' program is installed and on your executable path.\n"
+ );
+
+ return $clobber;
+
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile.pm b/lib/ArrayExpress/Datafile.pm
new file mode 100644
index 0000000..caae04b
--- /dev/null
+++ b/lib/ArrayExpress/Datafile.pm
@@ -0,0 +1,3685 @@
+#!/usr/bin/env perl
+#
+# Datafile.pm - an OO module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2005 ArrayExpress team, EBI
+#
+# $Id: Datafile.pm 2079 2008-06-12 08:43:31Z tfrayner $
+#
+
+package ArrayExpress::Datafile;
+
+use strict;
+use warnings;
+
+use charnames qw( :full );
+
+use Class::Std;
+use English qw( -no_match_vars );
+use Carp;
+use File::Spec;
+use Storable qw(dclone);
+use IO::File;
+use Scalar::Util qw(looks_like_number openhandle);
+use List::Util qw(sum first);
+use List::MoreUtils qw(any none);
+use Digest::MD5 qw(md5_hex);
+
+use ArrayExpress::Curator::Common qw(
+ strip_discards
+ get_indexcol
+ check_linebreaks
+ $RE_EMPTY_STRING
+ $RE_LINE_BREAK
+ $RE_SURROUNDED_BY_WHITESPACE
+ $RE_WITHIN_BRACKETS
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Datafile.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile.pm - an OO module providing methods
+for parsing data files.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile;
+ my $file = ArrayExpress::Datafile->new({
+ name => 'data.txt',
+ data_type => 'raw',
+ array_design_id => 'A-MEXP-123',
+ });
+
+=head1 DESCRIPTION
+
+This is a module providing methods for data file parsing. See also
+L<ArrayExpress::Datafile::Parser> for Datafile handler objects.
+
+=cut
+
+# Incrementable integer accessors
+my %row_count : ATTR( :name<row_count>, :default<0> );
+my %parse_errors : ATTR( :name<parse_errors>, :default<0> );
+my %not_null : ATTR( :name<not_null>, :default<0> );
+
+# Hash for further mutator construction (below).
+my %incr_integer_attr = (
+ row_count => \%row_count,
+ parse_errors => \%parse_errors,
+ not_null => \%not_null,
+);
+
+# Plain boolean flag accessors
+my %is_miamexpress : ATTR( :name<is_miamexpress>, :default<0> );
+my %is_exp : ATTR( :name<is_exp>, :default<0> );
+my %is_binary : ATTR( :default<0> ); # Autodetected
+my %is_illumina_fgem : ATTR( :default<undef> );
+
+# String accessors
+my %hyb_identifier : ATTR( :name<hyb_identifier>, :default<q{}> );
+my %hyb_sysuid : ATTR( :name<hyb_sysuid>, :default<q{}> );
+my %array_design_id : ATTR( :set<array_design_id>, :init_arg<array_design_id>, :default<q{}> );
+my %datamatrix_chip_type : ATTR( :set<dm_chip_type>, :default<q{}> );
+my %ded_identifier : ATTR( :name<ded_identifier>, :default<q{}> );
+my %test_data_line : ATTR( :name<test_data_line>, :default<q{}> );
+my %md5_digest : ATTR( :set<md5_digest>, :default<q{}> );
+my %qt_type : ATTR( :name<qt_type>, :default<q{}> );
+my %path : ATTR( :name<path>, :default<undef> );
+my %target_filename : ATTR( :name<target_filename>, :default<undef> );
+my %sdrf_id_column : ATTR( :name<sdrf_id_column>, :default<undef> );
+
+# Hashref accessors
+my %data_metrics : ATTR( :name<data_metrics>, :default<{}> );
+# my $intensity_vector : ATTR( :name<intensity_vector>, :default<{}> );
+
+# Arrayref accessors
+my %index_columns : ATTR( :name<index_columns>, :default<[]> );
+my %column_headings : ATTR( :name<column_headings>, :default<[]> );
+my %heading_qts : ATTR( :name<heading_qts>, :default<[]> );
+my %heading_hybs : ATTR( :name<heading_hybs>, :default<[]> );
+
+# Accumulative hashref accessor hashes (mutator and accessor methods below)
+my %fail_columns : ATTR( :default<{}> );
+my %fail_hybs : ATTR( :default<{}> );
+
+# Hash for further mutator construction (below).
+my %accum_hashref_attr = (
+ fail_columns => \%fail_columns,
+ fail_hybs => \%fail_hybs,
+);
+
+# More specialized cases; mutators below
+my %factor_value : ATTR( :get<factor_value>, :default<{}> );
+my %name : ATTR( :get<name>, :default<undef>, :init_arg<name> );
+my %exp_data : ATTR( :get<exp_data>, :default<{}> );
+my %mage_qtd : ATTR( :get<mage_qtd>, :default<undef> );
+my %mage_badata : ATTR( :name<mage_badata>, :default<undef> );
+my %filehandle : ATTR( :set<filehandle>, :default<undef> );
+
+# Enumerated types with validating mutators (below)
+my %ded_type : ATTR( :get<ded_type>, :default<q{}> );
+my %format_type : ATTR( :get<format_type>, :default<q{}> );
+my %data_type : ATTR( :get<data_type>, :default<q{}>, :init_arg<data_type> );
+my %linebreak_type : ATTR( :set<linebreak_type> :default<undef> );
+
+# Occasionally useful as a dummy object, in which case turn off
+# constructor argument validation. Particularly useful in unit testing.
+my %is_dummy : ATTR( :get<is_dummy>, :default<undef>, :init_arg<is_dummy> );
+
+# Error filehandle, allowing STDERR messages to be captured.
+my %error_fh : ATTR( :name<error_fh>, :default<\*STDOUT> );
+
+# ArrayDesign objects linked via this attribute.
+my %array_design : ATTR( :name<array_design>, :default<undef> );
+
+sub START {
+
+ my ($self, $id, $args) = @_;
+
+ # This sets the path as well, if not already initialized (this
+ # must be done after default attribute initialization).
+ $self->set_name($args->{name}) if $args->{name};
+
+ # A little validation of attributes.
+ if ( ! $self->get_is_dummy() ) {
+
+ if ( ! $self->get_name() ) {
+ croak("Error: File name attribute not set.");
+ }
+ if ( ! $self->get_data_type() ) {
+ croak(
+ sprintf(
+ "Error: File data_type attribute not set for file %s",
+ $self->get_name(),
+ )
+ );
+ }
+ if ( ! $self->get_array_design_id() ) {
+ croak(
+ sprintf(
+ "Error: File array_design_id attribute "
+ . "(e.g. accession number) not set for file %s",
+ $self->get_name(),
+ )
+ );
+ }
+ }
+}
+
+#######################
+# Main object methods #
+#######################
+
+sub parse_header {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Parse Data Matrix headers.
+ if ( $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+ || $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ if ( $self->get_mage_badata() ) {
+
+ # MAGE-TAB style data matrices.
+ $self->parse_data_matrix_header();
+ }
+ else {
+
+ # Old-style FGEMs.
+ $self->parse_fgem_header();
+ }
+ }
+ else {
+
+ # Check for raw/norm data file formats.
+ $self->parse_header_with_indices( $CONFIG->get_T2M_INDICES() );
+ }
+
+ return $self->get_format_type();
+}
+
+sub get_linebreak_type {
+
+ my ( $self ) = @_;
+
+ unless ( defined $linebreak_type{ ident $self } ) {
+
+ # Check the linebreaks and set the input record separator
+ # accordingly
+ my ( $count, $linebreak ) = check_linebreaks( $self->get_path() );
+
+ unless ( defined $linebreak ) {
+
+ # If we couldn't decide on a line ending format, we arrive
+ # here. Set some defaults here:
+ $self->set_row_count(0);
+ $self->set_format_type('Unknown');
+
+ # Set the linebreak type so that we don't try getting it
+ # again should the following croak() be trapped in an eval().
+ $linebreak_type{ ident $self } = 'Unknown';
+
+ # Crash hard. Typically calls to this method are wrapped
+ # in an eval to capture this error.
+ my $filename = ( File::Spec->splitpath( $self->get_path() ) )[2];
+ croak(
+ sprintf(
+ "ERROR: Cannot parse linebreaks for file %s (%s Unix, %s DOS, %s Mac)\n",
+ $filename,
+ $count->{'unix'},
+ $count->{'dos'},
+ $count->{'mac'},
+ ),
+ );
+ }
+
+ $linebreak_type{ ident $self } = $linebreak;
+ }
+
+ return $linebreak_type{ ident $self };
+}
+
+sub get_line_format {
+
+ my ( $self ) = @_;
+
+ my $linebreak = $self->get_linebreak_type();
+
+ my %separator = (
+ "\N{LINE FEED}" => 'Unix',
+ "\N{CARRIAGE RETURN}\N{LINE FEED}" => 'DOS',
+ "\N{CARRIAGE RETURN}" => 'Mac',
+ 'Unknown' => 'Unknown',
+ );
+
+ if ( defined $separator{ $linebreak } ) {
+ return $separator{ $linebreak };
+ }
+ else {
+
+ # Failure here reflects an inconsistency in the linebreaks
+ # supported here and in Common::check_linebreaks, so it
+ # warrants a full confess().
+ confess("Error: Unrecognized linebreaks.");
+ }
+}
+
+sub parse_datafile {
+
+ my ( $self, $QTs, $hyb_ids, $norm_ids ) = @_;
+
+ ref $QTs eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $hyb_ids eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $norm_ids eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Rewind the file before running any checks.
+ seek($self->get_filehandle, 0, 0)
+ or croak("Error rewinding filehandle: $!");
+
+ my $linebreak;
+ eval {
+ $linebreak = $self->get_linebreak_type();
+ };
+ if ( $EVAL_ERROR ) {
+
+ # Skip this file if linebreak parsing failed.
+ return ( [], $EVAL_ERROR );
+ }
+ local $INPUT_RECORD_SEPARATOR = $linebreak;
+
+ # We now treat all datafiles as though they will be parsed by our
+ # Datafile::Parser system (rather than the MIAMExpress datafile
+ # parsers).
+ $self->parse_header();
+ my $rc = q{};
+ unless ( scalar @{ $self->get_index_columns() } ) {
+ $rc .= "Unable to detect supported data file column headings.\n";
+ }
+
+ if ( $self->get_format_type() =~ /^Illumina/
+ && $self->is_illumina_fgem() ) {
+
+ # Fix Illumina data matrix-style files.
+ $self->rewrite_illumina_as_fgem();
+ }
+ else {
+
+ # Rewrite non-Generic data formats into a temporary Generic
+ # filehandle. This is now all handled within the
+ # fix_known_text_format method.
+ $self->fix_known_text_format();
+ }
+
+ # Allow Normalization IDs as column labels for data matrices.
+ if ( $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+ || $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ $hyb_ids = { %{ $hyb_ids }, %{ $norm_ids } };
+ }
+
+ # Check the column headings here. This populates %fail_columns and
+ # %fail_hybs
+ my $column_rc;
+ $column_rc = $self->check_column_headings( $QTs, $hyb_ids )
+ unless $rc;
+
+ # feature_coords is an array of "1.1.1.1"-style coord strings.
+ my ( $feature_coords, $parse_rc );
+ ( $feature_coords, $parse_rc ) = $self->_parse_datarows()
+ unless $rc;
+
+ $rc .= $column_rc if $column_rc;
+ $rc .= $parse_rc if $parse_rc;
+
+ return ( $feature_coords, $rc );
+
+}
+
+sub is_ignored_qt {
+
+ # Method returns true if the argument matches against the
+ # Config.pm list of ignored QTs.
+ my ( $self, $qt ) = @_;
+
+ return ( first { $qt =~ m/\A\s*$_\s*\z/ms }
+ @{ $CONFIG->get_IGNORED_QTS() || [] } );
+}
+
+sub check_column_headings {
+
+ # need lists of approved QTs, most likely on a per-datafile-format
+ # basis. also check QT types (MAGE spec; float, boolean, enum? If
+ # standard, what?). Make sure QTs are free of spaces and other
+ # typos also need the hyb names here for combined data matrix
+ # files.
+
+ my ( $self, $QTs, $hyb_ids ) = @_;
+
+ ref $QTs eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ $hyb_ids ||= {};
+
+ # Figure out which software type best represents the column
+ # headings; value will be returned as $self->get_qt_type.
+ my $rc = $self->_derive_consensus_software($QTs);
+
+ my $software_QTs = $self->get_data_metrics();
+
+ HEADING:
+ foreach my $heading ( @{ $self->get_heading_qts() } ) {
+
+ # Skip common column headings (various index columns etc.)
+ next HEADING if ( $self->is_ignored_qt($heading) );
+
+ # Record a failing column if it's not represented in the consensus
+ # software type (qt_type).
+ unless ( $software_QTs && $software_QTs->{$heading} ) {
+ $self->add_fail_columns($heading);
+ }
+
+ }
+
+ # Deal with combined data files
+ if ( $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+ || $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+ HEADING:
+ foreach my $column ( @{ $self->get_heading_hybs() } ) {
+ foreach my $id (@$column) {
+ $self->add_fail_hybs($id) unless $hyb_ids->{$id};
+ }
+ }
+
+ # Internal consistency check
+ croak( "Error: Hyb and QT numbers differ for file "
+ . $self->get_name()
+ . "\n" )
+ unless ( $#{ $self->get_heading_qts() } == $#{ $self->get_heading_hybs() } );
+
+ }
+
+ return $rc;
+}
+
+sub parse_exp_file {
+
+ # Parse EXP file and capture the relevant metadata. Returns undef
+ # on failure.
+ my $self = shift;
+ my $section = 'header';
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $expfile = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ my $exp_data = {};
+
+ EXP_LINE:
+ while ( my $line = <$expfile> ) {
+ chomp $line;
+
+ # Just to be doubly sure about DOS-style line endings (FIXME
+ # is this really necessary? chomp() should have done the
+ # job...)
+ $line =~ s/$RE_LINE_BREAK//xms;
+
+ if ( $line =~ m/\A $RE_WITHIN_BRACKETS/xms ) {
+
+ $section = $1;
+
+ }
+ else {
+
+ my @linearray = split /\t/, $line;
+
+ next EXP_LINE unless $linearray[0]; # lose the undefined rows
+
+ $exp_data->{$section}{ $linearray[0] } = $linearray[1]
+ if ( $#linearray == 1 );
+
+ }
+ }
+
+ close $expfile or croak("Unable to close EXP filehandle: $!\n");
+
+ $self->set_exp_data($exp_data);
+
+ return $exp_data;
+}
+
+sub fix_known_text_format { # This subroutine maps values in file
+ # format to the appropriate reformatting
+ # sub. To add a new format, edit the
+ # Constants.pm file to indicate the
+ # coordinate columns and set the format CV
+ # string, write the conversion sub and link
+ # the two here.
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh;
+
+ my $format_type = $self->get_format_type();
+
+ my %dispatch = (
+ 'GenePix' => '_fix_genepix',
+ 'ArrayVision' => '_fix_arrayvision',
+ 'ArrayVision_lg2' => '_fix_arrayvision_lg2',
+ 'Agilent' => '_fix_agilent',
+ 'Scanalyze' => '_fix_scanalyze',
+ 'ScanArray' => '_fix_scanarray',
+ 'QuantArray' => '_fix_scanarray',
+ 'Spotfinder' => '_fix_spotfinder',
+ 'MEV' => '_fix_mev',
+ 'BlueFuse' => '_fix_bluefuse',
+ 'UCSFSpot' => '_fix_ucsfspot',
+ 'CodeLink' => '_fix_codelink',
+ 'NimbleScanFeature' => '_fix_nimblescanfeat',
+ 'NimbleScanNorm' => '_fix_nimblescanfeat',
+ 'NimblegenNASA' => '_fix_nimblegennasa',
+ 'AppliedBiosystems' => '_fix_appliedbiosystems',
+ 'Illumina' => '_fix_illumina_perhyb',
+ 'ImaGene' => '_fix_imagene',
+ 'ImaGene3' => '_fix_imagene3',
+ 'ImaGene7' => '_fix_genepix',
+ 'CSIRO_Spot' => '_fix_csiro_spot',
+ );
+
+ if ( my $method = $dispatch{$format_type} ) {
+ $input_fh = $self->$method;
+ }
+
+ # Exchange the old filehandle for the new one.
+ $self->set_filehandle($input_fh) if $input_fh;
+
+ # return value is effectively a true = success return code (not always used).
+ return $input_fh;
+
+}
+
+sub strip_and_sort {
+
+ # Sub to rewrite a datafile, stripping out unwanted columns and
+ # substituting null entries.
+
+ my ( $self, $output_file, $new_headings ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $output_file and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $new_headings eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Create a temporary file in MC/MR/C/R format; sorted on the fly.
+ # We want to sort our output; here we do it on the fly using unix
+ # sort (this is much faster than using perl).
+ #
+ # Windows support - omit the key flags. N.B. we also have to
+ # explicitly close these filehandles for Windows, otherwise we
+ # cannot delete the temporary files. Win2000 ActiveState Perl 5.8.6
+ # registers as "MSWin32"
+ #
+ my $sortkeys = q{};
+ unless ( $^O =~ m/\A mswin/ixms ) {
+ $sortkeys = ' -n ';
+ my $columnno = 0;
+
+ # We reorder the file below so that the first four columns are
+ # index columns, regardless of where they are in the original
+ # file.
+ foreach my $index ( 0 .. $#{ $self->get_index_columns() } ) {
+
+ # $index is 0..n; we need 1..(n+1)
+ $sortkeys .= q{ -k } . ( $index + 1 ) . q{ };
+ }
+ }
+
+ my @indexcols = @{ $self->get_index_columns() };
+
+ my @original_headings = @{ $self->get_column_headings };
+
+ my %is_heading_to_keep = map { $_ => 1 } @$new_headings;
+
+ # Organize our new file information before we start
+ # processing. This effectively reorders the columns so that the
+ # indexcols come first.
+ my @fixed_headings = @original_headings[@indexcols];
+ my @fixed_qts;
+
+ # This will be used as an array slice below to reorder
+ # the column values on the fly.
+ my @fixed_indices = @indexcols;
+
+ # Here we sort the rest of the columns. This enables us to
+ # generate a common QT Dimension for files such as GenePix which
+ # are not always ordered identically.
+ my %heading_to_index
+ = map { $original_headings[$_] => $_ } ( 0 .. $#original_headings );
+
+ # Check that the column headings are all unique (not true e.g. for FGEM).
+ if ( scalar(@original_headings) == scalar( keys %heading_to_index ) ) {
+
+ # If they are, sort using the lookup hash %heading_to_index
+ foreach my $heading_name ( sort keys %heading_to_index ) {
+ my $index = $heading_to_index{$heading_name};
+ if ( $is_heading_to_keep{$heading_name}
+ && ( none { $index == $_ } @indexcols ) ) {
+
+ push( @fixed_headings, $heading_name );
+ push( @fixed_qts, $heading_name );
+ push( @fixed_indices, $index );
+ }
+ }
+ }
+ else {
+
+ # Files with non-unique column headings can't be sorted.
+ foreach my $index ( 0 .. $#original_headings ) {
+ my $heading_name = $original_headings[$index];
+ if ( $is_heading_to_keep{$heading_name}
+ && ( none { $index == $_ } @indexcols ) ) {
+
+ push( @fixed_headings, $heading_name );
+ push( @fixed_qts, $heading_name );
+ push( @fixed_indices, $index );
+ }
+ }
+ }
+
+ # Actually open the file as a pipe via the external "sort" utility.
+ open( my $output_fh, q{|-}, qq{sort $sortkeys > "$output_file"} )
+ or croak("Error opening output file $output_file: $!\n");
+
+ # Read through the file, using @fixed_indices to decide what to
+ # keep.
+ DATA_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ # Empty lines, especially at the end of files, can be a
+ # problem. We just dump them here and carry on.
+ if ( $line =~ $RE_EMPTY_STRING ) {
+ print {$self->get_error_fh()} ("Warning: skipping empty line in data file.\n");
+ next DATA_LINE;
+ }
+
+ # Strip all kinds of line ending
+ $line =~ s/$RE_LINE_BREAK//xms;
+
+ # We used to use Text::CSV_XS here, but this has to handle
+ # non-validated data files which occasionally crashed it.
+ my @line_array = split /\t/, $line, -1;
+
+ # Strip whitespace on the fly here, create the new line from
+ # the @fixed_indices array slice of the old one.
+ my @new_line = map {
+ my ( $value ) = defined $_ ? ( $_ =~ $RE_SURROUNDED_BY_WHITESPACE ) : q{};
+ defined $value && ( $value ne q{} )
+ ? $value
+ : 'null'
+ } @line_array[@fixed_indices];
+
+ print $output_fh ( join( "\t", @new_line ), $INPUT_RECORD_SEPARATOR );
+
+ }
+
+ close($output_fh)
+ or croak("Error: unable to close temporary filehandle: $!\n");
+
+ # Set the new information for this object
+ $self->set_index_columns( [ 0 .. $#indexcols ] );
+ $self->set_column_headings( \@fixed_headings );
+ $self->set_heading_qts( \@fixed_qts );
+ $self->set_path($output_file);
+
+ # Relink the filehandle.
+ my $self_fh = IO::File->new( $output_file, '<' )
+ or
+ croak("Error: Unable to reopen temporary file after sorting: $!\n");
+ $self->set_filehandle($self_fh);
+
+ return;
+}
+
+sub percent_null {
+
+ my ($self) = @_;
+
+ my $data_metrics = $self->get_data_metrics();
+ my $data_rowno = $self->get_row_count();
+
+ # We want to count the intersection between the QTs (heading_qts)
+ # and the known QTs (data_metrics).
+ my $data_colno
+ = ( grep { exists $data_metrics->{$_} } @{ $self->get_heading_qts() } );
+
+ my $total_notnull = $self->get_not_null;
+
+ my $percent_null;
+ if ( $data_colno && $data_rowno && $total_notnull ) {
+ $percent_null
+ = 100 * ( ( $data_colno * $data_rowno ) - $total_notnull )
+ / ( $data_colno * $data_rowno );
+
+ # Round to 5dp.
+ return sprintf("%.5f", $percent_null);
+ }
+
+ else {
+ return ("N/A");
+ }
+}
+
+sub parse_header_with_indices {
+
+ my ( $self, $indices ) = @_;
+
+ ref $indices eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $type = $self->get_data_type();
+
+ my $linecount = 0; # Track the number of lines seen
+
+ my $column_headings = [];
+ my $indexcols = [];
+ my $format;
+
+ HEADERLINE:
+ while ( my $line = <$input_fh> ) {
+
+ # Give up after we've seen a good chunk of the file (this is a
+ # performance measure).
+ last HEADERLINE if ( $linecount > 1000 );
+ $linecount++;
+
+ # Just to be doubly sure about DOS-style line endings
+ $line =~ s/$RE_LINE_BREAK//xms;
+
+ @$column_headings = split /\t/, $line, -1;
+
+ # Strip off surrounding quotes; I assume our loader can cope with
+ # QTs in quotes. Easy to change if not (but see parse_datafile for
+ # another instance of this!!!).
+ foreach my $heading (@$column_headings) {
+ $heading =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+ }
+
+ # Here we do a check on column heading for our supported file
+ # formats.
+
+ # MX FGEM first. FIXME consider removing this special-case;
+ # its only real effect is to limit FGEM files to single
+ # indexcol formats; maybe we can remove this assumption
+ # elsewhere in the code and simplify this.
+ if ( $type && $type eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ # Check for Reporter Identifier column, CS id or Affy Probe ID
+ foreach my $col_format ( qw( FGEM FGEM_CS AffyNorm GEO Illumina ) ) {
+
+ # FGEM only ever has one identifier column.
+ my $heading = $CONFIG->get_T2M_INDICES()->{$col_format}[0];
+ my $column = get_indexcol(
+ $column_headings,
+ qr/$heading/,
+ );
+ unless ( $column == -1 ) {
+
+ # Column found; we stop looking.
+ $indexcols = [ $column ];
+ $format = $col_format;
+ last HEADERLINE;
+ }
+ }
+
+ # If no suitable identifier columns found, check the next line.
+ next HEADERLINE;
+
+ }
+
+ # Non-FGEM files (everything else - raw, normalized)
+ else {
+
+ # N.B. for ordered searches here use a Tie::IxHash to
+ # store the indices in Config.pm.
+
+ COLUMNFORMAT:
+ foreach my $col_format ( keys %$indices ) {
+
+ # Some index sets don't have 'Generic' indices; we skip them
+ # here to avoid autovivification bugs below
+ next COLUMNFORMAT unless ( $indices->{$col_format} );
+
+ foreach my $header ( @{ $indices->{$col_format} } ) {
+
+ # Empty string is special-cased.
+ my $column = get_indexcol(
+ $column_headings,
+ ( $header eq q{} ? $header : qr/$header/ ),
+ );
+
+ if ( $column == -1 ) {
+
+ # Column not found; reset $indexcols and skip
+ # to the next format type.
+ $indexcols = [];
+ next COLUMNFORMAT;
+ }
+ else {
+
+ # Column found; add to $indexcols.
+ push( @{ $indexcols }, $column );
+ }
+ }
+
+ # All columns found, we stop looking.
+ $format = $col_format;
+ last HEADERLINE;
+ }
+ }
+ } # End of HEADERLINE
+
+ # Put the results where we can find them later.
+ if ( $format ) {
+ $self->set_column_headings( $column_headings );
+ $self->set_index_columns( $indexcols );
+ $self->set_heading_qts( strip_discards( $indexcols, $column_headings ) );
+ }
+ $self->set_format_type( $format || 'Unknown' );
+
+ return;
+}
+
+sub is_illumina_fgem {
+
+ # Takes a parsed Datafile, checks column_headings to see whether
+ # it's an FGEM as opposed to per-hyb data.
+
+ my $self = shift;
+
+ unless ( defined $is_illumina_fgem{ident $self} ) {
+ unless ( $self->get_format_type =~ /Illumina/i ) {
+ croak("Can't call is_illumina_fgem() on non-Illumina file.\n");
+ }
+
+ # Start by assuming the negative.
+ $is_illumina_fgem{ident $self} = 0;
+
+ # Check the headings for repeated values. Make sure we make a
+ # copy of the array here, as we're going to modify it.
+ my @headings = @{ $self->get_column_headings() };
+ my %seen;
+
+ HEADING:
+ foreach my $heading (@headings) {
+
+ # Strip off everything before the last period, or return
+ # the whole thing. Assumes that Illumina QTs never contain
+ # periods.
+ if ( $heading =~ m/\A .* \. ([^\.]+) \z/gxms ) {
+ $heading = $1;
+ }
+ if ( $heading && $seen{$heading}++ ){
+ $is_illumina_fgem{ident $self} = 1;
+ last HEADING;
+ }
+ }
+ }
+
+ return $is_illumina_fgem{ident $self};
+}
+
+sub rewrite_illumina_as_fgem {
+
+ my $self = shift;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ unless ( $self->get_format_type =~ /Illumina/i && $self->is_illumina_fgem ) {
+ croak("File is not an Illumina multi-hyb data matrix.\n");
+ }
+
+ # Rewrite the file here (reuse _fix_illumina() code)
+ my $input_fh = $self->_fix_illumina();
+
+ # Exchange the old filehandle for the new one.
+ $self->set_filehandle($input_fh);
+
+ # Rewrite the headings as standard FGEM-style. Assumes that
+ # Illumina QTs never contain periods.
+ my ( @new_headings, @file_qts, @hyb_ids );
+ foreach my $heading ( @{ $self->get_column_headings() } ) {
+ my ($hyb, $qt) = ($heading =~ /\A (.*) \. ([^\.]+) \z/xms);
+ if ( $qt && $hyb ) {
+ push @new_headings, $qt;
+ push @file_qts, $qt;
+ push @hyb_ids, [ $hyb ];
+ }
+ else {
+
+ # Index column.
+ push @new_headings, $heading;
+ }
+ }
+ $self->set_format_type('FGEM');
+ $self->set_column_headings(\@new_headings);
+ $self->set_heading_qts(\@file_qts);
+ $self->set_heading_hybs(\@hyb_ids);
+
+ # If it's not already transformed data, set it as a measured data matrix.
+ if ( $self->get_data_type() eq 'raw' ) {
+ $self->set_data_type( $CONFIG->get_RAW_DM_FILE_TYPE() );
+ $self->set_sdrf_id_column('Hybridization Name');
+ }
+ else {
+ $self->set_data_type( $CONFIG->get_FGEM_FILE_TYPE() );
+ $self->set_sdrf_id_column('Normalization Name');
+ }
+
+ return;
+}
+
+sub get_dm_chip_type {
+
+ # Return the chip_type associated with a data matrix file via its
+ # MAGE BioAssayData association.
+ my ( $self ) = @_;
+
+ unless ( $datamatrix_chip_type{ ident $self } ) {
+ if ( my $badata = $self->get_mage_badata() ) {
+
+ NVT:
+ foreach my $nvt ( @{ $badata->getPropertySets() || [] } ) {
+ if ( $nvt->getName() =~ /\A CDF \z/ixms ) {
+ my $chip_type = $nvt->getValue();
+ $chip_type =~ s/\.CDF \z//ixms;
+ if ( $chip_type ) {
+ $datamatrix_chip_type{ ident $self } = $chip_type;
+ last NVT;
+ }
+ }
+ }
+ }
+ }
+
+ return $datamatrix_chip_type{ ident $self };
+}
+
+sub get_array_design_id {
+
+ my ( $self ) = @_;
+
+ return $self->get_dm_chip_type() || $array_design_id{ ident $self };
+}
+
+sub parse_fgem_header : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Initial basic parse to get our header info.
+ $self->parse_header_with_indices( $CONFIG->get_T2M_INDICES() );
+
+ # Sort out column_headings, heading_qts and heading_hybs here.
+
+ # Allow checks to omit already-recognized index columns, such
+ # as the rather polymorphic ProbeSet ID for AffyNorm.
+ my %is_index_heading = map { $_ => 1 }
+ map { $self->get_column_headings()->[$_] }
+ @{ $self->get_index_columns() };
+
+ my ( @new_headings, @file_qts, @hyb_ids );
+
+ COLUMN_HEADING:
+ foreach my $heading ( @{ $self->get_column_headings() } ) {
+
+ # Skip index column headings.
+ if ( $is_index_heading{ $heading } ) {
+ push @new_headings, $heading;
+ }
+ else {
+
+ # FIXME this regexp may need some work:
+ if ( my ( $qt_heading, $hyb_string )
+ = ( $heading =~ m{\A \s* (.*?) \s* \( (.*) \) \s* \z}xms ) ) {
+
+ push( @new_headings, $qt_heading );
+ push( @file_qts, $qt_heading );
+
+ # Hyb ids split into paren-encapsulated list,
+ # e.g. QT(Hyb1)(Hyb2)
+ my @ids = split /\)\s*\(/, $hyb_string;
+ push( @hyb_ids, \@ids );
+ }
+
+ else {
+
+ # Failure to parse FGEM heading. Make plenty of noise
+ # about this by dumping the full heading
+ # everywhere. This will be picked up elsewhere ard
+ # complained about.
+ push( @new_headings, $heading );
+ push( @file_qts, $heading );
+ push( @hyb_ids, [ $heading ] );
+ }
+ }
+ }
+
+ $self->set_column_headings( \@new_headings );
+ $self->set_heading_hybs( \@hyb_ids );
+ $self->set_heading_qts( \@file_qts );
+
+ return;
+}
+
+sub parse_data_matrix_header : PRIVATE {
+
+ my ( $self ) = @_;
+
+ unless ( ( $self->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+ || $self->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() )
+ && $self->get_mage_badata() ) {
+ croak("Error: Unsuitable Datafile object passed to rewrite method.");
+ }
+
+ # Make sure we know what our line format is.
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Parse the two-line header. FIXME this assumes the rather rigid
+ # two-line header is the first thing in the file. We should at
+ # least allow comment lines (#...) here.
+ my $header1 = <$input_fh>;
+ chomp $header1;
+ $header1 =~ s/\"//g;
+ my $header2 = <$input_fh>;
+ chomp $header2;
+ $header2 =~ s/\"//g;
+ my @harry1 = split /\t/, $header1;
+ my @harry2 = split /\t/, $header2;
+
+ # $harry1[0] should be an SDRF column reference, while $harry2[0]
+ # has to be Reporter Identifier or CompositeSequence Identifier
+ # (CompositeElement) at the moment.
+
+ # Note the SDRF columns to use for later bioassay mapping.
+ my $sdrf_colref = $harry1[0];
+ $self->set_sdrf_id_column( $sdrf_colref );
+ my @hyb_ids;
+ for ( my $i = 1; $i <= $#harry1; $i++ ) {
+ my @hybs = split /;/, $harry1[$i];
+ push @hyb_ids, \@hybs;
+ }
+ $self->set_heading_hybs( \@hyb_ids );
+
+ # Coerce the DesignElement identifier column into something we can
+ # use for MAGEv1.
+ my $de_type = $harry2[0];
+ my $format;
+ if ( $de_type =~ /Composite *Element(?: *REF)?/i ) {
+ $de_type = 'CompositeSequence Identifier';
+ $format = 'FGEM_CS';
+ }
+ elsif ( $de_type =~ /Reporter(?: *REF)?/i ) {
+ $de_type = 'Reporter Identifier';
+ $format = 'FGEM';
+ }
+
+ if ( $format ) {
+ $self->set_index_columns( [ 0 ] );
+ $self->set_column_headings( [ $de_type, @harry2[1..$#harry2] ] );
+ $self->set_heading_qts( [ @harry2[1..$#harry2] ] );
+ }
+ $self->set_format_type( $format || 'Unknown' );
+
+ return;
+}
+
+####################
+# Accessor methods #
+####################
+
+#####################
+# Generic accessors #
+#####################
+
+{
+ ## no critic ProhibitNoStrict
+ no strict qw(refs);
+ ## use critic ProhibitNoStrict
+
+ # Create the integer incrementors here
+ while (my ($method, $hash) = each %incr_integer_attr) {
+ *{"increment_$method"} = sub {
+ my ( $self, $value ) = @_;
+ $value = 1 unless defined($value);
+ looks_like_number($value)
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ $hash->{ident $self} += $value;
+ return $hash->{ident $self};
+ };
+ }
+
+ # Create the accumulative arrayref accessors here
+ while (my ($method, $hash) = each %accum_hashref_attr) {
+ *{"get_$method"} = sub { # Returns arrayref
+ my $self = shift;
+ return [ sort keys %{ $hash->{ident $self} } ];
+ };
+ *{"add_$method"} = sub { # Push on assignment (i.e. add to
+ # old).
+ my $self = shift;
+
+ confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless scalar(@_);
+
+ foreach (@_) {
+ $hash->{ident $self}{$_}++;
+ }
+ return [ sort keys %{ $hash->{ident $self} } ];
+ };
+ }
+}
+
+################################
+# Specialized accessor methods #
+################################
+
+sub get_filehandle {
+
+ my $self = shift;
+
+ unless ( $filehandle{ident $self} ) {
+ croak("Error: file path not set") unless $self->get_path();
+ $filehandle{ident $self} = IO::File->new($self->get_path(), '<');
+ }
+
+ return $filehandle{ident $self};
+}
+
+sub get_is_binary {
+
+ my $self = shift;
+
+ if (! defined($self->get_path())) {
+ croak("Error: Datafile path not set.\n");
+ }
+ elsif (! -r $self->get_path() ) {
+ croak("Error: cannot read file " . $self->get_path . "\n");
+ }
+ $is_binary{ident $self} = -B $self->get_path() ? 1 : 0;
+
+ return $is_binary{ident $self};
+}
+
+sub get_md5_digest {
+
+ my $self = shift;
+
+ unless ( $md5_digest{ident $self} ) {
+ $self->set_md5_digest( $self->calculate_md5_digest() );
+ }
+
+ return $md5_digest{ident $self};
+}
+
+sub calculate_md5_digest : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $fh = $self->get_filehandle();
+
+ my $pos = tell( $fh );
+
+ # NOTE that we only ever use seek/read on binary files (don't mix
+ # these with sysseek/sysread) because other operations use seek
+ # elsewhere. Note that the Affymetrix file parsers use sys*
+ # functions exclusively though.
+ seek( $fh, 0, 0 )
+ or croak("Error: Unable to rewind binary file for hashing.");
+
+ my $md5 = Digest::MD5->new();
+ my $chunk;
+ my $chunksize = 65536; # 64k for reasonable efficiency (untested though).
+ while ( my $bytes = read( $fh, $chunk, $chunksize ) ) {
+ $md5->add( $chunk );
+ }
+
+ seek( $fh, $pos, 0 )
+ or croak("Error: Unable to reset binary filehandle after hashing.");
+
+ return $md5->hexdigest();
+}
+
+sub add_factor_value {
+
+ # Takes (category, value); returns hash ref; category => [value1, value2]
+ my ( $self, $category, $value ) = @_;
+
+ # Check the already-assigned FVs for this category, add if not
+ # present.
+ $factor_value{ident $self}{$category} ||= [];
+ my $found = first { $value eq $_ }
+ @{ $factor_value{ident $self}{$category} };
+ push( @{ $factor_value{ident $self}{$category} }, $value )
+ unless $found;
+
+ return $factor_value{ident $self};
+}
+
+sub set_ded_type { # string
+
+ my ( $self, $type ) = @_;
+
+ confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($type);
+
+ unless ( any { $type eq $_ }
+ qw(Feature Reporter CompositeSequence) ) {
+ confess(qq{Unrecognized DesignElement class "$type".});
+ }
+ $ded_type{ident $self} = $type;
+
+ return $ded_type{ident $self};
+}
+
+sub set_format_type { # string (enum: various as set in Config.pm,
+ # "Affymetrix" and "Unknown")
+
+ my ( $self, $type ) = @_;
+
+ confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($type);
+
+ my @allowed_types = qw(Unknown Affymetrix);
+ foreach my $constant (
+ $CONFIG->get_T2M_INDICES()
+ ) {
+ push( @allowed_types, keys %$constant );
+ }
+ unless ( any { $type eq $_ } @allowed_types ) {
+ confess("Unrecognized format type $type");
+ }
+ $format_type{ident $self} = $type;
+
+ return $format_type{ident $self};
+}
+
+sub set_data_type { # string (enum)
+
+ my ( $self, $type ) = @_;
+
+ confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($type);
+
+ my @allowed_types = (
+ @{ $CONFIG->get_T2M_FILE_TYPES() },
+ $CONFIG->get_FGEM_FILE_TYPE(),
+ $CONFIG->get_RAW_DM_FILE_TYPE(),
+ qw(EXP),
+ );
+ unless ( any { $type eq $_ } @allowed_types ) {
+ confess("Unrecognized data type $type");
+ }
+ $data_type{ident $self} = $type;
+
+ return $data_type{ident $self};
+}
+
+sub set_name { # string; filename only. Sets path if not otherwise set
+
+ my ( $self, $name ) = @_;
+
+ confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($name);
+
+ ref $name and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ $name{ident $self} = $name;
+ $self->set_path( File::Spec->rel2abs( $name ) )
+ unless $self->get_path();
+
+ return $name{ident $self};
+}
+
+sub set_exp_data { # complex hash ref
+ my ( $self, $data ) = @_;
+
+ confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($data);
+
+ ref $data eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ $exp_data{ident $self} = $data;
+
+ $self->set_is_exp(1);
+ $self->set_format_type('Affymetrix');
+ $self->set_data_type('EXP');
+
+ return $exp_data{ident $self};
+}
+
+sub set_mage_qtd {
+
+ my ( $self, $qtd ) = @_;
+
+ confess( $CONFIG->get_ERROR_MESSAGE_ARGS() ) unless defined($qtd);
+
+ $qtd->isa('Bio::MAGE::BioAssayData::QuantitationTypeDimension')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ $mage_qtd{ident $self} = $qtd;
+
+ return $mage_qtd{ident $self};
+}
+
+###################
+# Private methods #
+###################
+
+sub _get_blocks : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $blocks = [];
+
+ my $saved_position = tell($input_fh);
+
+ # Text::CSV_XS not appropriate here - bad data can crash it too easily.
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line, -1;
+
+ my $row_coords = $self->_get_row_coords( \@line_array );
+ $blocks
+ = _update_blocks( $blocks, \@line_array, $self->get_index_columns() );
+
+ }
+
+ seek( $input_fh, $saved_position, 0 );
+
+ return $blocks;
+
+}
+
+sub _update_blocks : PRIVATE { # Updated for Scanalyze
+
+ my ( $blocks, $line_array, $indexcols ) = @_;
+
+ ref $blocks eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $line_array eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $indexcols eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $block_num = $line_array->[ $indexcols->[0] ];
+ my $x = $line_array->[ $indexcols->[3] ];
+ my $y = $line_array->[ $indexcols->[4] ];
+ $blocks->[$block_num]{max_x} ||= $x;
+ $blocks->[$block_num]{max_x} = $x
+ if ( $blocks->[$block_num]{max_x} < $x );
+
+ $blocks->[$block_num]{min_x} ||= $x;
+ $blocks->[$block_num]{min_x} = $x
+ if ( $blocks->[$block_num]{min_x} > $x );
+
+ $blocks->[$block_num]{max_y} ||= $y;
+ $blocks->[$block_num]{max_y} = $y
+ if ( $blocks->[$block_num]{max_y} < $y );
+
+ $blocks->[$block_num]{min_y} ||= $y;
+ $blocks->[$block_num]{min_y} = $y
+ if ( $blocks->[$block_num]{min_y} > $y );
+
+ return $blocks;
+
+}
+
+sub _get_row_coords : PRIVATE {
+
+ my ( $self, $line_array ) = @_;
+
+ ref $line_array eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $row_coords = [];
+ my $format_type = $self->get_format_type();
+
+ # Affy has yet to be sorted out FIXME - will probably use
+ # composite/reporter IDs, not feature coordinates
+ if ( $format_type eq 'Affymetrix' ) {
+ return $row_coords;
+ }
+
+ # Strip out whitespace, initiate undef indices
+ foreach my $index ( @{ $self->get_index_columns() } ) {
+
+ # Prevent warnings about uninitiated indices
+ $line_array->[$index] = q{} unless defined( $line_array->[$index] );
+ $line_array->[$index] =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms
+ unless $self->get_is_miamexpress();
+ }
+
+ # Simple for Generic MetaColumn/MetaRow/Column/Row format files
+ if ( first { $format_type eq $_ } qw(Generic
+ FGEM
+ FGEM_CS
+ GEO
+ AffyNorm) ) {
+
+ foreach my $index ( @{ $self->get_index_columns() } ) {
+ push @$row_coords, $line_array->[$index];
+ }
+
+ return $row_coords; # MC/MR/C/R or FGEM
+ }
+
+ # Make preparations for converting GenePix/Scanalyze coordinates to
+ # Generic ones. @blocks is an array of hashes, $blocks[$block_num]
+ # = {max_x => "highest x coordinate in the block"} This is processed
+ # later in the subroutine
+ if ( first { $format_type eq $_ } qw(GenePix Scanalyze ImaGene7) ) {
+
+ foreach my $index ( @{ $self->get_index_columns() }[ 0 .. 2 ] ) {
+ push @$row_coords, $line_array->[$index];
+ }
+
+ return $row_coords;
+ } # B/C/R; will convert below
+
+ # This should never happen:
+ croak(
+ sprintf(
+ "Error: Unknown file type %s Internal script error in sub _get_row_coords.\n",
+ $format_type,
+ )
+ );
+}
+
+sub _parse_datarows : PRIVATE { # Generic, Affymetrix and FGEM/FGEM_CS only
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $rc = q{};
+
+ # We do the data metrics thing on a hashref rather than using
+ # expensive method calls.
+ my $metrics = $self->get_data_metrics();
+ my $format_type = $self->get_format_type();
+ my $headings = $self->get_column_headings;
+
+ # Initialise some values in our data summary hashref, $metrics
+ foreach my $heading ( keys %{$metrics} ) {
+ $metrics->{$heading}{max} = -4294967295; # 2^32 - 1
+ $metrics->{$heading}{min} = 4294967295;
+ $metrics->{$heading}{benford} = 0;
+ }
+
+ # Now we process the remainder of the data lines. This while loop is
+ # where the script spends the vast majority of its time so we want
+ # to be quite careful here.
+
+ # We may want to skip columns, e.g. if there's no heading at all:
+ my %bad_column;
+ my @feature_coords;
+
+ # The line numbers to use for file-vs-file comparisons.
+ my %selected_line_no = map {$_ => 1} (3, 4, 6, 7, 9, 31, 33, 35, 37, 39);
+
+ # For Pearson and the like
+ my %intensity_vector;
+ my $notnull = 0;
+
+ DATA_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ # just to be doubly sure about DOS-style line endings
+ $line =~ s/$RE_LINE_BREAK//xms;
+
+ # Prevent run-on into the mask/outlier sections of Affy CEL files
+ last DATA_LINE
+ if ( $format_type eq 'Affymetrix'
+ && ( $line =~ $RE_EMPTY_STRING ) );
+
+ # Increment the counter, take a line where selected
+ my $current_row = $self->increment_row_count();
+ if ( $selected_line_no{$current_row} ) {
+ my $test_vector = $self->get_test_data_line()
+ . md5_hex($line);
+ $self->set_test_data_line($test_vector)
+ }
+
+ # Check for empty lines (typically the last one). These are
+ # typically not major problems, so we just print to error_fh.
+ if ( $line =~ $RE_EMPTY_STRING ) {
+ printf {$self->get_error_fh()} (
+ "Warning: Blank line at data row %s in file %s\n",
+ $self->get_row_count(),
+ $self->get_path(),
+ );
+ next DATA_LINE;
+ }
+
+ # We skip column-wise checks (percent null, Benford's law,
+ # Pearson's) checks for Affy CELv3. This gives an approx. 4x
+ # increase in speed.
+ next DATA_LINE if ( $format_type eq 'Affymetrix' );
+
+ # Negative LIMIT preserves trailing empty values
+ my @line_array = split /\t/, $line, -1;
+
+ # Get the row coordinates. We will fix these below; we can't do it
+ # on the fly because e.g. for GenePix we need some global data
+ # before we can tell where the blocks fit into the
+ # MetaColumn-MetaRow format.
+ my $row_coords = $self->_get_row_coords( \@line_array );
+
+ # Transform arrayref into .-delimited string (this is slightly
+ # better memory-wise than an AoA).
+ if (@$row_coords) {
+ my $coord_string = join( '.', @$row_coords );
+ push( @feature_coords, $coord_string );
+ }
+
+ # Now we take a look at the data
+
+ my %measured_data; # channel => intensity value
+ my $colno = -1; # kludgey but better than the alternative
+
+ COLUMN_VALUE:
+ foreach my $value (@line_array) {
+
+ # Check we haven't already marked this column as bad.
+ $colno++; # first column is zero in the @column_headings array
+ next COLUMN_VALUE if ( $bad_column{$colno} );
+
+ my $heading = $headings->[$colno];
+
+ # Quick check that we're not on the brink of disaster (data in
+ # columns with no headings)
+ if ( !defined($heading) && defined($value) ) {
+ $rc .= "ERROR: data in column "
+ . ( $colno + 1 )
+ . " has no column heading!\n";
+ $bad_column{$colno}++;
+ next COLUMN_VALUE;
+ }
+
+ # Skip if the column has no attributes.
+ next COLUMN_VALUE unless $metrics->{$heading};
+
+ # We also skip if the column contains non-numeric values (and
+ # flag up an error!) This tests for the general form of a
+ # number.
+
+ # It's okay if this is meant to be a string, set error flag if not
+ if ( $metrics->{$heading}{datatype}
+ && ( $metrics->{$heading}{datatype} eq 'string_datatype' ) ) {
+ $notnull++;
+ next COLUMN_VALUE; # no further analysis on string values.
+ }
+
+ # Screen out unexpected null and string values here.
+ unless ( looks_like_number($value) ) {
+ my $error_message;
+ if ( $value eq q{} ) {
+ $error_message = 'Null in numeric data field';
+ }
+ else {
+ $error_message = 'Text in numeric data field';
+ }
+ $metrics->{$heading}{errors}{$error_message}++;
+ next COLUMN_VALUE;
+ }
+
+ # Update maximum and minimum values on a per-column basis
+ if ( $value < $metrics->{$heading}{min} ) {
+ $metrics->{$heading}{min} = $value;
+ }
+ elsif ( $value > $metrics->{$heading}{max} ) {
+ $metrics->{$heading}{max} = $value;
+ }
+
+ # Check that boolean values are 0 or 1
+ if ( $metrics->{$heading}{datatype}
+ && ( $metrics->{$heading}{datatype} eq 'boolean' )
+ && ( $value != 1 )
+ && ( $value != 0 ) ) {
+
+ my $error_message = 'Boolean value not 0 or 1';
+ $metrics->{$heading}{errors}{$error_message}++;
+
+ }
+
+ # These next few things need an actual value to work on
+ # (i.e. not zero at this point)
+ unless ( $value == 0 ) {
+
+ $notnull++; # We count zeroes as nulls, at least for now
+
+ # Check for floats vs. integers.
+ if (
+
+ # not integer or boolean; presumably a float
+ ( int($value) != $value )
+
+ # in a column where it's not supposed to be
+ && ( $metrics->{$heading}{datatype} ne 'float' )
+ ) {
+ my $error_message = 'Floats in non-float data field';
+ $metrics->{$heading}{errors}{$error_message}++;
+ }
+
+ # Do the Benford's Law thing.
+ # N.B. we only do this for floats that are declared as such
+ # New and Improved - uses vec() for greater efficiency.
+
+ # We just do this on MeasuredSignal and DerivedSignal.
+ my $subclass = $metrics->{$heading}{subclass};
+ if ( $subclass && $subclass =~ m/Signal/oxms ) {
+
+ # Strip out leading whitespace, zeros, minuses and
+ # decimal points.
+ $value =~ s/\A \s* [0.-]*//oxms;
+ my $first_char = substr( $value, 0, 1 );
+ vec( $metrics->{$heading}{benford}, $first_char, 32 )++;
+
+ # get intensities by channel
+ # MeasuredSignal (raw data) only, for now
+ if ( $subclass eq 'MeasuredSignal' ) {
+ $measured_data{ $metrics->{$heading}{channel} } +=
+ $metrics->{$heading}{is_background}
+ ? -$value
+ : $value;
+ }
+ }
+ }
+ }
+
+ # FIXME intensity vector capture currently inactivated - we're not
+ # using it at all yet, and it adds to the memory overhead.
+ #
+ # # Vector for Pearson correlation coefficient calculations
+ # # Ignore this if no suitable data found
+ # if (my $num_channels = scalar (grep defined, keys %measured_data)){
+
+ # my $measured_signal;
+ # if ($num_channels == 2){ # two-channel is a special case
+ # my ($numerator, $denominator) = sort keys %measured_data;
+
+# # Avoid illegal division by zero; the Pearson routines later will ignore non-numbers
+# $measured_signal = $measured_data{$denominator} ?
+# ($measured_data{$numerator} / $measured_data{$denominator}) : 'NaN';
+# }
+# else { # one channel or >2 channel data is simply averaged
+# $measured_signal = sum(values %measured_data) / $num_channels;
+# }
+# my $row_id = join('.',@$row_coords);
+# $intensity_vector{$row_id} = $measured_signal;
+# }
+ }
+
+ # FIXME - see above for intensity vec comments
+ # $self->set_intensity_vector(\%intensity_vector);
+
+ $self->increment_not_null($notnull);
+
+ $self->set_data_metrics($metrics);
+
+ return ( \@feature_coords, $rc );
+
+}
+
+sub _derive_consensus_software : PRIVATE {
+
+ my ( $self, $QTs ) = @_;
+
+ ref $QTs eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my ( @file_qts );
+ my $rc = q{};
+
+ # Allow checks to omit already-recognized index columns, such as
+ # the rather polymorphic ProbeSet ID for AffyNorm.
+ my %is_index_heading = map { $_ => 1 }
+ map { $self->get_column_headings()->[$_] }
+ @{ $self->get_index_columns() };
+
+ COLUMN_HEADING:
+ foreach my $heading ( @{ $self->get_column_headings() } ) {
+
+ # Skip index column headings.
+ push( @file_qts, $heading ) unless ( $is_index_heading{ $heading } );
+ }
+
+ # Initialize our counters
+ my $consensus = 'Unknown';
+ my %count;
+ foreach my $software ( keys %{$QTs}, $consensus ) {
+ $count{$software} = 0;
+ }
+
+ # Do the count
+ SOFTWARE_TYPE:
+ foreach my $software ( keys %{$QTs} ) {
+
+ foreach my $qt (@file_qts) {
+ $count{$software}++ if ( $QTs->{$software}{$qt} );
+ $consensus = $software
+ if ( $count{$software} > $count{$consensus} );
+ }
+ }
+
+ # Check for ambiguity
+ SOFTWARE_TYPE:
+ foreach my $software ( keys %{$QTs} ) {
+
+ # same-scoring softwares will be summarized as 'Ambiguous'; this
+ # is thought to be better than randomly picking one of the
+ # alternatives.
+ if ( ( $count{$software} == $count{$consensus} )
+ && ( $software ne $consensus )
+ && $count{$consensus} ) {
+ $consensus = 'Ambiguous';
+ print STDERR ( "WARNING: Ambiguous software type for file "
+ . $self->get_name()
+ . "\n" );
+ $rc .= "Ambiguous QuantitationTypes;"
+ . " unable to determine software type.\n";
+ last SOFTWARE_TYPE;
+ }
+ }
+
+ # Note down the kind of QTs we have
+ $self->set_qt_type($consensus);
+
+ # We want all the known QTs in data_metrics, not just the headings,
+ # because some manufacturers have ConfidenceIndicators mapped to QTs
+ # which don't appear in the data file.
+ # Make sure we're making a deep copy of the relevant QT_hash
+ # portion; otherwise this comes back to bite us later!
+ my $known_qts =
+ ( $QTs->{$consensus} && ( ref( $QTs->{$consensus} ) eq 'HASH' ) )
+ ? dclone $QTs->{$consensus}
+ : {};
+ $self->set_data_metrics($known_qts);
+
+ return $rc;
+}
+
+sub _average_block_dimension : PRIVATE {
+
+ my ($blocks, $maxdim, $mindim) = @_;
+
+ ref $blocks eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ defined ($maxdim) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ defined ($mindim) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $count = 0;
+ my $total = 0;
+ for ( my $block_num = 1; $block_num <= $#{$blocks}; $block_num++ ) {
+ my $delta_y = $blocks->[$block_num]{$maxdim}
+ - $blocks->[$block_num]{$mindim};
+
+ if ( $delta_y > 10 ) { # 10 here is arbitrary; sometimes blocks on
+ # the same row have slightly different
+ # Y-coords, and here we attempt not to count
+ # them. The real results should hopefully
+ # drown out the errors.
+ $total += $delta_y;
+ $count++;
+ }
+ }
+
+ return ( $count ? ( $total / $count ) : 0 ); # avoid divide-by-zero
+
+}
+
+sub _average_block_height : PRIVATE {
+
+ my ($blocks) = @_;
+
+ ref $blocks eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ return _average_block_dimension($blocks, 'max_y', 'min_y');
+
+}
+
+sub _average_block_width : PRIVATE {
+
+ my ($blocks) = @_;
+
+ ref $blocks eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ return _average_block_dimension($blocks, 'max_x', 'min_x');
+
+}
+
+sub _slice_minus_discards : PRIVATE {
+
+ my ($self) = @_;
+
+ my %discards = map { $_ => 1 } @{ $self->get_index_columns() };
+ my @slice;
+ my $lastheading = $#{ $self->get_column_headings() };
+ foreach ( 0 .. $lastheading ) {
+ push( @slice, $_ ) unless $discards{$_};
+ }
+
+ return ( \@slice, $lastheading );
+
+}
+
+sub _fix_scanalyze : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Store the start of the data, for use in cases where the SPOT
+ # line is not found.
+ my $init_pos = tell($input_fh);
+
+ seek($input_fh, 0, 0) or croak("Error seeking in filehandle: $!");
+
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Scan through the REMARKs and empty lines, and parse out the
+ # mapping from channel to fluor, if we can. The first SPOT line is
+ # the start of the data; however if it's not present we want to be
+ # able to parse these data anyway.
+ my $pos = tell($input_fh);
+ my $line = <$input_fh>;
+
+ my $info = {};
+
+ LINE:
+ until ( $line =~ m/\A SPOT \b/xms ) {
+ $pos = tell($input_fh); # Store the last-but-one line position
+ $line = <$input_fh>;
+
+ # End of file (prevent infinite loops in files without SPOT).
+ unless ( defined $line ) {
+ $pos = $init_pos;
+ last LINE;
+ }
+
+ # Only Cy3/Cy5 and Alexa555/647 supported for now FIXME.
+ #
+ # We default to Cy3 or Alexa555 in CH1; this is to match the usage seen in
+ # Stanford submissions.
+ my ($channel, $fluor);
+ if ( ( $channel, $fluor )
+ = ( $line =~ m{ \b(\w+)\b \s+ IMAGE \s+ .* Cy.*([35]) }ixms ) ) {
+
+ $info->{ lc($channel) } = ( $fluor == 3 ) ? "CH1" : "CH2";
+ }
+ elsif ( ( $channel, $fluor )
+ = ( $line =~ m{ \b(\w+)\b \s+ IMAGE \s+ .* Alexa(555|647) }ixms ) ) {
+
+ $info->{ lc($channel) } = ( $fluor == 555 ) ? "CH1" : "CH2";
+ }
+ }
+
+ $self->_map_channel_to_fluor($info);
+
+ seek( $input_fh, $pos, 0 )
+ or croak("Error seeking in data file: $!\n"); # Back up one line
+
+ my $blocks = $self->_get_blocks();
+
+ # Block height is used below as a margin of error to cope with the
+ # real-world fact that spotters don't run in perfect grids.
+ # Likewise this approach is also imperfect.
+ my $block_height = _average_block_height($blocks);
+
+ # Construct a block -> MC/MR lookup table (this takes into account
+ # the Y coord as well as X). Numbering is from the top left corner
+ # of the grid.
+ my $last_max_x = 0;
+ my $last_max_y = 0;
+ my $metacolumn = 0;
+ my $metarow = 1;
+ my $populated_x_grid = 0;
+ my $populated_y_grid = 0;
+ my $populated_x_coordinate = 0;
+
+ foreach my $block ( @{$blocks}[ 1 .. $#$blocks ] ) { # blocks start at 1
+
+ if ( $block->{max_x} < $last_max_x ) {
+ if ( $block->{max_y} < ( $last_max_y - ( $block_height / 2 ) ) ) {
+ print {$self->get_error_fh()}
+ ("Error: Overlapping blocks in ScanAlyze array.\n");
+ }
+
+ else { # X is less than in previous block, but Y is not.
+
+ # The following checks whether the current block x coord is
+ # less than the previous metablock max x coord.
+ if ( $block->{max_x}
+ < ( $populated_x_coordinate - ( $block_height / 2 ) ) ) {
+
+ # If so, reset back to zero (full carriage return).
+ $populated_x_grid = 0;
+ $populated_x_coordinate = 0;
+ $populated_y_grid = $metarow;
+ }
+
+ # Set the metacolumn back to the end of the last completed
+ # metablock, move down a row.
+ $metacolumn = $populated_x_grid + 1;
+ $metarow++;
+
+ }
+ }
+ else {
+
+ # Move across to the next metacolumn as normal; skip if we're
+ # moving across to the next block.
+ $metacolumn++
+ unless (
+ $block->{max_y} < ( $last_max_y - ( $block_height / 2 ) ) );
+ }
+
+ if ( $block->{max_y} < ( $last_max_y - ( $block_height / 2 ) ) ) {
+ if ( $block->{max_x} < $last_max_x ) {
+ print {$self->get_error_fh()}
+ ("Error: Overlapping blocks in ScanAlyze array.\n");
+ }
+
+ else { # Y is less than in previous block, but X is not.
+
+ # Update the completed metablock column info
+ $populated_x_grid = $metacolumn;
+ $populated_x_coordinate = $block->{max_x};
+
+ # Back up the metarow to the last completed metablock row,
+ # move over to the appropriate metacolumn
+ $metarow = $populated_y_grid + 1;
+ $metacolumn++;
+ }
+ }
+ else {
+
+ # metarow stays the same (simple move of one block to the
+ # right). This space left blank intentionally.
+ }
+
+ $block->{metarow} = $metarow;
+ $block->{metacolumn} = $metacolumn;
+
+ $last_max_x = $block->{max_x};
+ $last_max_y = $block->{max_y};
+
+ }
+
+ # Convert the GRID/COL/ROW coords into MC/MR/R/C
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $block_num = $line_array[ $self->get_index_columns()->[0] ];
+ my $column = $line_array[ $self->get_index_columns()->[1] ];
+ my $row = $line_array[ $self->get_index_columns()->[2] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ my $metacolumn = $blocks->[$block_num]{metacolumn};
+ my $metarow = $blocks->[$block_num]{metarow};
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+
+}
+
+sub _fix_genepix : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ my $blocks = $self->_get_blocks();
+
+ # Block width is used below as a margin of error to cope with
+ # array designs with only one MetaColumn, and the real-world fact
+ # that spotters don't run in perfect grids. Likewise this
+ # approach is also imperfect.
+ my $half_block_width = _average_block_width($blocks) / 2;
+
+ # Construct a block -> MC/MR lookup table
+ my $last_max_x = 0;
+ my $metacolumn = 0;
+ my $metarow = 1;
+ foreach my $block ( @{$blocks}[ 1 .. $#$blocks ] ) { # Blocks start at 1
+
+ if ( $block->{max_x} < ($last_max_x + $half_block_width) ) {
+
+ # New MetaRow, "carriage return" for MetaColumn.
+ $metarow++;
+ $metacolumn = 0;
+ }
+ $metacolumn++;
+
+ $block->{metarow} = $metarow;
+ $block->{metacolumn} = $metacolumn;
+
+ $last_max_x = $block->{max_x}
+
+ }
+
+ # Convert the BCR coords into MC/MR/R/C
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $block_num = $line_array[ $self->get_index_columns()->[0] ];
+ my $column = $line_array[ $self->get_index_columns()->[1] ];
+ my $row = $line_array[ $self->get_index_columns()->[2] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ my $metacolumn = $blocks->[$block_num]{metacolumn};
+ my $metarow = $blocks->[$block_num]{metarow};
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+
+}
+
+sub _fix_arrayvision : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $primary = $line_array[ $self->get_index_columns()->[0] ];
+ my $secondary = $line_array[ $self->get_index_columns()->[1] ];
+
+ # This format supplied by Elizabeth Herbolsheimer (WMIT)
+ my ( $metarow, $metacolumn ) = ( $primary =~ m/(\d+) - (\d+)/ );
+ my ( $row, $column ) = ( $secondary =~ m/(\d+) - (\d+)/ );
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _fix_arrayvision_lg2 : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ # Flag to indicate whether we've found features or not.
+ my $is_feature_level;
+
+ # Process the file.
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $spotlabels = $line_array[ $self->get_index_columns()->[0] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ # If we have feature-level data, handle it here.
+ if ( my @coords
+ = ($spotlabels =~ m/(\w+) ?- ?(\w+) ?: ?(\w+)(?: ?- ?(\w+))?/) ) {
+
+ # Strip out "R" and "C" from block-level coords.
+ my ($metarow) = ($coords[0] =~ m/(\d)+/);
+ my ($metacolumn) = ($coords[1] =~ m/(\d)+/);
+
+ # Convert text coords into numbers (whose idiotic idea was this, anyway?).
+ my $row = ord(uc($coords[2])) - 64;
+ my $column = ord(uc($coords[3])) - 64;
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ $is_feature_level = 1;
+ }
+ # Otherwise, we assume reporter-level data.
+ else {
+ unshift( @$new_line, $spotlabels );
+ $is_feature_level = 0;
+ }
+
+ # Print out the reconstructed line; quell undef warnings.
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ if ( $is_feature_level ) {
+ $self->_genericize_fileinfo();
+ }
+ else {
+ $self->_reporter_genericize_fileinfo();
+ }
+
+ return $fixed_fh;
+}
+
+sub _fix_agilent : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $row = $line_array[ $self->get_index_columns()->[0] ];
+ my $column = $line_array[ $self->get_index_columns()->[1] ];
+
+ # We assume here that Agilent only prints their arrays in single
+ # blocks
+ my $metarow = 1;
+ my $metacolumn = 1;
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _fix_nimblescanfeat : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $column = $line_array[ $self->get_index_columns()->[0] ];
+ my $row = $line_array[ $self->get_index_columns()->[1] ];
+
+ # Nimblegen, like Agilent, only prints their arrays in single
+ # blocks
+ my $metarow = 1;
+ my $metacolumn = 1;
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _fix_nimblegennasa : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $column = $line_array[ $self->get_index_columns()->[0] ];
+ my $row = $line_array[ $self->get_index_columns()->[1] ];
+
+ # Nimblegen, like Agilent, only prints their arrays in single
+ # blocks
+ my $metarow = 1;
+ my $metacolumn = 1;
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _fix_appliedbiosystems : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $probe_id = $line_array[ $self->get_index_columns()->[0] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $probe_id );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ # FIXME this may actually be compseq, not reporter - depends on
+ # array design TBA.
+ $self->_reporter_genericize_fileinfo();
+
+ $self->_strip_common_heading_suffixes();
+
+ return $fixed_fh;
+}
+
+sub _fix_illumina_perhyb : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Generic Illumina fixes.
+ my $fixed_fh = $self->_fix_illumina();
+
+ # Per-hyb fixes. Don't strip suffixes if there's only one data column!
+ my $num_datacols = scalar( @{ $self->get_column_headings() } )
+ - scalar( @{ $self->get_index_columns() } );
+ if ( $num_datacols > 1 ) {
+ $self->_strip_common_heading_prefixes();
+ }
+
+ return $fixed_fh;
+}
+
+sub _fix_illumina : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ # Fix either per-hyb or FGEM Illumina data.
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $probe_id = $line_array[ $self->get_index_columns()->[0] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $probe_id );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_reporter_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _strip_common_heading_prefixes : PRIVATE {
+
+ my $self = shift;
+
+ # Common suffixes contain metainfo; we don't want them in our
+ # internal QT processing. Here we strip them out.
+ my @headings = @{ $self->get_column_headings() };
+ my $identifier_col = shift @headings; # Drop the identifier column.
+ my $prefix = q{};
+
+ # Adapted from similar code by Albannach on perlmonks.org node 274134
+ CHAR:
+ for my $length ( 1 .. length( $headings[0] ) ) {
+ $prefix = substr( $headings[0], 0, $length - 1 );
+ last CHAR unless ( scalar grep {/^\Q$prefix\E/} @headings ) == @headings;
+ }
+
+ # We've gone one CHAR too far - strip off the last character:
+ chop $prefix;
+
+ # Remove the suffix from all the headings.
+ foreach my $heading (@headings) {
+ $heading =~ s/^\Q$prefix\E//;
+ }
+ $self->set_column_headings( [ $identifier_col, @headings ] );
+
+ return;
+}
+
+sub _strip_common_heading_suffixes : PRIVATE {
+
+ my $self = shift;
+
+ # Common suffixes contain metainfo; we don't want them in our
+ # internal QT processing. Here we strip them out.
+ my @headings = @{ $self->get_column_headings() };
+ my $identifier_col = shift @headings; # Drop the identifier column.
+ my $suffix = q{};
+
+ # Adapted from similar code by Albannach on perlmonks.org node 274134
+ CHAR:
+ for my $length ( 1 .. length( $headings[0] ) ) {
+ $suffix = substr( $headings[0], -($length), $length );
+ last CHAR unless ( scalar grep {/\Q$suffix\E$/} @headings ) == @headings;
+ }
+
+ # We've gone one CHAR too far - strip off the first character:
+ substr( $suffix, 0, 1, q{} );
+
+ # Remove the suffix from all the headings.
+ foreach my $heading (@headings) {
+ $heading =~ s/\Q$suffix\E$//;
+ }
+ $self->set_column_headings( [ $identifier_col, @headings ] );
+
+ return;
+}
+
+sub _scanarray_parse_image : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $line = <$input_fh>;
+ my @info_headings = split /\t/, $line;
+ my %info;
+
+ IMAGE_LINE:
+ while ( $line = lc(<$input_fh>) ) {
+ last IMAGE_LINE if ( $line =~ m/\A end [ ]/ixms );
+
+ my @info_data = split /\t/, $line, -1;
+
+ my ( $channel, $dye, $fallback );
+ for ( my $i = 0; $i <= $#info_headings; $i++ ) {
+
+ # Skip blank values.
+ next unless $info_data[$i];
+
+ $channel = $info_data[$i]
+ if ( $info_headings[$i] =~ m/channel/ixms );
+ $dye = $info_data[$i]
+ if ( $info_headings[$i] =~ m/fluorophore?/ixms );
+
+ # Fall back to image file name (dubious FIXME).
+ $fallback ||= $info_data[$i]
+ if ( $info_headings[$i] =~ m/image/ixms );
+ }
+
+ # Attempt some smart matching on the results.
+ my $cy3_regexp = qr/ cy (?:anine?)? [-_ ]* 3/ixms;
+ my $cy5_regexp = qr/ cy (?:anine?)? [-_ ]* 5/ixms;
+ if ($dye) {
+ $dye = 'Cy3' if ( $dye =~ $cy3_regexp );
+ $dye = 'Cy5' if ( $dye =~ $cy5_regexp );
+
+ $info{$channel} = $dye;
+ }
+ elsif ( $fallback ) {
+
+ # This is a fairly desperate measure. We try to guess the
+ # label used from the image filename.
+ my $label;
+ $label = 'Cy3' if ( $fallback =~ $cy3_regexp );
+ $label = 'Cy5' if ( $fallback =~ $cy5_regexp );
+
+ $info{$channel} = $label;
+ }
+ }
+ return \%info;
+}
+
+sub _scanarray_parse_measurements : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ # Read in any measurements, if present
+ my %measurements;
+ my @measurement_headings = @{ $self->get_column_headings() };
+
+ MEASUREMENT_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ last MEASUREMENT_LINE if ( $line =~ m/\A end [ ]/ixms );
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+ my $metarow = $line_array[ $self->get_index_columns()->[1] ];
+ my $column = $line_array[ $self->get_index_columns()->[2] ];
+ my $row = $line_array[ $self->get_index_columns()->[3] ];
+
+ for ( my $i = 0; $i <= $#{ $self->get_column_headings() }; $i++ ) {
+ $measurements{"$metacolumn.$metarow.$column.$row"}
+ { $self->get_column_headings()->[$i] } = $line_array[$i];
+ }
+ }
+ return ( \%measurements, \@measurement_headings );
+}
+
+sub _parse_scanarray_header : PRIVATE {
+
+ # Input filehandle should be set to the start of the DATA section
+ # (immediately after the header line) upon exit from this sub.
+
+ my ( $self, $content_start ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $info = {};
+ my $measurements = {};
+ my $measurement_headings = [];
+ seek( $input_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ HEADER_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ # Check we've not run past the first set of headers
+ # (measurement or data). Sometimes DATA starts without a BEGIN
+ # DATA tag.
+ if ( tell($input_fh) > $content_start ) {
+ seek( $input_fh, $content_start, 0 )
+ or croak("Error seeking in data file: $!\n");
+ last HEADER_LINE;
+ }
+
+ # Parse the various block types.
+ if ( my ($data_block_type) = ( $line =~ m/\A begin [ ] (\w*)/ixms ) )
+ {
+
+ # Image info present in header, parse ready for
+ # re-assigning QTs.
+ ( $data_block_type =~ m/image/ixms ) && do {
+ $info = $self->_scanarray_parse_image();
+ };
+
+ # Header contains measurements block as first parsable
+ # header row; parse it into the measurement variables.
+ ( $data_block_type =~ m/measurements/ixms ) && do {
+ seek( $input_fh, $content_start, 0 )
+ or croak("Error seeking in data file: $!\n");
+ ( $measurements, $measurement_headings )
+ = $self->_scanarray_parse_measurements();
+
+ # The $file object currently thinks the measurement
+ # section is the start of the data. Re-parse to find
+ # the DATA section.
+ $self->parse_header();
+ last HEADER_LINE;
+ };
+
+ # Data block is the first parsable header row; seek to the
+ # top of the content (DATA) section, drop out of the
+ # header loop.
+ ( $data_block_type =~ m/data/ixms ) && do {
+ seek( $input_fh, $content_start, 0 )
+ or croak("Error seeking in data file: $!\n");
+ last HEADER_LINE;
+ };
+ }
+ }
+ return ( $info, $measurements, $measurement_headings );
+}
+
+sub _fix_scanarray : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ # Figure out which channel is which from the file header, detect
+ # measurements if present
+ my $content_start = tell($input_fh);
+
+ # Check the header for image/channel/fluorophore info. Set the
+ # input_fh to the top of the DATA section.
+ my ( $info, $measurements, $measurement_headings )
+ = $self->_parse_scanarray_header( $content_start );
+
+ # Reformat the coordinates in the data section
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ my %data_headings = map { $_ => 1 } @{ $self->get_column_headings() };
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ DATA_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ last DATA_LINE if ( $line =~ m/\A end [ ]/ixms );
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+ my $metarow = $line_array[ $self->get_index_columns()->[1] ];
+ my $column = $line_array[ $self->get_index_columns()->[2] ];
+ my $row = $line_array[ $self->get_index_columns()->[3] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ # Add measurement columns where they're missing from the data section
+ if ( my $header_row
+ = $measurements->{"$metacolumn.$metarow.$column.$row"} ) {
+ foreach my $measurement ( sort keys %$header_row ) {
+ push( @$new_line, $header_row->{$measurement} )
+ unless ( $data_headings{$measurement} );
+ }
+ }
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ # Fix the measurement headings, if we have them
+ foreach my $measurement ( sort @$measurement_headings ) {
+ push( @{ $self->get_column_headings() }, $measurement )
+ unless ( $data_headings{$measurement} );
+ }
+
+ $self->_map_channel_to_fluor($info);
+
+ $self->_genericize_fileinfo();
+
+ # Check that there's no data we're missing
+ FOOTER_LINE:
+ while ( my $line = <$input_fh> ) {
+ if ( $line =~ m/\S/xms && $line !~ m/filter/ixms )
+ { # Any non-whitespace
+ print {$self->get_error_fh()} (
+ "WARNING: Possible data after END DATA marker is being discarded.\n"
+ );
+ last FOOTER_LINE;
+ }
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ return $fixed_fh;
+
+}
+
+sub _map_channel_to_fluor : PRIVATE {
+
+ my ( $self, $info ) = @_;
+
+ ref $info eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $warning_given;
+
+ # Fix the column headings
+ HEADING:
+ foreach my $heading ( @{ $self->get_column_headings() } ) {
+
+ my ($wanted) = ( $heading =~ m/\A (ch\d+) [ a-z]/ixms )
+ or next HEADING;
+ $wanted = lc($wanted);
+
+ unless ( $info->{$wanted} ) {
+ unless ( $warning_given->{$wanted} ) {
+ print {$self->get_error_fh()} (
+ "Warning: unable to parse channel $wanted to fluorophore mapping from file header.\n"
+ );
+ $warning_given->{$wanted}++;
+ }
+ next HEADING;
+ }
+
+ $heading =~ s/\A $wanted/$info->{$wanted}/iexms;
+
+ }
+
+ return;
+}
+
+sub _fix_simple_mcmr : PRIVATE {
+
+ # A generic method to fix files in which the index columns 0..3
+ # are MetaColumn, MetaRow, Column, Row in that order (this is
+ # controled by the T2M_INDICES option in Config.pm).
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+ my $metarow = $line_array[ $self->get_index_columns()->[1] ];
+ my $column = $line_array[ $self->get_index_columns()->[2] ];
+ my $row = $line_array[ $self->get_index_columns()->[3] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _fix_spotfinder : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $fixed_fh = $self->_fix_simple_mcmr();
+}
+
+sub _fix_mev : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $fixed_fh = $self->_fix_simple_mcmr();
+}
+
+sub _fix_bluefuse : PRIVATE {
+
+ # Note that ideally we would also fix the CH1/CH2 QTs to point to
+ # fluors FIXME (the problem is that there's no standard tag for this
+ # in the files).
+
+ my ( $self ) = @_;
+
+ my $fixed_fh = $self->_fix_simple_mcmr();
+
+ return $fixed_fh;
+}
+
+sub _fix_ucsfspot : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+ my $metarow = $line_array[ $self->get_index_columns()->[1] ];
+ my $column = $line_array[ $self->get_index_columns()->[2] ];
+ my $row = $line_array[ $self->get_index_columns()->[3] ];
+
+ # Strip zero-padding
+ $metacolumn =~ s/^0+// if defined($metacolumn);
+ $metarow =~ s/^0+// if defined($metarow);
+ $column =~ s/^0+// if defined($column);
+ $row =~ s/^0+// if defined($row);
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _fix_csiro_spot : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $fixed_fh = $self->_fix_simple_mcmr();
+
+ return $fixed_fh;
+}
+
+sub _fix_imagene : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Try and determine which label was used from the file header.
+ my $label;
+ my $content_start = tell($input_fh);
+ seek($input_fh, 0, 0) or croak("Error rewinding file for input: $!");
+ HEADERLINE:
+ while (my $line = <$input_fh>) {
+
+ # Skip if at the end of the header.
+ if (tell($input_fh) > $content_start) {
+ seek($input_fh, $content_start, 0)
+ or croak("Error resetting filehandle to data section: $!");
+ last HEADERLINE;
+ }
+
+ # Dye info only in filename (if then!)
+ if ( $line =~ m/\bImage File\b/ ) {
+ ($label) = ($line =~ m/(?:\b|_) (cy[35]|alexa\d{3}|green|red) (?:\b|_)/ixms);
+
+ # Fix casing issues.
+ if ($label) {
+ $label =~ s/cy/Cy/i;
+ $label =~ s/alexa/Alexa/i;
+ $label =~ s/red/Cy5/i;
+ $label =~ s/green/Cy3/i;
+ }
+ }
+ }
+ if ($label) {
+ my $columns = $self->get_column_headings();
+ for (my $i = 0; $i <= $#{ $columns }; $i++) {
+
+ # Don't alter the index columns.
+ unless ( first { $i == $_ } @{ $self->get_index_columns() } ) {
+ $columns->[$i] .= "_$label";
+ }
+ }
+ $self->set_column_headings($columns);
+ }
+ else {
+ printf {$self->get_error_fh()} (
+ "Warning: Unable to determine channel assignment for file %s\n",
+ $self->get_name(),
+ );
+ }
+
+ my $fixed_fh = $self->_fix_simple_mcmr();
+
+ return $fixed_fh;
+}
+
+sub _imagene3_parse_measurements : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Read in any measurements, if present
+ my %measurements;
+ my @measurement_headings = @{ $self->get_column_headings() };
+ my $data_section_line;
+
+ # No explicit "End" line, we have to spot the next "Begin" line
+ # and backtrack.
+ my $previous_line_loc = tell($input_fh);
+
+ MEASUREMENT_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ if ( $line =~ m/\A begin [ ]/ixms ) {
+ $data_section_line = $line;
+ last MEASUREMENT_LINE;
+ }
+ $previous_line_loc = tell($input_fh);
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+ my @line_array = split /\t/, $line;
+
+ my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+ my $metarow = $line_array[ $self->get_index_columns()->[1] ];
+ my $column = $line_array[ $self->get_index_columns()->[2] ];
+ my $row = $line_array[ $self->get_index_columns()->[3] ];
+
+ for ( my $i = 0; $i <= $#{ $self->get_column_headings() }; $i++ ) {
+ $measurements{"$metacolumn.$metarow.$column.$row"}
+ { $self->get_column_headings()->[$i] } = $line_array[$i];
+ }
+ }
+
+ # Rewind to the previous line.
+ seek($input_fh, $previous_line_loc, 0);
+
+ return ( \%measurements, \@measurement_headings, $data_section_line );
+}
+
+sub _parse_imagene3_header : PRIVATE {
+
+ # Input filehandle should be set to the start of the DATA section
+ # (immediately after the header line) upon exit from this sub.
+
+ my ( $self, $content_start ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $measurements = {};
+ my $measurement_headings = [];
+ my $data_section_line;
+ seek( $input_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ HEADER_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ # Check we've not run past the first set of headers
+ # (measurement or data). Sometimes DATA starts without a BEGIN
+ # DATA tag.
+ if ( tell($input_fh) > $content_start ) {
+ seek( $input_fh, $content_start, 0 )
+ or croak("Error seeking in data file: $!\n");
+ last HEADER_LINE;
+ }
+
+ # Parse the various block types.
+ if ( my ($data_block_type) = ( $line =~ m/\A begin [ ] (.*)/ixms ) )
+ {
+
+ # Header contains measurements block as first parsable
+ # header row; parse it into the measurement variables.
+ if ( $data_block_type =~ m/log [ ]+ ratio [ ]+ data/ixms ) {
+ seek( $input_fh, $content_start, 0 )
+ or croak("Error seeking in data file: $!\n");
+ ( $measurements, $measurement_headings, $data_section_line )
+ = $self->_imagene3_parse_measurements();
+
+ # The $file object currently thinks the measurement
+ # section is the start of the data. Re-parse to find
+ # the DATA section.
+ $self->parse_header();
+ last HEADER_LINE;
+ }
+
+ # Data block is the first parsable header row; seek to the
+ # top of the content (DATA) section, drop out of the
+ # header loop.
+ elsif ( $data_block_type =~ m/extracted [ ] data/ixms ) {
+ $data_section_line = $line;
+ seek( $input_fh, $content_start, 0 )
+ or croak("Error seeking in data file: $!\n");
+ last HEADER_LINE;
+ }
+ }
+ }
+ return ( $measurements, $measurement_headings, $data_section_line );
+}
+
+sub _fix_imagene3 : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $content_start = tell($input_fh);
+
+ # Check the header for image/channel/fluorophore info. Set the
+ # input_fh to the top of the DATA section. Detect measurements if
+ # present. If we ever get these files with meaningful channel
+ # information we would incorporate it in this method call.
+ my ( $measurements, $measurement_headings, $data_section_line )
+ = $self->_parse_imagene3_header( $content_start );
+
+ unless ( defined($data_section_line) && $data_section_line =~ /\A begin /ixms ) {
+ die("Error: Unable to parse extracted data section headings.");
+ }
+
+ # Sort out the Control vs. Experimental columns here, modify the QT names.
+ my (undef, @data_sections) = grep { $_ } split /\t/, $data_section_line;
+ my %is_index = map { $_ => 1 } @{ $self->get_index_columns() };
+ my %qt_processed;
+ my $headings = $self->get_column_headings();
+ DATA_HEADING:
+ for ( my $i = 0; $i < @{ $headings }; $i++ ) {
+ next DATA_HEADING if ($is_index{$i} || ! $headings->[$i]);
+ my $section_num = $qt_processed{ $headings->[$i] } || 0;
+ if ( my $section = $data_sections[$section_num] ) {
+ $qt_processed{ $headings->[$i] }++;
+ $headings->[$i] = "$section $headings->[$i]";
+ }
+ else {
+ die("Error: More duplicate column headings than there are data sections.");
+ }
+ }
+
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ my %data_headings = map { $_ => 1 } @{ $self->get_column_headings() };
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ # Drop columns without headings.
+ my @stripped_slice = grep { $headings->[$_] } @$slice;
+ $slice = \@stripped_slice;
+
+ # N.B. this won't work if the index columns are ever allowed to
+ # contain q{}.
+ my @stripped_headings = grep { $_ } @{ $headings };
+ $self->set_column_headings( \@stripped_headings );
+
+ DATA_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+
+ # Skip over empty lines, end on any "end" or "begin" markers.
+ next DATA_LINE if ( $line =~ m/\A \s* \z/ixms );
+ last DATA_LINE if ( $line =~ m/\A (begin|end) [ ]/ixms );
+
+ my @line_array = split /\t/, $line;
+
+ my $metacolumn = $line_array[ $self->get_index_columns()->[0] ];
+ my $metarow = $line_array[ $self->get_index_columns()->[1] ];
+ my $column = $line_array[ $self->get_index_columns()->[2] ];
+ my $row = $line_array[ $self->get_index_columns()->[3] ];
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ # Add measurement columns where they're missing from the data section
+ # FIXME we want to use the $measurements value preferentially for ImaGene3.
+ if ( my $header_row
+ = $measurements->{"$metacolumn.$metarow.$column.$row"} ) {
+ foreach my $measurement ( sort keys %$header_row ) {
+ push( @$new_line, $header_row->{$measurement} )
+ unless ( $data_headings{$measurement} );
+ }
+ }
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ # Add the measurement headings, if we have them. FIXME sort out
+ # the logic here to preferentially use $measurements headings.
+ foreach my $measurement ( sort @$measurement_headings ) {
+ push( @{ $self->get_column_headings() }, $measurement )
+ unless ( $data_headings{$measurement} );
+ }
+
+ # Check that there's no data we're missing
+ FOOTER_LINE:
+ while ( my $line = <$input_fh> ) {
+ if ( $line =~ m/\S/xms )
+ { # Any non-whitespace
+ print {$self->get_error_fh()} (
+ "WARNING: Possible data after END DATA marker is being discarded.\n"
+ );
+ last FOOTER_LINE;
+ }
+ }
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _fix_codelink : PRIVATE {
+
+ my ( $self ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $self->get_linebreak_type();
+
+ my $input_fh = $self->get_filehandle()
+ or croak("Error: Unable to retrieve filehandle.");
+
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ my $fixed_fh = IO::File->new_tmpfile;
+
+ # Set up an array slice base to use for the stripped array (faster
+ # than strip_discards()).
+ my ( $slice, $lastheading ) = $self->_slice_minus_discards;
+
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/$RE_LINE_BREAK//xms;
+
+ # Apparently CodeLink can have empty lines at the end.
+ next if $line =~ m/$RE_EMPTY_STRING/xms;
+
+ my @line_array = split /\t/, $line;
+
+ my $row = $line_array[ $self->get_index_columns()->[0] ];
+ my $column = $line_array[ $self->get_index_columns()->[1] ];
+
+ # We assume here that CodeLink only prints their arrays in single
+ # blocks; I suspect this may have to change FIXME.
+ my $metarow = 1;
+ my $metacolumn = 1;
+
+ my @rowslice = ( @$slice, ( $lastheading + 1 ) .. $#line_array );
+ my $new_line = [ @line_array[@rowslice] ];
+
+ unshift( @$new_line, $metacolumn, $metarow, $column, $row );
+
+ {
+ no warnings qw(uninitialized);
+ print $fixed_fh ( join( "\t", @$new_line ), $INPUT_RECORD_SEPARATOR );
+ }
+
+ }
+
+ seek( $fixed_fh, 0, 0 ) or croak("Error seeking in data file: $!\n");
+
+ $self->_genericize_fileinfo();
+
+ return $fixed_fh;
+}
+
+sub _genericize_fileinfo : PRIVATE {
+
+ my $self = shift;
+
+ my $new_headings
+ = strip_discards( $self->get_index_columns(), $self->get_column_headings() );
+
+ unshift( @$new_headings, 'MetaColumn', 'MetaRow', 'Column', 'Row' );
+
+ $self->set_index_columns( [ 0, 1, 2, 3 ] );
+ $self->set_format_type('Generic');
+ $self->set_column_headings( \@$new_headings );
+
+ return;
+}
+
+sub _reporter_genericize_fileinfo : PRIVATE {
+
+ my $self = shift;
+
+ my $new_headings
+ = strip_discards( $self->get_index_columns(), $self->get_column_headings() );
+
+ unshift( @$new_headings, 'Reporter Identifier' );
+
+ $self->set_index_columns( [0] );
+ $self->set_format_type('FGEM');
+ $self->set_column_headings( \@$new_headings );
+
+ return;
+}
+
+sub _compseq_genericize_fileinfo : PRIVATE {
+
+ my $self = shift;
+
+ my $new_headings
+ = strip_discards( $self->get_index_columns(), $self->get_column_headings() );
+
+ unshift( @$new_headings, 'CompositeSequence Identifier' );
+
+ $self->set_index_columns( [0] );
+
+ # Not FGEM_CS as otherwise this crashes in DataFile::Parser.
+ $self->set_format_type('FGEM');
+ $self->set_column_headings( \@$new_headings );
+
+ return;
+}
+
+=head2 Accessor methods
+
+=over 2
+
+=item set_row_count
+
+Setter method for the total row count.
+
+=item get_row_count
+
+Getter method for the total row count.
+
+=item increment_row_count
+
+Adds one (or the passed value) to the overall row count. Returns the
+new row count.
+
+=item get_parse_errors
+
+Getter method for the total number of parse errors.
+
+=item increment_parse_errors
+
+Adds one (or the passed value) to the overall parsing error
+count. Returns the new error count.
+
+=item get_not_null
+
+The number of data file cells which are not null relating to known
+QTs.
+
+=item increment_not_null
+
+This is an incremental counter for the number of data file cells which
+are not null relating to known QTs. Returns the new "not null" count.
+
+=item set_hyb_identifier
+
+Setter method for the Tab2MAGE or MIAMExpress hybridization identifier
+associated with a raw or normalized data file. Does not apply to FGEM
+files.
+
+=item get_hyb_identifier
+
+Getter method for the Tab2MAGE or MIAMExpress hybridization identifier
+associated with a raw or normalized data file. Does not apply to FGEM
+files.
+
+=item set_hyb_sysuid
+
+Setter method for the internal MIAMExpress SYSUID value associated
+with a raw or normalized data file. Does not apply to FGEM files.
+
+=item get_hyb_sysuid
+
+Getter method for the internal MIAMExpress SYSUID value associated
+with a raw or normalized data file. Does not apply to FGEM files.
+
+=item set_array_design_id
+
+Setter method for an identifier linking the data file to an array
+design. For Tab2MAGE this identifier is the ArrayExpress array
+accession number. For MIAMExpress this identifier is the internal
+ArrayExpress Oracle database identifier for the array design.
+
+=item get_array_design_id
+
+Getter method for an identifier linking the data file to an array
+design. For Tab2MAGE this identifier is the ArrayExpress array
+accession number. For MIAMExpress this identifier is the internal
+ArrayExpress Oracle database identifier for the array design.
+
+=item set_ded_identifier
+
+Setter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.
+
+=item get_ded_identifier
+
+Getter method for the MAGE identifier string representing the
+DesignElementDimension which has been associated with the file.
+
+=item set_array_design
+
+Setter method for the ArrayDesign object associated with this data
+file. See L<ArrayExpress::Datafile::ArrayDesign> for information on
+this class.
+
+=item get_array_design
+
+Getter method for the Array Design object associated with this data
+file.
+
+=item set_data_metrics
+
+The set_data_metrics method is a setter method for a hashref
+which relates actual column heading to datatype, scale and
+subclass. The keys are QT names as defined in
+L<ArrayExpress::Datafile::QT_list>. Note that the returned hashref
+should only have daughter hashrefs as values, and so if no
+datatype,scale or subclass info is available for a column heading
+(e.g. MetaRow, MetaColumn) then that coumn should not be
+represented. In practice, this method should only return information
+on the QTs for a single software type (see
+$self->check_column_headings).
+
+=item get_data_metrics
+
+Getter method for data metrics hashref (see set_data_metrics).
+
+=item set_intensity_vector
+
+Setter method for a hashref linking a datafile row identifier
+(e.g. "1.1.4.1") to a measured data value. Used in Pearson correlation
+coefficient calculation.
+
+=item get_intensity_vector
+
+Getter method for a hashref linking a datafile row identifier
+(e.g. "1.1.4.1") to a measured data value. Used in Pearson correlation
+coefficient calculation.
+
+=item set_index_columns
+
+Setter method for an arrayref describing the array indices of the
+coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.
+
+=item get_index_columns
+
+Getter method for an arrayref describing the array indices of the
+coordinate columns (MetaColumn, MetaRow etc.) in
+$self->get_column_headings.
+
+=item set_column_headings
+
+Setter method for the actual column headings found in the data file
+(arrayref).
+
+=item get_column_headings
+
+Getter method for the actual column headings found in the data file
+(arrayref).
+
+=item set_heading_qts
+
+Setter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated QT
+will appear multiple times (as for example, in a FGEM data file).
+
+=item get_heading_qts
+
+Getter method for an arrayref containing a list of the actual
+recognized QTs in the file. This is not a uniqued list - a repeated QT
+will appear multiple times (as for example, in a FGEM data file).
+
+=item set_heading_hybs
+
+Setter method for an arrayref containing a list of (potential) hyb ids
+derived from the column headings of a FGEM file. These are checked
+elsewhere.
+
+=item get_heading_hybs
+
+Getter method for an arrayref containing a list of (potential) hyb ids
+derived from the column headings of a FGEM file. These are checked
+elsewhere.
+
+=item add_fail_columns
+
+Method which adds the passed argument to a list of column headings
+which are unrecognized.
+
+=item get_fail_columns
+
+Returns an arrayref listing the unrecognized column headings (uniqued
+and sorted).
+
+=item add_fail_hybs
+
+Method which adds the passed argument to a list of unrecognized
+hybridization identifiers parsed from FGEM column headings which are
+unrecognized. Hybridization identifiers are either the Tab2MAGE or
+MIAMExpress user-supplied names for the hybridizations.
+
+=item get_fail_hybs
+
+Returns an arrayref listing the unrecognised hyb identifiers (uniqued
+and sorted).
+
+=item set_is_exp
+
+Setter method for boolean flag indicating whether the file is an EXP file or not.
+
+=item get_is_exp
+
+Getter method for EXP file flag.
+
+=item get_is_binary
+
+Getter method for binary file flag.
+
+=item set_is_miamexpress
+
+Setter method for boolean flag indicating whether the file is part of
+a MIAMExpress submission, for which slightly different validation
+rules are used. More typically the is_miamexpress argument to new() is used.
+
+=item get_is_miamexpress
+
+Getter method for MIAMExpress file flag.
+
+=item add_factor_value
+
+Mutator method for the experimental factor values associated with the
+file. Takes (category, value) as an argument, adds them to the list.
+
+=item get_factor_value
+
+Getter method for factor values associated with the file; returns a
+hashref in the form:
+
+ {category => [value1, value2, ...], ...}
+
+=item set_ded_type
+
+Setter method for the type of DesignElementDimension associated with
+the file (Feature, Reporter or CompositeSequence).
+
+=item get_ded_type
+
+Getter method for the type of DesignElementDimension associated with
+the file.
+
+=item set_format_type
+
+Setter method for the format type (e.g., Affymetrix, GenePix, BlueFuse
+etc.). See L<ArrayExpress::Curator::Config> for the enumerated types.
+
+=item get_format_type
+
+Getter method for the format type.
+
+=item set_data_type
+
+Setter method for the data type (e.g., raw, normalized,
+transformed). See L<ArrayExpress::Curator::Config> for the enumerated
+types. Also allowed is the 'EXP' file type (Affymetrix).
+
+=item get_data_type
+
+Getter method for the data type.
+
+=item set_qt_type
+
+Setter method for the QT type associated with the file. This is
+derived from the software names defined in
+L<ArrayExpress::Datafile::QT_list>.
+
+=item get_qt_type
+
+Getter method for the QT type associated with the file.
+
+=item set_path
+
+Setter method for the full filesystem path of the data file.
+
+=item get_path
+
+Getter method for the full filesystem path of the data file.
+
+=item set_name
+
+Setter method for the name of the file. Also sets $self->set_path if
+it has not been otherwise set. Note that this is not the same
+behaviour as the "name" argument to the object constructor, which does
+not change the path at all.
+
+=item get_name
+
+Getter method for the name of the file.
+
+=item set_target_filename
+
+Setter method for the name of the output stripped data file.
+
+=item get_target_filename
+
+Getter method for the name of the output stripped data file.
+
+=item get_linebreak_type
+
+Getter method for the line-ending character(s). Can be C<\n>, C<\r\n>,
+C<\r> or C<Unknown>.
+
+=item get_line_format
+
+Getter method for the line-ending format (Mac, Unix, DOS or
+Unknown). Used in reports.
+
+=item set_exp_data
+
+Setter method for storing the output of the $self->parse_exp_file
+method.
+
+=item get_exp_data
+
+Getter method for retrieving the output of the
+$self->parse_exp_file method.
+
+=item set_mage_qtd
+
+Setter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object associated
+with the file.
+
+=item get_mage_qtd
+
+Getter method for the
+Bio::MAGE::BioAssayData::QuantitationTypeDimension object associated
+with the file.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
+
diff --git a/lib/ArrayExpress/Datafile/Affymetrix.pm b/lib/ArrayExpress/Datafile/Affymetrix.pm
new file mode 100644
index 0000000..4ccc367
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix.pm
@@ -0,0 +1,166 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix data files.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Affymetrix.pm 1857 2007-12-16 19:53:50Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Affymetrix.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix - a factory class for generating
+Affymetrix data file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+
+ $cel->parse();
+
+ $cel->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix file formats. CEL, CHP and EXP formats are supported, with
+limited CDF parsing. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use Scalar::Util qw(openhandle);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+);
+
+sub make_parser {
+
+ my ( $self, $file ) = @_;
+
+ unless ( -r $file ) {
+ croak(qq{Error: Cannot read input file "$file".});
+ }
+
+ my %dispatch = (
+ qr/\.CEL \z/ixms => 'ArrayExpress::Datafile::Affymetrix::CELFactory',
+ qr/\.CDF \z/ixms => 'ArrayExpress::Datafile::Affymetrix::CDFFactory',
+ qr/\.CHP \z/ixms => 'ArrayExpress::Datafile::Affymetrix::CHPFactory',
+ qr/\.EXP \z/ixms => 'ArrayExpress::Datafile::Affymetrix::EXPFactory',
+ );
+
+ my $class;
+ DISPATCH:
+ while ( my ( $ext, $target ) = each %dispatch ) {
+ if ( $file =~ $ext ) {
+ $class = $target;
+ last DISPATCH;
+ }
+ }
+ unless ( $class ) {
+ croak("Error: Unrecognized filename extension: $file");
+ }
+
+ # Hand off to the appropriate subclass. Allow Perl::Critic to ignore this line:
+ ## no critic ProhibitStringyEval
+ eval "require $class";
+ ## use critic ProhibitStringyEval
+ if ( $@ ) {
+ confess("Error loading subclass $class: $@");
+ }
+ my $fac = $class->new();
+
+ return $fac->make_parser($file);
+}
+
+sub read_magic : RESTRICTED {
+
+ my ( $self, $input ) = @_;
+
+ my $fh;
+ if ( openhandle($input) ) {
+ $fh = $input;
+ }
+ else {
+ $fh = IO::File->new( $input, '<' )
+ or croak("Unable to open CDF file $input : $!\n");
+ }
+
+ binmode($fh);
+ sysseek( $fh, 0, 0 )
+ or croak(
+ qq{Error rewinding filehandle for "magic" integer check : $!\n});
+
+ my $magic = get_integer($fh);
+
+ return wantarray ? ( $magic, $fh ) : $magic;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDF.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
new file mode 100644
index 0000000..5703ed6
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
@@ -0,0 +1,136 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CDF.pm 2023 2008-04-13 11:25:46Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CDF.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CDF.pm - CDF data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::CDF );
+
+=head1 DESCRIPTION
+
+This module is an abstract superclass used in parsing and export of
+data from Affymetrix CDF files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+=over 2
+
+=item get_num_qc_cells()
+
+The number of cells on the chip dedicated to QC measurements.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDF;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+);
+
+my %num_qc_cells : ATTR( :name<num_qc_cells>, :default<undef> );
+
+sub parse {
+
+ my ( $self ) = @_;
+
+ my $input = $self->get_input();
+
+ my $fh;
+ if ( openhandle($input) ) {
+ $fh = $input;
+ }
+ else {
+ $fh = IO::File->new( $input, '<' )
+ or croak("Unable to open CDF file $input : $!\n");
+ }
+
+ binmode($fh);
+ sysseek( $fh, 0, 0 )
+ or croak("Error rewinding filehandle for input: $!\n");
+
+ my $magic = get_integer($fh);
+
+ unless ($magic == $self->get_required_magic()) {
+ croak("Error: Incorrect parser class used for CDF type ($magic)");
+ }
+
+ $self->parse_cdf($fh);
+
+ return;
+}
+
+sub get_probeset_ids {
+
+ # Returns a list of probeset identifiers, in order.
+
+ my ( $self ) = @_;
+
+ my $data = $self->get_data();
+
+ return [ map { $_->{name} } @{ $data } ];
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
new file mode 100644
index 0000000..acdad49
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
@@ -0,0 +1,201 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: GDAC_CDF.pm 2081 2008-06-14 22:00:45Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: GDAC_CDF.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::GDAC_CDF.pm - GDAC CDF data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF;
+
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF->new({
+ input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CDF files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most accessors and methods are implemented in the superclass; see
+L<ArrayExpress::Datafile::Affymetrix::CDF>.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF;
+use base 'ArrayExpress::Datafile::Affymetrix::CDF';
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(openhandle);
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+);
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_required_magic(1178878811);
+
+ return;
+}
+
+#############################
+# GDAC CDF specific methods #
+#############################
+
+sub parse_cdf : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ binmode( $fh, ":crlf" );
+ seek( $fh, 0, 0 ) or croak("Error rewinding filehandle for input: $!\n");
+
+ my $label = <$fh>;
+ $label =~ s/[\r\n]* \z//xms;
+ unless ( $label eq '[CDF]' ) {
+ croak("Error: Unrecognized CDF file format: $label\n");
+ }
+
+ my $version = <$fh>;
+
+ # We just want the version string.
+ $version =~ s{\A Version= ([^\r\n]*) [\r\n]*}{$1}xms;
+ unless ( $version eq 'GC3.0' ) {
+ croak("Error: Unrecognized CDF file version: $version\n");
+ }
+ $self->set_version($version);
+
+ my $line = q{};
+ until ( $line eq '[Chip]' ) { $line = $self->_get_line($fh); }
+
+ $self->set_chip_type( ( split /=/, $self->_get_line($fh) )[1] );
+ $self->set_num_columns( ( split /=/, $self->_get_line($fh) )[1] );
+ $self->set_num_rows( ( split /=/, $self->_get_line($fh) )[1] );
+ $self->set_num_cells( ( split /=/, $self->_get_line($fh) )[1] );
+ my $max_cell = ( ( split /=/, $self->_get_line($fh) )[1] );
+ $self->set_num_qc_cells( ( split /=/, $self->_get_line($fh) )[1] );
+
+ # Here we skip all the QC info for now
+
+ my $data;
+ $data->[ $self->get_num_cells() - 1 ] = {};
+
+ # Generate a mapping of unit names and numbers
+ foreach my $cell (@$data) {
+
+ my ( $unitno, $unitname );
+ until ( $line =~ m{\[ Unit \d+ \]}xms ) {
+ unless ( defined( $line = <$fh> ) ) {
+ croak("Premature end of CDF file.");
+ }
+ }
+
+ until ( ( $unitname ) = ( $line =~ m{\A Name= ([^\r\n]+) [\r\n]* \z}xms ) ) {
+ unless ( defined( $line = <$fh> ) ) {
+ croak("Premature end of CDF file.");
+ }
+ }
+ until ( ( $unitno ) = ( $line =~ m{\A UnitNumber= (\d+) [\r\n]* \z}xms ) ) {
+ unless ( defined( $line = <$fh> ) ) {
+ croak("Premature end of CDF file.");
+ }
+ }
+
+ # Expression CDFs don't keep the name in the unit, but in the blocks.
+ if ( $unitname eq 'NONE' ) {
+ $unitname = undef;
+ until ( $line =~ m!\[Unit($unitno)_Block\d+\]! ) {
+ unless ( defined( $line = <$fh> ) ) {
+ croak("Premature end of CDF file.");
+ }
+ }
+ until ( ($unitname) = ( $line =~ m/^Name=([^\r\n]+)[\r\n]*$/ ) ) {
+ unless ( defined( $line = <$fh> ) ) {
+ croak("Premature end of CDF file.");
+ }
+ }
+ }
+
+ unless ($unitname) {
+ croak("Error: No name for unit $unitno.\n");
+ }
+ $cell->{cell_no} = $unitno;
+ $cell->{name} = $unitname;
+ }
+ $self->set_data($data);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
new file mode 100644
index 0000000..0eb10d0
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
@@ -0,0 +1,226 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: XDA_CDF.pm 2023 2008-04-13 11:25:46Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: XDA_CDF.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF.pm - XDA CDF data
+file parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF;
+
+ my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF->new({
+ input => 'HG-U133A.cdf',
+ });
+ $cdf->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix XDA
+CDF files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most parsing methods and accessors are implemented in the
+superclasses. See L<ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF> for
+information.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF;
+use base 'ArrayExpress::Datafile::Affymetrix::CDF';
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+);
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_required_magic(67);
+
+ return;
+}
+
+############################
+# XDA CDF specific methods #
+############################
+
+sub parse_cdf : RESTRICTED {
+
+ # NB quite a lot of stuff is being discarded here. This relieves
+ # memory overhead, but we may want to add some things back at some
+ # stage.
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ binmode($fh);
+ sysseek( $fh, 0, 0 )
+ or croak("Error rewinding filehandle for input: $!\n");
+
+ my $magic = get_integer($fh);
+ unless ( $magic == 67 ) {
+ croak("Error: Unrecognized CDF type: $magic\n");
+ }
+
+ # Header parsing
+ $self->set_version( get_integer($fh) );
+ $self->set_num_columns( get_unsigned_short($fh) );
+ $self->set_num_rows( get_unsigned_short($fh) );
+ $self->set_num_cells( get_integer($fh) ); # cells == probesets
+ $self->set_num_qc_cells( get_integer($fh) );
+
+ # header reseq_ref_seq
+ my $reseq_ref_seq = get_ascii( $fh, get_integer($fh) );
+
+ # Initialize $data. Points to an array of hashes
+ my $data;
+ $data->[ $self->get_num_cells() - 1 ] = {};
+
+ # Initialize $qc_data. Points to an array of hashes. FIXME discarded
+ # at the moment.
+ my $qc_data;
+ $qc_data->[ $self->get_num_qc_cells() - 1 ] = {};
+
+ # Get probeset names. This is what we're mainly interested in here.
+ foreach my $cell (@$data) {
+ $cell->{name} = get_ascii( $fh, 64 );
+
+ # Remove trailing zeroes (non-ASCII data).
+ $cell->{name} =~ s/\x00* \z//xms;
+ }
+
+ foreach my $qc_cell (@$qc_data) {
+ get_integer($fh);
+ } # qc_cell filepos
+ foreach my $cell (@$data) { get_integer($fh) } # cell filepos
+
+ foreach my $qc_cell (@$qc_data) {
+ get_unsigned_short($fh); # qc_cell type
+ my $num_probes = get_integer($fh); # qc_cell num_probes
+
+ for ( 0 .. ( $num_probes - 1 ) ) { # We just discard all this for now.
+
+ get_unsigned_short($fh); # probe x
+ get_unsigned_short($fh); # probe y
+ get_unsigned_char($fh); # probe length
+
+ # NB docs say this is unsigned but they appear to be wrong:
+ get_signed_char($fh); # probe match_flag
+ get_unsigned_char($fh); # probe bkd_flag
+
+ }
+ }
+
+ foreach my $cell (@$data) {
+
+ get_unsigned_short($fh); # cell type
+ get_unsigned_char($fh); # cell direction
+ get_integer($fh); # cell num_atoms
+ my $num_blocks = get_integer($fh);
+ get_integer($fh); # cell num_cells
+ $cell->{cell_no} = get_integer($fh); # corresponds to probe set number
+ get_unsigned_char($fh); # cell cells_per_atom
+
+ foreach my $block ( @{ $cell->{block} }[ 0 .. ( $num_blocks - 1 ) ] )
+ {
+
+ get_integer($fh); # block num_atoms
+ my $num_cells = get_integer($fh);
+ get_unsigned_char($fh); # block cells_per_atom
+ get_unsigned_char($fh); # block direction
+ get_integer($fh); # block atom1_pos
+ get_integer($fh); # block atom2_pos
+ $block->{name} = get_ascii( $fh, 64 );
+
+ my @cells;
+ foreach my $cell ( @cells[ 0 .. ( $num_cells - 1 ) ] ) {
+
+ get_integer($fh); # cell atom_no
+ get_unsigned_short($fh); # cell x
+ get_unsigned_short($fh); # cell y
+
+ # Relative to seq for reseq cells:
+ get_integer($fh); # cell index_pos
+ get_ascii( $fh, 1 ); # cell probe_base
+ get_ascii( $fh, 1 ); # cell target_base
+
+ }
+ }
+ }
+
+ $self->set_data($data);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
new file mode 100644
index 0000000..45ee500
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
@@ -0,0 +1,130 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CDFFactory.pm 1857 2007-12-16 19:53:50Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CDFFactory.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CDFFactory - a factory class for generating
+Affymetrix CDF file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CDFFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::CDFFactory->new();
+
+ my $cdf = $fac->make_parser( 'Data1.CDF' );
+
+ $cdf->parse();
+
+ $cdf->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix CDF files. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CDFFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+
+sub make_parser {
+
+ my ( $self, $input ) = @_;
+
+ my ( $magic, $fh ) = $self->read_magic( $input );
+
+ # FIXME it would be good to derive this automatically from what's
+ # available.
+ my %dispatch = (
+ 1178878811 => 'ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF',
+ 67 => 'ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF',
+ );
+
+ my $class;
+ unless ( $class = $dispatch{ $magic } ) {
+ croak("Error: Unrecognized CDF file type: $magic");
+ }
+
+ # Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+ ## no critic ProhibitStringyEval
+ eval "require $class";
+ ## use critic ProhibitStringyEval
+ if ( $@ ) {
+ confess("Error loading subclass $class: $@");
+ }
+ return $class->new({
+ input => $fh,
+ });
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CEL.pm b/lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
new file mode 100644
index 0000000..d530dea
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
@@ -0,0 +1,294 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CEL.pm 2023 2008-04-13 11:25:46Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CEL.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CEL.pm - CEL data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CEL;
+
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL->new({
+ input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+
+=head1 DESCRIPTION
+
+This module implements an abstract superclass used in parsing and
+export of data from Affymetrix CEL files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+=over 2
+
+=item parse_header()
+
+This method will take the C<input> attribute and parse only the header
+metadata. Note that for older CEL file formats, the numbers of masked,
+outlier or modified cells are not set by this method, since these
+values are embedded in the main body of the data.
+
+=item export($fh)
+
+This method takes a filehandle and prints the parsed expression data
+out to it. The QuantitationTypeDimension and DesignElementDimension of
+the resulting matrix are given by the C<get_qtd> and C<get_ded> methods,
+respectively.
+
+=item get_ded($chip_type)
+
+This method takes an optional string argument representing the chip type (e.g.,
+"HG-U133A") and uses it to generate a reference to an array of Feature
+identifiers arranged in the order that the B<export> method outputs
+them (i.e., a DesignElementDimension). The chip type can be derived in
+a number of ways; if EXP files are available, for instance, the
+$exp->get_chip_type() method should return an appropriate string. If this
+argument is not supplied then the $cel->get_chip_type() method
+is used to derive a value; note however that that method relies
+on parsing an undocumented tag and as such it may fail.
+
+=item get_num_masked()
+
+The number of masked cells.
+
+=item get_num_outliers()
+
+The number of outlier cells.
+
+=item get_num_modified()
+
+The number of modified cells (this should always be zero).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CEL;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+);
+
+my %dedimension : ATTR( :set<ded>, :default<[]> );
+my %num_masked : ATTR( :name<num_masked>, :default<undef> );
+my %num_outliers : ATTR( :name<num_outliers>, :default<undef> );
+my %num_modified : ATTR( :name<num_modified>, :default<undef> );
+
+# Standard CEL QTD
+Readonly my $AFFY_QTD_CEL => [
+ qw(
+ CELX
+ CELY
+ CELIntensity
+ CELIntensityStdev
+ CELPixels
+ CELOutlier
+ CELMask
+ )
+];
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_data_storage('HASH');
+
+ # Set the stardard qtd for CEL data.
+ $self->set_headings($AFFY_QTD_CEL);
+
+ return;
+}
+
+sub parse {
+
+ my ( $self ) = @_;
+
+ # Parse_header returns the filehandle set to the top of the main data.
+ my $fh = $self->parse_header();
+
+ $self->parse_cel_body($fh);
+
+ return;
+}
+
+sub parse_header {
+
+ my ( $self ) = @_;
+
+ my $fh = $self->get_filehandle();
+
+ binmode($fh);
+ sysseek( $fh, 0, 0 )
+ or croak(
+ qq{Error rewinding filehandle for "magic" integer check : $!\n});
+
+ $self->set_magic( get_integer($fh) );
+
+ unless ($self->get_magic() == $self->get_required_magic()) {
+ croak("Error: Incorrect parser class used for CEL type ("
+ . $self->get_magic() . ")");
+ }
+
+ $self->parse_cel_header($fh);
+
+ return $fh; # Filehandle now set for reading main data body.
+}
+
+sub export { # Now in full tab-delimited glory...
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ my $data = $self->get_data();
+
+ foreach my $coord ( sort keys %$data ) {
+ print $fh ( join( q{}, $coord, "\t", $data->{$coord}, "\n" ) );
+ }
+
+ return;
+}
+
+sub get_ded {
+
+ my ( $self, $chip_type ) = @_;
+
+ ref $chip_type and confess( 'Bad parameters passed to method' );
+
+ unless ( scalar @{ $dedimension{ident $self} } ) {
+
+ # If no chip type passed, try and figure it out on our own.
+ $chip_type ||= $self->get_chip_type();
+
+ # Basic sanity check here.
+ croak("No chip type information available to CEL->get_ded()\n")
+ unless $chip_type;
+
+ my $data = $self->get_data();
+
+ my @features;
+
+ # Initialize the array; prevents memory fragmentation (maybe).
+ $#features = scalar( grep { defined $_ } values %$data ) - 1;
+
+ my $counter = 0;
+ foreach my $coord ( sort keys %$data ) {
+
+ my ( $colno, $rowno ) = split /\t/, $coord;
+ $features[$counter] = "Affymetrix:Feature:"
+ . $chip_type
+ . ":Probe($colno,$rowno)";
+
+ $counter++;
+ }
+
+ $dedimension{ident $self} = \@features;
+
+ }
+
+ return $dedimension{ident $self};
+}
+
+sub parse_v3_header_tags : RESTRICTED {
+
+ # NB this method is also useful to the CELv4 parser.
+
+ my ( $self, $tagstr ) = @_;
+
+ defined( $tagstr ) or confess( 'Bad parameters passed to method' );
+
+ my @lines = split /[\r\n]+/, $tagstr;
+
+ my %tag;
+ foreach my $line ( @lines ) {
+ my ( $key, $value ) = split /=/, $line, 2;
+ $tag{ $key } = $value;
+ }
+
+ # CEL v4 parsing also uses this, but we don't want to override the
+ # 'official' values.
+ $self->set_num_columns( $tag{ 'Cols' } ) unless $self->get_num_columns();
+ $self->set_num_rows( $tag{ 'Rows' } ) unless $self->get_num_rows();
+ $self->set_algorithm( $tag{ 'Algorithm' } || 'Unknown' );
+
+ # The following is an undocumented tag, but seems to be consistently
+ # used and is extremely useful. It's likely to be used by GDAC
+ # Exporter (Affy) as it's the only way of identifying the chip type
+ # if only the CEL file is available.
+ my ($chip_type) = ( $tag{ 'DatHeader' } =~ m{[ ] ([^ .]*).1sq [ ]}ixms );
+ $self->set_chip_type($chip_type);
+
+ my @params = split /;/, $tag{ 'AlgorithmParameters' };
+
+ my $parameter_hash = {};
+ foreach my $param (@params) {
+ my ( $key, $value ) = split /:/, $param;
+ $parameter_hash->{$key} = $value;
+ }
+
+ $self->add_parameters($parameter_hash);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
new file mode 100644
index 0000000..a5685dd
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
@@ -0,0 +1,281 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CELv3.pm 2021 2008-04-09 09:55:25Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CELv3.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CEL::CELv3.pm - CELv3 data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv3;
+
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv3->new({
+ input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CEL (v3) files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. See
+L<ArrayExpress::Datafile::Affymetrix::CEL> for details.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CEL::CELv3;
+use base 'ArrayExpress::Datafile::Affymetrix::CEL';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_required_magic(1279607643);
+
+ return;
+}
+
+##########################
+# CELv3-specific methods #
+##########################
+
+sub parse_cel_header : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ # Set fh to plain text parsing, and rewind the file
+ binmode( $fh, ":crlf" );
+ seek( $fh, 0, 0 ) or croak("Error rewinding filehandle for input: $!\n");
+
+ # Read the data in as a series of arrays/hashes.
+
+ ##########
+ # HEADER #
+ ##########
+ my $label = $self->_get_line($fh);
+ unless ( $label eq '[CEL]' ) {
+ croak("Error: unknown CEL file format: $label\n");
+ }
+
+ $self->set_version( ( split /\=/, $self->_get_line($fh) )[1] );
+
+ my $line = q{};
+ until ( $line =~ m/\A \[HEADER\]/xms || ! defined( $line ) ) {
+ $line = $self->_get_line($fh);
+ }
+
+ my $header_tags = q{};
+
+ LINE:
+ while ( $line = <$fh> ) {
+ last LINE if ( $line =~ m/\A \s* \z/xms );
+ $header_tags .= $line;
+ }
+
+ $self->parse_v3_header_tags($header_tags);
+
+ #############
+ # INTENSITY #
+ #############
+ until ( $line =~ m/\A \[INTENSITY\]/xms || ! defined( $line ) ) {
+ $line = $self->_get_line($fh);
+ }
+
+ $self->set_num_cells( ( split /\=/, $self->_get_line($fh) )[1] );
+
+ if ( $self->get_num_cells()
+ != ( $self->get_num_rows() * $self->get_num_columns() ) ) {
+ carp(
+ "Format error: number of cells does not agree with row and column numbers"
+ );
+ }
+
+ $self->add_stats(
+ { 'Number of Cells' => $self->get_num_cells(),
+ 'Rows' => $self->get_num_rows(),
+ 'Columns' => $self->get_num_columns(),
+ }
+ );
+
+ return;
+}
+
+sub parse_cel_body : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ # Our filehandle should be set to the main data column headings line.
+ if ( $self->_get_line($fh) ne "CellHeader=X\tY\tMEAN\tSTDV\tNPIXELS" ) {
+ croak("Error: unrecognized CEL [INTENSITY] column headings.\n");
+ }
+
+ my $data;
+ my $line = q{};
+
+ LINE:
+ while ( $line = $self->_get_line($fh) ) {
+
+ last LINE if ( $line =~ m/\A \s* \z/xms );
+
+ # Strip unnecessary whitespace.
+ $line =~ s/\A [ ]*//xms;
+ $line =~ s/[ ]* \z//xms;
+ $line =~ s/[ ]* \t [ ]*/\t/gxms;
+
+ my @line_array = split /\t/, $line;
+
+ my $colno = $line_array[0];
+ my $rowno = $line_array[1];
+
+ $data->{"$colno\t$rowno"} = sprintf( "%.2f\t%.2f\t%d\tfalse\tfalse",
+ @line_array[ 2 .. 4 ] );
+
+ }
+
+ #########
+ # MASKS #
+ #########
+ until ( $line =~ m/\A \[MASKS\]/xms || ! defined( $line ) ) {
+ $line = $self->_get_line($fh);
+ }
+
+ $self->set_num_masked( ( split /\=/, $self->_get_line($fh) )[1] );
+
+ $self->add_stats( { 'Number Cells Masked' => $self->get_num_masked() } );
+
+ if ( $self->_get_line($fh) ne "CellHeader=X\tY" ) {
+ croak("Error: unrecognized CEL [MASKS] column headings.\n");
+ }
+
+ LINE:
+ while ( $line = $self->_get_line($fh) ) {
+
+ last LINE if ( $line =~ m/\A \s* \z/xms );
+
+ my @line_array = split /\t/, $line;
+
+ my $colno = $line_array[0];
+ my $rowno = $line_array[1];
+
+ $data->{"$colno\t$rowno"} =~ s{\t false \z}{\ttrue}xms;
+ }
+
+ ############
+ # OUTLIERS #
+ ############
+ until ( $line =~ m/\A \[OUTLIERS\]/xms || ! defined( $line ) ) {
+ $line = $self->_get_line($fh);
+ }
+
+ $self->set_num_outliers( ( split /\=/, $self->_get_line($fh) )[1] );
+
+ $self->add_stats( { 'Number Outlier Cells' => $self->get_num_outliers() } );
+
+ if ( $self->_get_line($fh) ne "CellHeader=X\tY" ) {
+ croak("Error: unrecognized CEL [OUTLIERS] column headings.\n");
+ }
+
+ LINE:
+ while ( $line = $self->_get_line($fh) ) {
+
+ last LINE if ( $line =~ m/\A \s* \z/xms );
+
+ my @line_array = split /\t/, $line;
+
+ my $colno = $line_array[0];
+ my $rowno = $line_array[1];
+
+ $data->{"$colno\t$rowno"} =~ s{\t false \t (?= \w+ \z)}
+ {\ttrue\t}xms;
+ }
+
+ ############
+ # MODIFIED #
+ ############
+ until ( $line =~ m/\A \[MODIFIED\]/xms || ! defined( $line ) ) {
+ $line = $self->_get_line($fh);
+ }
+
+ $self->set_num_modified( ( split /\=/, $self->_get_line($fh) )[1] );
+
+ $self->add_stats( { 'Number Cells Modified' => $self->get_num_modified() } );
+
+ warn( "Warning: ignoring " . $self->get_num_modified() . " cells.\n" )
+ if $self->get_num_modified();
+
+ if ( $self->_get_line($fh) ne "CellHeader=X\tY\tORIGMEAN" ) {
+ croak("Error: unrecognized CEL [MODIFIED] column headings.\n");
+ }
+
+ # Insert the data table into the object
+ $self->set_data($data);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
new file mode 100644
index 0000000..939db7f
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
@@ -0,0 +1,298 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CELv4.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CELv4.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CEL::CELv4.pm - CELv4 data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CEL::CELv4;
+
+ my $cel = ArrayExpress::Datafile::Affymetrix::CEL::CELv4->new({
+ input => 'mydatafile.CEL',
+ });
+ $cel->parse();
+ $cel->export($output_fh);
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix XDA
+CEL (v4) files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+Most parsing methods and accessors are implemented in the
+superclasses. See L<ArrayExpress::Datafile::Affymetrix::CEL::CELv3> for
+information. The following attributes are specific to CELv4 files:
+
+=over 2
+
+=item get_cell_margin()
+
+The cell margin.
+
+=item get_num_subgrids()
+
+The number of subgrids used on the chip (N.B. this is currently
+UNTESTED).
+
+=item get_subgrids()
+
+A reference to a hash containing subgrid information (N.B. this is
+currently UNTESTED).
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CEL::CELv4;
+use base 'ArrayExpress::Datafile::Affymetrix::CEL';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw(openhandle);
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+);
+
+# Subgrids are v4 CEL only UNTESTED FIXME.
+my %num_subgrids : ATTR( :name<num_subgrids>, :default<undef> );
+my %subgrids : ATTR( :name<subgrids>, :default<{}> );
+my %cell_margin : ATTR( :name<cell_margin>, :default<undef> );
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_required_magic(64);
+
+ return;
+}
+
+#################
+# CELv4 methods #
+#################
+
+sub parse_v4_parameters : PRIVATE {
+
+ my ( $self, $paramstr ) = @_;
+
+ ref $paramstr and confess( 'Bad parameters passed to method' );
+
+ my @params = split /[;\s]+/, $paramstr;
+
+ my $parameter_hash;
+ foreach my $param (@params) {
+ my ( $key, $value ) = split /[:=]/, $param;
+ $parameter_hash->{$key} = $value;
+ }
+
+ $self->add_parameters($parameter_hash);
+
+ return;
+}
+
+sub parse_cel_header : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ # Binary-mode filehandle, rewind the file
+ binmode( $fh, ":raw" );
+ sysseek( $fh, 0, 0 )
+ or croak("Error rewinding filehandle for input: $!\n");
+
+ # We will read the data in as a series of arrays/hashes:
+
+ my $magic = get_integer($fh);
+ unless ( $magic == 64 ) {
+ croak("Error: Unrecognized CEL type: $magic\n");
+ }
+
+ # Header parsing
+ $self->set_version( get_integer($fh) );
+ $self->set_num_columns( get_integer($fh) );
+ $self->set_num_rows( get_integer($fh) );
+ $self->set_num_cells( get_integer($fh) ); # col x row - tested below
+
+ $self->add_stats(
+ { 'Number of Cells' => $self->get_num_cells(),
+ 'Rows' => $self->get_num_rows(),
+ 'Columns' => $self->get_num_columns(),
+ }
+ );
+
+ if ( $self->get_num_cells() != ( $self->get_num_rows() * $self->get_num_columns() ) ) {
+ carp(
+ "Format error: number of cells does not agree with row and column numbers"
+ );
+ }
+
+ $self->parse_v3_header_tags( get_ascii( $fh, get_integer($fh) ) );
+
+ $self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+
+ $self->parse_v4_parameters( get_ascii( $fh, get_integer($fh) ) );
+
+ $self->set_cell_margin( get_integer($fh) );
+ $self->set_num_outliers( get_DWORD($fh) );
+ $self->set_num_masked( get_DWORD($fh) );
+ $self->set_num_subgrids( get_integer($fh) );
+
+ $self->add_stats(
+ { 'Number Cells Masked' => $self->get_num_masked(),
+ 'Number Outlier Cells' => $self->get_num_outliers(),
+ }
+ );
+
+ return;
+}
+
+sub parse_cel_body : RESTRICTED {
+
+ # Parses CEL files (passed diff test against old CEL file format,
+ # so more-or-less okay, but see subgrid comment at end)
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ # Data starts here. These are sizable arrays. Our filehandle should
+ # have been set to the start of the main data loop section.
+
+ # Read intensities
+ # $self->get_data() points to a hash of data values
+ my $data;
+
+ # Y is rows, X is columns.
+ for ( my $rowno = 0; $rowno < $self->get_num_rows(); $rowno++ ) {
+ for ( my $colno = 0; $colno < $self->get_num_columns(); $colno++ ) {
+
+ $data->{"$colno\t$rowno"} = sprintf(
+ "%.2f\t%.2f\t%d\tfalse\tfalse",
+ round( get_float($fh), 1 ),
+ round( get_float($fh), 1 ),
+ get_unsigned_short($fh),
+ );
+
+ }
+ }
+
+ # Read masked cells
+ for ( my $cell = 0; $cell < $self->get_num_masked(); $cell++ ) {
+ my $colno = get_unsigned_short($fh);
+ my $rowno = get_unsigned_short($fh);
+ $data->{"$colno\t$rowno"} =~ s{\t false \z}{\ttrue}xms;
+ }
+
+ # Read outlier cells
+ for ( my $cell = 0; $cell < $self->get_num_outliers(); $cell++ ) {
+ my $colno = get_unsigned_short($fh);
+ my $rowno = get_unsigned_short($fh);
+ $data->{"$colno\t$rowno"} =~ s{\t false \t (?= \w+ \z)}
+ {\ttrue\t}xms;
+ }
+
+ # NB we should parse out the subgrids here FIXME
+ # Note - the following is untested code
+ my $subgrids;
+ for ( my $cell = 0; $cell < $self->get_num_subgrids(); $cell++ ) {
+ $subgrids->[$cell]{row} = get_integer($fh);
+ $subgrids->[$cell]{column} = get_integer($fh);
+
+ $subgrids->[$cell]{ul_x} = get_float($fh); # pixel coords
+ $subgrids->[$cell]{ul_y} = get_float($fh);
+ $subgrids->[$cell]{ur_x} = get_float($fh);
+
+ # typo in spec fixed here, I hope
+ $subgrids->[$cell]{ur_y} = get_float($fh);
+ $subgrids->[$cell]{ll_x} = get_float($fh);
+ $subgrids->[$cell]{ll_y} = get_float($fh);
+ $subgrids->[$cell]{lr_x} = get_float($fh);
+
+ # typo in spec fixed here, I hope
+ $subgrids->[$cell]{lr_y} = get_float($fh);
+
+ $subgrids->[$cell]{left_pos} = get_integer($fh); # cell positions
+ $subgrids->[$cell]{top_pos} = get_integer($fh);
+ $subgrids->[$cell]{right_pos} = get_integer($fh);
+ $subgrids->[$cell]{bottom_pos} = get_integer($fh);
+
+ # We currently have no information on the format of these entries in
+ # text files, so we don't print them out FIXME
+ }
+
+ $self->set_subgrids($subgrids);
+
+ # Insert the data table into the object
+ $self->set_data($data);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
new file mode 100644
index 0000000..9621093
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
@@ -0,0 +1,131 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CELFactory.pm 1984 2008-03-03 17:47:18Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CELFactory.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CELFactory - a factory class for generating
+Affymetrix CEL file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CELFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::CELFactory->new();
+
+ my $cel = $fac->make_parser( 'Data1.CEL' );
+
+ $cel->parse();
+
+ $cel->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix CEL files. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CELFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+
+sub make_parser {
+
+ my ( $self, $input ) = @_;
+
+ my ( $magic, $fh ) = $self->read_magic( $input );
+
+ # FIXME it would be good to derive this automatically from what's
+ # available.
+ my %dispatch = (
+ 1279607643 => 'ArrayExpress::Datafile::Affymetrix::CEL::CELv3',
+ 64 => 'ArrayExpress::Datafile::Affymetrix::CEL::CELv4',
+ 315 => 'ArrayExpress::Datafile::Affymetrix::Calvin::CEL',
+ );
+
+ my $class;
+ unless ( $class = $dispatch{ $magic } ) {
+ croak("Error: Unrecognized CEL file type: $magic");
+ }
+
+ # Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+ ## no critic ProhibitStringyEval
+ eval "require $class";
+ ## use critic ProhibitStringyEval
+ if ( $@ ) {
+ confess("Error loading subclass $class: $@");
+ }
+ return $class->new({
+ filehandle => $fh,
+ });
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
new file mode 100644
index 0000000..37f5a1c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
@@ -0,0 +1,375 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHP.pm 2025 2008-04-15 23:10:14Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CHP.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP - GDAC CHP data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP );
+
+=head1 DESCRIPTION
+
+Abstract superclass for parsing and export of data from Affymetrix CHP
+files.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+=over 2
+
+=item C<parse_header()>
+
+This method will take the C<input> attribute and parse only the header
+metadata.
+
+=item C<export($fh, $cdf)>
+
+This method takes a filehandle and prints the parsed expression data
+out to it. The method also requires a pre-parsed CDF object to be
+passed to it. The QuantitationTypeDimension and DesignElementDimension
+of the resulting matrix are given by the C<get_qtd> and C<get_ded>
+methods, respectively.
+
+=item C<get_ded($cdf, $chip_type)>
+
+This method takes a pre-parsed CDF object, and an optional string
+argument representing the chip type (e.g., "HG-U133A"). The method
+uses these arguments to generate a reference to an array of
+CompositeSequence identifiers, arranged in the order that the
+B<export> method outputs them (i.e., a DesignElementDimension). The
+chip type can be derived in a number of ways; if EXP files are
+available, for instance, the $exp->get_chip_type() method should
+return an appropriate string. If this argument is not supplied then
+the chip_type() methods for the CHP and CDF objects are inspected.
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+);
+
+my %affy_qtd_chp : ATTR( :get<affy_qtd_chp> );
+my %affy_qtd_chp_comparison : ATTR( :get<affy_qtd_chp_comparison> );
+my %affy_qtd_chp_snp : ATTR( :get<affy_qtd_chp_snp> );
+my %affy_qtd_chp_snp100 : ATTR( :get<affy_qtd_chp_snp100> );
+
+sub BUILD {
+
+ my ( $self, $id, $args ) = @_;
+
+ # Standard CHP QTDs.
+ $affy_qtd_chp{ident $self} = [
+ qw(
+ ProbeSetName
+ CHPPairs
+ CHPPairsUsed
+ CHPSignal
+ CHPDetection
+ CHPDetectionPvalue
+ )
+ ];
+
+ $affy_qtd_chp_comparison{ident $self} = [
+ qw(
+ ProbeSetName
+ CHPPairs
+ CHPPairsUsed
+ CHPSignal
+ CHPDetection
+ CHPDetectionPvalue
+ CHPCommonPairs
+ CHPSignalLogRatio
+ CHPSignalLogRatioLow
+ CHPSignalLogRatioHigh
+ CHPChange
+ CHPChangePvalue
+ )
+ ];
+
+ $affy_qtd_chp_snp{ident $self} = [
+ qw(
+ ProbeSetName
+ CHPAllele
+ CHPAllelePvalue
+ CHPAlleleRAS1
+ CHPAlleleRAS2
+ )
+ ];
+
+ $affy_qtd_chp_snp100{ident $self} = [
+ qw(
+ ProbeSetName
+ CHPAllele
+ CHPAllelePvalue
+ CHPAllelePvalueAA
+ CHPAllelePvalueAB
+ CHPAllelePvalueBB
+ CHPAllelePvalueNoCall
+ )
+ ];
+
+ return;
+}
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_required_magic(1701733703);
+
+ return;
+}
+
+sub parse {
+
+ my ( $self ) = @_;
+
+ my $fh = $self->parse_header();
+
+ $self->parse_chp($fh);
+
+ return;
+}
+
+sub parse_header {
+
+ my ( $self ) = @_;
+
+ my $fh = $self->get_filehandle();
+
+ binmode($fh);
+ sysseek( $fh, 0, 0 )
+ or croak(
+ qq{Error rewinding filehandle for "magic" integer check : $!\n});
+
+ $self->set_magic( get_integer($fh) );
+
+ unless ($self->get_magic() == $self->get_required_magic()) {
+ croak("Error: Incorrect parser class used for CHP type ("
+ . $self->get_magic() . ")");
+ }
+
+ $self->parse_chp_header($fh);
+
+ return $fh; # Filehandle now set for reading main data body.
+}
+
+sub export { # CONFIRMED BY DIFF
+
+ my ( $self, $fh, $cdf ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+ $cdf->isa('ArrayExpress::Datafile::Affymetrix::Parser')
+ or confess( 'Bad parameters passed to method' );
+
+ # Basic sanity check here. If the parse hasn't been run yet, do so.
+ unless ( scalar @{ $cdf->get_probeset_ids() } ) {
+ $cdf->parse();
+ }
+
+ my $data = $self->get_data();
+ my $probesets = $cdf->get_probeset_ids();
+ my @qtd = @{ $self->get_headings() };
+
+ my @detection = ( q{Present}, q{Marginal}, q{Absent}, q{No Call}, );
+ my @change = (
+ q{null},
+ q{Increase},
+ q{Decrease},
+ q{Marginal Increase},
+ q{Marginal Decrease},
+ q{No change},
+ q{No call},
+ );
+ my @allele = (
+ q{NoCall}, q{NoCall}, q{NoCall}, q{NoCall}, q{NoCall}, q{NoCall},
+ q{AA}, q{BB}, q{AB}, q{AB_A}, q{AB_B}, q{NoCall},
+ );
+
+ for ( my $i = 0; $i <= $#{$data}; $i++ ) {
+
+ my @line_array;
+
+ carp( "WARNING: No CDF data for CHP unit "
+ . ( $i + 1 )
+ . ". Incorrect CDF file used?\n" )
+ unless $probesets->[$i];
+ push( @line_array, ( $probesets->[$i] || q{} ) ); # ProbeSetName
+
+ # Skip the first item (ProbeSetName).
+ foreach my $qt ( @qtd[ 1 .. $#qtd ] ) {
+
+ my $value = $data->[$i]{$qt};
+
+ QTYPE:
+ { # some reformatting required
+
+ ( $qt eq 'CHPDetection' )
+ && do { $value = $detection[$value] || q{null}; last QTYPE; };
+
+ ( $qt eq 'CHPSignal' )
+ && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPDetectionPvalue' )
+ && do { $value = sprintf( "%.5f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPSignalLogRatio' )
+ && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPSignalLogRatioLow' )
+ && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPSignalLogRatioHigh' )
+ && do { $value = sprintf( "%.1f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPChange' )
+ && do { $value = $change[$value] || q{null}; last QTYPE; };
+
+ ( $qt eq 'CHPChangePvalue' )
+ && do { $value = sprintf( "%.5f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPAllele' )
+ && do { $value = $allele[$value] || q{null}; last QTYPE; };
+
+ ( $qt eq 'CHPAllelePvalue' )
+ && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPAlleleRAS1' )
+ && do { $value = sprintf( "%.4f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPAlleleRAS2' )
+ && do { $value = sprintf( "%.4f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPAllelePvalueAA' )
+ && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPAllelePvalueAB' )
+ && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPAllelePvalueBB' )
+ && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+ ( $qt eq 'CHPAllelePvalueNoCall' )
+ && do { $value = sprintf( "%.6f", $value ); last QTYPE; };
+
+ }
+
+ # Safety net
+ unless ( defined($value) ) { $value = q{} }
+
+ push( @line_array, $value );
+
+ }
+
+ print $fh ( join( "\t", @line_array ), "\n" );
+
+ }
+
+ return;
+}
+
+sub get_ded {
+
+ my ( $self, $cdf, $chip_type ) = @_;
+
+ $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF')
+ or confess( 'Bad parameters passed to method' );
+ ref $chip_type and confess( 'Bad parameters passed to method' );
+
+ # If no chip type passed, try and figure it out on our own
+ $chip_type ||= $self->get_chip_type() || $cdf->get_chip_type();
+
+ # Basic sanity check here.
+ croak("Error: No chip type information available to CHP->get_ded()\n")
+ unless $chip_type;
+
+ # If the parse hasn't been run yet, do so.
+ unless ( scalar @{ $cdf->get_probeset_ids() } ) {
+ $cdf->parse();
+ }
+
+ my $data = $self->get_data();
+ my $probesets = $cdf->get_probeset_ids();
+
+ my @composite_sequences;
+
+ for ( my $i = 0; $i <= $#{$data}; $i++ ) {
+ carp( "WARNING: No CDF data for CHP unit "
+ . ( $i + 1 )
+ . ". Incorrect CDF file used?\n" )
+ unless $probesets->[$i];
+ push( @composite_sequences,
+ "Affymetrix:CompositeSequence:$chip_type:"
+ . ( $probesets->[$i] || q{} ) );
+ }
+
+ return \@composite_sequences;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
new file mode 100644
index 0000000..5d4aa0d
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
@@ -0,0 +1,229 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPv12.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CHPv12.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::CHPv12 - CHPv12 data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv12;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv12->new({
+ input => 'mydata.CHP',
+ });
+
+ $chp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CHP (v12) files.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::CHPv12;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+ get_hexadecimal
+);
+
+###########################
+# CHPv12-specific methods #
+###########################
+
+# flag to reduce method call overhead.
+my $comparison_data_found;
+
+sub parse_expression_cell : RESTRICTED {
+
+ # NB quite a lot of stuff is being discarded here. This relieves
+ # memory overhead, but we may want to add some things back at some
+ # stage.
+
+ my ( $self, $fh, $cell ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ $cell->{'CHPPairs'} = get_integer($fh);
+ $cell->{'CHPPairsUsed'} = get_integer($fh);
+
+ # N.B. CHP versions 8 and 12 are interchangable throughout
+ # this section, but the v8 float format defeated me, which
+ # is why version 8 gets such short shrift.
+ for ( 1 .. 5 ) { get_integer($fh); } # Unused
+
+ $cell->{'CHPDetectionPvalue'} = round( get_float($fh), 5 );
+
+ get_float($fh); # Unused
+
+ $cell->{'CHPSignal'} = round( get_float($fh), 1 );
+ $cell->{'CHPDetection'} = get_integer($fh);
+
+ #################################################################
+ # ***** N.B. *****
+ #
+ # Most of the rest of the parsing below is unused, but is left
+ # here for reference and because removing it would really screw
+ # up the main data value parsing
+ #################################################################
+
+ # Pairs section is not used
+ # Initialize the sub-array
+ my $pairs;
+ for ( my $pair = 0; $pair < $cell->{'CHPPairs'}; $pair++ ) {
+ $pairs->[$pair] = {};
+ }
+
+ foreach my $pair (@$pairs) {
+
+ get_float($fh); # pair background
+ get_integer($fh); # 2 = used, 0 = not # pair used
+ get_integer($fh); # pair PM_Xcoord
+ get_integer($fh); # pair PM_Ycoord
+ get_float($fh); # pair PM_intensity
+ get_float($fh); # pair PM_stdev
+ get_integer($fh); # pair PM_pixels
+ get_signed_char($fh); # pair PM_masked
+ get_signed_char($fh); # pair PM_outlier
+ get_integer($fh); # pair MM_Xcoord
+ get_integer($fh); # pair MM_Ycoord
+ get_float($fh); # pair MM_intensity
+ get_float($fh); # pair MM_stdev
+ get_integer($fh); # pair MM_pixels
+ get_signed_char($fh); # pair MM_masked
+ get_signed_char($fh); # pair MM_outlier
+ }
+
+ # This part below has now been tested.
+
+ my $comparison_exists = get_integer($fh);
+
+ if ($comparison_exists) {
+
+ # set the qtd for ExpressionStat data with comparison data
+ $self->set_headings( $self->get_affy_qtd_chp_comparison() )
+ unless $comparison_data_found;
+ $comparison_data_found++;
+
+ $cell->{'CHPCommonPairs'} = get_integer($fh);
+
+ for ( 1 .. 3 ) { get_integer($fh); } # Unused
+
+ $cell->{'CHPChange'} = get_integer($fh);
+
+ # This is not actually exported at the moment
+ $cell->{baseline_absent} = get_signed_char($fh);
+
+ get_signed_char($fh); # Unused
+ get_integer($fh); # Unused
+ get_integer($fh); # Unused
+
+ my $signal_log_ratio_high_1000
+ = get_integer($fh); # divide by 1000 to get float value
+ $cell->{'CHPSignalLogRatioHigh'}
+ = round( ( $signal_log_ratio_high_1000 / 1000 ), 1 );
+
+ get_integer($fh); # Unused
+ get_integer($fh); # Unused
+
+ my $signal_log_ratio_1000
+ = get_integer($fh); # divide by 1000 to get float value
+ $cell->{'CHPSignalLogRatio'}
+ = round( ( $signal_log_ratio_1000 / 1000 ), 1 );
+
+ get_integer($fh); # Unused
+
+ my $signal_log_ratio_low_1000
+ = get_integer($fh); # divide by 1000 to get float value
+ $cell->{'CHPSignalLogRatioLow'}
+ = round( ( $signal_log_ratio_low_1000 / 1000 ), 1 );
+
+ $cell->{'CHPChangePvalue'} = round( get_float($fh), 5 );
+
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
new file mode 100644
index 0000000..3af2b3b
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
@@ -0,0 +1,208 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPv13.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CHPv13.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::CHPv13 - CHPv13 data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv13;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv13->new({
+ input => 'mydata.CHP',
+ });
+
+ $chp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CHP (v12) files.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::CHPv13;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+ get_hexadecimal
+);
+
+###########################
+# CHPv13-specific methods #
+###########################
+
+# flag to reduce method call overhead
+my $comparison_data_found;
+
+sub parse_expression_cell : RESTRICTED {
+
+ # NB quite a lot of stuff is being discarded here. This relieves
+ # memory overhead, but we may want to add some things back at some
+ # stage.
+
+ my ( $self, $fh, $cell ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ $cell->{'CHPPairs'} = get_integer($fh);
+ $cell->{'CHPPairsUsed'} = get_integer($fh);
+
+ # N.B. CHP versions 8 and 12 are interchangable throughout
+ # this section, but the v8 float format defeated me, which
+ # is why version 8 gets such short shrift.
+ get_integer($fh); # cell num_pairs_used2
+
+ $cell->{'CHPDetectionPvalue'} = round( get_float($fh), 5 );
+
+ $cell->{'CHPSignal'} = round( get_float($fh), 1 );
+ $cell->{'CHPDetection'} = get_integer($fh);
+
+ #################################################################
+ # ***** N.B. *****
+ #
+ # Most of the rest of the parsing below is unused, but is left
+ # here for reference and because removing it would really screw
+ # up the main data value parsing
+ #################################################################
+
+ # Pairs section is not used
+ # Initialize the sub-array
+ my $pairs;
+ for ( my $pair = 0; $pair < $cell->{'CHPPairs'}; $pair++ ) {
+ $pairs->[$pair] = {};
+ }
+
+ foreach my $pair (@$pairs) {
+
+ get_float($fh); # pair background
+ get_integer($fh); # 2 = used, 0 = not # pair used
+
+ get_unsigned_short($fh); # pair PM_Xcoord
+ get_unsigned_short($fh); # pair PM_Ycoord
+ get_unsigned_short($fh); # pair MM_Xcoord
+ get_unsigned_short($fh); # pair MM_Ycoord
+ }
+
+ # This part below has now been tested.
+
+ my $comparison_exists = get_integer($fh);
+
+ if ($comparison_exists) {
+
+ # set the qtd for ExpressionStat data with comparison data
+ $self->set_headings( $self->get_affy_qtd_chp_comparison() )
+ unless $comparison_data_found;
+ $comparison_data_found++;
+
+ $cell->{'CHPCommonPairs'} = get_integer($fh);
+ $cell->{'CHPChange'} = get_integer($fh);
+
+ # This is not actually exported at the moment
+ $cell->{baseline_absent} = get_signed_char($fh);
+
+ my $signal_log_ratio_high_1000
+ = get_integer($fh); # divide by 1000 to get float value
+ $cell->{'CHPSignalLogRatioHigh'}
+ = round( ( $signal_log_ratio_high_1000 / 1000 ), 1 );
+
+ get_integer($fh); # Unused
+
+ my $signal_log_ratio_1000
+ = get_integer($fh); # divide by 1000 to get float value
+ $cell->{'CHPSignalLogRatio'}
+ = round( ( $signal_log_ratio_1000 / 1000 ), 1 );
+
+ my $signal_log_ratio_low_1000
+ = get_integer($fh); # divide by 1000 to get float value
+ $cell->{'CHPSignalLogRatioLow'}
+ = round( ( $signal_log_ratio_low_1000 / 1000 ), 1 );
+
+ $cell->{'CHPChangePvalue'} = round( get_float($fh), 5 );
+
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
new file mode 100644
index 0000000..08940a5
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
@@ -0,0 +1,115 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPv8.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CHPv8.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::CHPv8 - CHPv8 data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::CHPv8;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::CHPv8->new({
+ input => 'mydata.CHP',
+ });
+
+ $chp->parse_header();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix GDAC
+CHP (v8) files. Note that only the data file header can be parsed
+using this module.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::CHPv8;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Class::Std;
+
+###########################
+# CHPv8-specific methods #
+###########################
+
+sub parse_chp : RESTRICTED {
+
+ # N.B. CHP versions 8 and 12 are very similar in many respects,
+ # but the v8 float format defeated me, which is why version 8 gets
+ # such short shrift.
+
+ # We can't parse CHPv8 files fully. See the GDAC_CHP superclass
+ # for CHPv8 header parsing.
+
+ my ( $self, $fh ) = @_;
+
+ # Caller has to trap this in an eval. We really don't like these
+ # files.
+ croak("Error: CHP file version 8 not fully supported.\n");
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
new file mode 100644
index 0000000..2399827
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
@@ -0,0 +1,379 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: GDAC_CHP.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: GDAC_CHP.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP - GDAC CHP data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP );
+
+=head1 DESCRIPTION
+
+Abstract superclass for parsing and export of data from Affymetrix CHP
+files. For older GDAC CHP files this class also implements a file
+header parsing method, which can be used to determine the version of
+the file (e.g. v8, v12, v13).
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+=over 2
+
+=item C<parse_header()>
+
+This method will take the C<input> attribute and parse only the header
+metadata.
+
+=item C<export($fh, $cdf)>
+
+This method takes a filehandle and prints the parsed expression data
+out to it. The method also requires a pre-parsed CDF object to be
+passed to it. The QuantitationTypeDimension and DesignElementDimension
+of the resulting matrix are given by the C<get_qtd> and C<get_ded>
+methods, respectively.
+
+=item C<get_ded($cdf, $chip_type)>
+
+This method takes a pre-parsed CDF object, and an optional string
+argument representing the chip type (e.g., "HG-U133A"). The method
+uses these arguments to generate a reference to an array of
+CompositeSequence identifiers, arranged in the order that the
+B<export> method outputs them (i.e., a DesignElementDimension). The
+chip type can be derived in a number of ways; if EXP files are
+available, for instance, the $exp->get_chip_type() method should
+return an appropriate string. If this argument is not supplied then
+the chip_type() methods for the CHP and CDF objects are inspected.
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP';
+
+use strict;
+use warnings;
+
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+ get_hexadecimal
+);
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_required_magic(1701733703);
+
+ return;
+}
+
+#############################
+# GDAC CHP specific methods #
+#############################
+
+sub _parse_gdac_chp_params : PRIVATE {
+
+ my ( $self, $paramstr ) = @_;
+
+ ref $paramstr and confess( 'Bad parameters passed to method' );
+
+ my @params = split /\s+/, $paramstr;
+
+ my $parameter_hash;
+ foreach my $param (@params) {
+ my ( $name, $value ) = split /=/, $param;
+ $parameter_hash->{$name} = $value;
+ }
+
+ $self->add_parameters($parameter_hash);
+
+ return;
+}
+
+sub _parse_gdac_chp_stats : PRIVATE {
+
+ my ( $self, $statstr ) = @_;
+
+ ref $statstr and confess( 'Bad parameters passed to method' );
+
+ my @statlists = split /[\s]+/, $statstr;
+
+ my $stats_hash;
+ foreach my $statlist (@statlists) {
+ my ( $prefix, $valuestring ) = split /=/, $statlist;
+ my @stats = split /,/, $valuestring;
+ foreach my $stat (@stats) {
+ my ( $value, $name ) = reverse( split /:/, $stat );
+ my $fullkey = $name ? "$prefix $name" : $prefix;
+ $stats_hash->{$fullkey} = $value;
+ }
+ }
+
+ $self->add_stats($stats_hash);
+
+ return;
+}
+
+sub parse_chp_header : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ # Set fh to binary parsing, and rewind the file
+ binmode( $fh, ":raw" );
+ sysseek( $fh, 0, 0 )
+ or croak("Error rewinding filehandle for input: $!\n");
+
+ # Read the data in as a series of arrays/hashes
+ my $label = get_ascii( $fh, 22 );
+ unless ( $label eq 'GeneChip Sequence File' ) {
+ croak("Error: unknown CHP file format: $label\n");
+ }
+
+ # Set the stardard qtd for ExpressionStat data.
+ $self->set_headings( $self->get_affy_qtd_chp() );
+
+ # Store this in a scalar var for speed of access.
+ my $version = get_integer($fh);
+
+ $self->set_version( $version );
+
+ if ( $version == 8 ) {
+
+ # Get what we can (version, algorithm, parameters, cell
+ # numbers and chip_type). We don't attempt to parse the actual data.
+ $self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+ $self->_parse_gdac_chp_params( get_ascii( $fh, get_integer($fh) ) );
+
+ $self->set_num_columns( get_integer($fh) );
+ $self->set_num_rows( get_integer($fh) );
+ $self->set_num_cells( get_integer($fh) ); # Total
+
+ my $max_cell_no = get_integer($fh); # Deprecated
+ my $num_qc_cells = get_integer($fh);
+
+ # Seek to the chip_type record.
+ my $skipped = ( $max_cell_no + $self->get_num_cells() ) * 2 * 4;
+ sysseek($fh, $skipped, 1); # FIXME find a way to die meaningfully here.
+
+ # header probe_array_type; NB contains a lot of junk.
+ my $probe_array_type = get_ascii( $fh, 256 );
+ $probe_array_type = ( split /[\n\r]+/, $probe_array_type )[0];
+ $probe_array_type =~ s{\A ([\w-]*) .*}{$1}xms;
+ $self->set_chip_type($probe_array_type);
+
+ # And that's all we can parse for now.
+ return;
+ }
+ elsif ( $version != 12 && $version != 13 ) {
+
+ # We have no idea what this might be.
+ croak("Fatal error: unrecognized CHP file version $version");
+ }
+
+ $self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+
+ # Algorithm version (skip for version 8 CHP)
+ get_ascii( $fh, get_integer($fh) );
+
+ $self->_parse_gdac_chp_params( get_ascii( $fh, get_integer($fh) ) );
+
+ # Next line skipped for version 8 CHP
+ $self->_parse_gdac_chp_stats( get_ascii( $fh, get_integer($fh) ) );
+
+ $self->set_num_columns( get_integer($fh) );
+ $self->set_num_rows( get_integer($fh) );
+ $self->set_num_cells( get_integer($fh) ); # Total
+
+ return;
+}
+
+sub parse_chp : RESTRICTED { # CONFIRMED BY DIFF
+
+ # NB quite a lot of stuff is being discarded here. This relieves
+ # memory overhead, but we may want to add some things back at some
+ # stage.
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ my $max_cell_no = get_integer($fh); # Deprecated
+ my $num_qc_cells = get_integer($fh);
+
+ if ( $self->get_num_cells > $max_cell_no ) {
+ croak(
+ "Error: Number of probe sets greater than the maximum possible!\n"
+ );
+ }
+
+ # Initialize the array. Makes subsequent steps safer & easier
+ # NB. when num_cells doesn't agree with num_rows*num_cells, it
+ # appears that num_cells is the one to trust. This may not be a
+ # good assumption in future.
+ my $data;
+ $data->[ $self->get_num_cells() - 1 ] = {};
+
+ # Cell numbers are deprecated
+ foreach my $cell (@$data) { get_integer($fh) } # cell number
+ foreach my $cell (@$data) { get_integer($fh) } # cell num_pairs2
+ for ( $self->get_num_cells() .. ( $max_cell_no - 1 ) ) {
+ get_integer($fh); # Unused
+ }
+
+ foreach my $cell (@$data) { $cell->{type} = get_integer($fh) }
+
+ for ( $self->get_num_cells() .. ( $max_cell_no - 1 ) ) {
+ get_integer($fh);
+ } # Unused
+ foreach my $cell (@$data) { get_integer($fh) } # cell num_probes
+
+ # header probe_array_type; NB contains a lot of junk.
+ my $probe_array_type = get_ascii( $fh, 256 );
+
+ $probe_array_type = ( split /[\n\r]+/, $probe_array_type )[0];
+ $probe_array_type =~ s{\A ([\w-]*) .*}{$1}xms;
+
+ $self->set_chip_type($probe_array_type);
+
+ # header parent_cel_filename; NB contains a lot of junk.
+ my $parent_cel = get_ascii( $fh, 256 );
+
+ # header programmatic_id
+ my $programmatic_id = get_ascii( $fh, get_integer($fh) );
+
+ foreach my $cell (@$data) {
+
+ if ( $cell->{type} == 3 ) { # Expression data
+
+ # This call will vary between versions 12 and 13 of the
+ # CHP file format.
+ $self->parse_expression_cell( $fh, $cell );
+
+ }
+ elsif ( $cell->{type} == 2 ) { # Genotyping data
+
+ croak(
+ "Error: Genotyping data file parsing not yet implemented for CHP v3.\n"
+ );
+
+ }
+ else {
+
+ croak("Error: Cell type $cell->{type} not known.\n");
+
+ }
+ }
+
+ # Insert the data table into the object
+ $self->set_data($data);
+
+ # The next few sections are a work-in-progress
+ my $reseq_length = get_integer($fh);
+
+ if ($reseq_length) {
+
+ croak("Error: Resequencing data file parsing not yet implemented.\n");
+
+ }
+
+ my @qc_cells;
+ foreach my $qc_cell ( @qc_cells[ 0 .. ( $num_qc_cells - 1 ) ] ) {
+
+ my $num_probes = get_integer($fh);
+ get_integer($fh); # qc_cell type
+
+ my @probes;
+ foreach my $probe ( @probes[ 0 .. ( $num_probes - 1 ) ] ) {
+
+ get_integer($fh); # probe X_coord
+ get_integer($fh); # probe Y_coord
+ get_float($fh); # probe intensity
+ get_float($fh); # probe stdev
+ get_integer($fh); # probe pixels
+ get_float($fh); # probe background
+
+ }
+ }
+
+ return;
+}
+
+sub parse_expression_cell : RESTRICTED {
+
+ my ( $self, $fh, $cell ) = @_;
+
+ confess ("Error: Stub method called in abstract superclass.");
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
new file mode 100644
index 0000000..6f7da05
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
@@ -0,0 +1,335 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: XDA_CHP.pm 1906 2008-01-23 10:05:42Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: XDA_CHP.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP - XDA CHP data file
+parsing.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP;
+
+ my $chp = ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP->new({
+ input => 'mydata.CHP',
+ });
+
+ $chp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix XDA
+CHP files.
+
+Please see the L<ArrayExpress::Datafile::Affymetrix::Parser>
+documentation for methods common to all the Affymetrix file classes.
+
+=head1 METHODS
+
+Most methods are implemented in the superclass. Please see
+L<ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP> for details.
+
+=over 2
+
+=pod
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP;
+use base 'ArrayExpress::Datafile::Affymetrix::CHP';
+
+use strict;
+use warnings;
+use Readonly;
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ round
+);
+
+use ArrayExpress::Datafile::Binary qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+ get_hexadecimal
+);
+
+my %results_type : ATTR( :name<results_type>, :default<undef> );
+
+# The number of cells above which a genotyping chip is considered a
+# "100k" chip, rather than a "10k" chip. Currently the former start at
+# around 50k.
+Readonly my $LARGE_CHIP_THRESHOLD => 25000;
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ $self->set_required_magic(65);
+
+ return;
+}
+
+############################
+# XDA_CHP specific methods #
+############################
+
+# This sub parses CHP files. This has now been tested and found to work.
+
+sub parse_chp_header : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ # Binary-mode filehandle, rewind the file.
+ binmode( $fh, ":raw" );
+ sysseek( $fh, 0, 0 )
+ or croak("Error rewinding filehandle for input: $!\n");
+
+ my $magic = get_integer($fh);
+ unless ( $magic == 65 ) {
+ croak("Error: Unrecognized CHP type: $magic\n");
+ }
+
+ # Set the stardard qtd for ExpressionStat data.
+ $self->set_headings( $self->get_affy_qtd_chp() );
+
+ $self->set_version( get_integer($fh) );
+ $self->set_num_columns( get_unsigned_short($fh) );
+ $self->set_num_rows( get_unsigned_short($fh) );
+ $self->set_num_cells( get_integer($fh) ); # Non-QC cells only
+ my $num_qc_cells = get_integer($fh);
+ my $results_type = get_integer($fh);
+
+ $self->set_results_type( $results_type );
+
+ # Fix QTD for Genotyping (SNP) data
+ if ( $results_type == 1 ) {
+ if ( $self->get_num_cells < $LARGE_CHIP_THRESHOLD ) {
+
+ # 10k chip.
+ $self->set_headings( $self->get_affy_qtd_chp_snp() );
+ }
+ else {
+
+ # 100k chip
+ $self->set_headings( $self->get_affy_qtd_chp_snp100() );
+ }
+ }
+
+ my $programmatic_id = get_ascii( $fh, get_integer($fh) );
+ my $cel_filename = get_ascii( $fh, get_integer($fh) );
+ $self->set_chip_type( get_ascii( $fh, get_integer($fh) ) );
+ $self->set_algorithm( get_ascii( $fh, get_integer($fh) ) );
+ my $algorithm_version = get_ascii( $fh, get_integer($fh) );
+
+ # Parameters
+ my $num_params = get_integer($fh);
+ my $parameter_hash;
+ for ( 1 .. $num_params ) {
+
+ # Listed in the file as name, value
+ my $name = get_ascii( $fh, get_integer($fh) );
+ my $value = get_ascii( $fh, get_integer($fh) );
+
+ # Remove stray whitespace from value
+ $value =~ s{\A \s* (.*?) \s* \z}{$1}xms;
+
+ $parameter_hash->{$name} = $value;
+ }
+ $self->add_parameters($parameter_hash);
+
+ # Summary stats
+ my $num_stats = get_integer($fh); # not sure about this (not in the spec)
+ my $stats_hash;
+ for ( 1 .. $num_stats ) {
+
+ # Listed in the file as name, value
+ my $name = get_ascii( $fh, get_integer($fh) );
+ my $value = get_ascii( $fh, get_integer($fh) );
+
+ # Remove stray whitespace from value
+ $value =~ s{\A \s* (.*?) \s* \z}{$1}xms;
+
+ if ( $value =~ m/,/ ) { # Complex values may exist, e.g. Noise
+ foreach my $substat ( split /,/, $value ) {
+ my ( $subname, $subval ) = split /:/, $substat;
+ $stats_hash->{"$name $subname"} = $subval;
+ }
+ }
+ else {
+ $stats_hash->{$name} = $value;
+ }
+ }
+ $self->add_stats($stats_hash);
+
+ return;
+}
+
+sub parse_chp : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ # Background zones
+ my $num_bkd_zones = get_integer($fh);
+ my $bkd_smooth_factor = get_float($fh);
+ for ( 1 .. $num_bkd_zones ) {
+ my $bkd_zone; # discarded at the moment
+ $bkd_zone->{'x'} = get_float($fh);
+ $bkd_zone->{'y'} = get_float($fh);
+ $bkd_zone->{'value'} = get_float($fh);
+ }
+
+ my $results_type = $self->get_results_type();
+
+ # This next is only present in expression results type files:
+ my $expression_analysis_type;
+ if ( $results_type == 0 ) {
+ $expression_analysis_type = get_unsigned_char($fh);
+ if ( $expression_analysis_type == 1
+ || $expression_analysis_type == 3 ) {
+
+ # Set the qtd for ExpressionStat data with comparison data
+ $self->set_headings( $self->get_affy_qtd_chp_comparison() );
+ }
+ }
+
+ my $data_size = get_integer($fh); # The data record size in bytes
+
+ # Initialize the array. Makes subsequent steps safer & easier
+ my $data;
+ $data->[ $self->get_num_cells() - 1 ] = {};
+
+ foreach my $cell (@$data) {
+
+ # Expression results type files (N.B. this is the one we're mainly
+ # interested in.)
+ if ( $results_type == 0 ) {
+
+ $cell->{'CHPDetection'} = get_unsigned_char($fh);
+ $cell->{'CHPDetectionPvalue'} = round( get_float($fh), 5 );
+ $cell->{'CHPSignal'} = round( get_float($fh), 1 );
+ $cell->{'CHPPairs'} = get_unsigned_short($fh);
+ $cell->{'CHPPairsUsed'} = get_unsigned_short($fh);
+
+ if ( $expression_analysis_type == 1
+ || $expression_analysis_type == 3 ) {
+
+ $cell->{'CHPChange'} = get_unsigned_char($fh);
+ $cell->{'CHPChangePvalue'} = round( get_float($fh), 5 );
+ $cell->{'CHPSignalLogRatio'} = round( get_float($fh), 1 );
+ $cell->{'CHPSignalLogRatioLow'} = round( get_float($fh), 1 );
+ $cell->{'CHPSignalLogRatioHigh'} = round( get_float($fh), 1 );
+ $cell->{'CHPCommonPairs'} = get_unsigned_short($fh);
+
+ }
+
+ }
+
+ # Genotyping results type files.
+ elsif ( $results_type == 1 ) {
+
+ $cell->{CHPAllele} = get_unsigned_char($fh);
+ $cell->{CHPAllelePvalue} = get_float($fh);
+
+ # we distinguish between 10k and 100k arrays here:
+ if ( $self->get_num_cells() > $LARGE_CHIP_THRESHOLD ) {
+
+ # 100k arrays
+ $cell->{CHPAllelePvalueAA} = get_float($fh);
+ $cell->{CHPAllelePvalueAB} = get_float($fh);
+ $cell->{CHPAllelePvalueBB} = get_float($fh);
+ $cell->{CHPAllelePvalueNoCall} = get_float($fh);
+
+ }
+ else {
+
+ # 10k arrays
+ $cell->{CHPAlleleRAS1} = get_float($fh);
+ $cell->{CHPAlleleRAS2} = get_float($fh);
+ get_float($fh); # unused?
+ get_float($fh); # unused?
+
+ }
+ }
+
+ # Resequencing results type files.
+ elsif ( $results_type == 2 ) {
+ my $sequence_length = get_integer($fh);
+ $cell->{sequence} = get_ascii( $fh, $sequence_length );
+
+ # This should work. 4 is the integer size.
+ $cell->{base_call_score}
+ = get_float( $fh, ( $data_size - $sequence_length - 4 ) );
+ }
+
+ # Universal results type files.
+ elsif ( $results_type == 3 ) {
+ $cell->{bkd_value} = get_float($fh);
+ }
+ }
+
+ # Insert the data into the object
+ $self->set_data($data);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
new file mode 100644
index 0000000..3bdbf37
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
@@ -0,0 +1,159 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: CHPFactory.pm 1984 2008-03-03 17:47:18Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CHPFactory.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::CHPFactory - a factory class for generating
+Affymetrix CHP file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::CHPFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::CHPFactory->new();
+
+ my $cel = $fac->make_parser( 'Data1.CHP' );
+
+ $cel->parse();
+
+ $cel->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix CHP files. Both old (GDAC) and new (GCOS/XDA) file formats
+can be parsed.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::CHPFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+
+sub make_parser {
+
+ my ( $self, $input ) = @_;
+
+ my ( $magic, $fh ) = $self->read_magic( $input );
+
+ # FIXME it would be good to derive this automatically from what's
+ # available.
+ my %dispatch = (
+ 1701733703 => 'ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP',
+ 65 => 'ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP',
+ 315 => 'ArrayExpress::Datafile::Affymetrix::Calvin::CHP',
+ );
+
+ my $class;
+ unless ( $class = $dispatch{ $magic } ) {
+ croak("Error: Unrecognized CHP file type: $magic");
+ }
+
+ # Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+ ## no critic ProhibitStringyEval
+ eval "require $class";
+ ## use critic ProhibitStringyEval
+ if ( $@ ) {
+ confess("Error loading subclass $class: $@");
+ }
+ my $parser = $class->new({
+ filehandle => $fh,
+ });
+
+ # GDAC CHP files come in at least three different flavours.
+ if ( $parser->isa('ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP') ) {
+ $parser->parse_header();
+ my %gdac_dispatch = (
+ 8 => 'ArrayExpress::Datafile::Affymetrix::CHP::CHPv8',
+ 12 => 'ArrayExpress::Datafile::Affymetrix::CHP::CHPv12',
+ 13 => 'ArrayExpress::Datafile::Affymetrix::CHP::CHPv13',
+ );
+
+ my $class;
+ unless ( $class = $gdac_dispatch{ $parser->get_version() } ) {
+ croak("Error: Unrecognized CHP file version: " . $parser->get_version());
+ }
+
+ # Return the appropriate parser object. Allow Perl::Critic to ignore this line:
+ ## no critic ProhibitStringyEval
+ eval "require $class";
+ ## use critic ProhibitStringyEval
+ if ( $@ ) {
+ confess("Error loading subclass $class: $@");
+ }
+ $parser = $class->new({
+ filehandle => $fh,
+ });
+ }
+
+ return $parser;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm
new file mode 100644
index 0000000..2871b9c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Binary.pm
@@ -0,0 +1,145 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic binary file data parsing functions.used in
+# e.g. Affymetrix file parsing module(s)
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Binary.pm 1985 2008-03-03 18:32:27Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Binary;
+
+use strict;
+use warnings;
+
+use Carp;
+use Readonly;
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ get_unsigned_short
+ get_signed_short
+ get_unsigned_char
+ get_signed_char
+ get_unsigned_integer
+ get_signed_integer
+ get_wchar
+ get_wstring
+ get_string
+ get_datetime
+ get_locale
+ get_network_float
+);
+
+# This constant determines how floats are handled on big-endian systems.
+# NB. it is unclear at this stage whether 64-bit systems will work.
+Readonly my $IS_BIG_ENDIAN => unpack( "h*", pack( "s", 1 ) ) =~ /01/;
+
+#################################
+# Binary data parsing functions #
+#################################
+
+sub get_unsigned_short {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 2 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "n*", $value );
+}
+
+sub get_signed_short {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 2 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "n*", $value );
+}
+
+sub get_unsigned_char {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 1 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "C*", $value );
+}
+
+sub get_signed_char {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 1 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "c*", $value );
+}
+
+sub get_ascii {
+ my ( $fh, $length ) = @_;
+ my $num_bytes = sysread( $fh, my $value, $length );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "a*", $value );
+}
+
+sub get_unsigned_integer {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 4 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "N*", $value );
+}
+
+sub get_signed_integer {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 4 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "N*", $value );
+}
+
+sub get_wchar {
+ my ( $fh, $length ) = @_;
+ my $num_bytes = sysread( $fh, my $value, $length );
+ croak($!) unless defined($num_bytes);
+ return $value;
+}
+
+sub get_wstring {
+ my ( $fh, $total ) = @_;
+ my $length = get_signed_integer( $fh ) * 2;
+ my $value = get_wchar( $fh, $length );
+ if ( $total ) {
+ sysseek( $fh, $total - ( $length + 4 ), 1 );
+ }
+ my $ascii = pack "C*", unpack "n*", $value;
+ return $ascii;
+}
+
+sub get_string {
+ my ( $fh, $total ) = @_;
+ my $length = get_signed_integer( $fh );
+ my $value = get_ascii( $fh, $length );
+ if ( $total ) {
+ sysseek( $fh, $total - ( $length + 4 ), 1 );
+ }
+ return $value;
+}
+
+sub get_datetime {
+ my $fh = shift;
+ return get_wstring( $fh );
+}
+
+sub get_locale {
+ my $fh = shift;
+ return get_wchar( $fh, 14 );
+}
+
+sub get_network_float {
+ my ( $fh ) = @_;
+ my $num_bytes = sysread( $fh, my $value, 4 );
+ croak($!) unless defined($num_bytes);
+
+ my $retval;
+ if ($IS_BIG_ENDIAN) {
+ $retval = unpack 'f*', $value;
+ }
+ else {
+ $retval = unpack "f*", pack "N*", unpack "V*", $value;
+ }
+ return sprintf( "%.5f", $retval );
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm
new file mode 100644
index 0000000..622e794
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CEL.pm
@@ -0,0 +1,125 @@
+#!/usr/bin/env perl
+#
+# $Id: CEL.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::CEL;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Facade);
+
+use Class::Std;
+use Carp;
+use Readonly;
+
+# The parent class knows how to populate these.
+my %num_columns : ATTR( :name<num_columns>, :default<undef> );
+my %num_rows : ATTR( :name<num_rows>, :default<undef> );
+
+Readonly my @DATA_SET_NAMES => qw(Intensity StdDev Pixel Outlier Mask);
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ # Sort out the QTD here.
+ my $data_group = $self->get_data_group(0);
+ my $num_sets = $data_group->get_num_data_sets();
+
+ my @qtd = map { "Affymetrix:QuantitationType:$_" } @DATA_SET_NAMES;
+ $self->set_qtd( \@qtd );
+ $self->set_headings( \@DATA_SET_NAMES );
+
+ return;
+}
+
+sub export {
+
+ my ( $self, $output_fh ) = @_;
+
+ my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+
+ # The systell function doesn't exist, we use this instead.
+ my $init_pos = sysseek( $fh, 0, 1 );
+
+ my $data_group = $self->get_data_group(0);
+ my $num_sets = $data_group->get_num_data_sets();
+
+ # One big data set in memory. Not a good way to do things, but the
+ # alternative is a lot of seeking in the file, which we're not
+ # really set up for. Why does Affy insist on breaking up the CEL
+ # data this way?
+ my ( @data, %outlier, %mask );
+ for ( my $i = 0; $i < $num_sets; $i++ ) {
+
+ my $data_set = $data_group->get_data_set( $i );
+ my $set_name = $data_set->get_name();
+ my $readers = $data_set->get_data_readers();
+
+ sysseek( $fh, $data_set->get_data_table_start(), 0 )
+ or croak("Error seeking in filehandle: $!");
+
+ # N.B. the following assumes that Intensity, StdDev and Pixel
+ # data sets all have the same number and order of design
+ # elements.
+ for ( 1..$data_set->get_num_data_rows() ) {
+
+ if ( $set_name eq 'Outlier' ) {
+ my ( $x, $y ) = map { $_->() } @$readers;
+ $outlier{"$x,$y"}++;
+ }
+ elsif ( $set_name eq 'Mask' ) {
+ my ( $x, $y ) = map { $_->() } @$readers;
+ $mask{"$x,$y"}++ if ( defined $x && defined $y );
+ }
+ else {
+
+ # There should only be one reader per set here, but if
+ # not for some reason the values are joined with '|'.
+ my $row = join("|", map { $_->() } @$readers);
+ push @{ $data[$i] }, $row;
+ }
+ }
+ }
+
+ # Y is rows, X is columns.
+ for ( my $y = 0; $y < $self->get_num_rows(); $y++ ) {
+ for ( my $x = 0; $x < $self->get_num_columns(); $x++ ) {
+ my @values = map { shift( @{ $data[$_] } ) } qw( 0 1 2 );
+ push @values, $outlier{ "$x,$y" } ? 'true' : 'false';
+ push @values, $mask { "$x,$y" } ? 'true' : 'false';
+ print $output_fh (join("\t", @values), "\n");
+ }
+ }
+
+ # Reset the filehandle.
+ sysseek( $fh, $init_pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ return;
+}
+
+sub generate_ded : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ my $chip_type = $self->get_chip_type()
+ or croak("Error: Chip type is not known.");
+
+ my @ded;
+
+ # Y is rows, X is columns.
+ for ( my $y = 0; $y < $self->get_num_rows(); $y++ ) {
+ for ( my $x = 0; $x < $self->get_num_columns(); $x++ ) {
+ push @ded, sprintf(
+ "Affymetrix:Feature:$chip_type:Probe(%d,%d)", $x, $y,
+ );
+ }
+ }
+
+ $self->set_ded( \@ded );
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm
new file mode 100644
index 0000000..2ce3c04
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/CHP.pm
@@ -0,0 +1,83 @@
+#!/usr/bin/env perl
+#
+# $Id: CHP.pm 1982 2008-02-28 22:48:40Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::CHP;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Facade);
+
+use Class::Std;
+use Carp;
+
+sub export {
+
+ my ( $self, $output_fh, $cdf ) = @_;
+
+ my $data_set = $self->get_data_set();
+ my $data_type = $self->get_data_header()->get_data_type();
+
+ # MAS5 results incorporates a value mapping. FIXME how to fall
+ # back to 'No Call'?
+ if ( $data_type eq 'affymetrix-expression-probeset-analysis' ) {
+ $data_set->set_value_mapping(
+ [undef,{
+ 0 => 'Present',
+ 1 => 'Marginal',
+ 2 => 'Absent',
+ },undef,undef,undef,undef]
+ );
+ }
+
+ $data_set->export( $output_fh );
+
+ return;
+}
+
+sub generate_ded : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ my $data_set = $self->get_data_set();
+ my $chip_type = $self->get_chip_type()
+ or croak("Error: Chip type is not known.");
+
+ my $fh = $data_set->get_filehandle() or croak("Error: No filehandle.");
+ my $pos = $data_set->get_data_table_start()
+ or croak("Error: No data table start position.");
+
+ # The systell function doesn't exist, we use this instead.
+ my $init_pos = sysseek( $fh, 0, 1 );
+
+ # Seek to data_table_start, create array of column readers,
+ # and write the return values for each row to $output_fh.
+ sysseek( $fh, $pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ my @ded;
+ my $columns = $data_set->get_data_columns();
+ my $id_col = $columns->[0];
+ my $reader = $id_col->get_reader();
+ my $size = $id_col->get_size();
+ my $other = 0;
+ my $num_cols = scalar( @{ $columns } );
+ for ( my $i = 1; $i < $num_cols; $i++ ) {
+ $other += $columns->[$i]->get_size();
+ }
+ for ( 1..$data_set->get_num_data_rows() ) {
+ my $name = $reader->( $fh, $size );
+ push @ded, "Affymetrix:CompositeSequence:$chip_type:$name";
+ sysseek( $fh, $other, 1 );
+ }
+
+ $self->set_ded( \@ded );
+
+ # Reset the filehandle.
+ sysseek( $fh, $init_pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm
new file mode 100644
index 0000000..e282564
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Component.pm
@@ -0,0 +1,105 @@
+#!/usr/bin/env perl
+#
+# $Id: Component.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Component;
+
+use Class::Std;
+use Carp;
+use Readonly;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::Parameter;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+ get_string
+ get_wstring
+);
+
+my %position : ATTR( :name<position>, :default<undef> );
+my %filehandle : ATTR( :name<filehandle>, :default<undef> );
+my %parameters : ATTR( :name<parameters>, :default<[]> );
+
+# This constant determines how floats are handled on big-endian systems.
+# NB. it is unclear at this stage whether 64-bit systems will work.
+Readonly my $IS_BIG_ENDIAN => unpack( "h*", pack( "s", 1 ) ) =~ /01/;
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $self->get_position() ) {
+ croak("Error: No file position given.\n");
+ }
+ unless ( $self->get_filehandle() ) {
+ croak("Error: No filehandle given.\n");
+ }
+
+ return;
+}
+
+sub parse_parameter : RESTRICTED {
+
+ # This method _does_ have an effect on filehandle position.
+ my ( $self, $fh ) = @_;
+
+ my $pname = get_wstring( $fh );
+ my $pvalue = get_string( $fh );
+ my $ptype = get_wstring( $fh );
+
+ my %mime_map = (
+ 'text/x-calvin-integer-8' => 'c*',
+ 'text/x-calvin-unsigned-integer-8' => 'C*',
+ 'text/x-calvin-integer-16' => 's*',
+ 'text/x-calvin-unsigned-integer-16' => 'n*',
+ 'text/x-calvin-integer-32' => 'N*',
+ 'text/x-calvin-unsigned-integer-32' => 'N*',
+ 'text/plain' => 'a*',
+ 'text/ascii' => 'a*',
+ );
+
+ my $conv_val;
+ if ( $ptype eq 'text/x-calvin-float' ) {
+ if ($IS_BIG_ENDIAN) {
+ $conv_val = unpack 'f*', $pvalue;
+ }
+ else {
+ $conv_val = unpack "f*", pack "N*", unpack "V*", $pvalue;
+ }
+ }
+ elsif (my $packtype = $mime_map{ $ptype }) {
+ $conv_val = (unpack( $packtype, $pvalue ))[0];
+ }
+ else {
+ croak("Unrecognized MIME type: $ptype");
+ }
+
+ if ( $ptype eq 'text/plain' ) {
+ $conv_val = pack "C*", unpack "n*", $conv_val;
+ $conv_val =~ s/\x{0}* \z//xms;
+ }
+ elsif ( $ptype eq 'text/x-calvin-float' ) {
+ $conv_val = sprintf("%.5f", $conv_val);
+ }
+
+ my $param = ArrayExpress::Datafile::Affymetrix::Calvin::Parameter->new({
+ name => $pname,
+ value => $conv_val,
+ type => $ptype,
+ });
+
+ return $param;
+}
+
+sub add_parameters : RESTRICTED {
+
+ my ( $self, @params ) = @_;
+
+ push @{ $parameters{ ident $self } }, @params;
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm
new file mode 100644
index 0000000..6fb6e05
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataColumn.pm
@@ -0,0 +1,68 @@
+#!/usr/bin/env perl
+#
+# $Id: DataColumn.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn;
+
+# Simple class to store column definitions.
+
+use Class::Std;
+use Carp;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+ get_signed_char
+ get_unsigned_char
+ get_signed_short
+ get_unsigned_short
+ get_signed_integer
+ get_unsigned_integer
+ get_network_float
+ get_string
+ get_wstring
+);
+
+my %name : ATTR( :name<name>, :default<undef> );
+my %type : ATTR( :name<type>, :default<undef> );
+my %size : ATTR( :name<size>, :default<undef> );
+my %reader : ATTR( :name<reader>, :default<undef> );
+
+# FIXME some of these may need sprintf-style reformatting.
+my %readermap = (
+ 0 => \&get_signed_char,
+ 1 => \&get_unsigned_char,
+ 2 => \&get_signed_short,
+ 3 => \&get_unsigned_short,
+ 4 => \&get_signed_integer,
+ 5 => \&get_unsigned_integer,
+ 6 => \&get_network_float,
+ 7 => \&get_string,
+ 8 => \&get_wstring,
+);
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $self->get_name() ) {
+ croak("Error: Column name not set.");
+ }
+
+ if ( my $type = $self->get_type() ) {
+ if ( my $coderef = $readermap{$type} ) {
+ $self->set_reader( $coderef );
+ }
+ else {
+ croak("Error: Unrecognized column type $type.");
+ }
+ }
+ else {
+ croak("Error: Column header type not set.");
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm
new file mode 100644
index 0000000..a9c4968
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataGroup.pm
@@ -0,0 +1,105 @@
+#!/usr/bin/env perl
+#
+# $Id: DataGroup.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Component);
+
+use Class::Std;
+use Carp;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataSet;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+ get_signed_integer
+ get_unsigned_integer
+ get_wstring
+);
+
+my %next_group_position : ATTR( :name<next_group_position>, :default<undef> );
+my %name : ATTR( :name<name>, :default<undef> );
+my %num_data_sets : ATTR( :name<num_data_sets>, :default<undef> );
+my %data_set : ATTR( :set<data_set>, :default<[]> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+ my $pos = $self->get_position() or croak("Error: No file position.");
+
+ # The systell function doesn't exist, we use this instead.
+ my $init_pos = sysseek( $fh, 0, 1 );
+
+ # File header is at the beginning of the file (no surprises there
+ # then).
+ sysseek( $fh, $pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ my $next_pos = get_unsigned_integer( $fh );
+ $self->set_next_group_position( $next_pos ) if $next_pos;
+
+ # We recurse into data set metadata here.
+ my $first_set_pos = get_unsigned_integer( $fh );
+
+ $self->set_num_data_sets( get_signed_integer( $fh ) );
+ $self->set_name( get_wstring( $fh ) );
+
+ my $first_set = ArrayExpress::Datafile::Affymetrix::Calvin::DataSet->new({
+ filehandle => $fh,
+ position => $first_set_pos,
+ });
+
+ $data_set{ident $self}[0] = $first_set;
+
+ $self->populate_data_sets( $first_set, $self->get_num_data_sets() - 1 );
+
+ # Reset the filehandle.
+ sysseek( $fh, $init_pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ return;
+}
+
+sub populate_data_sets : PRIVATE {
+
+ my ( $self, $set, $num_data_sets ) = @_;
+
+ # Recursion end point.
+ return unless $num_data_sets;
+
+ $set ||= $self->get_data_set( 0 )
+ or croak("Error: Initial data set not created.");
+
+ my $next_pos = $set->get_next_set_position()
+ or croak("Error: Data set gives no position for next set.");
+
+ my $next_set = ArrayExpress::Datafile::Affymetrix::Calvin::DataSet->new({
+ filehandle => $self->get_filehandle(),
+ position => $next_pos,
+ });
+ push @{ $data_set{ ident $self } }, $next_set;
+
+ # We need to recurse across sets here until we run out of data
+ # sets.
+ $self->populate_data_sets( $next_set, $num_data_sets - 1 );
+
+ return;
+}
+
+sub get_data_set {
+
+ my ( $self, $num ) = @_;
+
+ # These should all have been created in START.
+ unless ( defined $data_set{ ident $self }[ $num ] ) {
+ croak("Error: No data set found for number $num");
+ }
+
+ return $data_set{ ident $self }[ $num ];
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm
new file mode 100644
index 0000000..4f1ca86
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataHeader.pm
@@ -0,0 +1,80 @@
+#!/usr/bin/env perl
+#
+# $Id: DataHeader.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Component);
+
+use Class::Std;
+use Carp;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+ get_signed_integer
+ get_datetime
+ get_locale
+ get_string
+);
+
+my %file_identifier : ATTR( :name<file_identifier>, :default<undef> );
+my %data_type : ATTR( :name<data_type>, :default<undef> );
+my %creation_date : ATTR( :name<creation_date>, :default<undef> );
+my %locale_info : ATTR( :name<locale_info>, :default<undef> );
+my %parent_headers : ATTR( :name<parent_headers>, :default<[]> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+ my $pos = $self->get_position() or croak("Error: No file position.");
+
+ # The systell function doesn't exist, we use this instead.
+ my $init_pos = sysseek( $fh, 0, 1 );
+
+ # In principle this is probably unnecessary, but we do it anyway
+ # just to be on the safe side.
+ sysseek( $fh, $pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ $self->set_data_type( get_string( $fh ) );
+ $self->set_file_identifier( get_string( $fh ) );
+ $self->set_creation_date( get_datetime( $fh ) );
+ $self->set_locale_info( get_locale( $fh ) );
+
+ my $num_parameters = get_signed_integer( $fh );
+ for ( 1..$num_parameters ) {
+ my $param = $self->parse_parameter( $fh );
+ $self->add_parameters( $param );
+ }
+
+ # Recurse into the data headers, populating a nested hashref.
+ my $num_parents = get_signed_integer( $fh );
+ foreach my $num ( 1..$num_parents ) {
+ my $current_pos = sysseek( $fh, 0, 1 );
+ my $parent = __PACKAGE__->new({
+ filehandle => $fh,
+ position => $current_pos,
+ });
+ $self->add_parent_headers( $parent );
+ }
+
+ # Reset the filehandle.
+ sysseek( $fh, $init_pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ return;
+}
+
+sub add_parent_headers : PRIVATE {
+
+ my ( $self, @parents ) = @_;
+
+ push @{ $parent_headers{ ident $self } }, @parents;
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm
new file mode 100644
index 0000000..d6a6f9c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/DataSet.pm
@@ -0,0 +1,143 @@
+#!/usr/bin/env perl
+#
+# $Id: DataSet.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::DataSet;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Component);
+
+use Class::Std;
+use Carp;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+ get_signed_char
+ get_signed_integer
+ get_unsigned_integer
+ get_wstring
+);
+
+my %next_set_position : ATTR( :name<next_set_position>, :default<undef> );
+my %name : ATTR( :name<name>, :default<undef> );
+my %num_data_rows : ATTR( :name<num_data_rows>, :default<undef> );
+my %data_table_start : ATTR( :name<data_table_start>, :default<undef> );
+my %data_columns : ATTR( :name<data_columns>, :default<[]> );
+my %value_mapping : ATTR( :name<value_mapping>, :default<[]> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+ my $pos = $self->get_position() or croak("Error: No file position.");
+
+ # The systell function doesn't exist, we use this instead.
+ my $init_pos = sysseek( $fh, 0, 1 );
+
+ sysseek( $fh, $pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ $self->set_data_table_start( get_unsigned_integer( $fh ) );
+ $self->set_next_set_position( get_unsigned_integer( $fh ) );
+ $self->set_name( get_wstring( $fh ) );
+
+ my $num_parameters = get_signed_integer( $fh );
+ for ( 1..$num_parameters ) {
+ my $param = $self->parse_parameter( $fh );
+ $self->add_parameters( $param );
+ }
+
+ my $num_columns = get_unsigned_integer( $fh );
+
+ for ( 1..$num_columns ) {
+ my $cname = get_wstring( $fh );
+ my $ctype = get_signed_char( $fh );
+ my $csize = get_signed_integer( $fh );
+ my $column = ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn->new({
+ name => $cname,
+ type => $ctype,
+ size => $csize,
+ });
+ $self->add_data_columns( $column );
+ }
+
+ $self->set_num_data_rows( get_unsigned_integer( $fh ) );
+
+ # Reset the filehandle.
+ sysseek( $fh, $init_pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ return;
+}
+
+sub export {
+
+ my ( $self, $output_fh ) = @_;
+
+ my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+ my $pos = $self->get_data_table_start()
+ or croak("Error: No data table start position.");
+
+ # The systell function doesn't exist, we use this instead.
+ my $init_pos = sysseek( $fh, 0, 1 );
+
+ # Seek to data_table_start, create array of column readers,
+ # and write the return values for each row to $output_fh.
+ sysseek( $fh, $pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ # Our column readers are created as an array of closures.
+ my $readers = $self->get_data_readers();
+ my $mappers = $self->get_value_mapping();
+
+ # Read the values in, and use any (optional) mappings to modify
+ # the output data on the fly.
+ for ( 1..$self->get_num_data_rows() ) {
+ my @row_values;
+ foreach my $i ( 0..$#$readers ) {
+ my $val = $readers->[$i]->();
+ if ( my $map = $mappers->[$i] ) {
+ $val = $map->{$val};
+ }
+ push @row_values, $val;
+ }
+ print $output_fh (join("\t", @row_values), "\n");
+ }
+
+ # Reset the filehandle.
+ sysseek( $fh, $init_pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ return;
+}
+
+sub get_data_readers {
+
+ my ( $self ) = @_;
+
+ my $fh = $self->get_filehandle() or croak("Error: No filehandle.");
+
+ # Our column readers are created as an array of closures. Note
+ # that before you use these you need to sysseek to
+ # $self->get_data_table_start().
+ my @readers = map {
+ my $x = $_;
+ sub { $x->get_reader()->( $fh, $x->get_size() ) };
+ } @{ $self->get_data_columns() };
+
+ return \@readers;
+}
+
+sub add_data_columns : PRIVATE {
+
+ my ( $self, @columns ) = @_;
+
+ push @{ $data_columns{ ident $self } }, @columns;
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm
new file mode 100644
index 0000000..7847c51
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Facade.pm
@@ -0,0 +1,127 @@
+#!/usr/bin/env perl
+#
+# $Id: Facade.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Facade;
+use base qw(ArrayExpress::Datafile::Affymetrix::Calvin::Generic);
+
+use Class::Std;
+use Carp;
+
+my %data_set : ATTR( :name<data_set>, :default<undef> );
+my %chip_type : ATTR( :name<chip_type>, :default<undef> );
+my %algorithm : ATTR( :name<algorithm>, :default<undef> );
+my %parameters : ATTR( :get<parameters>, :default<{}> );
+my %stats : ATTR( :get<stats>, :default<{}> );
+my %qtd : ATTR( :name<qtd>, :default<[]> );
+my %ded : ATTR( :set<ded>, :default<[]> );
+my %headings : ATTR( :name<headings>, :default<[]> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ my $group = $self->get_data_group(0);
+ my $data_set = $group->get_data_set(0);
+
+ $self->set_data_set( $data_set );
+
+ # Num_columns and num_rows are only really available to the CEL
+ # subclass.
+ my %param2attr = (
+ 'affymetrix-algorithm-name' => 'set_algorithm',
+ 'affymetrix-array-type' => 'set_chip_type',
+ 'affymetrix-cel-cols' => 'set_num_columns',
+ 'affymetrix-cel-rows' => 'set_num_rows',
+ );
+
+ my $params = $self->get_data_header()->get_parameters();
+ my $pname;
+ foreach my $param ( @$params ) {
+ my $setter = $param2attr{ $param->get_name() };
+ if ( $setter && $self->can($setter) ) {
+ $self->$setter( $param->get_value() );
+ }
+ elsif ( ( $pname )
+ = ( $param->get_name() =~ m/affymetrix-algorithm-param-(.*)/xms ) ) {
+ $self->add_parameter( $pname, $param->get_value() );
+ }
+ elsif ( ( $pname )
+ = ( $param->get_name() =~ m/affymetrix-chipsummary-(.*)/xms ) ) {
+ $self->add_stat( $pname, $param->get_value() );
+ }
+ }
+
+ my $columns = $data_set->get_data_columns();
+ my ( @headings, @qts );
+ foreach my $col ( @$columns ) {
+ push @headings, $col->get_name();
+ push @qts, 'Affymetrix:QuantitationType:' . $col->get_name();
+ }
+
+ $self->set_headings( \@headings );
+ $self->set_qtd( \@qts );
+}
+
+sub add_parameter : RESTRICTED {
+
+ my ( $self, $key, $value ) = @_;
+
+ $parameters{ ident $self }{ $key } = $value;
+
+ return;
+}
+
+sub add_stat : RESTRICTED {
+
+ my ( $self, $key, $value ) = @_;
+
+ $stats{ ident $self }{ $key } = $value;
+
+ return;
+}
+
+sub export {
+
+ my ( $self, $output_fh, $cdf ) = @_;
+
+ my $data_set = $self->get_data_set();
+ my $data_type = $self->get_data_header()->get_data_type();
+
+ $data_set->export( $output_fh );
+
+ return;
+}
+
+sub parse {
+ # Dummy method - the Calvin parser does everything on
+ # instantiation, but this is part of the published API.
+}
+
+sub parse_header {
+ # Dummy method - the Calvin parser does everything on
+ # instantiation, but this is part of the published API.
+}
+
+sub generate_ded : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ croak("Error: Stub method called in abstract superclass.");
+}
+
+sub get_ded {
+
+ my ( $self, $cdf, $chip_type ) = @_;
+
+ unless ( scalar @{ $ded{ ident $self } } ) {
+ $self->generate_ded();
+ }
+
+ return $ded{ ident $self };
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm
new file mode 100644
index 0000000..ada77f5
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Generic.pm
@@ -0,0 +1,128 @@
+#!/usr/bin/env perl
+#
+# $Id: Generic.pm 1984 2008-03-03 17:47:18Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Generic;
+
+use Class::Std;
+use Carp;
+
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup;
+require ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader;
+
+use ArrayExpress::Datafile::Affymetrix::Calvin::Binary qw(
+ get_signed_integer
+ get_unsigned_char
+ get_unsigned_integer
+);
+
+my %filename : ATTR( :get<filename>, :init_arg<filename>, :default<undef> );
+my %filehandle : ATTR( :set<filehandle>, :init_arg<filehandle>, :default<undef> );
+
+my %magic : ATTR( :name<magic>, :default<undef> );
+my %version : ATTR( :name<version>, :default<undef> );
+my %num_data_groups : ATTR( :name<num_data_groups>, :default<undef> );
+my %data_group : ATTR( :set<data_group>, :default<[]> );
+my %data_header : ATTR( :name<data_header>, :default<undef> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $filename{ ident $self } || $filehandle{ ident $self } ) {
+ croak("Error: no filename or filehandle attribute set.\n");
+ }
+
+ my $fh = $self->get_filehandle();
+
+ # The systell function doesn't exist, we use this instead.
+ my $init_pos = sysseek( $fh, 0, 1 );
+
+ # File header is at the beginning of the file (no surprises there
+ # then).
+ sysseek( $fh, 0, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ my $magic = get_unsigned_char( $fh );
+ unless( $magic == 59 ) {
+ croak("Error: Unrecognized file magic number: $magic");
+ }
+ $magic{ ident $self } = $magic;
+
+ $version{ ident $self } = get_unsigned_char( $fh );
+ $num_data_groups{ ident $self } = get_signed_integer( $fh );
+ my $first_group_pos = get_unsigned_integer( $fh );
+ my $first_group = ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup->new({
+ filehandle => $fh,
+ position => $first_group_pos,
+ });
+ $data_group{ ident $self }[0] = $first_group;
+
+ # This recurses through the file to set the positions of all the
+ # data groups.
+ $self->populate_data_groups($first_group, $self->get_num_data_groups() - 1);
+
+ # This iterates and recurses over the data headers.
+ my $data_header = ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader->new({
+ filehandle => $fh,
+ position => 10,
+ });
+
+ $self->set_data_header( $data_header );
+
+ # Reset the filehandle.
+ sysseek( $fh, $init_pos, 0 )
+ or croak("Error resetting filehandle: $!");
+
+ return;
+}
+
+sub get_filehandle : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ unless ( $filehandle{ ident $self } ) {
+ open( my $fh, '<', $self->get_filename() )
+ or croak("Error: Unable to open input file: $!");
+ $filehandle{ ident $self } = $fh;
+ }
+
+ return $filehandle{ ident $self };
+}
+
+sub get_data_group {
+
+ my ( $self, $num ) = @_;
+
+ # These should all have been created in START.
+ unless ( defined $data_group{ ident $self }[ $num ] ) {
+ croak("Error: No data group found for number $num");
+ }
+
+ return $data_group{ ident $self }[ $num ];
+}
+
+sub populate_data_groups : PRIVATE {
+
+ my ( $self, $group, $num_groups ) = @_;
+
+ return unless $num_groups;
+
+ $group ||= $self->get_data_group( 0 )
+ or croak("Error: Initial data group not created.\n");
+
+ # The last data group should point to zero (but we don't rely on it).
+ my $next_group = ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup->new({
+ filehandle => $self->get_filehandle(),
+ position => $group->get_next_group_position(),
+ });
+ push @{ $data_group{ ident $self } }, $next_group;
+ $self->populate_data_groups($next_group, $num_groups - 1);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm
new file mode 100644
index 0000000..fe6efc6
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Calvin/Parameter.pm
@@ -0,0 +1,19 @@
+#!/usr/bin/env perl
+#
+# $Id: Parameter.pm 1973 2008-02-27 18:10:51Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Datafile::Affymetrix::Calvin::Parameter;
+
+# Simple class to store NVT-style parameter values.
+
+use Class::Std;
+use Carp;
+
+my %name : ATTR( :name<name>, :default<undef> );
+my %value : ATTR( :name<value>, :default<undef> );
+my %type : ATTR( :name<type>, :default<undef> );
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/EXP.pm b/lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
new file mode 100644
index 0000000..e40c387
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
@@ -0,0 +1,396 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix EXP files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: EXP.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: CDF.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::EXP - EXP data file parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::EXP;
+
+ my $exp = ArrayExpress::Datafile::Affymetrix::EXP->new({
+ input => 'data1.EXP',
+ });
+ $exp->parse();
+
+=head1 DESCRIPTION
+
+This module implements parsing and export of data from Affymetrix EXP files.
+
+Please see L<ArrayExpress::Datafile::Affymetrix::Parser> for
+methods common to all the Affymetrix parser classes.
+
+=head1 METHODS
+
+=over 2
+
+=item export($fh)
+
+This method takes a filehandle and prints out the EXP file in its
+original format. This method is not particularly useful and is really
+for testing purposes only.
+
+=item get_chip_lot()
+
+The lot number of the chip.
+
+=item get_operator()
+
+The person who performed the procedure.
+
+=item get_protocol()
+
+The name of the hybridization protocol used (e.g. EukGE-WS2v4).
+
+=item get_station()
+
+The station number.
+
+=item get_module()
+
+The module number.
+
+=item get_hyb_date()
+
+The date on which the hybridization was performed. This is returned in
+the same format as in the EXP file; no sanitization is performed.
+
+=item get_pixel_size()
+
+Pixel size (integer).
+
+=item get_filter()
+
+Filter (570nm).
+
+=item get_scan_temp()
+
+Scan temperature.
+
+=item get_scan_date()
+
+The date on which the scanning was performed. This is returned in
+the same format as in the EXP file; no sanitization is performed.
+
+=item get_scanner_id()
+
+Scanner ID.
+
+=item get_num_scans()
+
+Number of scans performed.
+
+=item get_scanner_type()
+
+Scanner type.
+
+=item get_hyb_parameters()
+
+A reference to an array of named hybridization parameters. Each
+parameter is coded as a separate hash with a single {name => value}
+pair. Typically this method is not very useful; you should probably be
+using the B<parameters> or B<add_parameters> methods instead.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::EXP;
+use base 'ArrayExpress::Datafile::Affymetrix::Parser';
+
+use strict;
+use warnings;
+
+use English qw( -no_match_vars );
+use Carp;
+use Scalar::Util qw( openhandle );
+use IO::File;
+use Class::Std;
+
+use ArrayExpress::Curator::Common qw(
+ check_linebreaks
+);
+
+my %chip_lot : ATTR( :name<chip_lot>, :default<undef> );
+my %operator : ATTR( :name<operator>, :default<undef> );
+my %protocol : ATTR( :name<protocol>, :default<undef> );
+my %station : ATTR( :name<station>, :default<undef> );
+my %module : ATTR( :name<module>, :default<undef> );
+my %hyb_date : ATTR( :name<hyb_date>, :default<undef> );
+my %pixel_size : ATTR( :name<pixel_size>, :default<undef> );
+my %filter : ATTR( :name<filter>, :default<undef> );
+my %scan_temp : ATTR( :name<scan_temp>, :default<undef> );
+my %scan_date : ATTR( :name<scan_date>, :default<undef> );
+my %scanner_id : ATTR( :name<scanner_id>, :default<undef> );
+my %num_scans : ATTR( :name<num_scans>, :default<undef> );
+my %scanner_type : ATTR( :name<scanner_type>, :default<undef> );
+my %hyb_parameters : ATTR( :name<hyb_parameters>, :default<[]> );
+
+sub parse {
+
+ my ( $self ) = @_;
+
+ my $input = $self->get_input();
+
+ my ( $fh, $linebreak );
+ if ( openhandle($input) ) {
+ $fh = $input;
+ }
+ else {
+ my $counts;
+ ( $counts, $linebreak ) = check_linebreaks($input);
+ unless ($linebreak) {
+ croak( "Error: Unable to parse line endings in file $input: "
+ . "($counts->{unix} Unix, $counts->{dos} DOS, $counts->{mac} Mac)\n"
+ );
+ }
+ $fh = IO::File->new( $input, '<' )
+ or croak("Unable to open EXP file $input : $!\n");
+ }
+
+ # Localise our line ending variable to the scope of this subroutine.
+ local $INPUT_RECORD_SEPARATOR = $linebreak;
+
+ # Setting binmode here interferes with the linebreak processing, so
+ # we don't.
+ # binmode($fh, ":crlf");
+ seek( $fh, 0, 0 ) or croak("Error rewinding filehandle for input: $!\n");
+
+ my $label = <$fh>;
+ $label =~ s/[\r\n]* \z//xms;
+
+ # Allow following whitespace - not in the Affy spec, but all too common.
+ unless ( $label
+ =~ m{\A Affymetrix\ GeneChip\ Experiment\ Information \s* \z}xms ) {
+
+ # Strip out Mac line endings from the error message - they play
+ # havoc with the terminal and obscure the error.
+ $label =~ s/\r//g;
+ croak("Error: Unrecognized EXP file format: $label\n");
+ }
+
+ my $version = <$fh>;
+ $version =~ s{\A Version \t (\d+) [\r\n]*}{$1}xms; # Just want the number
+ $self->set_version($version);
+
+ my $line = q{};
+
+ until ( $line =~ m{\A \[Sample\ Info\]}xms ) {
+ $line = <$fh>;
+ $line =~ s/[\r\n]* \z//xms;
+ }
+
+ LINE:
+ while ( my $line = <$fh> ) {
+ $line =~ s/[\r\n]* \z//xms
+ ; # just to be sure about DOS-style line endings
+ last LINE if ( $line =~ m/\A \s* \z/xms );
+ my ( $parameter, $value ) = split /\t/, $line;
+
+ SAMPLEINFO:
+ {
+
+ ( $parameter eq 'Chip Type' )
+ && do { $self->set_chip_type($value); last SAMPLEINFO; };
+
+ ( $parameter eq 'Chip Lot' )
+ && do { $self->set_chip_lot($value); last SAMPLEINFO; };
+
+ ( $parameter eq 'Operator' )
+ && do { $self->set_operator($value); last SAMPLEINFO; };
+
+ }
+ }
+
+ until ( $line =~ m{\A \[Fluidics\]}xms ) {
+ $line = <$fh>;
+ $line =~ s/[\r\n]* \z//xms;
+ }
+ my $param_count = 0; # Hyb parameter numbering starts at zero
+
+ LINE:
+ while ( my $line = <$fh> ) {
+ $line =~ s/[\r\n]* \z//xms
+ ; # just to be sure about DOS-style line endings
+ last LINE if ( $line =~ m/\A \s* \z/xms );
+ my ( $parameter, $value ) = split /\t/, $line;
+
+ FLUIDICS:
+ {
+
+ ( $parameter eq 'Protocol' )
+ && do { $self->set_protocol($value); last FLUIDICS; };
+
+ ( $parameter eq 'Station' )
+ && do { $self->set_station($value); last FLUIDICS; };
+
+ ( $parameter eq 'Module' )
+ && do { $self->set_module($value); last FLUIDICS; };
+
+ ( $parameter eq 'Hybridize Date' )
+ && do { $self->set_hyb_date($value); last FLUIDICS; };
+
+ # Wash params here. We put the originals in hyb_parameters(),
+ # and the mage-sanitized ones in parameters()
+ my $mage_param
+ = "HybridizationStep$param_count-" . $self->get_protocol();
+ $self->add_parameters( { $mage_param => $value } );
+ $self->add_hyb_parameters( { $parameter => $value } );
+ $param_count++;
+ }
+ }
+
+ until ( $line =~ m{\A \[Scanner\]}xms ) {
+ $line = <$fh>;
+ $line =~ s/[\r\n]* \z//xms;
+ }
+
+ LINE:
+ while ( my $line = <$fh> ) {
+ $line =~ s/[\r\n]* \z//xms
+ ; # just to be sure about DOS-style line endings
+ last LINE if ( $line =~ m/\A \s* \z/xms );
+ my ( $parameter, $value ) = split /\t/, $line;
+
+ SCANNER:
+ {
+
+ ( $parameter eq 'Pixel Size' )
+ && do { $self->set_pixel_size($value); last SCANNER; };
+
+ ( $parameter eq 'Filter' )
+ && do { $self->set_filter($value); last SCANNER; };
+
+ ( $parameter eq 'Scan Temperature' )
+ && do { $self->set_scan_temp($value); last SCANNER; };
+
+ ( $parameter eq 'Scan Date' )
+ && do { $self->set_scan_date($value); last SCANNER; };
+
+ ( $parameter eq 'Scanner ID' )
+ && do { $self->set_scanner_id($value); last SCANNER; };
+
+ ( $parameter eq 'Number of Scans' )
+ && do { $self->set_num_scans($value); last SCANNER; };
+
+ ( $parameter eq 'Scanner Type' )
+ && do { $self->set_scanner_type($value); last SCANNER; };
+
+ }
+ }
+
+ return;
+}
+
+####################
+# Accessor Methods #
+####################
+
+# The hyb_parameters accessor is a special case
+
+sub add_hyb_parameters : PRIVATE { # arrayref (adds to old params)
+
+ my ( $self, $params ) = @_;
+
+ ref $params eq 'HASH'
+ or confess( 'Bad parameters passed to method' );
+ push( @{ $hyb_parameters{ident $self} }, $params );
+
+ return;
+}
+
+sub export { # This is really only for checking purposes
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ print $fh (
+ "Affymetrix GeneChip Experiment Information\n",
+ "Version\t", $self->get_version, "\n",
+ "\n",
+
+ "[Sample Info]\n",
+ "Chip Type\t", $self->get_chip_type, "\n",
+ "Chip Lot\t", $self->get_chip_lot, "\n",
+ "Operator\t", $self->get_operator, "\n"
+ );
+ print $fh ("\n");
+
+ printf $fh ( "[Fluidics]\nProtocol\t%s\n", $self->get_protocol() );
+
+ foreach my $parameter ( @{ $self->get_hyb_parameters() } ) {
+ while ( my ( $key, $value ) = each %$parameter ) {
+ print $fh ( "$key\t", $value, "\n" );
+ }
+ }
+
+ printf $fh (
+ "Station\t%s\nModule\t%s\nHybridize Date\t%s\n\n",
+ $self->get_station,
+ $self->get_module,
+ $self->get_hyb_date,
+ );
+
+ printf $fh (
+ "[Scanner]\nPixel Size\t%s\nFilter\t%s\nScan Temperature\t%s\n"
+ . "Scan Date\t%s\nScanner ID\t%s\nNumber of Scans\t%s\nScanner Type\t%s\n",
+ $self->get_pixel_size,
+ $self->get_filter,
+ $self->get_scan_temp,
+ $self->get_scan_date,
+ $self->get_scanner_id,
+ $self->get_num_scans,
+ $self->get_scanner_type,
+ );
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm b/lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
new file mode 100644
index 0000000..8c3af01
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
@@ -0,0 +1,108 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix binary data files.
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: EXPFactory.pm 1808 2007-11-01 18:55:34Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: EXPFactory.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::EXPFactory - a factory class for generating
+Affymetrix EXP file parsing objects.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Affymetrix::EXPFactory;
+
+ my $fac = ArrayExpress::Datafile::Affymetrix::EXPFactory->new();
+
+ my $exp = $fac->make_parser( 'Data1.EXP' );
+
+ $exp->parse();
+
+ $exp->export($output_filehandle);
+
+=head1 DESCRIPTION
+
+This module is a factory class used in to create data file parsers for
+Affymetrix EXP files.
+
+=head1 METHODS
+
+=over 2
+
+=item new()
+
+The class constructor.
+
+=item make_parser($file)
+
+This method takes a filename argument and returns a parser object that
+can then be used to process the data file and return interesting
+values.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::EXPFactory;
+use base 'ArrayExpress::Datafile::Affymetrix';
+
+use strict;
+use warnings;
+
+use Class::Std;
+
+sub make_parser {
+
+ my ( $self, $input ) = @_;
+
+ # There's only one EXP file type, making this extremely simple.
+ require ArrayExpress::Datafile::Affymetrix::EXP;
+ return ArrayExpress::Datafile::Affymetrix::EXP->new({
+ input => $input,
+ });
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Affymetrix/Parser.pm b/lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
new file mode 100644
index 0000000..aca3060
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
@@ -0,0 +1,355 @@
+#!/usr/bin/env perl
+#
+# Module to parse Affymetrix data files.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Parser.pm 2021 2008-04-09 09:55:25Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Affymetrix/Parser.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Affymetrix::Parser - an Affymetrix data file parsing
+module.
+
+=head1 SYNOPSIS
+
+ use base qw( ArrayExpress::Datafile::Affymetrix::Parser );
+
+=head1 DESCRIPTION
+
+This module is an abstract superclass used in the parsing and export
+of data from Affymetrix file formats. CEL, CHP and EXP formats are
+supported, with limited CDF parsing. Both old (GDAC) and new
+(GCOS/XDA) file formats can be parsed.
+
+There is a set of methods common to all the Affymetrix file classes,
+listed below. There are additional methods, specific to each class,
+which are documented in the relevant pages.
+
+=head1 METHODS
+
+The following methods are common to all classes.
+
+=over 2
+
+=item new({ input => 'myfile.CEL' })
+
+The class constructor. This method returns a an object of the
+appropriate class, without performing any additional processing.
+
+=item parse()
+
+This method will take the value for the C<input> attribute and parse the
+data into memory so that it can be interrogated using the methods below.
+
+=back
+
+=head2 Accessor methods
+
+Each of these methods acts as both setter and getter for the
+attributes in question. Typically these will be used to access the
+data and metadata which was extracted using the B<parse>
+method. Please see the respective subclass documentation for
+information on the B<export> and B<get_ded> methods. Note that many of the
+following will have no meaning for EXP file metadata.
+
+=over 2
+
+=item get_version()
+
+The version number or string associated with the file. For CEL and CHP
+files this should be either 3 or 4.
+
+=item get_num_columns()
+
+The number of columns on the array.
+
+=item get_num_rows()
+
+The number of rows on the array.
+
+=item get_num_cells()
+
+The number of cells on the array. For CEL files this corresponds to
+the number of columns multiplied by the number of rows.
+
+=item get_algorithm()
+
+The name of the algorithm used to produce the data (e.g. "Percentile",
+"ExpressionStat").
+
+=item get_chip_type()
+
+The type of chip used (e.g., HG-U133A). This is supported for EXP
+files, CEL files, CHP files and GDAC format CDF files. Note that for
+CEL files this relies on parsing a header tag which is not actually
+documented by Affymetrix, and so it is possible that this method is
+not to be trusted in such cases.
+
+=item get_parameters()
+
+A reference to a hash with parameter {name => value} pairs. Parameters
+are grouped as follows:
+
+ CEL: Feature extraction parameters
+ CHP: Normalization parameters
+ EXP: Hybridization parameters (numbered).
+
+=item get_stats()
+
+A reference to a hash with statistic {name => value} pairs. Statistics
+are grouped as follows:
+
+ CEL: Feature extraction summary statistics
+ CHP: Normalization summary statistics
+
+=item get_qtd()
+
+A reference to an array listing the QuantitationType identifiers (long
+form) in the column order that they are output by the B<export>
+method. See also the B<headings> method below.
+
+=item get_headings()
+
+A reference to an array listing the QuantitationType names (short
+form) in the column order that they are output by the B<export>
+method. Using this method also populates the B<qtd> method data
+structure, but not vice versa.
+
+=cut
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::Affymetrix::Parser;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use Readonly;
+use Scalar::Util qw(openhandle);
+
+use ArrayExpress::Curator::Common qw(clean_hash);
+
+my %input : ATTR( :name<input>, :default<undef> );
+my %filehandle : ATTR( :init_arg<filehandle>, :default<undef> );
+my %magic : ATTR( :name<magic>, :default<undef> );
+my %required_magic : ATTR( :name<required_magic>, :default<undef> );
+
+my %num_columns : ATTR( :name<num_columns>, :default<undef> );
+my %num_rows : ATTR( :name<num_rows>, :default<undef> );
+my %num_cells : ATTR( :name<num_cells>, :default<undef> );
+my %version : ATTR( :name<version>, :default<undef> );
+my %algorithm : ATTR( :name<algorithm>, :default<undef> );
+my %chip_type : ATTR( :name<chip_type>, :default<undef> );
+my %parameters : ATTR( :get<parameters>, :default<{}> );
+my %stats : ATTR( :get<stats>, :default<{}> );
+my %qtd : ATTR( :name<qtd>, :default<[]> );
+my %headings : ATTR( :get<headings>, :default<[]> );
+my %data_matrix : ATTR( :default<[]> );
+my %data_storage : ATTR( :name<data_storage>, :default<'ARRAY'> );
+
+# Prefix used in the construction of QT identifiers
+Readonly my $AFFY_QT_PREFIX => 'Affymetrix:QuantitationType:';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $input{ ident $self } || $filehandle{ ident $self } ) {
+ confess("Error: no input to parsing module.");
+ }
+
+ return;
+}
+
+sub get_filehandle : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ my $input = $self->get_input();
+ if ( ! $filehandle{ ident $self } && $input ) {
+ if ( openhandle( $input ) ) {
+ $filehandle{ ident $self } = $input;
+ }
+ else {
+ $filehandle{ ident $self } = IO::File->new( $input, '<' )
+ or croak(qq{Unable to open file "$input" : $!\n});
+ }
+ }
+
+ return $filehandle{ ident $self };
+}
+
+sub parse {
+ confess("Error: Stub method called in abstract superclass.");
+}
+
+sub set_parameters : RESTRICTED {
+
+ my ( $self, $hashref ) = @_;
+
+ ref $hashref eq 'HASH'
+ or confess( 'Bad parameters passed to method' );
+
+ # strip out undef or empty string values
+ my $cleaned = clean_hash($hashref);
+
+ $parameters{ident $self} = $cleaned;
+
+ return;
+}
+
+sub add_parameters : RESTRICTED {
+
+ # When called with a reference to a hash of parameter {name =>
+ # value} pairs this method adds those parameters to any
+ # pre-existing ones. Parameters having the same name are
+ # overwritten.
+
+ my ( $self, $hashref ) = @_;
+
+ ref $hashref eq 'HASH'
+ or confess( 'Bad parameters passed to method' );
+
+ # strip out undef or empty string values
+ my $cleaned = clean_hash($hashref);
+
+ @{ $parameters{ident $self} }{ keys %$cleaned } = values %$cleaned;
+
+ return;
+}
+
+sub set_stats : RESTRICTED {
+
+ my ( $self, $hashref ) = @_;
+
+ ref $hashref eq 'HASH'
+ or confess( 'Bad parameters passed to method' );
+
+ # strip out undef or empty string values
+ my $cleaned = clean_hash($hashref);
+
+ $stats{ident $self} = $cleaned;
+
+ return;
+}
+
+sub add_stats : RESTRICTED {
+
+ # When called with a reference to a hash of statistic {name =>
+ # value} pairs this method adds those statistics to any
+ # pre-existing ones. Statistics having the same name are
+ # overwritten.
+
+ my ( $self, $hashref ) = @_;
+
+ ref $hashref eq 'HASH'
+ or confess( 'Bad parameters passed to method' );
+
+ # strip out undef or empty string values
+ my $cleaned = clean_hash($hashref);
+
+ @{ $stats{ident $self} }{ keys %$cleaned } = values %$cleaned;
+
+ return;
+}
+
+sub set_headings : RESTRICTED { # arrayref (replaces old qtd); CEL and CHP only
+ my ( $self, $list ) = @_;
+
+ ref $list eq 'ARRAY'
+ or confess( 'Bad parameters passed to method' );
+ $headings{ident $self} = $list;
+
+ my @qtd;
+ foreach my $heading ( @{ $list } ) {
+ push( @qtd, $AFFY_QT_PREFIX . $heading );
+ }
+ $qtd{ident $self} = \@qtd;
+
+ return;
+}
+
+# set/get_data is a restricted method used in the Affymetrix subclasses
+# only. It provides access to the parsed data.
+
+sub set_data : RESTRICTED {
+
+ # Arrayref by default; CEL.pm however uses a hashref. We make this
+ # restricted to hide this complexity.
+
+ my ( $self, $data ) = @_;
+
+ # Subclasses using non-arrayref storage need to set_data with
+ # e.g. an empty hashref before calling this.
+ ref $data eq $self->get_data_storage()
+ or confess( 'Bad parameters passed to method' );
+
+ $data_matrix{ident $self} = $data;
+
+ return;
+}
+
+sub get_data : RESTRICTED {
+
+ my ( $self, $data ) = @_;
+
+ return $data_matrix{ident $self};
+}
+
+sub _get_line : RESTRICTED {
+
+ my ( $self, $fh ) = @_;
+
+ openhandle($fh) or confess( 'Bad parameters passed to method' );
+
+ my $line = <$fh>;
+ $line =~ s/[\r\n]* \z//xms if $line;
+ return $line;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/ArrayDesign.pm b/lib/ArrayExpress/Datafile/ArrayDesign.pm
new file mode 100644
index 0000000..d75e4a3
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/ArrayDesign.pm
@@ -0,0 +1,215 @@
+#!/usr/bin/env perl
+#
+# ArrayDesign.pm
+#
+# Tim Rayner 2008 ArrayExpress team, EBI
+#
+# $Id: ArrayDesign.pm 1987 2008-03-06 11:29:22Z tfrayner $
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::ArrayDesign - memory-efficient handling of
+array design information.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile:::ArrayDesign
+
+=head1 DESCRIPTION
+
+ArrayDesign objects are used during experiment checking to handle
+array design elements in a memory-efficient manner.
+
+=head2 Accessor methods
+
+=over 2
+
+=item set_accession
+
+Setter method for the array accession number relating to this design.
+
+=item get_accession
+
+Getter method for the array accession number relating to this design.
+
+=item set_adf_features
+
+Setter method for a hashref with keys which are period-delim strings
+of the feature coordinates found in the array design associated with
+the file.
+
+=item get_adf_features
+
+Getter method for a hashref with keys which are period-delim strings
+of the feature coordinates found in the array design associated with
+the file.
+
+=item set_adf_reporters
+
+Setter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.
+
+=item get_adf_reporters
+
+Getter method for a hashref with keys which are the reporter
+identifiers found in the array design associated with the file.
+
+=item set_adf_compseqs
+
+Setter method for a hashref with keys which are the composite sequence
+identifiers found in the array design associated with the file.
+
+=item get_adf_compseqs
+
+Getter method for a hashref with keys which are the composite sequence
+identifiers found in the array design associated with the file.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2004.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::ArrayDesign;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+use Class::Std;
+use Storable qw(store_fd fd_retrieve);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+my %feature_fh : ATTR( :default<undef> );
+my %reporter_fh : ATTR( :default<undef> );
+my %compseq_fh : ATTR( :default<undef> );
+
+my %accession : ATTR( :name<accession>, :default<undef> );
+
+sub set_features {
+
+ my ( $self, $features ) = @_;
+
+ $self->set_design_elements( \%feature_fh, $features );
+}
+
+sub get_features {
+
+ my ( $self ) = @_;
+
+ return $self->get_design_elements( \%feature_fh );
+}
+
+sub set_reporters {
+
+ my ( $self, $reporters ) = @_;
+
+ $self->set_design_elements( \%reporter_fh, $reporters );
+}
+
+sub get_reporters {
+
+ my ( $self ) = @_;
+
+ return $self->get_design_elements( \%reporter_fh );
+}
+
+sub set_compseqs {
+
+ my ( $self, $compseqs ) = @_;
+
+ $self->set_design_elements( \%compseq_fh, $compseqs );
+}
+
+sub get_compseqs {
+
+ my ( $self ) = @_;
+
+ return $self->get_design_elements( \%compseq_fh );
+}
+
+sub set_design_elements : PRIVATE {
+
+ my ( $self, $attr_var, $elements ) = @_;
+
+ defined($attr_var)
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $elements eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $fh = IO::File->new_tmpfile();
+
+ store_fd( $elements, $fh )
+ or confess("Error caching array design element list: $!");
+ seek($fh, 0, 0) or confess("Error rewinding design element cache file: $!");
+
+ $attr_var->{ident $self} = $fh;
+
+ return;
+}
+
+sub get_design_elements : PRIVATE {
+
+ my ( $self, $attr_var ) = @_;
+
+ defined($attr_var)
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Return empty hash if array not in cache.
+ my $elements = {};
+ if ( my $fh = $attr_var->{ident $self} ) {
+
+ my $pos = tell($fh);
+
+ seek($fh, 0, 0) or confess("Error rewinding design element cache file: $!");
+
+ $elements = fd_retrieve( $fh )
+ or confess("Error retrieving cached array design elements: $!");
+
+ seek($fh, $pos, 0) or confess("Error resetting design element cache file: $!");
+ }
+
+ return $elements;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Binary.pm b/lib/ArrayExpress/Datafile/Binary.pm
new file mode 100644
index 0000000..e66f233
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Binary.pm
@@ -0,0 +1,189 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic binary file data parsing functions.used in
+# e.g. Affymetrix file parsing module(s)
+#
+# Tim Rayner 2004, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Binary.pm 1973 2008-02-27 18:10:51Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Binary;
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../index.html">
+ <img src="../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Binary.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Binary.pm - a module providing functions for
+reading files in binary form.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Binary qw(get_integer get_float);
+
+ my $int = get_integer($fh);
+ my $float = get_float($fh);
+
+=head1 DESCRIPTION
+
+This is a simple module providing functions which read an appropriate
+number of bytes from the passed filehandle, and return the
+appropriately unpacked value for further processing. Since this module
+was originally developed to aid Affymetrix file parsing there is a
+certain bias as to which data types are handled (the list is not
+comprehensive; nor is it intended to be).
+
+=head1 FUNCTIONS
+
+=over 2
+
+=item get_integer( $fh )
+
+=item get_unsigned_short( $fh )
+
+=item get_unsigned_char( $fh )
+
+=item get_signed_char( $fh )
+
+=item get_float( $fh, $length )
+
+=item get_ascii( $fh, $length )
+
+=item get_hexadecimal( $fh )
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+use Carp;
+use Readonly;
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+ get_hexadecimal
+);
+
+# This constant determines how floats are handled on big-endian systems.
+# NB. it is unclear at this stage whether 64-bit systems will work.
+Readonly my $IS_BIG_ENDIAN => unpack( "h*", pack( "s", 1 ) ) =~ /01/;
+
+#################################
+# Binary data parsing functions #
+#################################
+
+sub get_integer { # Signed integer only.
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 4 );
+ croak($!) unless defined($num_bytes);
+
+ # Little-endian long, for portability's sake
+ return ( unpack "V*", $value );
+}
+
+sub get_DWORD {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 4 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "V*", $value );
+}
+
+sub get_unsigned_short {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 2 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "v*", $value );
+}
+
+sub get_unsigned_char {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 1 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "C*", $value );
+}
+
+sub get_signed_char {
+ my $fh = shift;
+ my $num_bytes = sysread( $fh, my $value, 1 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "c*", $value );
+}
+
+sub get_float {
+ my ( $fh, $length ) = @_;
+ $length ||= 4; # defaults to a length of 4 bytes
+ my $num_bytes = sysread( $fh, my $value, $length );
+ croak($!) unless defined($num_bytes);
+ if ($IS_BIG_ENDIAN) {
+ return ( unpack 'f*', pack 'V*', unpack 'N*', $value );
+ }
+ else {
+ return ( unpack "f*", $value );
+ }
+}
+
+sub get_ascii {
+ my ( $fh, $length ) = @_;
+
+ # No longer default to a length of 1 byte - caused too many bugs
+ # $length ||= 1;
+ my $num_bytes = sysread( $fh, my $value, $length );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "a*", $value );
+}
+
+sub get_hexadecimal { # High nybble first
+ my ($fh) = @_;
+ my $num_bytes = sysread( $fh, my $value, 1 );
+ croak($!) unless defined($num_bytes);
+ return ( unpack "H*", $value );
+}
+
+1;
+
diff --git a/lib/ArrayExpress/Datafile/DataMatrix.pm b/lib/ArrayExpress/Datafile/DataMatrix.pm
new file mode 100644
index 0000000..a821a03
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/DataMatrix.pm
@@ -0,0 +1,762 @@
+#!/usr/bin/env perl
+#
+# Module to help generation of DataMatrix MAGE objects.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: DataMatrix.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Datafile::DataMatrix;
+
+use strict;
+use warnings;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+
+use Bio::MAGE qw(:ALL);
+
+require ArrayExpress::Datafile;
+use ArrayExpress::Curator::MAGE qw(
+ update_factorvalues
+ unique_identifier
+);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $EDF_TRANSFORMATIONNAME
+ $EDF_TRANSFORMATIONTYPE
+ $EDF_TRXN_PROTOCOL
+ $EDF_TRXN_SOFTWARE
+ $OE_VAL_NORMALIZATION
+ $OE_VAL_TRANSFORMATION_SOFTWARE
+);
+
+sub get_dimension_lists {
+
+ # Takes an AoA of hyb names and an array of qt names, returns the
+ # ba and qt dimensions as arrayrefs of names, and a BioDataCube
+ # order string.
+
+ my ( $self, $datamatrix_hybs, $datamatrix_qts ) = @_;
+
+ ref $datamatrix_qts eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $datamatrix_hybs eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $previous_qts = {};
+ my $ba_namerefs = [];
+ my $qt_names = [];
+ my $order;
+ my $period;
+
+ # Use hybs as the benchmark; QTs may have been dropped on parsing.
+ for ( my $i = 0; $i <= $#$datamatrix_hybs; $i++ ) {
+
+ # Get the variables for this column, for readability.
+ my $qt = $datamatrix_qts->[$i];
+ my $ba_list = $datamatrix_hybs->[$i];
+
+ # QT dimension
+ my $qt_seen;
+ if ($qt) {
+ unless ( $qt_seen = $previous_qts->{$qt} ) {
+ push( @$qt_names, $qt );
+ $previous_qts->{$qt}++;
+ }
+ }
+
+ # BA dimension
+ my %ba_key = map { $_ => 1 } sort @{$ba_list};
+ my $ba_seen;
+ foreach my $old (@$ba_namerefs) {
+
+ # Compare arrays $bioassays and $old.
+ # Set $ba_seen unless there is a difference.
+ $ba_seen++ unless ( grep { !exists $ba_key{$_} } sort @$old );
+ }
+ unless ($ba_seen) {
+
+ # add the bioassay to @$ba_namerefs
+ push( @$ba_namerefs, $ba_list );
+ }
+
+ # Set our BioDataCube order. This should be settled by the
+ # second DataMatrix column. Setting this on any other iteration
+ # leads to spurious results. See below for the special cases
+ # where either B or Q is 1.
+ if ( $i == 1 ) {
+ if ( $qt_seen && !$ba_seen ) { $order ||= 'DQB'; }
+ if ( $ba_seen && !$qt_seen ) { $order ||= 'DBQ'; }
+ }
+
+ # Confirm that the detected order is followed in subsequent columns.
+ elsif ( $i > 1 && $order ) {
+
+ # Set the periodicity of the file as soon as the next (qt
+ # || ba) is seen (dependent on the order of the file).
+ if ( !$period ) {
+ if ( $order eq 'DBQ' && !$ba_seen ) {
+ $period = scalar @$qt_names;
+ }
+ elsif ( $order eq 'DQB' && !$qt_seen ) {
+ $period = scalar @$ba_namerefs;
+ }
+ }
+
+ # First block done, $order is determined, now we start
+ # checking that the rest of the file is okay.
+ if ($period) {
+ my $prev_qt = $datamatrix_qts->[ $i - 1 ];
+ my $prev_ba_list = $datamatrix_hybs->[ $i - 1 ];
+
+ if ( $order eq 'DBQ' ) {
+ if ( $qt ne $qt_names->[ $i % $period ] ) {
+ $order = undef;
+ }
+ if (( $i % $period != 0 )
+ && (grep { !exists $ba_key{$_} }
+ sort @{$prev_ba_list}
+ )
+ ) {
+ $order = undef;
+ }
+ }
+
+ elsif ( $order eq 'DQB' ) {
+ if (grep { !exists $ba_key{$_} }
+ sort @{ $ba_namerefs->[ $i % $period ] }
+ ) {
+ $order = undef;
+ }
+ if ( ( $i % $period != 0 ) && ( $qt ne $prev_qt ) ) {
+ $order = undef;
+ }
+ }
+ }
+ }
+ }
+
+ # One extra check for special case where either Q==1 or B==1
+ # We prefer DBQ as ArrayExpress can handle it.
+ if ( @$qt_names == 1 || @$ba_namerefs == 1 ) { $order = 'DBQ'; }
+
+ if ($order) {
+ return ( $ba_namerefs, $qt_names, $order );
+ }
+ else {
+ return ( [], [], $order );
+ }
+
+}
+
+sub get_bioassay_qts : RESTRICTED {
+
+ my ($self, $bioassays) = @_;
+
+ ref $bioassays eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Find the highest-level QTs available for QuantitationTypeMapping
+ # N.B. this assumes a few things - that BioAssayData is associated
+ # with BioAssay (MAGE best practice), and that we've fully created
+ # these objects before trying to add the DataMatrix.
+ my %source_qts;
+ my %source_data;
+ foreach my $bioassay (@$bioassays) {
+ my $datalist;
+ if ( $bioassay->isa('Bio::MAGE::BioAssay::MeasuredBioAssay') ) {
+ $datalist = $bioassay->getMeasuredBioAssayData;
+ }
+ elsif ( $bioassay->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) {
+ $datalist = $bioassay->getDerivedBioAssayData;
+ }
+ if ($datalist) {
+ foreach my $data (@$datalist) {
+ $source_data{ $data->getIdentifier } = $data;
+ my $source_qtd;
+ if ( ( $source_qtd = $data->getQuantitationTypeDimension )
+ && ( my $source_qts = $source_qtd->getQuantitationTypes )
+ ) {
+ foreach my $qt (@$source_qts) {
+ $source_qts{ $qt->getIdentifier } = $qt;
+ }
+ }
+ }
+ }
+ }
+
+ my @sorted_qts = map { $source_qts{$_} } sort keys %source_qts;
+ my @sorted_data = map { $source_data{$_} } sort keys %source_data;
+
+ return ( \@sorted_qts, \@sorted_data );
+}
+
+sub create_mage {
+
+ my ( $self, $file, $datarow, $tab2mage, $identifier_template ) = @_;
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $datarow eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ $tab2mage->isa('ArrayExpress::Curator::Tab2MAGE')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $identifier_template
+ and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $datamatrix_hybs = $file->get_heading_hybs();
+ my $datamatrix_qts = $file->get_heading_qts();
+
+ my $bag_of = $tab2mage->get_bags();
+
+ # Construct the dimensions
+ my ( $ba_namerefs, $qt_names, $order )
+ = $self->get_dimension_lists( $datamatrix_hybs, $datamatrix_qts );
+
+ # Internal consistency check
+ warn("Warning: Order of DataMatrix data cube is indeterminate.\n")
+ unless $order;
+
+ # Warn on unsupported cube order
+ if ( $order && $order eq 'DQB' ) {
+ warn(
+ qq{WARNING: A BioDataCube order of "$order" is not supported by the ArrayExpress MAGE-ML loader.\n}
+ );
+ }
+
+ # Create the actual qtd here
+ my ( $qt_list, $qtd_key ) = $tab2mage->qts_from_names( $file, $qt_names );
+
+ $identifier_template .= '.DataMatrix.' . unique_identifier;
+
+ my $qtd = $bag_of->{qtd}->(
+ $qtd_key,
+ { qt_list => $qt_list,
+ identifier_template => $identifier_template,
+ }
+ );
+ $file->set_mage_qtd($qtd);
+
+ # The per-column bioassays are created here.
+ my $ba_list = [];
+ my %source_qts;
+ my %source_data;
+ foreach my $listref (@$ba_namerefs) {
+
+ my $bioassay;
+
+ if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ # DBA can map to bag_of dba, mba or pba propagated up from
+ # pba (and one day directly to dba). QT and data mapping info needed.
+ my ( $qts, $datalist );
+ ( $bioassay, $qts, $datalist ) = $self->find_or_create_dm_dba(
+ $listref,
+ $bag_of,
+ $identifier_template,
+ );
+ foreach my $qt (@$qts) {
+ $source_qts { $qt->getIdentifier } = $qt;
+ }
+ foreach my $data (@$datalist) {
+ $source_data{ $data->getIdentifier } = $data;
+ }
+ }
+ elsif ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+ # MBA maps only to PBA although multiple PBAs may be
+ # combined into a single column.
+ $bioassay = $self->find_or_create_mba(
+ $listref,
+ $bag_of,
+ $identifier_template,
+ );
+ }
+ else {
+ croak(sprinf("Error: unknown DataMatrix data_type %s\n",
+ $file->get_data_type()) );
+ }
+ push( @$ba_list, $bioassay );
+ }
+
+ # Top-level DBA and DBAD. Note that a raw data matrix should
+ # ideally be coded as MBA here, but due to MAGEv1.1 limitations
+ # that's not possible. Instead we create a DBA pointing at the
+ # per-hyb MBAs.
+ $self->generate_top_level_dba_and_dbad(
+ $file,
+ $ba_list,
+ \%source_qts,
+ \%source_data,
+ $datarow,
+ $tab2mage,
+ $order,
+ $identifier_template,
+ );
+
+ return;
+}
+
+sub generate_top_level_dba_and_dbad : PRIVATE {
+
+ my ($self,
+ $file,
+ $ba_list,
+ $source_qts,
+ $source_data,
+ $datarow,
+ $tab2mage,
+ $order,
+ $identifier_template,
+ ) = @_;
+
+ ###################
+ # DerivedBioAssay #
+ ###################
+
+ my $bag_of = $tab2mage->get_bags();
+
+ # Convention dictates that Transformation name is 1:1 with file name.
+ my $trxn_name = $datarow->{$EDF_TRANSFORMATIONNAME} || $file->get_name();
+
+ # BioAssayMap and DBA both have the same $trxn_name internal identifier
+ my $dba = $bag_of->{datamatrix_dba}->(
+ $trxn_name,
+ { identifier_template => $identifier_template,
+ normalization_type => $datarow->{$EDF_TRANSFORMATIONTYPE},
+ source_bioassays => $ba_list,
+ bioassaymap_bag => $bag_of->{bam},
+ },
+ );
+
+ # BioAssayDimension and BioAssayMap
+ my $ba_dimension = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+ identifier => "$identifier_template.DerivedBioAssayDimension",
+ bioAssays => $ba_list,
+ );
+ my $ba_map = $dba->getDerivedBioAssayMap;
+
+ # Propagate the FVs to the new DBA
+ my %fv_list;
+ foreach my $bioassay (@$ba_list) {
+ if ( $bioassay->getBioAssayFactorValues ) {
+ foreach my $fv ( @{ $bioassay->getBioAssayFactorValues } ) {
+ $fv_list{ $fv->getIdentifier } = $fv;
+ }
+ }
+ }
+ $dba->setBioAssayFactorValues( [ values %fv_list ] );
+
+ #######################
+ # DerivedBioAssayData #
+ #######################
+ my $protocol;
+ $datarow->{$EDF_TRXN_PROTOCOL} && do {
+ $protocol = $bag_of->{protocol}->(
+ $datarow->{$EDF_TRXN_PROTOCOL},
+ { protocol_type => $OE_VAL_NORMALIZATION }
+ );
+ };
+
+ my $trxn_software = q{};
+ my $software_version = q{};
+ $datarow->{$EDF_TRXN_SOFTWARE} && do {
+ my $name;
+ ( $name, $software_version )
+ = $tab2mage->parse_software( $datarow->{$EDF_TRXN_SOFTWARE} );
+ $trxn_software = $bag_of->{software}->(
+ "TRXN:$name" . ( $software_version ? ":$software_version" : q{} ),
+ { identifier_template => "$identifier_template.Transformation",
+ name => $name,
+ type => $OE_VAL_TRANSFORMATION_SOFTWARE,
+ }
+ );
+ };
+
+ # Allow empty ded_type, e.g. for checker graph visualization.
+ my $ded;
+ if ( my $dedtype = $file->get_ded_type() ) {
+ my $dedclass
+ = "Bio::MAGE::BioAssayData::" . $file->get_ded_type() . "Dimension";
+ $ded = $dedclass->new( identifier => $file->get_ded_identifier() );
+ }
+
+ my @sorted_qts = map { $source_qts->{$_} } sort keys %$source_qts;
+ my @sorted_data = map { $source_data->{$_} } sort keys %$source_data;
+
+ my $dbad = $bag_of->{datamatrix_dbad}->(
+ $file->get_name(),
+ { filename => $file->get_target_filename(),
+ de_dimension => $ded,
+ qt_dimension => $file->get_mage_qtd(),
+ ba_dimension => $ba_dimension,
+ identifier_template => $identifier_template,
+ protocol => $protocol,
+ source_bioassay_dataset => \@sorted_data,
+ bioassay_map => $ba_map,
+ software => $trxn_software,
+ software_version => $software_version,
+ order => $order,
+ source_qts => \@sorted_qts,
+ qtm_bag => $bag_of->{qtm},
+ }
+ );
+
+ # Add data to the DBA.
+ $tab2mage->dba_add_data( $dba, $dbad );
+
+ return;
+}
+
+sub mbas_from_pbas : PRIVATE {
+
+ # Get a list of MBAs which were derived from a given list of PBAs.
+ my ( $self, $pbas, $bag_of ) = @_;
+ my @mbas;
+
+ foreach my $pba (@$pbas) {
+
+ # Handle the two-colour hyb coding. Assumes that merged PBAs
+ # have a .merged name suffix.
+ if ( $pba->getBioAssayCreation()
+ && ( scalar @{ $pba->getChannels() || [] } > 1 ) ) {
+
+ # FIXME we want to follow the BioAssayTreatment chain
+ # here, instead of relying on naming conventions.
+ my $pba_name = $pba->getName();
+ $pba = $bag_of->{extended_pba}->(
+ $pba->getName() . ".merged"
+ );
+ unless ( $pba ) {
+ die(sprintf(
+ "Internal error: Merged PBA undefined for two-color coding (%s).",
+ $pba_name)
+ );
+ }
+ }
+
+ foreach my $bioassay ( @{ $bag_of->{mba}->() } ) {
+ my $fext = $bioassay->getFeatureExtraction;
+ if ( $fext->getPhysicalBioAssaySource()->getIdentifier eq
+ $pba->getIdentifier ) {
+ push( @mbas, $bioassay );
+ }
+ }
+ }
+ return \@mbas;
+}
+
+sub dbas_from_mbas : PRIVATE {
+
+ # Get a list of non-DataMatrix DBAs derived from a given list of MBAs.
+ my ( $self, $mbas, $bag_of ) = @_;
+ my @dbas;
+
+ foreach my $mba (@$mbas) {
+ foreach my $dba ( @{ $bag_of->{dba}->() } ) {
+ my @bams = @{ $dba->getDerivedBioAssayMap() || [] };
+ foreach my $bam (@bams) {
+ foreach my $source ( @{ $bam->getSourceBioAssays || [] } ) {
+ if ( $source->getIdentifier eq $mba->getIdentifier ) {
+ push( @dbas, $dba );
+ }
+ }
+ }
+ }
+ }
+ return \@dbas;
+}
+
+sub retrieve_bioassay : RESTRICTED {
+
+ # Here we check for the existence of a bioassay with internal key
+ # $heading_hyb, and die if there isn't. This prevents a much
+ # messier crash later on. Return the MAGE object if found.
+
+ my ( $self, $heading_hyb, $ba_bag, $id_column, $fallback_bag ) = @_;
+
+ $id_column ||= 'hybridization/normalization ID';
+
+ # We don't want errors here to email the autosubmissions system admin.
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+
+ my $bioassay = $ba_bag->( $heading_hyb );
+
+ # Fallback if possible (e.g. MAGE-TAB mapping to Scan for
+ # single-hyb experiments).
+ if ( ! $bioassay && $fallback_bag ) {
+ $bioassay = $self->find_pba_for_bat( $heading_hyb, $fallback_bag );
+ }
+ unless ( $bioassay ) {
+ croak("ERROR: unrecognized data matrix $id_column:"
+ . " $heading_hyb\n");
+ }
+
+ # Reinstate the __DIE__ signal handler.
+ $SIG{__DIE__} = $sighandler if $sighandler;
+
+ return $bioassay;
+}
+
+sub find_pba_for_bat : PRIVATE {
+
+ my ( $self, $heading_hyb, $bag ) = @_;
+
+ foreach my $bioassay ( @{ $bag->() } ) {
+ foreach my $bat ( @{ $bioassay->getBioAssayTreatments() || [] } ) {
+
+ # Success.
+ return $bioassay if ( $bat->getName() eq $heading_hyb );
+ }
+ }
+ return; # Failure.
+}
+
+sub find_or_create_mba : PRIVATE {
+
+ my ( $self, $hybs, $bag_of, $identifier_template ) = @_;
+
+ ref $hybs eq 'ARRAY' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $bag_of eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $identifier_template
+ and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my %contributing_bioassays;
+
+ my %overall_pba;
+
+ foreach my $heading_hyb (@$hybs) {
+
+ my $pba = $self->retrieve_bioassay( $heading_hyb, $bag_of->{pba} );
+ $overall_pba{ $pba->getName() } = $pba;
+ }
+
+ # Either return the column bioassay, or a new ba mapping to
+ # multiple column bioassays.
+ my $datamatrix_bioassay = $self->find_or_create_per_column_mba(
+ [ values %overall_pba ],
+ $identifier_template,
+ $bag_of,
+ );
+
+ return $datamatrix_bioassay;
+}
+
+sub find_or_create_dm_dba : PRIVATE {
+
+ my ( $self, $hybs, $bag_of, $identifier_template ) = @_;
+
+ ref $hybs eq 'ARRAY' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $bag_of eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $identifier_template
+ and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my %contributing_bioassays;
+
+ my %known_pba_names = map { $_->getName => 1 } @{ $bag_of->{pba}->() };
+
+ # We need to propagate PBA object up to top-level MBA/DBA for
+ # correct mapping.
+ my @overall_mbas;
+ foreach my $heading_hyb (@$hybs) {
+
+ my $pba;
+
+ # This may fail if the data matrix maps to Normalizations
+ # rather than Hybridizations.
+ eval {
+ $pba = $self->retrieve_bioassay( $heading_hyb, $bag_of->{pba} );
+ };
+
+ if ($EVAL_ERROR) {
+
+ # Try again with DBAs if PBAs no good. We now allow this
+ # to croak on failure.
+ my $dba = $self->retrieve_bioassay( $heading_hyb, $bag_of->{dba} );
+ $contributing_bioassays{ $dba->getIdentifier } = $dba;
+ }
+ else {
+
+ # Get lists of MBAs and/or DBAs linked to the PBA. N.B. Any
+ # DBAs returned here should be certified non-DataMatrix.
+ my $mbas = $self->mbas_from_pbas( [$pba], $bag_of );
+ my $dbas = $self->dbas_from_mbas( $mbas, $bag_of );
+
+ push( @overall_mbas, @$mbas );
+
+ # Get a uniqued list of the highest-level bioassays possible
+ if ( scalar(@$dbas) ) {
+ foreach my $bioassay (@$dbas) {
+ $contributing_bioassays{ $bioassay->getIdentifier }
+ = $bioassay;
+ }
+ }
+ elsif ( scalar(@$mbas) ) {
+ foreach my $bioassay (@$mbas) {
+ $contributing_bioassays{ $bioassay->getIdentifier }
+ = $bioassay;
+ }
+ }
+ else {
+ $contributing_bioassays{ $pba->getIdentifier } = $pba;
+ }
+ }
+ }
+
+ # Get a list of QTs to use in QuantitationTypeMap(ping).
+ my ( $source_qts, $source_data )
+ = $self->get_bioassay_qts( [ values %contributing_bioassays ] );
+
+ # If no QTs returned from the top level, try again with all the MBAs
+ # we found.
+ unless (@$source_qts) {
+ ( $source_qts, $source_data )
+ = $self->get_bioassay_qts( \@overall_mbas );
+ }
+
+ # Either return the column bioassay, or a new ba mapping to
+ # multiple column bioassays.
+ my $datamatrix_bioassay = $self->find_or_create_per_column_bioassay(
+ [ values %contributing_bioassays ],
+ $identifier_template,
+ $bag_of->{datamatrix_dba}, # future derived matrices can't map to these.
+ $bag_of->{bam},
+ );
+
+ return ( $datamatrix_bioassay, $source_qts, $source_data );
+}
+
+sub find_or_create_per_column_mba : PRIVATE {
+
+ # Get a list of MBAs from the PBAs; pass this on up to
+ # find_or_create_per_column_bioassay to either return a single
+ # MBA or a DBA linked to multiple MBAs (e.g. from multiple scans).
+ my ( $self,
+ $pbas,
+ $identifier_template,
+ $bag_of,
+ ) = @_;
+
+ if ( scalar( @$pbas ) > 1 ) {
+
+ # This is a bit of a MAGE limitation - we'd need to come up
+ # with some kind of PBA-based hack to support this. These
+ # cases should be using DerivedBioAssays anyway.
+ croak(
+ "Error: Cannot map to multiple hybridizations in a single data matrix column."
+ );
+ }
+ elsif ( scalar( @$pbas ) != 1 ) {
+
+ # This should be unusual, we crash earlier in such cases.
+ croak(
+ "Error: Cannot retrieve PBA information."
+ );
+ }
+
+ my $mbas = $self->mbas_from_pbas( $pbas, $bag_of );
+
+ # Check that @$mbas has entries, if not create one and return it.
+ my $bioassay;
+
+ if ( scalar @$mbas ) {
+
+ $bioassay = $self->find_or_create_per_column_bioassay(
+ $mbas,
+ $identifier_template,
+ $bag_of->{dba}, # future derived matrices can map to these.
+ $bag_of->{bam},
+ );
+ }
+ else {
+
+ # MBA creation here.
+ my $unique = unique_identifier();
+ my $pba = $pbas->[0];
+ my $mba = $bag_of->{mba}->(
+ $pba->getName(),
+ {
+ identifier_template => $unique,
+ physical_bioassay => $pba,
+ }
+ );
+
+ # Propagate FVs from the PBA.
+ $self->update_bioassay_fvs(
+ $mba,
+ $pba->getBioAssayFactorValues(),
+ );
+
+ $bioassay = $mba;
+ }
+
+ return $bioassay;
+}
+
+sub find_or_create_per_column_bioassay : RESTRICTED {
+
+ my ( $self,
+ $source_bioassays,
+ $identifier_template,
+ $bioassay_bag,
+ $bam_bag,
+ ) = @_;
+
+ # $datamatrix_bioassay is the returned bioassay. It will be either a
+ # single contributing bioassay, or a new bioassay mapping to
+ # multiple contributing bioassays.
+ my $datamatrix_bioassay;
+
+ # Create BAM from @$source_bioassays unless there's only
+ # one bioassay involved.
+ if ( scalar( @$source_bioassays ) == 1 ) {
+
+ # Only one bioassay; simple case
+ $datamatrix_bioassay = $source_bioassays->[0];
+ }
+
+ else {
+
+ # Multiple bioassays specified
+ my $unique = unique_identifier;
+ my @ba_names;
+ foreach my $bioassay ( @$source_bioassays ) {
+ push( @ba_names, $bioassay->getName() );
+ }
+ my $name = join( ";", @ba_names ) || "DataMatrix.$unique";
+ my $identifier = "$identifier_template.$unique";
+ $datamatrix_bioassay = $bioassay_bag->(
+ $name,
+ { identifier_template => $identifier,
+ source_bioassays => $source_bioassays,
+ bioassaymap_bag => $bam_bag,
+ },
+ );
+
+ # Propagate FVs
+ foreach my $bioassay ( @$source_bioassays ) {
+ $self->update_bioassay_fvs( $datamatrix_bioassay,
+ $bioassay->getBioAssayFactorValues() );
+ }
+ }
+
+ return $datamatrix_bioassay;
+}
+
+sub update_bioassay_fvs : RESTRICTED {
+
+ # Wrapper method for updating the FVs associated with a bioassay.
+ my ( $self, $bioassay, $fvs ) = @_;
+
+ update_factorvalues( $bioassay, $fvs );
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Metrics.pm b/lib/ArrayExpress/Datafile/Metrics.pm
new file mode 100644
index 0000000..909326c
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Metrics.pm
@@ -0,0 +1,195 @@
+#!/usr/bin/env perl
+#
+# DataMetrics.pm - a module derived from and used in the experiment
+# checker script. Contains routines which might be useful elsewhere.
+#
+# Tim Rayner 2005 ArrayExpress team, EBI
+#
+# $Id: Metrics.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Metrics;
+
+use strict;
+use warnings;
+
+use Carp;
+use Scalar::Util qw(looks_like_number);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ check_data_metrics
+ calculate_benford
+ pearson_correlation
+);
+
+## Correlation subroutine originally written by Greg Grant from RAD
+sub pearson_correlation {
+
+ my ( $hash1, $hash2 ) = @_;
+
+ ref $hash1 eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $hash2 eq 'HASH' or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my @vector1;
+ my @vector2;
+ my $correlation;
+
+ my $i = 0;
+
+ # If both datasets have a value that looks like a number, use it in
+ # the correlation calculation.
+ foreach my $key ( keys %$hash2 ) {
+ if ( defined $hash1->{$key}
+ && looks_like_number( $hash1->{$key} )
+ && looks_like_number( $hash2->{$key} ) ) {
+ $vector1[$i] = $hash1->{$key};
+ $vector2[$i] = $hash2->{$key};
+ $i++;
+ }
+ }
+
+ my $vector_length = $i - 1;
+
+ my $xy = 0;
+ my $x = 0;
+ my $y = 0;
+ my $xx = 0;
+ my $yy = 0;
+
+ for ( my $i = 0; $i < $vector_length; $i++ ) {
+ $xy = $xy + $vector1[$i] * $vector2[$i];
+ $x = $x + $vector1[$i];
+ $y = $y + $vector2[$i];
+ $xx = $xx + ( $vector1[$i] )**2;
+ $yy = $yy + ( $vector2[$i] )**2;
+ }
+
+ if (sqrt(
+ ( $vector_length * $xx - $x * $x )
+ * ( $vector_length * $yy - $y * $y )
+ ) != 0
+ ) {
+
+ $correlation = ( $vector_length * $xy - $x * $y ) / (
+ sqrt(
+ ( $vector_length * $xx - $x * $x )
+ * ( $vector_length * $yy - $y * $y )
+ )
+ );
+
+ }
+ else {
+
+ $correlation = -2;
+
+ }
+
+ return $correlation;
+
+}
+
+sub calculate_benford {
+
+ my $data_metrics = shift;
+
+ ref $data_metrics eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my @benford;
+ foreach my $heading ( keys %$data_metrics ) {
+ for ( my $digit = 1; $digit < 10; $digit++ ) {
+ $benford[$digit] +=
+ $data_metrics->{$heading}{benford}
+ ? vec( $data_metrics->{$heading}{benford}, $digit, 32 )
+ : 0;
+ }
+ }
+
+ my $bentotal = 0;
+ foreach my $digitcount ( @benford[ 1 .. 9 ] ) {
+ $bentotal += $digitcount if $digitcount;
+ }
+ my $benval = $bentotal
+ ? ( $benford[1] / $bentotal ) * 100
+ : 'N/A'; # Avoid illegal division by zero if we've got real problems
+
+ return $benval;
+
+}
+
+sub check_data_metrics {
+
+ my $data_metrics = shift;
+
+ ref $data_metrics eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Do some checks on the data here.
+ foreach my $header ( keys %$data_metrics ) {
+
+ # Log ratios should be between certain limits.
+ if ($data_metrics->{$header}{scale}
+ && (
+
+ # column heading looks like 'log ratio'
+ ( $header =~ m/log ?ratio/i )
+
+ || (
+
+ # Or subclass is Ratio...
+ $data_metrics->{$header}{subclass}
+ && (( $data_metrics->{$header}{subclass} eq 'Ratio' )
+
+ # and heading/scale looks like 'log'
+ && ( ( $header =~ m/\blog/i ) )
+ || ( $data_metrics->{$header}{scale} =~ m/^log_/i )
+ )
+ )
+ )
+ ) {
+
+ LOGRATIOSCALECHECK:
+ {
+ my $error_message = 'Log ratios outside range';
+
+ ( $data_metrics->{$header}{scale} eq 'log_base_2' ) && do {
+ $data_metrics->{$header}{errors}{$error_message}++
+
+ # Scanners seem to max out at 100,000 (i.e. log2(100,000) = 17 or so)
+ if ( ( $data_metrics->{$header}{max} > 20 )
+ || ( $data_metrics->{$header}{min} < -20 ) );
+ last LOGRATIOSCALECHECK;
+ };
+
+ ( $data_metrics->{$header}{scale} eq 'log_base_10' ) && do {
+ $data_metrics->{$header}{errors}{$error_message}++
+
+ # Scanners seem to max out at 100,000 (i.e. log10(100,000) = 5)
+ if ( ( $data_metrics->{$header}{max} > 6 )
+ || ( $data_metrics->{$header}{min} < -6 ) );
+ last LOGRATIOSCALECHECK;
+ };
+
+ # Play it safe. We're unlikely to use this anyway
+ $data_metrics->{$header}{errors}{$error_message}++
+ if ( ( $data_metrics->{$header}{max} > 1 )
+ || ( $data_metrics->{$header}{min} < -1 ) );
+ }
+ }
+
+ # Check on any saturation data in the file (e.g. in GenePix files)
+ my $error_message = 'Saturation indicator too high';
+
+ # regexp may need work FIXME
+ if ( ( $header =~ m/\% ?sat/i )
+ && ( $data_metrics->{$header}{max} > 50 ) ) {
+ $data_metrics->{$header}{errors}{$error_message}++;
+ }
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/Parser.pm b/lib/ArrayExpress/Datafile/Parser.pm
new file mode 100644
index 0000000..262ba33
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/Parser.pm
@@ -0,0 +1,2016 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the tab2mage script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Parser.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+package ArrayExpress::Datafile::Parser;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Digest::MD5 qw( md5 ); # Used for tracking DEDs
+use IO::File;
+use File::Copy;
+use Carp;
+use Readonly;
+use Scalar::Util qw( openhandle );
+use List::Util qw(first);
+use List::MoreUtils qw( any );
+use English qw( -no_match_vars );
+use Storable qw(store_fd fd_retrieve);
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Datafile::QT_list qw(get_QTs);
+
+use ArrayExpress::Curator::Database qw(
+ retrieve_AE_featurelist
+ map_affy_accno_to_name
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $EDF_EXPTACCESSION
+ $EDF_EXPTDOMAIN
+ $EDF_EXPTNAME
+ $EDF_DYE
+ $EDF_HYB_PROTOCOL
+ $EDF_SCAN_PROTOCOL
+ $EDF_FEXT_PROTOCOL
+ $EDF_NORM_PROTOCOL
+ $EDF_SCAN_SOFTWARE
+ $EDF_FEXT_SOFTWARE
+ $EDF_NORM_SOFTWARE
+ $EDF_IMAGE_FORMAT
+ $EDF_ARRAYSERIAL
+ $EDF_FILE_PREFIX
+ $EDF_FILE_SUFFIX
+ $EDF_HYB_PARAMS
+ $EDF_SCAN_PARAMS
+ $EDF_FEXT_PARAMS
+ $EDF_NORM_PARAMS
+ $EDF_FEXT_STATS
+ $EDF_NORM_STATS
+ $EDF_HYB_HARDWARE
+ $EDF_SCAN_HARDWARE
+ $EDF_HYB_HW_PARAMS
+ $EDF_SCAN_HW_PARAMS
+ $EDF_NORMALIZATIONTYPE
+
+ $OE_VAL_NORMALIZATION
+ $OE_VAL_FEATUREEXTRACTION
+ $OE_VAL_SCANNING
+ $OE_VAL_HYBRIDIZATION
+
+ $AE_LABELCOMPOUND_PREFIX
+ $AE_CHANNEL_PREFIX
+);
+
+use ArrayExpress::Curator::Common qw(
+ get_filepath_from_uri
+ strip_discards
+ clean_hash
+ find_cdf
+ $RE_EMPTY_STRING
+);
+
+Readonly my $AFFY_PROTOCOL_PREFIX => 'Affymetrix:Protocol:';
+Readonly my $AFFY_PARAMETER_PREFIX => 'Affymetrix:Parameter:';
+
+my %quantitation_types : ATTR( :get<quantitation_types>, :default<undef> );
+my %use_binary_datafiles : ATTR( :get<use_binary_datafiles>, :init_arg<use_binary_datafiles>, :default<undef> );
+my %output_directory : ATTR( :get<output_directory>, :init_arg<output_directory>, :default<q{.}> );
+my %source_directory : ATTR( :get<source_directory>, :init_arg<source_directory>, :default<q{.}> );
+my %include_known_qts : ATTR( :get<include_known_qts>, :init_arg<include_known_qts>, :default<undef> );
+my %allow_undef_qts : ATTR( :get<allow_undef_qts>, :init_arg<allow_undef_qts>, :default<undef> );
+my %reporter_prefix : ATTR( :get<reporter_prefix>, :init_arg<reporter_prefix>, :default<undef> );
+my %compseq_prefix : ATTR( :get<compseq_prefix>, :init_arg<compseq_prefix>, :default<undef> );
+my %ignore_size_limits : ATTR( :name<ignore_size_limits>, :default<undef> );
+
+my %error_fh : ATTR( :name<error_fh>, :default<\*STDOUT> );
+my %clobber : ATTR( :name<clobber>, :default<0> );
+my %namespace : ATTR( :name<namespace>, :default<q{}> );
+my %is_standalone : ATTR( :name<is_standalone>, :default<undef> );
+my %feature_fh : ATTR( :set<feature_fh>, :default<undef> );
+my %cel_types : ATTR( :get<cel_types>, :default<{}> );
+my %array_cache : ATTR( :default<{}> );
+my %cdf_cache : ATTR( :default<{}> );
+my %ded_cache : ATTR( :default<{}> );
+
+my %protocol_bag : ATTR( :get<protocol_bag>, :init_arg<protocol_bag>, :default<sub{ die("Error: Parser protocol_bag not initialized.") }> );
+my %parameter_bag : ATTR( :get<parameter_bag>, :init_arg<parameter_bag>, :default<sub{ die("Error: Parser parameter_bag not initialized.") }> );
+
+sub START {
+
+ my ($self, $id, $args) = @_;
+
+ # Initialize QTs (this is more complicated than a simple setter
+ # method as provided by Class::Std). This *MUST* be done after
+ # object initialization.
+ $self->set_quantitation_types($args->{quantitation_types});
+
+ return;
+}
+
+sub get_unique_de_dimension {
+
+ my ( $self, $ded_array, $array, $ded_type ) = @_;
+
+ ref $ded_array eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $array and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $ded_type and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # We use an md5 hash string here rather than the full DED to save
+ # on memory.
+ my $md5 = Digest::MD5->new();
+ foreach my $de_id (@$ded_array) { $md5->add($de_id); }
+ my $ded_md5 = $md5->digest;
+
+ # Tracker hash format: $ded_cache = {$ded_md5 => $ded_identifier}
+ # If we've already seen this DED, use the old $ded_identifier and
+ # skip adding it again.
+ my $ded_identifier;
+
+ unless ( $ded_identifier = $self->get_ded_cache($ded_md5) ) {
+
+ $ded_identifier = $self->set_ded_cache(
+ $ded_md5,
+ $ded_array,
+ $array,
+ $ded_type,
+ );
+
+ }
+
+ return wantarray ? ( $ded_identifier, $ded_type ) : $ded_identifier;
+
+}
+
+sub parse {
+
+ my ( $self, $file, $datarow ) = @_;
+
+ $datarow ||= {};
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # recheck the file; some time may have elapsed
+ unless ( -e $file->get_path() ) {
+ croak( "ERROR: Missing data file: "
+ . $file->get_path()
+ . ". This script is aborting.\n" );
+ }
+
+ print STDOUT ( "Processing file " . $file->get_name() . "\n" );
+
+ # Set the error filehandle on Datafile to redirect warnings/errors.
+ $file->set_error_fh( $self->get_error_fh() );
+
+ # Draw a line in the log file.
+ my $filler_length = 80 - ( 3 + 2 + length( $file->get_name() ) );
+ print { $self->get_error_fh() }
+ ( q{-} x 3 . q{ }
+ . $file->get_name() . q{ }
+ . q{-} x $filler_length
+ . qq{\n} );
+
+ # Test for file size
+ if ( ( -s $file->get_path() > $CONFIG->get_MAX_DATAFILE_SIZE() )
+ && ! $self->get_ignore_size_limits() ) {
+
+ # Set some needed parameters
+ if ( $file->get_data_type eq 'raw' ) {
+ $file->set_ded_type('Feature');
+ }
+ else {
+ $file->set_ded_type('Reporter');
+ }
+ $file->set_ded_identifier(
+ '+++++++++++++++++++++ERROR++++++++++++++++++++++');
+
+ # Alert the user
+ my $error_msg = "\n *** PARSE ERROR: File "
+ . $file->get_name()
+ . " is too large to be processed safely.\n"
+ . " *** This file will be skipped, but a dummy BioAssayData object will be created.\n\n";
+ print { $self->get_error_fh() } ($error_msg);
+ print STDERR ($error_msg);
+ }
+
+ # If the file is small enough (usually it will be) we process it here.
+ elsif ( $file->get_is_binary() || ( $file->get_path() =~ m/\. (CEL|CHP) \z/ixms ) ) {
+
+ # Deal with Affy files.
+ $self->_parse_affy_datafile( $file, $datarow );
+ }
+
+ else { # Text files, including Affy CELv3.
+ $self->_parse_text_datafile( $file, $datarow );
+ }
+
+ return;
+
+}
+
+####################
+# Accessor methods #
+####################
+
+sub get_feature_fh {
+
+ # defaults to temporary filehandle on first use (i.e. not on
+ # object initialization).
+ my ( $self ) = @_;
+
+ $feature_fh{ident $self} ||= IO::File->new_tmpfile;
+
+ return $feature_fh{ident $self};
+}
+
+sub get_ded_count {
+
+ # Method to return the number of DEDs held in the DED
+ # tracker. Used to detect whether DED processing should be invoked
+ # following MAGE-ML generation.
+ my $self = shift;
+
+ return scalar(grep { defined $_ } values %{ $ded_cache{ident $self} });
+}
+
+###################
+# Private methods #
+###################
+
+sub set_quantitation_types : PRIVATE {
+
+ my ( $self, $qtfile ) = @_;
+
+ if ( $qtfile || ! $quantitation_types{ident $self} ) {
+ $quantitation_types{ident $self}
+ = get_QTs( $qtfile, $self->get_include_known_qts() );
+ }
+
+ return $quantitation_types{ident $self};
+}
+
+sub set_cel_types : PRIVATE {
+
+ my ( $self, %new_cels ) = @_;
+
+ foreach my $new_key ( keys %new_cels ) {
+ ref $new_key and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $new_cels{$new_key}
+ and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ $cel_types{ident $self}{$new_key} = $new_cels{$new_key};
+ }
+
+ return $cel_types{ident $self};
+}
+
+sub set_array_cache : PRIVATE {
+
+ # Requires an array id and featurelist hashref
+ my ( $self, $array, $featurelist ) = @_;
+
+ $array or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $featurelist eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $fh = IO::File->new_tmpfile();
+
+ store_fd( $featurelist, $fh )
+ or confess("Error caching array feature list: $!");
+ seek($fh, 0, 0) or confess("Error rewinding array cache file: $!");
+
+ $array_cache{ident $self}{$array} = $fh;
+
+ return $array_cache{ident $self}{$array};
+}
+
+sub get_array_cache : PRIVATE {
+
+ # Requires an array id
+ my ( $self, $array ) = @_;
+
+ $array or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Return undef if array not in cache.
+ my $featurelist;
+ if ( my $fh = $array_cache{ident $self}{$array} ) {
+
+ my $pos = tell($fh);
+
+ seek($fh, 0, 0) or confess("Error rewinding array cache file: $!");
+
+ $featurelist = fd_retrieve( $fh )
+ or confess("Error retrieving cached array feature list: $!");
+
+ seek($fh, $pos, 0) or confess("Error resetting array cache file: $!");
+ }
+
+ return $featurelist;
+}
+
+sub set_cdf_cache : PRIVATE {
+
+ # Requires a CDF id/filename and cdffile object
+ my ( $self, $cdffile, $cdf ) = @_;
+
+ $cdffile or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ $cdf_cache{ident $self}{$cdffile} = $cdf;
+ return $cdf_cache{ident $self}{$cdffile};
+}
+
+sub get_cdf_cache : PRIVATE {
+
+ # Requires a CDF id/filename.
+ my ( $self, $cdffile ) = @_;
+
+ $cdffile or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ return $cdf_cache{ident $self}{$cdffile};
+}
+
+sub set_ded_cache : PRIVATE {
+
+ my ( $self, $ded_md5, $ded_array, $array, $ded_type ) = @_;
+
+ defined($ded_md5) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ defined($array) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ defined($ded_type) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $ded_array eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $inverted_tracker; # values to keys; we drop the ded_md5 keys
+ foreach my $value ( values %{ $ded_cache{ident $self} } ) {
+ $inverted_tracker->{$value}++;
+ }
+
+ # Determine the DesignElementDimension identifier
+ my $i = 0;
+ my $ded_identifier;
+ while ( !$ded_identifier || $inverted_tracker->{$ded_identifier} ) {
+ $ded_identifier = $self->get_namespace()
+ . ".$array."
+ . ( ++$i ) . "."
+ . $ded_type
+ . "Dimension";
+ }
+
+ $ded_cache{ident $self}{$ded_md5} = $ded_identifier;
+
+ $self->_append_designelementdimension(
+ $ded_array,
+ $ded_identifier,
+ $ded_type,
+ );
+
+ return $ded_cache{ident $self}{$ded_md5};
+
+}
+
+sub get_ded_cache : PRIVATE {
+
+ my $self = shift;
+ my $ded_md5 = shift;
+
+ defined($ded_md5) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ return $ded_cache{ident $self}{$ded_md5};
+}
+
+sub _create_identifier_ded : PRIVATE {
+
+ my ( $self, $file, $indexcols, $output_fh, $array_id ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $file->get_linebreak_type();
+
+ my $input_fh = $file->get_filehandle()
+ or croak("Error retrieving filehandle.");
+
+ my $first_index_column = $file->get_column_headings()->[ $indexcols->[0] ];
+
+ my ( $ded_array, $ded_type, $de_prefix, $de_separator );
+
+ # Fix Affymetrix identifiers (this is AE-specific).
+ my $compseq_prefix = $self->get_compseq_prefix();
+ my $reporter_prefix = $self->get_reporter_prefix();
+ my $accno = $file->get_array_design_id();
+ if ( $accno && $accno =~ m/\A A-AFFY-\w+ \z/xms ) {
+ my $affyname = map_affy_accno_to_name($accno);
+ unless (defined $reporter_prefix) {
+ $reporter_prefix = "Affymetrix:Reporter:$affyname:";
+ }
+ unless (defined $compseq_prefix) {
+ $compseq_prefix = "Affymetrix:CompositeSequence:$affyname:";
+ }
+ }
+
+ # Only one index column suggests a Reporter/CS identifier column
+ if ( $first_index_column =~ m/Probe ?(Set)? ?(Name|ID)/i
+ || $file->get_dm_chip_type() ) {
+
+ # Post-Affy normalized data
+ $de_prefix = defined( $compseq_prefix )
+ ? $compseq_prefix
+ : "Affymetrix:CompositeSequence:$array_id:";
+
+ $ded_array = $self->_extract_designelementdimension(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ $de_prefix,
+ );
+ $ded_type = 'CompositeSequence';
+ }
+
+ elsif ( $first_index_column =~ m/Composite *Sequence *Identifier/i ) {
+
+ $de_prefix = defined( $compseq_prefix )
+ ? $compseq_prefix
+ : "ebi.ac.uk:MIAMExpress:CompositeSequence:$array_id.";
+
+ $ded_array = $self->_extract_designelementdimension(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ $de_prefix,
+ );
+ $ded_type = 'CompositeSequence';
+ }
+
+ elsif ( $first_index_column =~ m/Reporter *Identifier/i
+ || $first_index_column eq 'ID_REF' ) {
+
+ $de_prefix = defined( $reporter_prefix )
+ ? $reporter_prefix
+ : "ebi.ac.uk:MIAMExpress:Reporter:$array_id.";
+
+ $ded_array = $self->_extract_designelementdimension(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ $de_prefix,
+ );
+ $ded_type = 'Reporter';
+ }
+
+ else { # Unknown is flagged as a fatal error downstream
+
+ $ded_array = $self->_extract_designelementdimension(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ 'Unknown.',
+ );
+ $ded_type = 'Unknown';
+ }
+
+ return ( $ded_array, $ded_type );
+}
+
+sub _create_feature_ded : PRIVATE {
+
+ my ( $self, $file, $indexcols, $output_fh, $array_id ) = @_;
+
+ local $INPUT_RECORD_SEPARATOR = $file->get_linebreak_type();
+
+ my $input_fh = $file->get_filehandle()
+ or croak("Error retrieving filehandle.");
+
+ my $ded_type = 'Feature';
+ my $de_prefix = "ebi.ac.uk:MIAMExpress:Feature:$array_id.";
+ my $ded_array;
+
+ if ( $self->get_is_standalone() ) {
+
+ # In standalone mode, we make up our own identifiers using the
+ # MIAMExpress convention.
+ $ded_array = $self->_extract_designelementdimension(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ $de_prefix,
+ );
+ }
+
+ else {
+
+ # Get the array feature info from the public ArrayExpress
+ # database. Attempt to get features into the array cache, if
+ # we've not done so before.
+ my $featurelist;
+ unless ( $featurelist = $self->get_array_cache($array_id) ) {
+
+ # Note that this call may croak if AE is down;
+ # however, death here is the preferred outcome in such
+ # cases (otherwise we will probably generate the wrong
+ # feature ids). May revisit this later FIXME.
+ $featurelist = retrieve_AE_featurelist($array_id);
+
+ if ($featurelist) {
+ $self->set_array_cache( $array_id, $featurelist );
+ }
+ else {
+ print STDERR (
+ "Warning: Unable to retrieve feature information from"
+ . " ArrayExpress. Falling back to default Feature identifier format.\n"
+ );
+ }
+ }
+
+ # If the array is known to us, use the AE feature identifiers
+ if ( $featurelist ) {
+ my $coord_array
+ = $self->_extract_feature_coord_strings(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ );
+
+ foreach my $coord (@$coord_array) {
+ my $feature_id;
+ unless ( $feature_id = $featurelist->{$coord} ) {
+
+ # This is most likely a dummy array; dying here would just
+ # be irritating. We create a dummy identifier instead, at
+ # risk of failing to validate the output MAGE-ML against
+ # the database. A better way to handle dummy arrays in AE
+ # is needed to fix this kludge.
+
+ print STDERR (
+ "WARNING: Feature with coordinates $coord not found for array $array_id. ",
+ "A generic dummy feature identifier will be created.\n"
+ );
+
+ $feature_id = "${de_prefix}${coord}";
+ }
+
+ # Construct the DED
+ push( @$ded_array, $feature_id );
+ }
+ }
+
+ # If the array is not found in AE, we make up our own
+ # identifiers using the MIAMExpress convention.
+ else {
+ $ded_array = $self->_extract_designelementdimension(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ $de_prefix,
+ );
+ }
+ }
+
+ return ( $ded_array, $ded_type );
+}
+
+sub _create_designelementdimension : PRIVATE {
+
+ my ( $self, $file, $output_fh, $array_id ) = @_;
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my ( $ded_array, $ded_type );
+
+ my @indexcols = @{ $file->get_index_columns() };
+
+ # Set the DE type and identifier prefix. We default to MIAMExpress
+ # format here, as that is the commonest route for array submissions
+ # linked to Tab2MAGE.
+ if ( scalar @indexcols == 1 ) {
+
+ ( $ded_array, $ded_type )
+ = $self->_create_identifier_ded(
+ $file,
+ \@indexcols,
+ $output_fh,
+ $array_id,
+ );
+
+ }
+ else { # Normal MC/MR/C/R features
+
+ ( $ded_array, $ded_type )
+ = $self->_create_feature_ded(
+ $file,
+ \@indexcols,
+ $output_fh,
+ $array_id,
+ );
+ }
+
+ my $ded_identifier
+ = $self->get_unique_de_dimension(
+ $ded_array,
+ $array_id,
+ $ded_type,
+ );
+
+ # Fix up the Datafile object
+ $file->set_ded_identifier($ded_identifier);
+ $file->set_ded_type($ded_type);
+
+ # Strip out the index column information - the file columns have
+ # been stripped out by extract_designelementdimension.
+ my @original_headings = @{ $file->get_column_headings() };
+ my @fixed_headings;
+ foreach my $index ( 0 .. $#original_headings ) {
+ unless ( any { $index == $_ } @indexcols ) {
+ push @fixed_headings, $original_headings[$index];
+ }
+ }
+ $file->set_column_headings( \@fixed_headings );
+ $file->set_heading_qts( \@fixed_headings );
+ $file->set_index_columns( [] );
+ $file->set_filehandle( $output_fh );
+
+ return;
+}
+
+sub _append_designelementdimension : PRIVATE {
+
+ my ( $self, $ded_array, $ded_identifier, $ded_type ) = @_;
+
+ ref $ded_array eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $ded_identifier and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $ded_type and confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $tmpfh = $self->get_feature_fh();
+
+ my $assnref_type
+ = ( $ded_type eq 'Feature' ) ? 'ContainedFeatures' : $ded_type . 's';
+
+ # seek to the end of the file
+ seek( $tmpfh, 0, 2 )
+ or croak(
+ "Error finding DesignElementDimension filehandle EOF to append: $!\n"
+ );
+
+ print $tmpfh (
+ qq{ <${ded_type}Dimension identifier="$ded_identifier">\n});
+
+ print $tmpfh ( qq{ <${assnref_type}_assnreflist>\n});
+
+ foreach my $designelement_id (@$ded_array) {
+ print $tmpfh (
+ qq{ <${ded_type}_ref identifier="$designelement_id"/>\n}
+ );
+ }
+
+ print $tmpfh ( qq{ </${assnref_type}_assnreflist>\n});
+
+ print $tmpfh ( qq{ </${ded_type}Dimension>\n});
+
+ # Not really necessary at this point, just here for future safety.
+ $self->set_feature_fh($tmpfh);
+
+ return;
+}
+
+sub _extract_designelementdimension : PRIVATE {
+
+ my ( $self,
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ $prefix, ) = @_;
+
+ ref $indexcols eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ openhandle($output_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $ded_array = $self->_extract_feature_coord_strings(
+ $input_fh,
+ $output_fh,
+ $indexcols,
+ );
+
+ @$ded_array = map {"${prefix}${_}"} @$ded_array;
+
+ return ($ded_array);
+
+}
+
+sub _extract_feature_coord_strings : PRIVATE {
+
+ my ( $self, $input_fh, $output_fh, $indexcols ) = @_;
+
+ ref $indexcols eq 'ARRAY'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ openhandle($input_fh) or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ openhandle($output_fh)
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $coord_array = [];
+
+ DATA_LINE:
+ while ( my $line = <$input_fh> ) {
+
+ $line =~ s/[\r\n]*$//;
+
+ # Empty lines are skipped during data export, so we skip them
+ # here also.
+ if ( $line =~ $RE_EMPTY_STRING ) {
+ next DATA_LINE;
+ }
+
+ my @line_array = split /\t/, $line;
+
+ push( @$coord_array, ( join( ".", @line_array[@$indexcols] ) ) );
+
+ my $new_line = strip_discards( $indexcols, \@line_array );
+
+ print $output_fh ( join( "\t", @$new_line ), "\n" );
+
+ }
+
+ return ($coord_array);
+
+}
+
+sub _mage_param_hash : PRIVATE {
+
+ # Converts a hash of parameter values from the Affy parsers into a
+ # parameter hash suitable for feeding into MAGE::new_protocol().
+ my ( $param_values ) = @_;
+
+ my %param_hash =
+ map { $_ => { identifier => "${AFFY_PARAMETER_PREFIX}$_",
+ name => $_, } }
+ keys %$param_values;
+ return \%param_hash;
+}
+
+sub _extract_chp_data : PRIVATE {
+
+ my ( $self, $chp, $datarow ) = @_;
+
+ # Sort out the protocols
+ $datarow->{$EDF_NORM_PROTOCOL}
+ = "${AFFY_PROTOCOL_PREFIX}" . $chp->get_algorithm();
+
+ # FIXME these won't always be the case.
+ if ( $chp->get_version == 12 ) {
+ $datarow->{$EDF_NORM_SOFTWARE}
+ ||= 'Affymetrix MicroArraySuite (5.0)';
+ }
+ if ( $chp->get_version == 8 ) {
+ $datarow->{$EDF_NORM_SOFTWARE}
+ ||= 'Affymetrix MicroArraySuite (4.0)';
+ }
+ else {
+ $datarow->{$EDF_NORM_SOFTWARE}
+ ||= 'Affymetrix GeneChip Operating Software (1.2)';
+ }
+
+ # Parameters
+ my %chp_params;
+ foreach my $param ( keys %{ $chp->get_parameters() } ) {
+ $chp_params{"${AFFY_PARAMETER_PREFIX}$param"}
+ = $chp->get_parameters()->{$param};
+ }
+ $datarow->{$EDF_NORM_PARAMS} = \%chp_params;
+
+ # Make sure the protocol params are declared in any
+ # generated MAGE-ML.
+ $self->get_protocol_bag()->(
+ $datarow->{$EDF_NORM_PROTOCOL},
+ { protocol_type => $OE_VAL_NORMALIZATION,
+ name => $chp->get_algorithm(),
+ parameters => _mage_param_hash( $chp->get_parameters() ),
+ parameter_bag => $self->get_parameter_bag(),
+ }
+ );
+
+ # QC Stats
+ $datarow->{$EDF_NORM_STATS} = $chp->get_stats();
+
+ # DerivedBioAssayType. This is probably not foolproof FIXME
+ DBA_TYPE:
+ {
+ ( $chp->get_algorithm() =~ /ExpressionStat/ ) && do {
+ $datarow->{$EDF_NORMALIZATIONTYPE}
+ = "Statistical CHP Analysis";
+ last DBA_TYPE;
+ };
+ ( $chp->get_algorithm() =~ /ExpressionCall/ ) && do {
+ $datarow->{$EDF_NORMALIZATIONTYPE}
+ = "Empirical CHP Analysis";
+ last DBA_TYPE;
+ };
+ }
+
+ return;
+}
+
+sub _extract_cel_data : PRIVATE {
+
+ my ( $self, $cel, $datarow ) = @_;
+
+ # CEL parameters
+ my %cel_params;
+ foreach my $param ( keys %{ $cel->get_parameters() } ) {
+ $cel_params{"${AFFY_PARAMETER_PREFIX}$param"}
+ = $cel->get_parameters()->{$param};
+ }
+ $datarow->{$EDF_FEXT_PARAMS} = \%cel_params;
+
+ # Make sure the protocol params are declared in any
+ # generated MAGE-ML.
+ $self->get_protocol_bag()->(
+ $datarow->{$EDF_FEXT_PROTOCOL},
+ { protocol_type => $OE_VAL_FEATUREEXTRACTION,
+ name => 'Affymetrix CEL analysis (Percentile)',
+ parameters => _mage_param_hash( $cel->get_parameters() ),
+ parameter_bag => $self->get_parameter_bag(),
+ }
+ );
+
+ # QC Stats
+ $datarow->{$EDF_FEXT_STATS} = $cel->get_stats();
+
+ # Hardware (This is an Affy-specific hack for now FIXME)
+ $datarow->{$EDF_HYB_HARDWARE}
+ ||= 'Affymetrix:Hardware:FluidicsStation';
+ $datarow->{$EDF_SCAN_HARDWARE} ||= 'Affymetrix:Hardware:Scanner';
+
+ # FIXME this below won't always be the case
+ if ( $cel->get_version() == 3 ) {
+ $datarow->{$EDF_SCAN_SOFTWARE}
+ ||= 'Affymetrix MicroArraySuite (5.0)';
+ $datarow->{$EDF_FEXT_SOFTWARE}
+ ||= 'Affymetrix MicroArraySuite (5.0)';
+ }
+ elsif ( $cel->get_version() == 4 ) {
+ $datarow->{$EDF_SCAN_SOFTWARE}
+ ||= 'Affymetrix GeneChip Operating Software (1.2)';
+ $datarow->{$EDF_FEXT_SOFTWARE}
+ ||= 'Affymetrix GeneChip Operating Software (1.2)';
+ }
+ else {
+ $datarow->{$EDF_SCAN_SOFTWARE}
+ ||= 'Affymetrix Expression Console (1.1)';
+ $datarow->{$EDF_FEXT_SOFTWARE}
+ ||= 'Affymetrix Expression Console (1.1)';
+ }
+
+ # image format
+ $datarow->{$EDF_IMAGE_FORMAT} ||= 'Affymetrix_DAT';
+
+ return;
+}
+
+sub _read_exp_file : PRIVATE {
+
+ my ( $self, $expfile ) = @_;
+
+ # Check we don't have an alternate source directory
+ $expfile = get_filepath_from_uri(
+ $expfile,
+ $self->get_source_directory(),
+ );
+
+ # Parse the file.
+ my $factory = ArrayExpress::Datafile::Affymetrix->new();
+ my $exp = $factory->make_parser($expfile);
+ $exp->parse();
+
+ return $exp;
+}
+
+sub _extract_exp_data : PRIVATE {
+
+ my ( $self, $expfile, $datarow ) = @_;
+
+ my $exp = $self->_read_exp_file( $expfile );
+
+ # Correct the hyb protocol, if it's known. EXP file
+ # information trumps all.
+ if ( $exp->get_protocol() ) {
+ $datarow->{$EDF_HYB_PROTOCOL}
+ = "${AFFY_PROTOCOL_PREFIX}Hybridization-"
+ . $exp->get_protocol();
+ }
+
+ # EXP parameters
+ my %hyb_params;
+ foreach my $param ( keys %{ $exp->get_parameters() } ) {
+ $hyb_params{"${AFFY_PARAMETER_PREFIX}$param"}
+ = $exp->get_parameters()->{$param};
+ }
+ $datarow->{$EDF_HYB_PARAMS} = \%hyb_params;
+ $datarow->{$EDF_SCAN_PARAMS} = clean_hash(
+ {
+ "${AFFY_PARAMETER_PREFIX}ScanPixelSize" =>
+ $exp->get_pixel_size(),
+ "${AFFY_PARAMETER_PREFIX}NumberOfScans" =>
+ $exp->get_num_scans(),
+ }
+ );
+ $datarow->{$EDF_HYB_HW_PARAMS} = clean_hash(
+ {
+ "${AFFY_PARAMETER_PREFIX}FluidicsStationNumber" =>
+ $exp->get_station(),
+ "${AFFY_PARAMETER_PREFIX}FluidicsStationPosition" =>
+ $exp->get_module(),
+ }
+ );
+ $datarow->{$EDF_SCAN_HW_PARAMS} = clean_hash(
+ {
+ "${AFFY_PARAMETER_PREFIX}ScannerID" =>
+ $exp->get_scanner_id(),
+ "${AFFY_PARAMETER_PREFIX}ScannerType" =>
+ $exp->get_scanner_type(),
+ }
+ );
+
+ # Make sure the protocol params are declared in any
+ # generated MAGE-ML.
+ $self->get_protocol_bag()->(
+ $datarow->{$EDF_HYB_PROTOCOL},
+ { protocol_type => $OE_VAL_HYBRIDIZATION,
+ name => $exp->get_protocol(),
+ parameters => _mage_param_hash( $exp->get_parameters() ),
+ parameter_bag => $self->get_parameter_bag(),
+ }
+ );
+ $self->get_protocol_bag()->(
+ $datarow->{$EDF_SCAN_PROTOCOL},
+ { protocol_type => $OE_VAL_SCANNING,
+ name => 'Affymetrix Scanning',
+ parameters => _mage_param_hash(
+ {
+ 'ScanPixelSize' => $exp->get_pixel_size(),
+ 'NumberOfScans' => $exp->get_num_scans(),
+ },
+ ),
+ parameter_bag => $self->get_parameter_bag(),
+ }
+ );
+
+ # Grab the array chip lot if it's available. We default to the
+ # EXP file value as it's more likely to be correct.
+ $exp->get_chip_lot()
+ && do { $datarow->{$EDF_ARRAYSERIAL} = $exp->get_chip_lot(); };
+
+ return $exp->get_chip_type();
+}
+
+sub _get_bioassays_from_mbadata : PRIVATE {
+
+ my ( $self, $badata ) = @_;
+
+ my (@mbas, @pbas);
+ if ( my $dim = $badata->getBioAssayDimension() ) {
+ foreach my $mba ( @{ $dim->getBioAssays() || [] } ) {
+ push @mbas, $mba;
+ if ( my $fext = $mba->getFeatureExtraction() ) {
+ if ( my $pba = $fext->getPhysicalBioAssaySource() ) {
+ push @pbas, $pba;
+ }
+ }
+ }
+ }
+
+ return ( \@mbas, \@pbas );
+}
+
+sub _add_expdata_to_mbadata : PRIVATE {
+
+ # If the file has a ba_data with a NVT referring to what looks
+ # like an EXP file, we process it here and add whatever we can to
+ # the PBAs (which for single-colour hybs such as Affy will just be
+ # the actual hybridization PBA).
+ my ( $self, $expfile, $badata ) = @_;
+
+ my ( $mbas, $pbas ) = $self->_get_bioassays_from_mbadata( $badata );
+ my $exp = $self->_read_exp_file( $expfile );
+
+ if ( $exp->get_protocol() ) {
+ $self->_add_hybdata_to_pbas( $exp, $pbas );
+ }
+
+ $self->_add_scandata_to_pbas( $exp, $pbas );
+
+ return $exp->get_chip_type();
+}
+
+sub _add_scandata_to_pbas : PRIVATE {
+
+ my ( $self, $exp, $pbas ) = @_;
+
+ foreach my $pba ( @$pbas ) {
+
+ BATREAT:
+ foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+
+ # Don't add if there's already a protocolapp.
+ unless ( scalar( @{ $bat->getProtocolApplications() || [] } ) ) {
+ my ($protocol, $param_hash)
+ = $self->_create_affy_hyb_protocol( $exp );
+ my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $protocol,
+ activityDate => $exp->get_scan_date() || 'n/a',
+ );
+ while ( my ($name, $value) = each %{ $param_hash } ) {
+ my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+ parameterType => $self->get_parameter_bag->(
+ $protocol->getIdentifier().$name
+ ),
+ value => $value,
+ );
+ $pa->addParameterValues( $pv );
+ }
+ $bat->addProtocolApplications( $pa );
+ }
+
+ # Should be only one treatment for Affy.
+ last BATREAT;
+ }
+ }
+
+ return;
+}
+
+sub _create_affy_scan_protocol : PRIVATE {
+
+ my ( $self, $exp ) = @_;
+
+ my $accession = "${AFFY_PROTOCOL_PREFIX}Scan";
+
+ my $protocol = $self->get_protocol_bag()->(
+ $accession,
+ {
+ accession => $accession,
+ type => $OE_VAL_SCANNING,
+ name => 'Affymetrix Scanning Protocol',
+ }
+ );
+
+ my @scan_params;
+ my $param_hash = clean_hash(
+ {
+ "ScanPixelSize" => $exp->get_pixel_size(),
+ "NumberOfScans" => $exp->get_num_scans(),
+ }
+ );
+
+ my $mage_params = _mage_param_hash( $param_hash );
+ while ( my ( $name, $args ) = each %{ $mage_params } ) {
+ push @scan_params, $self->get_parameter_bag()->(
+ $accession.$name,
+ $args,
+ );
+ }
+ $self->_add_params_to_protocol( \@scan_params, $protocol );
+
+ return $protocol, $param_hash;
+}
+
+sub _add_hybdata_to_pbas : PRIVATE {
+
+ my ( $self, $exp, $pbas ) = @_;
+
+ foreach my $pba ( @$pbas ) {
+ if ( my $hyb = $pba->getBioAssayCreation() ) {
+
+ # Don't add if there's already a protocolapp.
+ unless ( scalar( @{ $hyb->getProtocolApplications() || [] } ) ) {
+ my $protocol = $self->_create_affy_hyb_protocol($exp);
+ my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $protocol,
+ activityDate => $exp->get_hyb_date() || 'n/a',
+ );
+ while ( my ($name, $value) = each %{ $exp->get_parameters() } ) {
+ my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+ parameterType => $self->get_parameter_bag->(
+ $protocol->getIdentifier().$name
+ ),
+ value => $value,
+ );
+ $pa->addParameterValues( $pv );
+ }
+ $hyb->addProtocolApplications( $pa );
+ }
+ }
+ }
+
+ return;
+}
+
+sub _create_affy_hyb_protocol : PRIVATE {
+
+ my ( $self, $exp ) = @_;
+
+ my $accession = "${AFFY_PROTOCOL_PREFIX}Hybridization-"
+ . $exp->get_protocol();
+
+ my $protocol = $self->get_protocol_bag()->(
+ $accession,
+ {
+ accession => $accession,
+ type => $OE_VAL_HYBRIDIZATION,
+ name => $exp->get_protocol(),
+ }
+ );
+
+ my @params;
+ my $mage_params = _mage_param_hash( $exp->get_parameters() );
+ while ( my ( $name, $args ) = each %{ $mage_params } ) {
+ push @params, $self->get_parameter_bag()->(
+ $accession.$name,
+ $args,
+ );
+ }
+ $self->_add_params_to_protocol( \@params, $protocol );
+
+ return $protocol;
+}
+
+sub _add_celdata_to_mbadata : PRIVATE {
+
+ # Navigate to MBA and add a protocolapp,
+ # summarystats; FIXME also software, hardware etc?
+ my ( $self, $cel, $badata ) = @_;
+
+ my ( $mbas, $pbas ) = $self->_get_bioassays_from_mbadata( $badata );
+
+ foreach my $mba ( @$mbas ) {
+ if ( my $fext = $mba->getFeatureExtraction() ) {
+
+ # Don't add if there's already a protocolapp.
+ unless ( scalar( @{ $fext->getProtocolApplications() || {} } ) ) {
+ my $protocol
+ = $self->_create_affy_fext_protocol( $cel );
+ my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $protocol,
+ activityDate => 'n/a',
+ );
+ while ( my ($name, $value) = each %{ $cel->get_parameters() } ) {
+ my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+ parameterType => $self->get_parameter_bag->(
+ $protocol->getIdentifier().$name
+ ),
+ value => $value,
+ );
+ $pa->addParameterValues( $pv );
+ }
+ $fext->addProtocolApplications( $pa );
+ }
+ }
+ }
+
+ $self->_add_summarystats_to_badata( $cel->get_stats(), $badata );
+
+ return;
+}
+
+sub _add_summarystats_to_badata : PRIVATE {
+
+ my ( $self, $stats, $badata ) = @_;
+
+ if ( $stats ) {
+ my @statistics;
+ foreach my $name ( sort keys %$stats ) {
+ push(
+ @statistics,
+ Bio::MAGE::NameValueType->new(
+ name => $name,
+ value => $stats->{$name},
+ )
+ );
+ }
+ $badata->setSummaryStatistics( \@statistics );
+ }
+
+ return;
+}
+
+sub _create_affy_fext_protocol : PRIVATE {
+
+ my ( $self, $cel ) = @_;
+
+ my $accession = 'P-AFFY-6';
+
+ my $protocol = $self->get_protocol_bag()->(
+ $accession,
+ {
+ accession => $accession,
+ type => $OE_VAL_FEATUREEXTRACTION,
+ name => 'Affymetrix CEL analysis (Percentile)',
+ }
+ );
+
+ my @params;
+ my $mage_params = _mage_param_hash( $cel->get_parameters() );
+ while ( my ( $name, $args ) = each %{ $mage_params } ) {
+ push @params, $self->get_parameter_bag()->(
+ $accession.$name,
+ $args,
+ );
+ }
+ $self->_add_params_to_protocol( \@params, $protocol );
+
+ return $protocol;
+}
+
+sub _add_chpdata_to_dbadata : PRIVATE {
+
+ my ( $self, $chp, $badata ) = @_;
+
+ if ( my $txn = $badata->getProducerTransformation() ) {
+
+ # Don't add if there's already a protocolapp.
+ unless ( scalar( @{ $txn->getProtocolApplications() || {} } ) ) {
+ my $protocol
+ = $self->_create_affy_norm_protocol( $chp );
+ my $pa = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $protocol,
+ activityDate => 'n/a',
+ );
+ while ( my ($name, $value) = each %{ $chp->get_parameters() } ) {
+ my $pv = Bio::MAGE::Protocol::ParameterValue->new(
+ parameterType => $self->get_parameter_bag->(
+ $protocol->getIdentifier().$name
+ ),
+ value => $value,
+ );
+ $pa->addParameterValues( $pv );
+ }
+ $txn->addProtocolApplications( $pa );
+ }
+ }
+
+ $self->_add_summarystats_to_badata( $chp->get_stats(), $badata );
+
+ return;
+}
+
+sub _create_affy_norm_protocol : PRIVATE {
+
+ my ( $self, $chp ) = @_;
+
+ my $accession = "${AFFY_PROTOCOL_PREFIX}" . $chp->get_algorithm();
+
+ my $protocol = $self->get_protocol_bag()->(
+ $accession,
+ {
+ accession => $accession,
+ type => $OE_VAL_NORMALIZATION,
+ name => $chp->get_algorithm(),
+ }
+ );
+
+ my @params;
+ my $mage_params = _mage_param_hash( $chp->get_parameters() );
+ while ( my ( $name, $args ) = each %{ $mage_params } ) {
+ push @params, $self->get_parameter_bag()->(
+ $accession.$name,
+ $args,
+ );
+ }
+ $self->_add_params_to_protocol( \@params, $protocol );
+
+ return $protocol;
+}
+
+sub _add_params_to_protocol : PRIVATE {
+
+ my ( $self, $params, $protocol ) = @_;
+
+ foreach my $param ( @$params ) {
+ my $new_id = $param->getIdentifier();
+ my $found = first {
+ $_->getIdentifier() eq $new_id
+ } @{ $protocol->getParameterTypes() || [] };
+ $protocol->addParameterTypes($param) unless $found;
+ }
+
+ return;
+}
+
+sub _set_pbas_channel_and_label : PRIVATE {
+
+ my ( $self, $pbas, $labelname ) = @_;
+
+ foreach my $pba ( @{ $pbas || [] } ) {
+
+ # Set up the default objects.
+ my $default_label = Bio::MAGE::BioMaterial::Compound->new(
+ identifier => $AE_LABELCOMPOUND_PREFIX . $labelname,
+ name => $labelname,
+ );
+ my $default_channel = Bio::MAGE::BioAssay::Channel->new(
+ identifier => $AE_CHANNEL_PREFIX . $labelname,
+ name => $labelname,
+ labels => [ $default_label ],
+ );
+
+ # Fix the labels. We systematically replace all "unknown"
+ # labels with the default label.
+ if ( my $hyb = $pba->getBioAssayCreation() ) {
+
+ BMM:
+ foreach my $bmm ( @{ $hyb->getSourceBioMaterialMeasurements() || [] } ) {
+ my $le = $bmm->getBioMaterial();
+
+ # Make sure we're dealing with the right kind of object.
+ next BMM unless $le->can('getLabels');
+
+ my @newlabels;
+ if ( my $labels = $le->getLabels() ) {
+ while ( my $l = shift @{ $labels } ) {
+ if ( lc($l->getIdentifier())
+ eq lc($AE_LABELCOMPOUND_PREFIX . 'unknown') ) {
+ push @newlabels, $default_label;
+ }
+ else {
+ push @newlabels, $l;
+ }
+ }
+ }
+ else {
+ push @newlabels, $default_label;
+ }
+ $le->setLabels( \@newlabels );
+ }
+ }
+
+ # Fix the channels. We systematically replace all "unknown"
+ # channels with the default channel.
+ my @newchannels;
+ if ( my $channels = $pba->getChannels() ) {
+ while ( my $ch = shift @{ $channels } ) {
+ if ( lc($ch->getIdentifier())
+ eq lc($AE_CHANNEL_PREFIX . 'unknown') ) {
+ push @newchannels, $default_channel;
+ }
+ else {
+ push @newchannels, $ch;
+ }
+ }
+ }
+ else {
+ push @newchannels, $default_channel;
+ }
+ $pba->setChannels( \@newchannels );
+ }
+
+ return;
+}
+
+sub _parse_affy_datafile : PRIVATE {
+
+ my ( $self, $file, $datarow ) = @_;
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $datarow eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ my $badata = $file->get_mage_badata();
+
+ $file->set_format_type('Affymetrix');
+
+ require ArrayExpress::Datafile::Affymetrix;
+ my $factory = ArrayExpress::Datafile::Affymetrix->new();
+
+ # dye used
+ $datarow->{$EDF_DYE} ||= 'biotin';
+
+ if ( $file->get_name() =~ m/\.CEL$/i ) {
+
+ # Sort out the protocols (Unknown hyb protocol is the default)
+ if ( $datarow->{$EDF_HYB_PROTOCOL} ) {
+
+ # Users can use the Affy protocol name here directly, but this
+ # is ignored if the protocol is named like a known protocol
+ # accession pattern.
+ unless ( $datarow->{$EDF_HYB_PROTOCOL}
+ =~ m!^${AFFY_PROTOCOL_PREFIX}Hybridization!
+ || $datarow->{$EDF_HYB_PROTOCOL} =~ m!^P-\w+-\w+!i )
+ {
+ $datarow->{$EDF_HYB_PROTOCOL}
+ = "${AFFY_PROTOCOL_PREFIX}Hybridization-"
+ . $datarow->{$EDF_HYB_PROTOCOL};
+ }
+ } # See below for EXP and fallback values
+
+ $datarow->{$EDF_SCAN_PROTOCOL} = "${AFFY_PROTOCOL_PREFIX}Scan";
+ $datarow->{$EDF_FEXT_PROTOCOL} = 'P-AFFY-6';
+
+ # Fallback value for hyb protocol
+ $datarow->{$EDF_HYB_PROTOCOL}
+ ||= "${AFFY_PROTOCOL_PREFIX}Hybridization-Unknown";
+
+ # Sort out our EXP file. First, the Tab2MAGE version:
+ my $chip_type;
+ if ( my $expfile
+ = $datarow->{ $EDF_FILE_PREFIX . 'exp' . $EDF_FILE_SUFFIX } ) {
+
+ # EXP is still the first place we look for chip type.
+ $chip_type = $self->_extract_exp_data(
+ $expfile,
+ $datarow,
+ );
+ }
+
+ # MAGE-TAB object creation.
+ if ( $badata ) {
+
+ my ( $mbas, $pbas ) = $self->_get_bioassays_from_mbadata( $badata );
+
+ # Set the default label in cases where it's not already given.
+ $self->_set_pbas_channel_and_label($pbas, 'biotin');
+
+ # Next, EXP file for MAGE-TAB:
+ my $expfile;
+ foreach my $mba ( @$mbas ) {
+ if ( my $nvts = $mba->getPropertySets() ) {
+
+ NVT:
+ foreach my $nvt ( @$nvts ) {
+ if ( $nvt->getName() =~ m/\A EXP \z/ixms
+ && $nvt->getValue() =~ m/\.EXP \z/ixms ) {
+ $expfile = $nvt->getValue();
+ last NVT; # Only process the first EXP comment.
+ }
+ }
+ }
+ }
+ if ( $expfile ) {
+ $chip_type = $self->_add_expdata_to_mbadata(
+ $expfile,
+ $badata,
+ );
+ }
+ }
+
+ # Parse the CEL file, output the raw data
+ my $cel = $factory->make_parser( $file->get_path() );
+
+ if ( $self->get_use_binary_datafiles() ) {
+
+ # Code the Affy files natively in MAGE-ML; just populate the
+ # metadata.
+ $cel->parse_header();
+
+ # Make a note of the CEL version for later
+ # introduction into the MAGE-ML
+ my %typemap = (
+ 3 => 'CELv3',
+ 4 => 'CELv4',
+ 1 => 'CEL_AGCC',
+ );
+ my $type = $typemap{ $cel->get_version() }
+ or croak( sprintf(
+ "ERROR: Unrecognized CEL version: %s\n",
+ $cel->get_version()
+ ));
+
+ # Copy the file to the target directory, note the type.
+ $self->_mageml_file_as_binary( $file, $type );
+ }
+
+ else {
+
+ # Parse the whole CEL file and export it as a matrix for
+ # encoding in MAGE. This will use a lot of memory for larger
+ # Affy chips.
+ $cel->parse();
+
+ my $target_file
+ = File::Spec->catfile( $self->get_output_directory(),
+ $file->get_name() . ".proc" );
+ $file->set_target_filename(
+ File::Spec->abs2rel(
+ $target_file, $self->get_output_directory()
+ )
+ );
+ my $data_fh = IO::File->new( $target_file, '>' )
+ or croak(
+ "Error: Unable to open output file $target_file: $!\n");
+ $cel->export($data_fh);
+ }
+
+ # Sort out our fallback chip type if no EXP file available. We
+ # could go as far as parsing the CDF here, but that only works
+ # for some versions and is extra overhead.
+ my $stripped_cdfname;
+ if ( $stripped_cdfname
+ = $datarow->{ $EDF_FILE_PREFIX . 'cdf' . $EDF_FILE_SUFFIX } ) {
+ $stripped_cdfname =~ s/\.cdf//i;
+ }
+ $chip_type ||= $cel->get_chip_type()
+ || $stripped_cdfname
+ || 'Unknown';
+
+ # Get the DesignElementDimension
+ $file->set_ded_type('Feature');
+
+ # If we're not fully parsing the CEL data, just use a standard DED
+ if ( $self->get_use_binary_datafiles() ) {
+ $file->set_ded_identifier(
+ "Affymetrix:FeatureDimension:$chip_type");
+ }
+
+ # For full CEL parsing, derive a new DED and link to that.
+ else {
+ my $ded = $cel->get_ded($chip_type);
+ my $ded_identifier = $self->get_unique_de_dimension(
+ $ded,
+ $file->get_array_design_id(),
+ $file->get_ded_type(),
+ );
+ $file->set_ded_identifier($ded_identifier);
+ }
+
+ # Define the QuantitationTypeDimension
+ $file->set_column_headings( $cel->get_headings() );
+ $file->check_column_headings( $self->get_quantitation_types );
+
+ # Check here whether $file->get_mage_badata
+ # set, if so navigate to MBA and add a protocolapp,
+ # summarystats, software, hardware etc.
+ if ( $badata ) {
+
+ # MAGE-TAB parsing here.
+ $self->_add_celdata_to_mbadata( $cel, $badata );
+ }
+ else {
+
+ # Tab2MAGE parsing here; dumps CEL data into $datarow for
+ # later.
+ $self->_extract_cel_data( $cel, $datarow );
+ }
+
+ }
+ elsif ( $file->get_name() =~ m/\.CHP$/i ) {
+
+ # Sort out our CDF file (note that this is backed up by
+ # $chp->get_chip_type, below).
+ my $cdffile
+ = $datarow->{ $EDF_FILE_PREFIX . 'cdf' . $EDF_FILE_SUFFIX };
+
+ # Parse the CHP file, output the normalized data
+ my $chp = $factory->make_parser( $file->get_path() );
+
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ local $EVAL_ERROR;
+ eval {
+ $chp->parse();
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+
+ # Handle CHP parsing failure (CHPv8 will do this).
+ if ( $EVAL_ERROR ) {
+
+ if ( $chp->get_version == 8 ) {
+ $file->set_ded_type('CompositeSequence');
+ $file->set_ded_identifier(
+ "Affymetrix:CompositeSequenceDimension:"
+ . $chp->get_chip_type()
+ );
+
+ # Copy the file to the target directory, note the
+ # type.
+ $self->_mageml_file_as_binary(
+ $file,
+ "CHPv" . $chp->get_version,
+ );
+ }
+ else {
+ croak("Error: Cannot parse CHP: $EVAL_ERROR");
+ }
+
+ }
+ else {
+
+ # Successful parse; write out the data, set DED.
+ $self->_output_chp_data(
+ $chp,
+ $file,
+ $cdffile,
+ );
+ }
+
+ # Define the QuantitationTypeDimension
+ $file->set_column_headings( $chp->get_headings() );
+ $file->check_column_headings( $self->get_quantitation_types );
+
+ # Check here whether $file->get_mage_badata
+ # set, if so navigate to DBAD and add a protocolapp,
+ # summarystats.
+ if ( $badata ) {
+
+ # MAGE-TAB.
+ $self->_add_chpdata_to_dbadata( $chp, $badata );
+ }
+ else {
+
+ # Tab2MAGE. Extract CHP data into $datarow.
+ $self->_extract_chp_data( $chp, $datarow );
+ }
+ }
+ else {
+ croak(
+ sprintf(
+ qq{Error: unrecognized file type for file "%s" (Affymetrix must be .CEL or .CHP)\n},
+ $file->get_name(),
+ ),
+ );
+ }
+
+ return;
+}
+
+sub _output_chp_data : PRIVATE {
+
+ my ($self, $chp, $file, $cdffile) = @_;
+
+ my $array_accession = $file->get_array_design_id()
+ or croak("Error: Datafile has no array accession.");
+
+ $cdffile ||= $chp->get_chip_type() . ".cdf";
+
+ # Parse the CDF, unless we have it already cached.
+ my $cdf;
+ unless ( $cdf = $self->get_cdf_cache(lc($cdffile)) ) {
+
+ my $factory = ArrayExpress::Datafile::Affymetrix->new();
+
+ # Check Affy library dir or source dir or working dir.
+ my ($actualcdf, $filepath) = find_cdf(
+ $cdffile,
+ $self->get_source_directory(),
+ );
+
+ unless ( $actualcdf ) {
+ croak("Error: CDF file $cdffile not found\n");
+ }
+
+ $cdf = $factory->make_parser($filepath);
+ $cdf->parse();
+ $self->set_cdf_cache( lc($cdffile), $cdf );
+ }
+
+ # Output the stripped data.
+ my $target_file = File::Spec->catfile(
+ $self->get_output_directory(),
+ $file->get_name() . ".proc",
+ );
+
+ $file->set_target_filename(
+ File::Spec->abs2rel( $target_file, $self->get_output_directory() )
+ );
+
+ my $data_fh = IO::File->new( $target_file, '>' )
+ or croak("Error: Unable to open output file $target_file: $!\n");
+
+ $chp->export( $data_fh, $cdf );
+
+ # Set the DesignElementDimension.
+ my $ded = $chp->get_ded($cdf);
+ $file->set_ded_type('CompositeSequence');
+ my $ded_identifier = $self->get_unique_de_dimension(
+ $ded,
+ $array_accession,
+ $file->get_ded_type(),
+ );
+ $file->set_ded_identifier($ded_identifier);
+
+ return;
+}
+
+sub _parse_binary_nimblescan : PRIVATE {
+
+ my ( $self, $file ) = @_;
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Enter a dummy DED identifier (no longer used, code kept here in
+ # case this policy ever changes).
+
+# my $ded_type = 'Reporter';
+# $file->set_ded_type($ded_type);
+# my $ded_identifier = $self->get_unique_de_dimension(
+# [sprintf( "ebi.ac.uk:Nimblegen:Reporter:%s", $file->get_array_design_id() )],
+# $file->get_array_design_id(),
+# $ded_type
+# );
+# $file->set_ded_identifier($ded_identifier);
+
+ # Make sure we know which software is being used.
+ my $rc = $file->check_column_headings( $self->get_quantitation_types );
+ if ($rc) {
+ print { $self->get_error_fh() } ( "ERROR: " . $file->get_name() . ": $rc" );
+ }
+
+ # Copy the file to the target dir, label it with a
+ # dataExternal format tag (This tag will probably change FIXME).
+ my $format = ( $file->get_format_type() eq 'NimbleScanFeature'
+ || $file->get_format_type() eq 'NimbleScanNorm')
+ ? 'NimbleScan'
+ : $file->get_format_type();
+ $self->_mageml_file_as_binary( $file, $format );
+
+ return;
+}
+
+sub _mageml_file_as_binary : PRIVATE {
+
+ # Takes a Datafile object and a type string (CELv3, CELv4
+ # etc.). Copies the actual file across to the target MAGE-ML
+ # directory, stores the type for use later.
+
+ my ( $self, $file, $type ) = @_;
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # For MAGE-TAB, rewrite the referenced filename back to its
+ # original form.
+ if ( my $data = $file->get_mage_badata() ) {
+ my $bdc = $data->getBioDataValues();
+ $bdc->setCube( $file->get_name() );
+ }
+
+ # Copy the untouched binary file across to the target directory
+ my $target_file
+ = File::Spec->rel2abs( $file->get_name(), $self->get_output_directory() );
+ $file->set_target_filename( $file->get_name() );
+ if ( $file->get_path ne $target_file ) {
+ copy( $file->get_path, $target_file )
+ or croak( "ERROR: Unable to copy binary data file "
+ . $file->get_path
+ . " into target directory: $!\n" );
+ }
+ else {
+ croak( q{ERROR: Source and target filenames are identical}
+ . qq{ ("$target_file"). Copying may destroy the original file.}
+ . q{ Please use alternative target directory.});
+ }
+
+ # Make a note of the file type (CELv3, CELv4, NimbleScan).
+ if ($type) {
+ $self->set_cel_types( $file->get_target_filename() => $type );
+ }
+ else {
+ croak("ERROR: No binary file type specified");
+ }
+
+ return;
+}
+
+sub _parse_text_datafile : PRIVATE {
+
+ my ( $self, $file, $datarow ) = @_;
+
+ $file->isa('ArrayExpress::Datafile')
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+ ref $datarow eq 'HASH'
+ or confess( $CONFIG->get_ERROR_MESSAGE_ARGS() );
+
+ # Default Array accession. This is changed if we encounter post-Affy
+ # data files (e.g. RMA data) to the name of the array. Used in
+ # constructing the CompositeSequence_refs.
+ my $arrayaccession = $file->get_array_design_id();
+
+ # This will hold a list of coordinate columns to be discarded.
+ my @unwanted_coord_columns;
+
+ # Figure out our line ending style (FIXME this should now be superfluous).
+ local $INPUT_RECORD_SEPARATOR = $file->get_linebreak_type();
+
+ # Figure out what kind of file we have, get column headings and indices
+ $file->parse_header();
+
+ unless ( scalar @{ $file->get_column_headings() } ) {
+ print STDERR (
+ "Error: No column headings for file " . $file->get_name() . "\n" );
+ }
+
+ # Used for AffyNorm; if not undef, will be used instead of the array
+ # accession to generate DesignElement ids.
+ my $chip_type;
+
+ FILEFORMAT:
+ {
+
+ # NimbleScan data files may be coded as binary.
+ if ( $self->get_use_binary_datafiles()
+ && ( $file->get_format_type =~ m/\A Nimble(Scan|gen)/xms ) ) {
+ $self->_parse_binary_nimblescan( $file );
+ return;
+ }
+
+ # For Illumina, check whether we have per-hyb data or not,
+ # prior to fixing anything.
+ elsif ( $file->get_format_type() =~ /^Illumina/
+ && $file->is_illumina_fgem() ) {
+ $file->rewrite_illumina_as_fgem();
+ }
+
+ # Next, scan through known file formats which need fixing
+ # (GenePix, etc.)
+ else {
+ my $rc = $file->fix_known_text_format();
+ last FILEFORMAT if $rc;
+ }
+
+ # Other minor tweaks. These first few are basically FGEM:
+ if ( $file->get_format_type eq 'FGEM_CS' ) {
+ $file->set_format_type('FGEM');
+ }
+ elsif ( $file->get_format_type eq 'GEO' ) {
+ $file->set_format_type('FGEM');
+
+ # ID_REF is assumed to be CompSeq ID.
+ my $cols = $file->get_column_headings();
+ $cols->[ $file->get_index_columns()->[0] ]
+ = 'CompositeSequence Identifier';
+ $file->set_column_headings($cols);
+ }
+ elsif ( $file->get_format_type eq 'AffyNorm' ) {
+ $file->set_format_type('FGEM');
+
+ # Now we know the dye
+ $datarow->{$EDF_DYE} ||= 'biotin';
+
+ # Use the name of the CDF file as our array name.
+ # FIXME to use strings from EXP, CEL, CHP where available.
+ if ( my $cdffile
+ = $datarow->{ $EDF_FILE_PREFIX . 'cdf' . $EDF_FILE_SUFFIX } ) {
+ $cdffile =~ s/\.cdf$//i;
+ $chip_type = $cdffile;
+ }
+
+ # Fall back to unknown.
+ $chip_type ||= 'Unknown';
+
+ # Record the chip_type for these data matrix files.
+ unless ( $file->get_dm_chip_type() ) {
+ $file->set_dm_chip_type($chip_type);
+ }
+ }
+
+ } # end FILEFORMAT
+
+ # Figure out which columns we want to keep, populate heading_qts,
+ # heading_hybs etc.
+ my $rc = $file->check_column_headings( $self->get_quantitation_types );
+ if ($rc) {
+ printf { $self->get_error_fh() } ( "ERROR: %s: %s", $file->get_name(), $rc );
+ }
+
+ # We've made this generic by now, or at least we should have (Affy
+ # would crash here, but we've already diverted it).
+ unless ( $file->get_format_type() eq 'Generic'
+ or $file->get_format_type() eq 'FGEM' ) {
+ croak( "Error: file "
+ . $file->get_name()
+ . " not in recognized format: "
+ . ( $file->get_format_type() || 'Unknown' )
+ . "\n" );
+ }
+
+ # FGEM (combined) data is treated as a special case - column
+ # headings do not equate directly to QTs.
+ my @column_headings = @{ $file->get_column_headings };
+
+ # Next, select only our columns of interest (based on QT name),
+ # write the outcome to log file.
+ my @new_column_headings;
+ foreach my $heading (@column_headings) {
+ if ( $file->get_data_metrics()->{$heading} ) {
+ push( @new_column_headings, $heading );
+ print { $self->get_error_fh() }
+ ( $file->get_name() . ": Recognized column kept: $heading\n" );
+ }
+ elsif ( $file->is_ignored_qt($heading) ) {
+ print { $self->get_error_fh() }
+ ( $file->get_name() . ": Stripping unwanted column: $heading\n" );
+ }
+ elsif ( $self->get_allow_undef_qts() ) {
+ push( @new_column_headings, $heading );
+ print { $self->get_error_fh() }
+ ( $file->get_name() . ": Keeping unrecognized column: $heading\n" );
+ }
+ else {
+ print { $self->get_error_fh() }
+ ( $file->get_name() . ": Omitting unrecognized column: $heading\n" );
+ }
+ }
+
+ # Set up some target filenames ($file->get_name() will be modified by
+ # strip_and_sort).
+ my $temporary_file = File::Spec->catfile( $self->get_output_directory(),
+ $file->get_name() . ".temporary",
+ );
+ my $target_file = File::Spec->catfile( $self->get_output_directory(),
+ $file->get_name() . ".proc" );
+
+ # Strip the file of unwanted columns, sort on the indices (coord
+ # or identifier), and move those indices to the leftmost
+ # columns. The Datafile is relinked to the temp filehandle.
+ $file->strip_and_sort( $temporary_file, \@new_column_headings );
+
+ # Open output filehandle
+ my $output_fh = IO::File->new( $target_file, '>' )
+ or croak("Error opening output file $target_file: $!\n");
+
+ # Generate DesignElementDimension, while stripping the feature
+ # coordinates/identifiers from the data file. This also relinks
+ # the Datafile object to the output filehandle.
+ $self->_create_designelementdimension(
+ $file,
+ $output_fh,
+ ( $chip_type || $arrayaccession ),
+ );
+
+ # End of DesignElementDimension processing
+ unlink($temporary_file)
+ or carp(
+ "Warning: Unable to delete temporary file $temporary_file: $!\n");
+
+ $file->set_target_filename(
+ File::Spec->abs2rel( $target_file, $self->get_output_directory() ) );
+
+ return;
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../../index.html">
+ <img src="../../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: ExperimentChecker.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::Parser - a module handling Datafile parsing
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::Parser;
+ my $parser = ArrayExpress::Datafile::Parser->new;
+
+=head1 DESCRIPTION
+
+The ArrayExpress::Datafile module provides methods for
+parsing individual data files. This module provides a global handler
+for Datafile objects.
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
+
diff --git a/lib/ArrayExpress/Datafile/QT_list.pm b/lib/ArrayExpress/Datafile/QT_list.pm
new file mode 100644
index 0000000..2e2f8df
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/QT_list.pm
@@ -0,0 +1,342 @@
+#!/usr/bin/env perl
+#
+# QT_list.pm
+#
+# $Id: QT_list.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+# This file contains QuantitationType information parsed out from our
+# QuantitationType survey spreadsheet, and from the relevant database
+# tables in MIAMExpress. This information has been primarily gathered
+# and curated by Ele Holloway.
+#
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: QT_list.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::Datafile::QT_list.pm - a module handling QuantitationType
+information.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::Datafile::QT_list qw(get_QTs writeQTs)
+ my $QT_hash = get_QTs();
+ write_QTs($QT_hash);
+
+=head1 DESCRIPTION
+
+This is a module providing QuantitationType support information and
+subroutines for the Tab2MAGE and experiment checker package.
+
+=head1 QUANTITATION TYPES
+
+Known QuantitationTypes are listed in the QT_list.pm module included
+with this distribution. This file includes a simple tab-delimited text
+section with subsections specific to a given software/manufacturer
+type. Each subsection begins with the tag "C<E<gt>E<gt>E<gt>>". There are
+several columns, but only the first is required:
+
+=over 1
+
+=begin html
+
+<table class="example">
+<tr><th>>>>QT software 1</th></tr>
+<tr><td>QT1 name</td><td>QT1 datatype</td><td>QT1 scale</td><td>QT1 subclass</td><td>QT1 is_background</td><td>QT1 target (confidence indicators)</td><td>QT1 channel</td><td>QT1 description</td></tr>
+<tr><td>QT2 name</td><td>QT2 datatype</td><td>QT2 scale</td><td>QT2 subclass</td><td>QT2 is_background</td><td>QT2 target (confidence indicators)</td><td>QT2 channel</td><td>QT2 description</td></tr>
+<tr><td>QT3 name</td><td>QT3 datatype</td><td>QT3 scale</td><td>QT3 subclass</td><td>QT3 is_background</td><td>QT3 target (confidence indicators)</td><td>QT3 channel</td><td>QT3 description</td></tr>
+<tr><td>QT4 name...</td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+<tr><th>>>>QT software 2</th></tr>
+<tr><td>QT5 name</td><td>QT5 datatype</td><td>QT5 scale</td><td>QT5 subclass</td><td>QT5 is_background</td><td>QT5 target (confidence indicators)</td><td>QT5 channel</td><td>QT5 description</td></tr>
+<tr><td>QT6 name...</td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td><td> </td></tr>
+</table>
+
+=end html
+
+=begin man
+
+ >>>QT source 1 (software name)[manufacturer 1]
+ QT1 name QT1 datatype QT1 scale QT1 subclass QT1 is_background QT1 conf. indicator tgt. QT1 channel QT1 description
+ QT2 name QT2 datatype QT2 scale QT2 subclass QT2 is_background QT2 conf. indicator tgt. QT2 channel QT2 description
+ QT3 name QT3 datatype QT3 scale QT3 subclass QT3 is_background QT3 conf. indicator tgt. QT3 channel QT3 description
+ QT4 name...
+ >>>QT source 2 (software name)[manufacturer 2]
+ QT5 name QT5 datatype QT5 scale QT5 subclass QT5 is_background QT5 conf. indicator tgt. QT5 channel QT5 description
+ QT6 name...
+
+=end man
+
+=back
+
+where QT1, QT2 and so on are the quantitation types. Note that for the
+resulting MAGE identifiers to have the correct namespace, the ">>>QT
+software" strings should contain the namespace in square brackets, for
+example:
+
+ >>>Feature Extraction Software[Agilent Technologies]
+
+Note also that to add to a pre-existing set of quantitation types from
+a given software, this initial string B<must> match that included in the
+Tab2MAGE package. Currently included software types are as follows:
+
+ AIDA[Raytest]
+ Affymetrix[Affymetrix]
+ AppliedBiosystems
+ ArrayGauge[FUJIFILM]
+ ArrayVision[Imaging Research]
+ BZScan[TAGC ERM206]
+ BlueFuse[BlueGnome]
+ ChipSkipper[EMBL]
+ CodeLink[Motorola Life Sciences]
+ CodeLink Expression Analysis[GE Healthcare]
+ Feature Extraction Software[Agilent Technologies]
+ GEMTools[Incyte Genomics]
+ GLEAMS[NuTec Sciences]
+ GenePix[Axon Instruments]
+ GeneTAC
+ ImaGene[BioDiscovery]
+ NimbleScan[NimbleGen Systems]
+ QuantArray[PerkinElmer]
+ ScanAlyze[Stanford University]
+ ScanArray Express[PerkinElmer]
+ SpotFinder[TIGR]
+ UCSF Spot
+ Uni of Toronto in-house analysis software
+ Tab2MAGE_FGEM[ebi.ac.uk]
+
+Each file may contain QT information from any number of software
+vendors. The contents of each column are described below:
+
+=head2 QT file columns
+
+=over 2
+
+=item name
+
+The name of the QuantitationType. This corresponds to the exact string found in the data files.
+
+=item datatype
+
+The DataType OntologyEntry to be used (MGED Ontology). Default: float
+
+=item scale
+
+The Scale OntologyEntry to be used (MGED Ontology). Default:
+linear_scale
+
+=item subclass
+
+The QuantitationType subclass to use (e.g., MeasuredSignal, Error
+etc.). Default: SpecializedQuantitationType
+
+=item is_background
+
+Boolean flag (0 or 1) indicating whether the QT is background. Default: 0
+
+=item confidence indicator target
+
+Only used for ConfidenceIndicator QTs (Error, PValue, and
+ExpectedValue). Indicates the target QT name to which the
+ConfidenceIndicator applies. Defaults to undefined.
+
+=item channel
+
+The channel to be associated with the QT. Note that QTs can only have
+one channel; this is a current limitation of the MAGE object
+model. QTs linked to more than one channel in the real world
+(e.g. ratios) should omit the channel value here.
+
+=item description
+
+A plain-text description of the QT.
+
+=back
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2005.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments. Particular credit
+goes to Ele Holloway, who was responsible for curating the lists of
+QuantitationTypes included with this script.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::Datafile::QT_list;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::File;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw($RE_LINE_BREAK);
+
+use base 'Exporter';
+our @EXPORT_OK = qw(
+ get_QTs
+ write_QTs
+);
+
+sub get_QTs {
+
+ # Accepts single filenames or arrayref list; also an optional
+ # "include_known" QTs argument.
+ my ( $extra_file_or_files, $include_known ) = @_;
+
+ # We now track whether a QT is user-defined or not.
+ my %QT_file = ( old => [], new => [] );
+
+ # Get a list of files containing QT info. Start with the default
+ # - the QTs listed in the QT_list.txt file in the same dir as this
+ # module. The default QTs can then be overwritten as desired by
+ # the user.
+ my $module_path = File::Spec->rel2abs(__FILE__);
+ my @module_dir_array = File::Spec->splitpath($module_path);
+ my $package_default = $ENV{PAR_TEMP} # Running under PAR
+ ? File::Spec->catfile( $ENV{PAR_TEMP},
+ qw( inc lib ArrayExpress Datafile QT_list.txt ) )
+ : File::Spec->catpath(
+ @module_dir_array[ 0, 1 ], 'QT_list.txt' );
+ my $default = $CONFIG->get_DEFAULT_QT_FILENAME() || $package_default;
+
+ if ( $include_known || !$extra_file_or_files ) {
+ push( @{ $QT_file{old} }, $default );
+ }
+
+ if ($extra_file_or_files) {
+ if ( ref($extra_file_or_files) eq 'ARRAY' ) {
+ foreach my $file (@$extra_file_or_files) {
+ push( @{ $QT_file{new} }, $file );
+ }
+ }
+ else {
+ push( @{ $QT_file{new} }, $extra_file_or_files );
+ }
+ }
+
+ # Add any files supplied to the routine.
+ my %QT_filehandle = ( old => [], new => [] );
+ foreach my $type ( keys %QT_file ) {
+ foreach my $file ( @{ $QT_file{$type} } ) {
+ my $filehandle = IO::File->new( $file, '<' )
+ or
+ croak("Error: QuantitationType file $file not found: $!\n");
+ push( @{ $QT_filehandle{$type} }, $filehandle );
+ }
+ }
+
+ # Loop through the filehandles.
+ my %QT_hash;
+ foreach my $type ( keys %QT_filehandle ) {
+ foreach my $filehandle ( @{ $QT_filehandle{$type} } ) {
+ my $software;
+
+ QTFILE_LINE:
+ while ( my $line = <$filehandle> ) {
+
+ # skip comment or blank lines
+ next QTFILE_LINE if ( $line =~ /^\#/ or $line =~ /^\s*$/ );
+
+ # This should support both unix or DOS line breaks.
+ $line =~ s/$RE_LINE_BREAK//g;
+
+ # Header for a software/manufacturer name. Remove
+ # leading and trailing whitespace
+ if ( $line =~ m/^>>>\s*(.*?)\s*$/ ) {
+ $software = $1;
+ next QTFILE_LINE;
+
+ }
+
+ # Everything else; the meat of the QTs
+ else {
+
+ # Strip whitespace in a moderately efficient manner
+ $line =~ s/^ *//;
+ $line =~ s/ *$//;
+ $line =~ s/ *\t */\t/g;
+
+ my @line_array = split /\t/, $line, -1;
+
+ $QT_hash{$software}{ $line_array[0] } = {
+ datatype => $line_array[1] || q{},
+ scale => $line_array[2] || q{},
+ subclass => $line_array[3] || q{},
+ is_background => $line_array[4] || q{},
+ confmap => $line_array[5] || q{},
+ channel => $line_array[6] || q{},
+ description => $line_array[7] || q{},
+ is_user_defined => ( $type eq 'new' ) || undef,
+ };
+ }
+ }
+ }
+ }
+ return ( \%QT_hash );
+}
+
+sub write_QTs {
+
+ my ( $new_QTs, $fh ) = @_;
+
+ print $fh <<"HEADER";
+# Name\tDataType\tScale\tMAGE Subclass\tisBackground\tConfidenceIndicator target QT\tChannel\tDescription
+HEADER
+
+ foreach my $software ( sort keys %$new_QTs ) {
+
+ print $fh (">>>$software\n");
+
+ my @lines;
+ while ( my ( $name, $slots ) = each %{ $new_QTs->{$software} } ) {
+ push(
+ @lines,
+ join( "\t",
+ $name, $slots->{datatype},
+ $slots->{scale}, $slots->{subclass},
+ $slots->{is_background}, $slots->{confmap},
+ $slots->{channel}, $slots->{description},
+ )
+ );
+ }
+
+ print $fh ( join( "\n", sort @lines ), "\n" );
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Datafile/QT_list.txt b/lib/ArrayExpress/Datafile/QT_list.txt
new file mode 100644
index 0000000..0420249
--- /dev/null
+++ b/lib/ArrayExpress/Datafile/QT_list.txt
@@ -0,0 +1,1087 @@
+#
+# $Id: QT_list.txt 2084 2008-06-20 16:41:44Z farne $
+#
+# -----------------------------------------------------------------------------------------------------
+#
+# This first section includes a set of "custom" QTs
+# which are commonly used but not defined elsewhere.
+#
+# Name DataType Scale MAGE Subclass isBackground ConfidenceIndicator target QT Channel Description
+>>>Tab2MAGE_FGEM[ebi.ac.uk]
+RMA float linear_scale DerivedSignal
+Normalized float linear_scale DerivedSignal
+Ratio float linear_scale Ratio
+# -----------------------------------------------------------------------------------------------------
+#
+# Below is the curated list of QTs as prepared and maintained by the ArrayExpress curators.
+# This will be updated periodically with new releases of this package.
+#
+# NOTE TO SELF - this part of the file will be automatically generated by Datafiles::write_QTs.
+# Attempt no changes here unless you are sure they won't be overwritten.
+#
+# Name DataType Scale MAGE Subclass isBackground ConfidenceIndicator target QT Channel Description
+>>>AIDA[Raytest]
+Area [pixel] float linear_scale SpecializedQuantitationType Spot area in pixels.
+Bkg SD Cy3 float linear_scale Error 1 Bkg [QL] Cy3 Cy3 Standard deviation of background channel Cy3.
+Bkg SD Cy5 float linear_scale Error 1 Bkg [QL] Cy5 Cy5 Standard deviation of background channel Cy5.
+Bkg [QL] Cy3 float linear_scale SpecializedQuantitationType 1 Cy3 Standard deviation of background channel Cy3.
+Bkg [QL] Cy5 float linear_scale SpecializedQuantitationType 1 Cy5 Standard deviation of background channel Cy5.
+Homogeneity [%] Cy3 float linear_scale SpecializedQuantitationType Cy3 Percentage of dot deviation from homogenous intensity distribution channel Cy3.
+Homogeneity [%] Cy5 float linear_scale SpecializedQuantitationType Cy5 Percentage of dot deviation from homogenous intensity distribution channel Cy5.
+Integral SD Cy3 float linear_scale Error Integral [QL] Cy3 Cy3 Standard deviation of integrated raw intensity channel Cy3.
+Integral SD Cy5 float linear_scale Error Integral [QL] Cy5 Cy5 Standard deviation of integrated raw intensity channel Cy5.
+Integral [QL] Cy3 float linear_scale MeasuredSignal Cy3 Integrated raw intensity of spot Channel Cy3.
+Integral [QL] Cy5 float linear_scale MeasuredSignal Cy5 Integrated raw intensity of spot Channel Cy5.
+Mean [QL] Cy3 float linear_scale MeasuredSignal Cy3 Mean (Integral/Area) Channel Cy3.
+Mean [QL] Cy5 float linear_scale MeasuredSignal Cy5 Mean (Integral/Area) Channel Cy5.
+Median [QL] Cy3 float linear_scale SpecializedQuantitationType Cy3 Median of the raw pixel intensity of spot Channel Cy3.
+Median [QL] Cy5 float linear_scale SpecializedQuantitationType Cy5 Median of the raw pixel intensity of spot Channel Cy5.
+Quality Cy3 boolean unscaled SpecializedQuantitationType Cy3 Quality flag Channel Cy3.
+Quality Cy5 boolean unscaled SpecializedQuantitationType Cy5 Quality flag Channel Cy5.
+Saturated [pixel] Cy3 float linear_scale SpecializedQuantitationType Cy3 Measure of saturation pixels channel Cy3.
+Saturated [pixel] Cy5 float linear_scale SpecializedQuantitationType Cy5 Measure of saturation pixels channel Cy5.
+>>>Affymetrix[Affymetrix]
+Probe Set Name string_datatype unscaled SpecializedQuantitationType The name of the probe set.
+Detection string_datatype unscaled PresentAbsent A measurement indicating if the transcript was detected (Present), not detected (Absent) or marginally detected (Marginal). Can be one of "Absent", "Marginal", "Present" or "No Call".
+Detection p-value float linear_scale PValue Detection The p-value calculated for a given detection call.
+Signal float linear_scale DerivedSignal A measure of the relative abundance of a transcript.
+Number of Pairs integer linear_scale SpecializedQuantitationType The number of probe pairs in the probe set.
+Number of Pairs Used integer linear_scale SpecializedQuantitationType The number of probe pairs in the probe set used in the Detection call.
+CELIntensity float linear_scale MeasuredSignal The intensity value of the probe. Represents the 75th percentile among the DAT file intensity values of the pixels in the feature.
+CELIntensityStdev float linear_scale SpecializedQuantitationType The standard deviation of the intensity of the probe.
+CELMask boolean linear_scale SpecializedQuantitationType A flag indicating that the probe was masked by the user.
+CELOutlier boolean linear_scale SpecializedQuantitationType A flag indicating that the software called this probe an outlier.
+CELPixels integer linear_scale SpecializedQuantitationType The number of pixels used to calculate the intensity.
+CELX integer linear_scale SpecializedQuantitationType The x coordinate of the probe.
+CELY integer linear_scale SpecializedQuantitationType The y coordinate of the probe.
+CHPAbsCall string_datatype unscaled PresentAbsent The call in an absolute analysis that indicates if the transcript was present (P), absent (A), or marginal (M).
+CHPAllele string_datatype unscaled SpecializedQuantitationType The allele call in a SNP analysis: AA, BB, AB, AB_A, AB_B, or "No call".
+CHPAllelePvalue float linear_scale PValue CHPAllele The p-value calculated for a given allele call.
+CHPAllelePvalueAA float linear_scale PValue CHPAllele The p-value calculated for a given AA allele call.
+CHPAllelePvalueAB float linear_scale PValue CHPAllele The p-value calculated for a given AB allele call.
+CHPAllelePvalueBB float linear_scale PValue CHPAllele The p-value calculated for a given BB allele call.
+CHPAllelePvalueNoCall float linear_scale PValue CHPAllele The p-value calculated for a given NoCall allele call.
+CHPAlleleRAS1 float linear_scale SpecializedQuantitationType
+CHPAlleleRAS2 float linear_scale SpecializedQuantitationType
+CHPAvgDiff float linear_scale DerivedSignal Average intensity difference
+CHPAvgDiffChange float linear_scale SpecializedQuantitationType The change in average difference
+CHPBaselineAbsent float linear_scale SpecializedQuantitationType Flag indicating if the baseline call was absent.
+CHPChange string_datatype unscaled PresentAbsent A call indicating an Increase (I), Marginal Increase (MI), No Change (NC), Decrease (D), or Marginal Decrease (MD) in transcript level between a baseline array and an experiment array. May also have the value "No Call".
+CHPChangePvalue float linear_scale PValue CHPChange Measures the probability that the expression levels of a probe set in two different arrays are the same or not. When the p-value is close to 0.5 they are likely to be the same. When the p-value is close to 0 the expression level in the experiment array is
+CHPCommonPairs integer linear_scale SpecializedQuantitationType The number of common probe pairs on two arrays (experiment versus baseline) after saturation across the probe set is determined.
+CHPDPos-DNegRatio float linear_scale Ratio The ratio of the change in positive to the change in negative pairs
+CHPDec integer linear_scale SpecializedQuantitationType The number of decreasing pairs
+CHPDecRatio float linear_scale Ratio The ratio of decreasing pairs to the total number of pairs.
+CHPDetection string_datatype unscaled PresentAbsent A measurement indicating if the transcript was detected (Present), not detected (Absent) or marginally detected (Marginal). Can be one of "A", "M", "P" or "No Call".
+CHPDetectionPvalue float linear_scale PValue CHPDetection See Detection p-value
+CHPDiffCall string_datatype unscaled PresentAbsent The comparison change call
+CHPFoldChange float linear_scale Ratio The fold change
+CHPInc integer linear_scale SpecializedQuantitationType The number of increasing pairs.
+CHPIncDecRatio float linear_scale Ratio The ratio of increase to decrease pairs.
+CHPIncRatio float linear_scale Ratio The ratio of increasing pairs to the total number of pairs
+CHPLogAvg float log_base_10 DerivedSignal Average log ratio
+CHPLogAvgRatioChange float log_base_10 SpecializedQuantitationType The change in log average ratio
+CHPNegChange integer linear_scale SpecializedQuantitationType The change in negative pairs
+CHPNegative integer linear_scale SpecializedQuantitationType Number of negative probe pairs
+CHPPairs integer linear_scale SpecializedQuantitationType The number of probe pairs in the probe set.
+CHPPairsInAvg integer linear_scale SpecializedQuantitationType Number of probe pairs used in computing the average intensity difference
+CHPPairsUsed integer linear_scale SpecializedQuantitationType The number of probe pairs in the probe set used in the Detection call.
+CHPPosChange float linear_scale SpecializedQuantitationType The change in positive pairs.
+CHPPosFraction float linear_scale Ratio The fraction of positive pairs
+CHPPosNegRatio float linear_scale Ratio The ratio of the positive to the negative pairs
+CHPPositive integer linear_scale SpecializedQuantitationType Number of positive probe pairs
+CHPSignal float linear_scale DerivedSignal A measure of the relative abundance of a transcript.
+CHPSignalLogRatio float log_base_2 Ratio The change in expression level for a transcript between a baseline and an experiment array. This change is expressed as the Log2 ratio.
+CHPSignalLogRatioHigh float log_base_2 Ratio The upper limit of the Signal Log Ratio within a 95% confidence interval.
+CHPSignalLogRatioLow float log_base_2 Ratio The lower limit of the Signal Log Ratio within a 95% confidence interval.
+CHPSortScore float linear_scale SpecializedQuantitationType The sort score.
+ProbeSetName string_datatype unscaled SpecializedQuantitationType The name of the probe set.
+Stat Pairs integer linear_scale SpecializedQuantitationType The number of probe pairs in the probe set.
+Stat Pairs Used integer linear_scale SpecializedQuantitationType The number of probe pairs in the probe set used in the Detection call.
+>>>AppliedBiosystems[Celera]
+Assay_Normalized_Signal float DerivedSignal
+CV float SpecializedQuantitationType
+Flags integer unscaled SpecializedQuantitationType
+SDEV float linear_scale MeasuredSignal
+S_N float SpecializedQuantitationType
+Signal float linear_scale MeasuredSignal
+>>>ArrayGauge[FUJIFILM]
+Dens.(Total PSL) float linear_scale MeasuredSignal 0 Total phospho stimulated luminescence.
+Norm Data float log_base_2 DerivedSignal
+Overshining Status boolean unscaled SpecializedQuantitationType During quantification the analysis spots that display false positive intensity due to non-specific signal from neighboring clones are excluded.
+Total PSL float linear_scale SpecializedQuantitationType 0 Total phospho stimulated luminescence.
+>>>ArrayVision[Imaging Research]
+% At Ceiling - Bkgd_Cy3 float linear_scale SpecializedQuantitationType Cy3 Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. V
+% At Ceiling - Bkgd_Cy5 float linear_scale SpecializedQuantitationType Cy5 Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. V
+% At Ceiling_Cy3 float linear_scale SpecializedQuantitationType Cy3 Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% At Ceiling_Cy5 float linear_scale SpecializedQuantitationType Cy5 Clipped pixels - A quality control measure that reports the proportion of pixels at the uppermost limit (ceiling) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% At Floor - Bkgd_Cy3 float linear_scale SpecializedQuantitationType Cy3 Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. Val
+% At Floor - Bkgd_Cy5 float linear_scale SpecializedQuantitationType Cy5 Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data. Val
+% At Floor_Cy3 float linear_scale SpecializedQuantitationType Cy3 Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% At Floor_Cy5 float linear_scale SpecializedQuantitationType Cy5 Clipped pixels - A quality control measure that reports the proportion of pixels at the lowermost limit (floor) of the density scale. The presence of clipped pixels suggest a problem during image formation, and can affect the validity of density data.
+% Replaced (AR VOL)_Cy3 float linear_scale SpecializedQuantitationType Cy3 Percentage of pixels replaced with estimated values in the calculation of AR volume.
+% Replaced (AR VOL)_Cy5 float linear_scale SpecializedQuantitationType Cy5 Percentage of pixels replaced with estimated values in the calculation of AR volume.
+AR VOL - RFU_Cy3 float linear_scale SpecializedQuantitationType Cy3 Artifact-removed density value for each spot, multiplied by its area.
+AR VOL - RFU_Cy5 float linear_scale SpecializedQuantitationType Cy5 Artifact-removed density value for each spot, multiplied by its area.
+Area - 5m2_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Area of the spot, background and/or reference region.
+Area - 5m2_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Area of the spot, background and/or reference region.
+Area - mm2_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Area of the spot, background and/or reference region.
+Area - mm2_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Area of the spot, background and/or reference region.
+Area - �m2_Cy3 linear_scale SpecializedQuantitationType Cy3 Area of the spot, background and/or reference region.
+Area - �m2_Cy5 linear_scale SpecializedQuantitationType Cy5 Area of the spot, background and/or reference region.
+Bkgd_Cy3 float linear_scale MeasuredSignal 1 Cy3 Background density or volume; background volume value is adjusted to the size of spot, where applicable.
+Bkgd_Cy5 float linear_scale MeasuredSignal 1 Cy5 Background density or volume; background volume value is adjusted to the size of spot, where applicable.
+Flag_Cy3 boolean linear_scale PresentAbsent Cy3 Select this measure to manually flag data in the data table. The value of this measure can be 0 or 1.
+Flag_Cy5 boolean linear_scale PresentAbsent Cy5 Select this measure to manually flag data in the data table. The value of this measure can be 0 or 1.
+MAD - RFU / 5m2_Cy3 float linear_scale SpecializedQuantitationType Cy3 Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / 5m2_Cy5 float linear_scale SpecializedQuantitationType Cy5 Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / mm2_Cy3 float linear_scale SpecializedQuantitationType Cy3 Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / mm2_Cy5 float linear_scale SpecializedQuantitationType Cy5 Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / �m2_Cy3 float linear_scale SpecializedQuantitationType Cy3 Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities median density); it is a measure of the variation around the median density value of the spot.
+MAD - RFU / �m2_Cy5 float linear_scale SpecializedQuantitationType Cy5 Median of absolute deviation - median of the absolute values of deviations from the median density (i.e., the absolute values of pixel densities median density); it is a measure of the variation around the median density value of the spot.
+Median Dens - Levels_Cy3 float linear_scale SpecializedQuantitationType Cy3 Median Density value of the spot Levels. ['Gray levels' (Levels) are the raw pixel intensity values upon which all other density/intensity scales are based.]
+Median Dens - Levels_Cy5 float linear_scale SpecializedQuantitationType Cy5 Median Density value of the spot Levels. ['Gray levels' (Levels) are the raw pixel intensity values upon which all other density/intensity scales are based.]
+Median Dens - RFU / �m2_Cy3 float linear_scale SpecializedQuantitationType Cy3
+Median Dens - RFU / �m2_Cy5 float linear_scale SpecializedQuantitationType Cy5
+S/N_Cy3 float linear_scale Ratio 1 Cy3 Signal-to-noise ratio - spot density minus background density, divided by the SD of the background density.
+S/N_Cy5 float linear_scale Ratio 1 Cy5 Signal-to-noise ratio - spot density minus background density, divided by the SD of the background density.
+SD - Levels_Cy3 float linear_scale SpecializedQuantitationType Cy3 Standard deviation of the pixel Levels values.
+SD - Levels_Cy5 float linear_scale SpecializedQuantitationType Cy5 Standard deviation of the pixel Levels values.
+SD - RFU / 5m2_Cy3 float linear_scale SpecializedQuantitationType Cy3 Standard deviation of the pixel density values.
+SD - RFU / 5m2_Cy5 float linear_scale SpecializedQuantitationType Cy5 Standard deviation of the pixel density values.
+SD - RFU / mm2_Cy3 float linear_scale SpecializedQuantitationType Cy3 Standard deviation of the pixel density values.
+SD - RFU / mm2_Cy5 float linear_scale SpecializedQuantitationType Cy5 Standard deviation of the pixel density values.
+SD - RFU / �m2_Cy3 float linear_scale SpecializedQuantitationType Cy3 Standard deviation of the pixel density values.
+SD - RFU / �m2_Cy5 float linear_scale SpecializedQuantitationType Cy5 Standard deviation of the pixel density values.
+SPOTLABEL
+Spot labels integer linear_scale SpecializedQuantitationType
+VOL - Levels x mm2_Cy3 float linear_scale SpecializedQuantitationType Cy3
+VOL - Levels x mm2_Cy5 float linear_scale SpecializedQuantitationType Cy5
+sARMDens_Cy3 float linear_scale MeasuredSignal Cy3 Background corrected ARM Density value. ARM Density value of the spot, minus the background Density value.
+sARMDens_Cy5 float linear_scale MeasuredSignal Cy5 Background corrected ARM Density value. ARM Density value of the spot, minus the background Density value.
+sARVOL_Cy3 float linear_scale SpecializedQuantitationType Cy3 Subtracted AR volume value (AR volume value of the spot minus the background volume value); the background value is adjusted to the size of the spot when applicable.
+sARVOL_Cy5 float linear_scale SpecializedQuantitationType Cy5 Subtracted AR volume value (AR volume value of the spot minus the background volume value); the background value is adjusted to the size of the spot when applicable.
+sMedianDens_Cy3 float linear_scale SpecializedQuantitationType Cy3 Background corrected Median Density value. Median Density value of the spot, minus the background Density value.
+sMedianDens_Cy5 float linear_scale SpecializedQuantitationType Cy5 Background corrected Median Density value. Median Density value of the spot, minus the background Density value.
+sVOL_Cy3 float linear_scale SpecializedQuantitationType Cy3 Background corrected Volume value. Volume value of the spot, minus the background Volume value. The background value is adjusted to the size of the spot when applicable. sVOL = DxA_data - ((DxA_bkgd * Area_data)/Area_bkgd)).
+sVOL_Cy5 float linear_scale SpecializedQuantitationType Cy5 Background corrected Volume value. Volume value of the spot, minus the background Volume value. The background value is adjusted to the size of the spot when applicable. sVOL = DxA_data - ((DxA_bkgd * Area_data)/Area_bkgd)).
+>>>BZScan[TAGC ERM206]
+Diameter float linear_scale SpecializedQuantitationType Automatically computed spot diameter.
+Fit_correction float linear_scale SpecializedQuantitationType 1 Background level by unit surface.
+Overshining_correction float linear_scale SpecializedQuantitationType Correction for overshining by unit radius.
+Q(fit,const) float linear_scale SpecializedQuantitationType Spot intensity based on user-provided diameter and theoretical fit to the data.
+Q(fit,var) float linear_scale SpecializedQuantitationType Spot intensity based on computed diameter and and theoretical fit to the data.
+Q(image,const) float linear_scale MeasuredSignal Spot intensity based on user-provided diameter and measured data.
+Q(image,var) float linear_scale SpecializedQuantitationType Spot intensity based on computed diameter and measured data.
+QM float linear_scale PValue Q(image,const) Quality metric: distance between measured data and theoretical fit.
+SpotQuality boolean unscaled PresentAbsent Present/Absent (automatic guess).
+>>>BeadStudio[Illumina]
+MIN_Signal float linear_scale SpecializedQuantitationType Minimum intensity of the Bead Type/target in the group.
+AVG_Signal float linear_scale MeasuredSignal Average intensity of the Bead Type/target in the group.
+MAX_Signal float linear_scale SpecializedQuantitationType Maximum intensity of the Bead Type/target in the group.
+NARRAYS integer linear_scale SpecializedQuantitationType Number of samples in the group.
+ARRAY_STDEV SpecializedQuantitationType Standard deviation associated with sample-to-sample variability within the group (undefined when the group contains a single sample).
+BEAD_STDEV float linear_scale Error AVG_Signal Average standard deviation associated with bead-to-bead variability for the samples in the group.
+BEAD_STDERR float linear_scale Error AVG_Signal Standard error of the mean of each probe.
+Avg_NBEADS integer linear_scale SpecializedQuantitationType Average number of beads per bead type representing probes for the gene.
+Detection float SpecializedQuantitationType 1-p-value computed from the background model characterizing the chance that the target sequence signal was distinguishable from the negative controls.
+Detection Pval float SpecializedQuantitationType
+DiffScore float SpecializedQuantitationType Statistical confidence that the gene's expression has changed with respect to a reference group.
+Concordance float SpecializedQuantitationType Number of probes showing upregulated signal compared to the number of probes showing downregulated signal.
+>>>BlueFuse[BlueGnome]
+AMPCH1 float linear_scale MeasuredSignal Cy3 Total signal in Cy3 channel.
+AMPCH2 float linear_scale MeasuredSignal Cy5 Total signal in Cy5 channel.
+AUTO EXCL string_datatype unscaled SpecializedQuantitationType Indication of whether a spot has been automatically excluded. Yes - it has been excluded, no - it has not been excluded.
+BLOCK integer linear_scale SpecializedQuantitationType The sequential number of the block on the array.
+CIRCULARITY float linear_scale SpecializedQuantitationType Estimate of the circularity of the spot, between 0 and 1. The nearer to 1, the more circular the spot.
+CONFIDENCE float linear_scale SpecializedQuantitationType The Confidence Estimate in the calculated ratio, between 0 and 1.
+FLAG string_datatype unscaled SpecializedQuantitationType The Confidence Flag calculated from the Confidence Estimate.
+GRID OFFSET float linear_scale SpecializedQuantitationType Distance in pixels of the spot from the ideal grid position.
+ID string_datatype unscaled SpecializedQuantitationType The ID of the spot, taken from ID field in GAL file.
+LOG10PRODUCT float log_base_10 SpecializedQuantitationType Log base 10 of the product of total signal in Cy3 channel multiplied by total signal in Cy5 channel.
+LOG10RATIO CH1/CH2 float log_base_10 SpecializedQuantitationType Log base 10 of the ratio of total signal in Cy3 channel divided by total signal in Cy5 channel.
+LOG10RATIO CH2/CH1 float log_base_10 SpecializedQuantitationType Log base 10 of the ratio of total signal in Cy5 channel divided by total signal in Cy3 channel.
+LOG10SUM float log_base_10 SpecializedQuantitationType Log base 10 of the sum of total signal in Cy3 channel plus total signal in Cy5 channel.
+LOG2PRODUCT float log_base_2 SpecializedQuantitationType Log base 2 of the product of total signal in Cy3 channel multiplied by total signal in Cy5 channel.
+LOG2RATIO CH1/CH2 float log_base_2 Ratio Log base 2 of the ratio of total signal in Cy3 channel divided by total signal in Cy5 channel.
+LOG2RATIO CH2/CH1 float log_base_2 SpecializedQuantitationType Log base 2 of the ratio of total signal in Cy5 channel divided by total signal in Cy3 channel.
+LOG2SUM float log_base_2 SpecializedQuantitationType Log base 2 of the sum of total signal in Cy3 channel plus total signal in Cy5 channel.
+MAN EXCL string_datatype unscaled SpecializedQuantitationType Indication of whether a spot has been manually excluded. Yes - it has been manually excluded, no - it has not been manually excluded.
+NAME string_datatype unscaled SpecializedQuantitationType The name of the spot, taken from Name field in GAL file.
+P ON CH1 float linear_scale SpecializedQuantitationType Cy3 Estimate of whether there is a biological signal in Cy3 channel, between 0 and 1.
+P ON CH2 float linear_scale SpecializedQuantitationType Cy5 Estimate of whether there is a biological signal in Cy5 channel, between 0 and 1.
+PELCOL integer linear_scale SpecializedQuantitationType Calculated horizontal position of the spot on image in pixels. 1 is the left column of pixels.
+PELROW integer linear_scale SpecializedQuantitationType Calculated vertical position of the spot on image in pixels. 1 is the top row of pixels.
+PRODUCT float linear_scale SpecializedQuantitationType Product of total signal in Cy3 channel multiplied by total signal in Cy5 channel.
+QUALITY SpecializedQuantitationType
+RADIUS float linear_scale SpecializedQuantitationType Estimate of the radius of the spot in pixels.
+RATIO CH1/CH2 float linear_scale SpecializedQuantitationType Ratio of total signal in Cy3 channel divided by total signal channel in Cy5 channel.
+RATIO CH2/CH1 float linear_scale SpecializedQuantitationType Ratio of total signal in Cy5 channel divided by total signal channel in Cy3 channel.
+SPOTNUM integer linear_scale SpecializedQuantitationType The sequential number of the spot on the array.
+SUM float linear_scale SpecializedQuantitationType Sum of total signal in Cy3 channel plus total signal in Cy5 channel.
+UNIFORMITY float linear_scale SpecializedQuantitationType Estimate of the uniformity of the spot, between 0 and 1. The nearer to 1 the more coin-shaped the spot, i.e. vertical sides and horizontal surface.
+>>>ChipSkipper[EMBL]
+Comment string_datatype unscaled SpecializedQuantitationType Additional description of spot.
+Comp_Channel Cy3 float linear_scale DerivedSignal Cy3 Normalised intensity of Cy3_Vol-BG.
+Comp_Channel Cy5 float linear_scale DerivedSignal Cy5 Normalised intensity of Cy5_Vol-BG.
+Comp_Directional_Ratio-Cy5/Cy3 float linear_scale SpecializedQuantitationType Compensated directional ratio: if Comp_Ratio > 1 then Directional = Ratio, if Ratio < 1 then Directional = -1/Ratio.
+Comp_Log2_Ratio float log_base_2 SpecializedQuantitationType Compensated Log2 ratio: log2 (Comp_Ratio-Cy5/Cy3).
+Comp_Ratio-Cy5/Cy3 float linear_scale Ratio Compensated ratio ( = Comp_Channel Cy5 / Comp_Channel Cy3 ).
+Cy3_Av-Vol/pixel float linear_scale SpecializedQuantitationType Cy3 Average intensity per pixel in color channel Cy3.
+Cy3_BG-Arith float linear_scale SpecializedQuantitationType 1 Cy3 Arithmetic average for local background in color channel Cy3.
+Cy3_BG-Geom float linear_scale SpecializedQuantitationType 1 Cy3 Geometric average for local background in color channel Cy3.
+Cy3_BG-Median float linear_scale SpecializedQuantitationType 1 Cy3 Median average for local background in color channel Cy3.
+Cy3_BG_Tot float linear_scale MeasuredSignal 1 Cy3 Total integrated background in color channel Cy3.
+Cy3_SNR1 float linear_scale Error Cy3_Vol-BG Cy3 Sdev of local background average in color channel Cy3.
+Cy3_SNR1_Score float linear_scale Error Cy3_Vol-BG Cy3 Quality value based on intensity and background for color channel Cy3 (range: 0 = bad....1 = good).
+Cy3_Sat-pixel float linear_scale SpecializedQuantitationType Cy3 Number of saturated pixel in color channel Cy3.
+Cy3_Spot-area float linear_scale SpecializedQuantitationType Cy3 Area of spot in colour channel Cy3 in pixels.
+Cy3_Vol-BG float linear_scale SpecializedQuantitationType 1 Cy3 Background reduced intensity of colour channel Cy3.
+Cy3_Volume integer linear_scale MeasuredSignal Cy3 Total integrated signal intensity in color channel Cy3.
+Cy5_Av-Vol/pixel float linear_scale SpecializedQuantitationType Cy5 Average intensity per pixel in color channel Cy5.
+Cy5_BG-Arith float linear_scale SpecializedQuantitationType 1 Cy5 Arithmetic average for local background in color channel Cy5.
+Cy5_BG-Geom float linear_scale SpecializedQuantitationType 1 Cy5 Geometric average for local background in color channel Cy5.
+Cy5_BG-Median float linear_scale SpecializedQuantitationType 1 Cy5 Median average for local background in color channel Cy5.
+Cy5_BG_Tot float linear_scale MeasuredSignal 1 Cy5 Total integrated background in color channel Cy5.
+Cy5_SNR1 float linear_scale Error Cy5_Vol-BG Cy5 Sdev of local background average in color channel Cy5.
+Cy5_SNR1_Score float linear_scale Error Cy5_Vol-BG Cy5 Quality value based on intensity and background for color channel Cy5 (range: 0 = bad....1 = good).
+Cy5_Sat-pixel float linear_scale SpecializedQuantitationType Cy5 Number of saturated pixel in color channel Cy5.
+Cy5_Spot-area float linear_scale SpecializedQuantitationType Cy5 Area of spot in colour channel Cy5 in pixels.
+Cy5_Vol-BG float linear_scale SpecializedQuantitationType 1 Cy5 Background reduced intensity of colour channel Cy5.
+Cy5_Volume integer linear_scale MeasuredSignal Cy5 Total integrated signal intensity in color channel Cy5.
+Directional_Ratio-Cy5/Cy3 float linear_scale SpecializedQuantitationType Directional ratio: if Ratio > 1 then Directional = Ratio, if Ratio < 1 then Directional = -1/Ratio.
+Flags string_datatype unscaled SpecializedQuantitationType Quality Flags: SNR1,SNR2 = low signal quality in color channel Cy5, Cy3; GS = low geometric score; MB = manually flagged bad.
+GS_Flags string_datatype linear_scale SpecializedQuantitationType P = pass or M = marginal flag.
+Group name string_datatype unscaled SpecializedQuantitationType Group name for this spot.
+Image-x-Pos integer linear_scale SpecializedQuantitationType Image coordinate of spot.
+Image-y-Pos integer linear_scale SpecializedQuantitationType Image coordinate of spot.
+Line_Counter integer unscaled SpecializedQuantitationType Line counter
+Log2_Ratio-Cy5/Cy3 float log_base_2 Ratio Log2 ratio: log2 (Ratio-Cy5/Cy3)
+Median of ratios float linear_scale SpecializedQuantitationType Median of ratios (Cy5/Cy3) from all pixel in this spot.
+Median of ratios % SDev float linear_scale SpecializedQuantitationType Standard deviation of Median of ratios in percent.
+Ratio of medians float linear_scale SpecializedQuantitationType Ratio of Medians (Cy5/Cy3) from all pixel in this spot.
+Ratio of medians % SDev float linear_scale SpecializedQuantitationType Standard deviation of Ratio of medians in percent.
+Ratio-Cy5/Cy3 float linear_scale SpecializedQuantitationType Direct ratio ( = Cy5_Vol-BG / Cy3_Vol-BG )
+Regulation string_datatype unscaled SpecializedQuantitationType If Flag = SNR1 then Regulation=>Down; if Flag = SNR2 then Regulation=>up; if Flag = SNR1 AND SNR2 then Regulation=>weak; else Regulation = Ratio-Cy5/Cy3.
+Rep_vol_BG2 float linear_scale SpecializedQuantitationType Average of background reduced volume for onchip replicas (when calculated).
+Replicas float linear_scale SpecializedQuantitationType
+SNR*GS float linear_scale SpecializedQuantitationType Number of saturated pixel in both colors (logical or).
+SNR-tot float linear_scale SpecializedQuantitationType Squared average of Cy5_SNR and Cy3_SNR.
+SNR-tot_Score float linear_scale SpecializedQuantitationType Squared average of Cy5_%SNR and Cy3_%SNR.
+Tot_Sat-pixel float linear_scale SpecializedQuantitationType Number of saturated pixel in color channel Cy5 OR color channel Cy3.
+>>>CodeLink[Motorola Life Sciences]
+Cy3 BG Mean float linear_scale MeasuredSignal 1 Cy3 Background mean in Cy3 channel.
+Cy3 BG Median integer linear_scale SpecializedQuantitationType 1 Cy3 Background median in Cy3 channel.
+Cy3 Mean float linear_scale SpecializedQuantitationType Cy3 Statistical mean (average) of the raw intensity signal (i.e. mean of all the pixels belonging to the feature region) in Cy3 channel.
+Cy3 Raw Intensity float linear_scale MeasuredSignal Cy3 The signal from the spot minus the median background in Cy3 channel.
+Cy5 BG Mean float linear_scale MeasuredSignal 1 Cy5 Background mean in Cy5 channel.
+Cy5 BG Median integer linear_scale SpecializedQuantitationType 1 Cy5 Background median in Cy5 channel.
+Cy5 Mean float linear_scale SpecializedQuantitationType Cy5 Statistical mean (average) of the raw intensity signal (i.e. mean of all the pixels belonging to the feature region) in Cy5 channel.
+Cy5 Raw Intensity float linear_scale MeasuredSignal Cy5 The signal from the spot minus the median background in Cy5 channel.
+Flag boolean unscaled SpecializedQuantitationType Flag indicates if the spot matched with quality measures (i.e shape, low signal, saturated). TRUE corresponds to features that have to be discarded according to the quality measures, while FALSE is the opposite.
+>>>Spot[CSIRO]
+# These first are just feature coordinate/index columns, and should be discarded.
+#indexs The index of the spot.
+#grid_r The row index of the grid.
+#grid_c The column index of the grid.
+#spot_r The row index of the spot within the its grid.
+#spot_c The column index of the spot within the its grid.
+area integer linear_scale SpecializedQuantitationType The number of pixels in the segmented spot.
+Gmean float linear_scale SpecializedQuantitationType 0 Cy3 The average pixel value within the spot: green band.
+Gmedian float linear_scale MeasuredSignal 0 Cy3 The median pixel value within the spot: green band.
+GIQR float 0 Cy3 The interquartile range of the logged pixel values within the spot: green band.
+Rmean float linear_scale SpecializedQuantitationType 0 Cy5 The average pixel value within the spot: red band.
+Rmedian float linear_scale MeasuredSignal 0 Cy5 The median pixel value within the spot: red band.
+RIQR float 0 Cy5 The interquartile range of the logged pixel values within the spot: red band.
+bg.Gmean float linear_scale SpecializedQuantitationType 1 Cy3 The average pixel value in the background region: green band.
+bg.Gmedian float linear_scale MeasuredSignal 1 Cy3 The median pixel value in the background region: green band.
+bg.Gsd float 1 Cy3
+bg.GIQR float 1 Cy3 The interquartile range of the logged pixel values in the background region: green band.
+bg.Rmean float linear_scale SpecializedQuantitationType 1 Cy5 The average pixel value in the background region: red band.
+bg.Rmedian float linear_scale MeasuredSignal 1 Cy5 The median pixel value in the background region: red band.
+bg.Rsd float 1 Cy5
+bg.RIQR float 1 Cy5 The interquartile range of the logged pixel values in the background region: red band.
+valleyG SpecializedQuantitationType Cy3 Median of median image values in four neighbouring "valley" regions: green band. "Valley" regions are square regions between spots.
+valleyR SpecializedQuantitationType Cy5 Median of median image values in four neighbouring "valley" regions: red band.
+morphG integer linear_scale SpecializedQuantitationType Cy3 Value of a morphological opening of the green band image at the nominal spot centre. Openings and erosions (see below) use a rectangular structuring element with side lengths equal to twice the average horizontal and vertical distances between spots.
+morphG.erode SpecializedQuantitationType Cy3 Value of a morphological erosion of the green band image at the nominal spot centre.
+morphG.close.open integer linear_scale SpecializedQuantitationType Cy3 Value of a morphological closing followed by a morphological opening of the green band image at the nominal spot centre.
+morphR integer linear_scale SpecializedQuantitationType Cy5 Value of a morphological opening of the red band image at the nominal spot centre.
+morphR.erode SpecializedQuantitationType Cy5 Value of a morphological erosion of the red band image at the nominal spot centre.
+morphR.close.open integer linear_scale SpecializedQuantitationType Cy5 Value of a morphological closing followed by a morphological opening of the red band image at the nominal spot centre.
+logratios float log_base_2 Ratio 0 The base-2 log of the background-adjusted ratio, R/G, using R/Gmedian for spot value and morphR/G for background.
+perimeter integer linear_scale SpecializedQuantitationType The perimeter of the spot as measured by the number of pixels in the spot which have a neighbouring pixel outside the spot. Each pixel is considered to have 8 neighbours in this process.
+circularity SpecializedQuantitationType A measure of circularity of the spot equal to area times four pi divided by perimeter squared. This is ideally one for circles and more than one for other shapes.
+badspot SpecializedQuantitationType A flag which is one if the spot area is greater than the product of the horizontal and vertical average spot separations, and zero otherwise.
+>>>CodeLink Expression Analysis[GE Healthcare]
+Raw_intensity float linear_scale SpecializedQuantitationType 0 Spot density, which is the difference between the spot mean and the local background median.
+Normalized_intensity float linear_scale DerivedSignal 0 Normalized intensity value (the raw intensity divided by the normalization factor).
+Quality_flag string_datatype unscaled SpecializedQuantitationType 0 Metrics the software uses to indicate the quality of a spot.
+Signal_strength float linear_scale Ratio 0 Estimated signal above its local background noise level. The ratio is expressed as the spot mean divided by the local background standard deviation.
+Spot_mean float linear_scale MeasuredSignal 0 Sum of all pixel values in the spot area divided by the number of pixels.
+Spot_median integer linear_scale SpecializedQuantitationType 0 Median pixel intensity computed over the spot area.
+Spot_stdev float linear_scale Error 0 Spot_mean Standard deviation of the pixel intensity in the spot.
+Spot_area integer linear_scale SpecializedQuantitationType 0 Number of pixels in the spot.
+Spot_diameter float linear_scale SpecializedQuantitationType 0 Spot diameter (in pixels) that the software calculates during data extraction.
+Spot_noise_level float linear_scale SpecializedQuantitationType 1 Local background noise level, given by the median signal of the pixels within the baackground ring.
+Bkgd_mean float linear_scale MeasuredSignal 1 Estimate of the background mean that the software computes, using the pixel intensities in the background area surrounding the spot.
+Bkgd_median integer linear_scale SpecializedQuantitationType 1 Estimate of the background median the software computes, using the pixel intensities inside the background area surrounding the spot.
+Bkgd_stdev float linear_scale Error 1 Bkgd_mean Estimate of the background standard deviation that the software computes, using the pixel intensities inside the background area surrounding the spot.
+Bkgd_area float linear_scale SpecializedQuantitationType 1 Number of pixels that the software includes to compute the estimated local background mean, median, and standard deviation.
+>>>Feature Extraction Software[Agilent Technologies]
+BGPixCorrelation float linear_scale SpecializedQuantitationType Ratio of estimated feature covariance in RedGreen space to product of feature Standard Deviation in Red Green space. The covariance of two features measures their tendency to vary together, i.e. to co-vary. In this case, it is a cumulative quantitation of
+BGSubSigCorrelation float linear_scale SpecializedQuantitationType Ratio of estimated background subtracted feature signal covariance in RG space to product of background subtracted feature Standard Deviation in RG space.
+DyeNormCorrelation float linear_scale SpecializedQuantitationType Dye normalized red and green pixel correlation.
+ErrorModel integer linear_scale SpecializedQuantitationType ErrorModel Indicates the error model that the user chose for feature extraction. A 0 indicates use of the Propogated Error Model. A 1 indicates use of the Universal Error Model.
+Failed boolean linear_scale Failed Set to one if the quantitation values of a feature are considered bad enough to thrown out in the higher level analysis. Features are thrown out if both channels are saturated or if either channel is a non-uniform outlier. A feature can be failed from out
+Green.BGPixSDev float linear_scale Error Green.Background Cy3
+Green.Background float linear_scale MeasuredSignal 1 Cy3
+Green.DerivedSignal float linear_scale DerivedSignal Cy3
+Green.DyeNormSignal float linear_scale SpecializedQuantitationType Cy3
+Green.MeasuredSignal float linear_scale MeasuredSignal Cy3
+Green.PixSDev float linear_scale Error Green.MeasuredSignal Cy3
+Green.ProcessedSigError float linear_scale Error Green.DerivedSignal Cy3
+HighEndCorrDNCorrelation float linear_scale SpecializedQuantitationType
+HighEndDyeNormApplied boolean linear_scale SpecializedQuantitationType Has a value of 0 or 1.
+IsManualFlag boolean linear_scale SpecializedQuantitationType If a feature has been moved manually this flag will be set to 1.
+IsNormalization boolean linear_scale SpecializedQuantitationType A boolean flag which indicates if a feature is used to measure dye bias. A 1 incidates the value was used.
+IsUsedBGAdjust float linear_scale SpecializedQuantitationType A boolean used to flag features used for computation of global BG offset. A 1 indicates the feature was used.
+LogRatio float log_base_10 Ratio log(REDsignal/GREENsignal) per feature. Signals in both channels are post dye normalization If SURROGATES are disabled, then this value will be set to: -2 if DyeNormRedSig <= 0.0 &DyeNormGreenSig > 0.0; 2 if DyeNormRedSig > 0.0 & DyeNormGreenSi
+LogRatioError float log_base_10 Error LogRatio LogRatioError = error of the log ratio calculated according to the error model chosen. If SURROGATES are disabled, then this value will be set to if DyeNormRedSig <= 0.0 OR DyeNormGreenSig <= 0.0
+PValueLogRatio float percent PValue LogRatio Significance level of the Log Ratio computed for a feature
+PixCorrelation float linear_scale SpecializedQuantitationType Ratio of estimated feature covariance in RedGreen space to product of feature Standard Deviation in Red Green space. The covariance of two features measures their tendency to vary together, i.e. to co-vary. In this case, it is a cumulative quantitation of
+PositionX float linear_scale SpecializedQuantitationType Found X coordinate of the feature centroid currently reported in pixels.
+PositionY float linear_scale SpecializedQuantitationType Found Y coordinate of the feature centroid currently reported in pixels.
+Red.BGPixSDev float linear_scale Error Red.Background Cy5
+Red.Background float linear_scale MeasuredSignal 1 Cy5
+Red.DerivedSignal float linear_scale DerivedSignal Cy5
+Red.DyeNormSignal float linear_scale SpecializedQuantitationType Cy5
+Red.MeasuredSignal float linear_scale MeasuredSignal Cy5
+Red.PixSDev float linear_scale Error Red.MeasuredSignal Cy5
+Red.ProcessedSigError float linear_scale Error Red.DerivedSignal Cy5
+SpotExtentX float linear_scale SpecializedQuantitationType The size of a spot in X in microns.
+SpotExtentY float linear_scale SpecializedQuantitationType The size of a spot in Y in microns.
+gBGMeanSignal float linear_scale MeasuredSignal 1 Cy3 Mean local background signal (local to corresponding feature) computed per channel.
+gBGMedianSignal float linear_scale SpecializedQuantitationType 1 Cy3 Median local background signal (local to corresponding feature) computed per channel.
+gBGNumPix integer linear_scale SpecializedQuantitationType 1 Cy3 Total number of pixels used to compute Local BG statistics per spot; ie. Total number of BG inlier pixels. This number is computed independently in each channel.
+gBGPixSDev float linear_scale Error 1 gBGMeanSignal Cy3 Standard deviation of all inlier pixels per Local BG of each feature, computed independently in each channel.
+gBGSDUsed float linear_scale Error 1 gBGUsed Cy3 Standard deviation of background used in green channel.
+gBGSubSigError float linear_scale Error gBGSubSignal Cy3 Propagated standard error as computed on net green background subtracted signal.
+gBGSubSignal float linear_scale SpecializedQuantitationType Cy3 The net green signal following the subtraction of the background from the raw mean green signal.
+gBGUsed float linear_scale SpecializedQuantitationType 1 Cy3 Background value subtracted from the raw mean signal to generate the BG subtracted signal; this value is computed per channel. If global BG subtraction is used the column is identical for every feature in a given channel.
+gBackground float linear_scale MeasuredSignal 1 Cy3 Mean local background signal (local to corresponding feature) computed per channel.
+gDerivedSignal float linear_scale DerivedSignal Cy3 The propagated feature signal, per channel, used for computation of log ratio.
+gDyeNormError float linear_scale Error gDyeNormSignal Cy3 The standard error associated with the dye normalized signal.
+gDyeNormSignal float linear_scale SpecializedQuantitationType Cy3 The dye normalized signal in the indicated channel.
+gHighEndCorrDNError float linear_scale Error gHighEndCorrDNSig Cy3
+gHighEndCorrDNSig float linear_scale SpecializedQuantitationType Cy3
+gIsBGNonUnifOL boolean linear_scale SpecializedQuantitationType Cy3 Boolean flag indicating if a background is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+gIsBGPopnOL boolean linear_scale SpecializedQuantitationType Cy3 Boolean flag indicating if a background is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an u
+gIsFeatNonUnifOL boolean linear_scale SpecializedQuantitationType Cy3 Boolean flag indicating if a feature is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+gIsFeatPopnOL boolean linear_scale SpecializedQuantitationType Cy3 Boolean flag indicating if a feature is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an uppe
+gIsFound boolean linear_scale SpecializedQuantitationType Cy3 A boolean used to flag found (strong) features. The flag is applied independently in each channel. A feature is considered found if the found spot centroid is within the bounds of the spot deviation limit with respect to corresponding nominal centroid. NO
+gIsInNegCtrlRange boolean linear_scale SpecializedQuantitationType Cy3 Boolean that identifies this spot as being in the range of background.
+gIsPosAndSignif boolean linear_scale SpecializedQuantitationType Cy3 Boolean flag indicating if the mean signal of a feature is greater than the corresponding background and if this difference is significant. Significance is established via a 2-sided t-test against the user-settable maximum p-value. A 1 indicates Feature i
+gIsSaturated boolean linear_scale SpecializedQuantitationType Cy3 Boolean flag indicating if a feature is saturated or not. A feature is saturated if 50% of the pixels in a feature are above the saturation threshold. A value of 1 means saturated.
+gIsWellAboveBG boolean linear_scale SpecializedQuantitationType Cy3 Boolean flag indicating if a feature is well above background or not. A feature must be gIsPosAndSignif and additionally the gBGSubSignal must be greater than 2.6*g(r)BG_SD. A 1 indicates a feature IsWellAboveBG.
+gMeanSignal float linear_scale MeasuredSignal Cy3 Raw mean signal of feature in green channel.
+gMeasuredSignal float linear_scale MeasuredSignal Cy3 Raw mean signal of feature in green channel.
+gMedianSignal float linear_scale SpecializedQuantitationType Cy3 Raw median signal of feature in green channel.
+gNetSignal float linear_scale SpecializedQuantitationType Cy3 The meanSignal - the scanner offset.
+gNumBGUsed integer linear_scale SpecializedQuantitationType 1 Cy3 A count of the number of background pixels used in the green channel.
+gNumPix integer linear_scale SpecializedQuantitationType Cy3 Total number of pixels used to compute feature statistics; ie. total number of inlier pixels/per spot, computed independently in each channel. The number of inlier pixels are the same in both channels.
+gNumPixOLHi integer linear_scale SpecializedQuantitationType Cy3 Number of outlier pixels per feature with intensity > upper threshold set via the pixel outlier rejection method. The number is computed independently in each channel. These pixels are omitted from all subsequent calculations.
+gNumPixOLLo integer linear_scale SpecializedQuantitationType Cy3 Number of outlier pixels per feature with intensity < lower threshold set via the pixel outlier rejection method. The number is computed independently in each channel. NOTE: The pixel outlier method is the ONLY step that removes data in Feature Extract
+gNumSatPix integer linear_scale SpecializedQuantitationType Cy3 Total number of saturated pixels per feature, computed per channel.
+gPValFeatEqBG float linear_scale SpecializedQuantitationType Cy3 P-value from t-test of significance between green Mean signal and green background.
+gPixSDev float linear_scale Error gMeanSignal Cy3 Standard deviation of all inlier pixels per feature; this is computed independently in each channel.
+gProcessedSigError float linear_scale Error gProcessedSignal Cy3 Standard error of propagated feature signal, per channel.
+gProcessedSignal float linear_scale DerivedSignal Cy3 The propagated feature signal, per channel, used for computation of log ratio.
+gSpatialDetrendIsInFilteredSet boolean linear_scale SpecializedQuantitationType Cy3 Set to true for a given feature if it is part of the filtered set used to fit the surface measuring the spatial trend across the microarray. This feature is considered part of the locally weighted x% of the features.
+gSpatialDetrendSurfaceValue float linear_scale SpecializedQuantitationType Cy3 Value of the smoothed surface derived from the set of features that are part of SpatialDetrendIsInFilteredSet.
+gSurrogateUsed float linear_scale SpecializedQuantitationType Cy3 The green surrogate value used.
+rBGMeanSignal float linear_scale MeasuredSignal 1 Cy5 Mean local background signal (local to corresponding feature) computed per channel.
+rBGMedianSignal float linear_scale SpecializedQuantitationType 1 Cy5 Median local background signal (local to corresponding feature) computed per channel.
+rBGNumPix integer linear_scale SpecializedQuantitationType 1 Cy5 Total number of pixels used to compute Local BG statistics per spot; ie. Total number of BG inlier pixels. This number is computed independently in each channel.
+rBGPixSDev float linear_scale Error 1 rBGMeanSignal Cy5 Standard deviation of all inlier pixels per Local BG of each feature, computed independently in each channel.
+rBGSDUsed float linear_scale Error 1 rBGUsed Cy5 Standard deviation of background used in red channel.
+rBGSubSigError float linear_scale Error rBGSubSignal Cy5 Propagated standard error as computed on net red background subtracted signal.
+rBGSubSignal float linear_scale SpecializedQuantitationType Cy5 The net red signal following the subtraction of the background from the raw mean red signal.
+rBGUsed float linear_scale SpecializedQuantitationType 1 Cy5 Background value subtracted from the raw mean signal to generate the BG subtracted signal; this value is computed per channel. If global BG subtraction is used the column is identical for every feature in a given channel.
+rBackground float linear_scale MeasuredSignal 1 Cy5 Mean local background signal (local to corresponding feature) computed per channel.
+rDerivedSignal float linear_scale DerivedSignal Cy5 The propagated feature signal, per channel, used for computation of log ratio.
+rDyeNormError float linear_scale Error rDyeNormSignal Cy5 The standard error associated with the dye normalized signal.
+rDyeNormSignal float linear_scale SpecializedQuantitationType Cy5 The dye normalized signal in the indicated channel.
+rHighEndCorrDNError float linear_scale Error rHighEndCorrDNSig Cy5
+rHighEndCorrDNSig float linear_scale SpecializedQuantitationType Cy5
+rIsBGNonUnifOL boolean linear_scale SpecializedQuantitationType Cy5 Boolean flag indicating if a background is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+rIsBGPopnOL boolean linear_scale SpecializedQuantitationType Cy5 Boolean flag indicating if a background is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an u
+rIsFeatNonUnifOL boolean linear_scale SpecializedQuantitationType Cy5 Boolean flag indicating if a feature is a NonUniformity Outlier or not. A feature is non-uniform if the pixel noise of feature exceeds a threshold established for a "uniform" feature. A 1 indicates Feature is a non-uniformity outlier.
+rIsFeatPopnOL boolean linear_scale SpecializedQuantitationType Cy5 Boolean flag indicating if a feature is a Population Outlier or not. Probes with replicate features on a microarray are examined using population statistics. A feature is a population outlier if its signal is less than a lower threshold or exceeds an uppe
+rIsFound boolean linear_scale SpecializedQuantitationType Cy5 A boolean used to flag found (strong) features. The flag is applied independently in each channel. A feature is considered found if the found spot centroid is within the bounds of the spot deviation limit with respect to corresponding nominal centroid. NO
+rIsInNegCtrlRange boolean linear_scale SpecializedQuantitationType Cy5 Boolean that identifies this spot as being in the range of background.
+rIsPosAndSignif boolean linear_scale SpecializedQuantitationType Cy5 Boolean flag indicating if the mean signal of a feature is greater than the corresponding background and if this difference is significant. Significance is established via a 2-sided t-test against the user-settable maximum p-value. A 1 indicates Feature i
+rIsSaturated boolean linear_scale SpecializedQuantitationType Cy5 Boolean flag indicating if a feature is saturated or not. A feature is saturated if 50% of the pixels in a feature are above the saturation threshold. A value of 1 means saturated.
+rIsWellAboveBG boolean linear_scale SpecializedQuantitationType Cy5 Boolean flag indicating if a feature is well above background or not. A feature must be gIsPosAndSignif and additionally the gBGSubSignal must be greater than 2.6*g(r)BG_SD. A 1 indicates a feature IsWellAboveBG.
+rMeanSignal float linear_scale MeasuredSignal Cy5 Raw mean signal of feature in red channel.
+rMeasuredSignal float linear_scale MeasuredSignal Cy5 Raw mean signal of feature in red channel.
+rMedianSignal float linear_scale SpecializedQuantitationType Cy5 Raw median signal of feature in red channel.
+rNetSignal float linear_scale SpecializedQuantitationType Cy5 The meanSignal - the scanner offset.
+rNumBGUsed integer linear_scale SpecializedQuantitationType 1 Cy5 A count of the number of background pixels used in the red channel.
+rNumPix integer linear_scale SpecializedQuantitationType Cy5 Total number of pixels used to compute feature statistics; ie. total number of inlier pixels/per spot, computed independently in each channel. The number of inlier pixels are the same in both channels.
+rNumPixOLHi integer linear_scale SpecializedQuantitationType Cy5 Number of outlier pixels per feature with intensity > upper threshold set via the pixel outlier rejection method. The number is computed independently in each channel. These pixels are omitted from all subsequent calculations.
+rNumPixOLLo integer linear_scale SpecializedQuantitationType Cy5 Number of outlier pixels per feature with intensity < lower threshold set via the pixel outlier rejection method. The number is computed independently in each channel. NOTE: The pixel outlier method is the ONLY step that removes data in Feature Extract
+rNumSatPix integer linear_scale SpecializedQuantitationType Cy5 Total number of saturated pixels per feature, computed per channel.
+rPValFeatEqBG float linear_scale SpecializedQuantitationType Cy5 P-value from t-test of significance between red Mean signal and red background.
+rPixSDev float linear_scale Error rMeanSignal Cy5 Standard deviation of all inlier pixels per feature; this is computed independently in each channel.
+rProcessedSigError float linear_scale Error rProcessedSignal Cy5 Standard error of propagated feature signal, per channel.
+rProcessedSignal float linear_scale DerivedSignal Cy5 The propagated feature signal, per channel, used for computation of log ratio.
+rSpatialDetrendIsInFilteredSet boolean linear_scale SpecializedQuantitationType Cy5 Set to true for a given feature if it is part of the filtered set used to fit the surface measuring the spatial trend across the microarray. This feature is considered part of the locally weighted x% of the features.
+rSpatialDetrendSurfaceValue float linear_scale SpecializedQuantitationType Cy5 Value of the smoothed surface derived from the set of features that are part of SpatialDetrendIsInFilteredSet.
+rSurrogateUsed float linear_scale SpecializedQuantitationType Cy5 The red surrogate value used.
+xDev float log_base_10 SpecializedQuantitationType Used to compute the Log Ratio PValue.
+>>>GEMTools[Incyte Genomics]
+BalancedDiffExpr float linear_scale SpecializedQuantitationType Balanced fold change.
+CloneID string_datatype unscaled SpecializedQuantitationType Incyte identifier for individual clone spotted on the array.
+CloneSource string_datatype unscaled SpecializedQuantitationType All arrayed elements were either clones derived from Incyte cDNA libraries, or sequences generated specifically to be used as hybridization and/or signal balancing controls.
+DiffExpr float linear_scale SpecializedQuantitationType Unbalanced fold change.
+GEMID string_datatype unscaled SpecializedQuantitationType Unique identifier.
+GeneID string_datatype unscaled SpecializedQuantitationType Incyte unique identifier for a sequence contig.
+GeneName string_datatype unscaled SpecializedQuantitationType Genbank annotation of Incyte sequence contig.
+IncyteCloneID string_datatype unscaled SpecializedQuantitationType Incyte identifier for individual clone spotted on the array.
+Location integer linear_scale SpecializedQuantitationType An Incyte code that can be used to determine the location of the spot on the array.
+P1Area% integer linear_scale SpecializedQuantitationType Cy5 Area of spot coverage.
+P1Description string_datatype unscaled SpecializedQuantitationType Cy5 Identifier or code name for Cy5 labeled probe.
+P1S/B float linear_scale SpecializedQuantitationType 1 Cy5 Element signal to background in the Cy5 channel.
+P1Signal integer linear_scale MeasuredSignal Cy5 Signal intensity in the Cy5 channel.
+P2Area% integer linear_scale SpecializedQuantitationType Cy3 Area of spot coverage.
+P2BalancedSignal integer linear_scale SpecializedQuantitationType Balanced Cy3 signal (balanced by total average signal intensity in both channels).
+P2Description string_datatype unscaled SpecializedQuantitationType Cy3 Identifier or code name for Cy3 labeled probe.
+P2S/B float linear_scale SpecializedQuantitationType 1 Cy3 Element signal to background in the Cy3 channel.
+P2Signal integer linear_scale MeasuredSignal Cy3 Signal intensity in the Cy3 channel.
+PCRStatus string_datatype unscaled SpecializedQuantitationType An indicator of the quality of the spotted cDNA.
+Probe1 string_datatype unscaled SpecializedQuantitationType Cy5 Unique identifier for Cy5 labeled probe.
+Probe2 string_datatype unscaled SpecializedQuantitationType Cy3 Unique identifier for Cy3 labeled probe.
+>>>GLEAMS[NuTec Sciences]
+BG.Cy3 float linear_scale MeasuredSignal 1 Cy3 Median pixel intensity computed over the local background region for Cy3.
+BG.Cy5 float linear_scale MeasuredSignal 1 Cy5 Median pixel intensity computed over the local background region for Cy5.
+Mean.Sig.Cy3 float linear_scale SpecializedQuantitationType Cy3 Pixel intensity averaged over the local signal region for Cy3.
+Mean.Sig.Cy5 float linear_scale SpecializedQuantitationType Cy5 Pixel intensity averaged over the local signal region for Cy5.
+Med.Sig.Cy3 integer linear_scale MeasuredSignal Cy3 Median pixel intensity computed over the local signal region for Cy3.
+Med.Sig.Cy5 integer linear_scale MeasuredSignal Cy5 Median pixel intensity computed over the local signal region for Cy5.
+SD.Cy3 float linear_scale Error Mean.Sig.Cy3 Cy3 Standard deviation of pixel intensities over the local signal region for Cy3.
+SD.Cy5 float linear_scale Error Mean.Sig.Cy5 Cy5 Standard deviation of pixel intensities over the local signal region for Cy5.
+Sig.Area integer linear_scale SpecializedQuantitationType Number of pixels in the local signal region.
+X integer linear_scale SpecializedQuantitationType Location of the center of the spot on the array on the X-axis.
+Y integer linear_scale SpecializedQuantitationType Location of the center of the spot on the array on the Y-axis.
+>>>GenePix[Axon Instruments]
+% > B595+1SD integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 595 nm.
+% > B595+2SD integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 595 nm.
+% > B685+1SD integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 685 nm.
+% > B685+2SD integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 685 nm.
+B595 integer linear_scale SpecializedQuantitationType 1 Cy3 The actual background value used for the feature in GenePix Pro calculations (as opposed to B595 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B595 CV integer linear_scale SpecializedQuantitationType 1 Cy3 The coefficient of variation of local background pixel intensity.
+B595 Mean integer linear_scale MeasuredSignal 1 Cy3 The mean feature background intensity at wavelength 595 nm.
+B595 Median integer linear_scale SpecializedQuantitationType 1 Cy3 The median feature background intensity at wavelength 595 nm.
+B595 SD integer linear_scale Error 1 B595 Median Cy3 The standard deviation of the feature background intensity at wavelength 595 nm.
+B685 integer linear_scale SpecializedQuantitationType 1 Cy5 The actual background value used for the feature in GenePix Pro calculations (as opposed to B685 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B685 CV integer linear_scale SpecializedQuantitationType 1 Cy5 The coefficient of variation of local background pixel intensity.
+B685 Mean integer linear_scale MeasuredSignal 1 Cy5 The mean feature background intensity at wavelength 685 nm.
+B685 Median integer linear_scale SpecializedQuantitationType 1 Cy5 The median feature background intensity at wavelength 685 nm.
+B685 SD integer linear_scale Error 1 B685 Median Cy5 The standard deviation of the feature background intensity at wavelength 685 nm.
+F595 % Sat. integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels at wavelength 595 nm that are saturated.
+F595 CV integer linear_scale SpecializedQuantitationType Cy3 The coefficient of variation of feature pixel intensity.
+F595 Mean integer linear_scale MeasuredSignal Cy3 Mean feature pixel intensity at wavelength 595 nm.
+F595 Mean - B595 integer linear_scale SpecializedQuantitationType Cy3 The mean feature pixel intensity at wavelength 595 nm with the median background subtracted.
+F595 Median integer linear_scale SpecializedQuantitationType Cy3 Median feature pixel intensity at wavelength 595 nm.
+F595 Median - B595 integer linear_scale SpecializedQuantitationType Cy3 The median feature pixel intensity at wavelength 595 nm with the median background subtracted.
+F595 SD integer linear_scale Error F595 Median Cy3 The standard deviation of the feature intensity at wavelength 595 nm.
+F595 Total Intensity integer linear_scale SpecializedQuantitationType Cy3 The sum of feature pixel intensities at 595 nm.
+F685 % Sat. integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels at wavelength 685 nm that are saturated.
+F685 CV integer linear_scale SpecializedQuantitationType Cy5 The coefficient of variation of feature pixel intensity.
+F685 Mean integer linear_scale MeasuredSignal Cy5 Mean feature pixel intensity at wavelength 685 nm.
+F685 Mean - B685 integer linear_scale SpecializedQuantitationType Cy5 The mean feature pixel intensity at wavelength 685 nm with the median background subtracted.
+F685 Median integer linear_scale SpecializedQuantitationType Cy5 Median feature pixel intensity at wavelength 685 nm.
+F685 Median - B685 integer linear_scale SpecializedQuantitationType Cy5 The median feature pixel intensity at wavelength 685 nm with the median background subtracted.
+F685 SD integer linear_scale Error F685 Median Cy5 The standard deviation of the feature intensity at wavelength 685 nm.
+F685 Total Intensity integer linear_scale SpecializedQuantitationType Cy5 The sum of feature pixel intensities at 685 nm.
+Log Ratio (685/595) float log_base_2 Ratio Log (base 2) transform of the ratio of the medians.
+Mean of Ratios (685/595) float linear_scale SpecializedQuantitationType The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (685/595) float linear_scale SpecializedQuantitationType The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Ratio of Means (685/595) float linear_scale SpecializedQuantitationType The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (685/595) float linear_scale SpecializedQuantitationType The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratios SD (685/595) float linear_scale Error Median of Ratios (685/595) The geometric standard deviation of the pixel intensity ratios.
+Rgn R2 (685/595) float linear_scale SpecializedQuantitationType The coefficient of determination for the current regression value.
+Rgn Ratio (685/595) float linear_scale SpecializedQuantitationType The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+SNR 595 float linear_scale SpecializedQuantitationType 1 Cy5 The signal-to-noise ratio at 595 nm, defined by (F595 Mean - B595 Mean) / (B595 SD)
+SNR 685 float linear_scale SpecializedQuantitationType 1 Cy5 The signal-to-noise ratio at 685 nm, defined by (F685 Mean - B685 Mean) / (B685 SD)
+Sum of Means (685/595) integer linear_scale SpecializedQuantitationType The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Medians (685/595) integer linear_scale SpecializedQuantitationType The sum of the median intensities for each wavelength, with the median background subtracted.
+% > B633+1SD integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 633 nm.
+% > B633+2SD integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 633 nm.
+B633 integer linear_scale SpecializedQuantitationType 1 Cy5 The actual background value used for the feature in GenePix Pro calculations (as opposed to B633 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B633 CV integer linear_scale SpecializedQuantitationType 1 Cy5 The coefficient of variation of local background pixel intensity.
+B633 Mean integer linear_scale MeasuredSignal 1 Cy5 The mean feature background intensity at wavelength 633 nm.
+B633 Median integer linear_scale SpecializedQuantitationType 1 Cy5 The median feature background intensity at wavelength 633 nm.
+B633 SD integer linear_scale Error 1 B633 Median Cy5 The standard deviation of the feature background intensity at wavelength 633 nm.
+F633 % Sat. integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels at wavelength 633 nm that are saturated.
+F633 CV integer linear_scale SpecializedQuantitationType Cy5 The coefficient of variation of feature pixel intensity.
+F633 Mean integer linear_scale MeasuredSignal Cy5 Mean feature pixel intensity at wavelength 633 nm.
+F633 Mean - B633 integer linear_scale SpecializedQuantitationType Cy5 The mean feature pixel intensity at wavelength 633 nm with the median background subtracted.
+F633 Median integer linear_scale SpecializedQuantitationType Cy5 Median feature pixel intensity at wavelength 633 nm.
+F633 Median - B633 integer linear_scale SpecializedQuantitationType Cy5 The median feature pixel intensity at wavelength 633 nm with the median background subtracted.
+F633 SD integer linear_scale Error F633 Median Cy5 The standard deviation of the feature intensity at wavelength 633 nm.
+F633 Total Intensity integer linear_scale SpecializedQuantitationType Cy5 The sum of feature pixel intensities at 633 nm.
+Log Ratio (633/532) float log_base_2 Ratio Log (base 2) transform of the ratio of the medians.
+Mean of Ratios (633/532) float linear_scale SpecializedQuantitationType The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (633/532) float linear_scale SpecializedQuantitationType The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Ratio of Means (633/532) float linear_scale SpecializedQuantitationType The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (633/532) float linear_scale SpecializedQuantitationType The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratios SD (633/532) float linear_scale Error Median of Ratios (633/532) The geometric standard deviation of the pixel intensity ratios.
+Rgn R2 (633/532) float linear_scale SpecializedQuantitationType The coefficient of determination for the current regression value.
+Rgn Ratio (633/532) float linear_scale SpecializedQuantitationType The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+SNR 633 float linear_scale SpecializedQuantitationType 1 Cy5 The signal-to-noise ratio at 633 nm, defined by (F633 Mean - B633 Mean) / (B633 SD)
+Sum of Means (633/532) integer linear_scale SpecializedQuantitationType The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Medians (633/532) integer linear_scale SpecializedQuantitationType The sum of the median intensities for each wavelength, with the median background subtracted.
+% > B532+1SD integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 532 nm.
+% > B532+1SD2 integer linear_scale SpecializedQuantitationType Cy3
+% > B532+2SD integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 532 nm.
+% > B532+2SD2 integer linear_scale SpecializedQuantitationType Cy3
+% > B563+1SD integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 563 nm.
+% > B563+2SD integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 563 nm.
+% > B635+1SD integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, at wavelength 635 nm.
+% > B635+1SD2 integer linear_scale SpecializedQuantitationType Cy5
+% > B635+2SD integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, at wavelength 635 nm.
+% > B635+2SD2 integer linear_scale SpecializedQuantitationType Cy5
+Autoflag boolean linear_scale SpecializedQuantitationType Reports whether or not a feature has been flagged from the Flag Features dialog box. It applies to good and bad flags only.
+B Pixels integer linear_scale SpecializedQuantitationType The total number of background pixels.
+B532 integer linear_scale SpecializedQuantitationType 1 Cy3 The actual background value used for the feature in GenePix Pro calculations (as opposed to B532 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B532 CV integer linear_scale SpecializedQuantitationType 1 Cy3 The coefficient of variation of local background pixel intensity.
+B532 Mean integer linear_scale MeasuredSignal 1 Cy3 The mean feature background intensity at wavelength 532 nm.
+B532 Median integer linear_scale SpecializedQuantitationType 1 Cy3 The median feature background intensity at wavelength 532 nm.
+B532 SD integer linear_scale Error 1 B532 Median Cy3 The standard deviation of the feature background intensity at wavelength 532 nm.
+B532 SD2 float linear_scale Error 1 B532 Median Cy3
+B563 Mean integer linear_scale MeasuredSignal 1 Cy3 The mean feature background intensity at wavelength 563 nm.
+B563 Median integer linear_scale SpecializedQuantitationType 1 Cy3 The median feature background intensity at wavelength 563 nm.
+B563 SD integer linear_scale Error 1 B563 Median Cy3 The standard deviation of the feature background intensity at wavelength 563 nm.
+B635 integer linear_scale SpecializedQuantitationType 1 Cy5 The actual background value used for the feature in GenePix Pro calculations (as opposed to B635 Median, for example, which is the local median background.) This column is required because GenePix Pro 5.0 has global and negative control background subtrac
+B635 CV integer linear_scale SpecializedQuantitationType 1 Cy5 The coefficient of variation of local background pixel intensity.
+B635 Mean integer linear_scale MeasuredSignal 1 Cy5 The mean feature background intensity at wavelength 635 nm.
+B635 Median integer linear_scale SpecializedQuantitationType 1 Cy5 The median feature background intensity at wavelength 635 nm.
+B635 SD integer linear_scale Error 1 B635 Median Cy5 The standard deviation of the feature background intensity at wavelength 635 nm.
+B635 SD2 float linear_scale Error 1 B635 Median Cy5
+Circularity integer linear_scale SpecializedQuantitationType A measure of circularity from 0 to 100, using a metric based on the variance of the distance of each boundary pixel to the centroid of the feature: 100 is most circular, 0 is most non-circular. Circular features always have a circularity of 100, square fe
+Dia. integer linear_scale SpecializedQuantitationType The diameter in um of the feature-indicator.
+F Pixels integer linear_scale SpecializedQuantitationType The total number of feature pixels.
+F532 % Sat. integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels at wavelength 532 nm that are saturated.
+F532 CV integer linear_scale SpecializedQuantitationType Cy3 The coefficient of variation of feature pixel intensity.
+F532 Mean integer linear_scale MeasuredSignal Cy3 Mean feature pixel intensity at wavelength 532 nm.
+F532 Mean - B532 integer linear_scale SpecializedQuantitationType Cy3 The mean feature pixel intensity at wavelength 532 nm with the median background subtracted.
+F532 Median integer linear_scale SpecializedQuantitationType Cy3 Median feature pixel intensity at wavelength 532 nm.
+F532 Median - B532 integer linear_scale SpecializedQuantitationType Cy3 The median feature pixel intensity at wavelength 532 nm with the median background subtracted.
+F532 SD integer linear_scale Error F532 Median Cy3 The standard deviation of the feature intensity at wavelength 532 nm.
+F532 Total Intensity integer linear_scale SpecializedQuantitationType Cy3 The sum of feature pixel intensities at 532 nm.
+F563 % Sat. integer linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels at wavelength 563 nm that are saturated.
+F563 Mean integer linear_scale MeasuredSignal Cy3 Mean feature pixel intensity at wavelength 563 nm.
+F563 Mean - B563 integer linear_scale SpecializedQuantitationType Cy3 The mean feature pixel intensity at wavelength 563 nm with the median background subtracted.
+F563 Median integer linear_scale SpecializedQuantitationType Cy3 Median feature pixel intensity at wavelength 563 nm.
+F563 Median - B563 integer linear_scale SpecializedQuantitationType Cy3 The median feature pixel intensity at wavelength 563 nm with the median background subtracted.
+F563 SD integer linear_scale Error F563 Median Cy3 The standard deviation of the feature intensity at wavelength 563 nm.
+F635 % Sat. integer linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels at wavelength 635 nm that are saturated.
+F635 CV integer linear_scale SpecializedQuantitationType Cy5 The coefficient of variation of feature pixel intensity.
+F635 Mean integer linear_scale MeasuredSignal Cy5 Mean feature pixel intensity at wavelength 635 nm.
+F635 Mean - B635 integer linear_scale SpecializedQuantitationType Cy5 The mean feature pixel intensity at wavelength 635 nm with the median background subtracted.
+F635 Median integer linear_scale SpecializedQuantitationType Cy5 Median feature pixel intensity at wavelength 635 nm.
+F635 Median - B635 integer linear_scale SpecializedQuantitationType Cy5 The median feature pixel intensity at wavelength 635 nm with the median background subtracted.
+F635 SD integer linear_scale Error F635 Median Cy5 The standard deviation of the feature intensity at wavelength 635 nm.
+F635 Total Intensity integer linear_scale SpecializedQuantitationType Cy5 The sum of feature pixel intensities at 635 nm.
+Flags integer linear_scale SpecializedQuantitationType The type of flag associated with a feature: -100 = user-flagged null spot; -50 = software-flagged null spot; 0 = spot valid.
+Log Ratio float log_base_2 Ratio Log (base 2) transform of the ratio of the medians.
+Log Ratio (532/635) float log_base_2 Ratio log (base 2) transform of the ratio of the medians.
+Log Ratio (635/532) float log_base_2 Ratio Log (base 2) transform of the ratio of the medians.
+Mean of Ratios float linear_scale SpecializedQuantitationType The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Mean of Ratios (532/635) float linear_scale SpecializedQuantitationType The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Mean of Ratios (635/532) float linear_scale SpecializedQuantitationType The geometric mean of the pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios float linear_scale SpecializedQuantitationType The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (532/635) float linear_scale SpecializedQuantitationType The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Median of Ratios (635/532) float linear_scale SpecializedQuantitationType The median of pixel-by-pixel ratios of pixel intensities, with the median background subtracted.
+Normalize integer unscaled PresentAbsent The normalization status of the feature (included/not included).
+Ratio of Means float linear_scale SpecializedQuantitationType The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Means (532/635) float linear_scale SpecializedQuantitationType The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Means (635/532) float linear_scale SpecializedQuantitationType The ratio of the arithmetic mean intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians float linear_scale SpecializedQuantitationType The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (532/635) float linear_scale SpecializedQuantitationType The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratio of Medians (635/532) float linear_scale SpecializedQuantitationType The ratio of the median intensities of each feature for each wavelength, with the median background subtracted.
+Ratios SD float linear_scale Error Median of Ratios The geometric standard deviation of the pixel intensity ratios.
+Ratios SD (532/635) float linear_scale Error Median of Ratios (532/635) The geometric standard deviation of the pixel intensity ratios.
+Ratios SD (635/532) float linear_scale Error Median of Ratios (635/532) The geometric standard deviation of the pixel intensity ratios.
+Rgn R (532/635) float linear_scale SpecializedQuantitationType
+Rgn R2 float linear_scale SpecializedQuantitationType The coefficient of determination for the current regression value.
+Rgn R2 (532/635) float linear_scale SpecializedQuantitationType The coefficient of determination for the current regression value.
+Rgn R2 (635/532) float linear_scale SpecializedQuantitationType The coefficient of determination for the current regression value.
+Rgn Ratio float linear_scale SpecializedQuantitationType The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+Rgn Ratio (532/635) float linear_scale SpecializedQuantitationType The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+Rgn Ratio (635/532) float linear_scale SpecializedQuantitationType The regression ratio of every pixel in a 2-feature-diameter circle around the center of the feature.
+Rgn R� float linear_scale SpecializedQuantitationType
+Rgn R� (635/532) float linear_scale SpecializedQuantitationType
+Rgn R� (532/635) float linear_scale SpecializedQuantitationType
+SNR 532 float linear_scale SpecializedQuantitationType 1 Cy3 The signal-to-noise ratio at 532 nm, defined by (F532 Mean - B532 Mean) / (B532 SD)
+SNR 635 float linear_scale SpecializedQuantitationType 1 Cy5 The signal-to-noise ratio at 635 nm, defined by (F635 Mean - B635 Mean) / (B635 SD)
+Sum of Means integer linear_scale SpecializedQuantitationType The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Means (532/635) integer linear_scale SpecializedQuantitationType The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Means (635/532) integer linear_scale SpecializedQuantitationType The sum of the arithmetic mean intensities for each wavelength, with the median background subtracted.
+Sum of Medians integer linear_scale SpecializedQuantitationType The sum of the median intensities for each wavelength, with the median background subtracted.
+Sum of Medians (532/635) integer linear_scale SpecializedQuantitationType The sum of the median intensities for each wavelength, with the median background subtracted.
+Sum of Medians (635/532) integer linear_scale SpecializedQuantitationType The sum of the median intensities for each wavelength, with the median background subtracted.
+X integer linear_scale SpecializedQuantitationType The X-coordinate in um of the center of the feature-indicator associated with the feature, where (0,0) is the top left of the image.
+Y integer linear_scale SpecializedQuantitationType The Y-coordinate in um of the center of the feature-indicator associated with the feature, where (0,0) is the top left of the image.
+>>>GeneTAC
+Background med Cy3 float linear_scale MeasuredSignal 1 Cy3
+Background med Cy5 float linear_scale MeasuredSignal 1 Cy5
+Background med ratio float linear_scale Ratio 1
+Background st dev Cy3 float linear_scale Error 1 Background med Cy3 Cy3
+Background st dev Cy5 float linear_scale Error 1 Background med Cy5 Cy5
+Signal med Cy3 float linear_scale MeasuredSignal Cy3
+Signal med Cy5 float linear_scale MeasuredSignal Cy5
+Signal med ratio float linear_scale Ratio
+Signal st dev Cy3 float linear_scale Error Signal med Cy3 Cy3
+Signal st dev Cy5 float linear_scale Error Signal med Cy5 Cy5
+>>>ImaGene[BioDiscovery]
+Area To Perimeter_Cy3 float linear_scale SpecializedQuantitationType Cy3 This quality measure defines the spot's circularity. Area of a spot is divided by a square of spot perimeter and multiplied by 4π. As a result, this measure ranges from 0 (highly non-circular shape) to 1 (a perfect circle).
+Area To Perimeter_Cy5 float linear_scale SpecializedQuantitationType Cy5 This quality measure defines the spot's circularity. Area of a spot is divided by a square of spot perimeter and multiplied by 4π. As a result, this measure ranges from 0 (highly non-circular shape) to 1 (a perfect circle).
+Background Area_Cy3 integer linear_scale SpecializedQuantitationType 1 Cy3 Number of pixels in the local background region.
+Background Area_Cy5 integer linear_scale SpecializedQuantitationType 1 Cy5 Number of pixels in the local background region.
+Background Contamination Present_Cy3 boolean linear_scale SpecializedQuantitationType Cy3
+Background Contamination Present_Cy5 boolean linear_scale SpecializedQuantitationType Cy5
+Background Mean float linear_scale MeasuredSignal 1
+Background Mean_Cy3 float linear_scale MeasuredSignal 1 Cy3 Pixel intensity averaged over the local background region.
+Background Mean_Cy5 float linear_scale MeasuredSignal 1 Cy5 Pixel intensity averaged over the local background region.
+Background Median_Cy3 float linear_scale SpecializedQuantitationType 1 Cy3 Median pixel intensity computed over the local background region.
+Background Median_Cy5 float linear_scale SpecializedQuantitationType 1 Cy5 Median pixel intensity computed over the local background region.
+Background Mode_Cy3 float linear_scale SpecializedQuantitationType 1 Cy3 Mode pixel intensity computed over the local background region.
+Background Mode_Cy5 float linear_scale SpecializedQuantitationType 1 Cy5 Mode pixel intensity computed over the local background region.
+Background Stdev float linear_scale Error 1 Background Mean
+Background Stdev_Cy3 float linear_scale Error 1 Background Mean_Cy3 Cy3 Standard deviation of pixel intensities over the local background region.
+Background Stdev_Cy5 float linear_scale Error 1 Background Mean_Cy5 Cy5 Standard deviation of pixel intensities over the local background region.
+Background Total_Cy3 integer linear_scale SpecializedQuantitationType 1 Cy3 Total pixel intensity summed over the local background region.
+Background Total_Cy5 integer linear_scale SpecializedQuantitationType 1 Cy5 Total pixel intensity summed over the local background region.
+Background contamination present_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot passed background contamination test, 1 if it did not.
+Background contamination present_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot passed background contamination test, 1 if it did not.
+CM Offset-X_Cy3 float linear_scale SpecializedQuantitationType Cy3 X offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset-X_Cy5 float linear_scale SpecializedQuantitationType Cy5 X offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset-Y_Cy3 float linear_scale SpecializedQuantitationType Cy3 Y offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset-Y_Cy5 float linear_scale SpecializedQuantitationType Cy5 Y offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset_Cy3 float linear_scale SpecializedQuantitationType Cy3 Offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM Offset_Cy5 float linear_scale SpecializedQuantitationType Cy5 Offset (in pixels) of the spot's center of the mass from the expected position in the grid.
+CM-X_Cy3 float linear_scale SpecializedQuantitationType Cy3 X coordinate of the center of the mass of spot's signal region.
+CM-X_Cy5 float linear_scale SpecializedQuantitationType Cy5 X coordinate of the center of the mass of spot's signal region.
+CM-Y_Cy3 float linear_scale SpecializedQuantitationType Cy3 Y coordinate of the center of the mass of spot's signal region.
+CM-Y_Cy5 float linear_scale SpecializedQuantitationType Cy5 Y coordinate of the center of the mass of spot's signal region.
+Control_Cy3 string_datatype unscaled SpecializedQuantitationType Cy3 Name of a control type for current spot (no name means the spot is not a control spot).
+Control_Cy5 string_datatype unscaled SpecializedQuantitationType Cy5 Name of a control type for current spot (no name means the spot is not a control spot).
+Diameter integer linear_scale SpecializedQuantitationType
+Diameter_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Diameter (in pixels) of grid circle corresponding to the spot.
+Diameter_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Diameter (in pixels) of grid circle corresponding to the spot.
+Empty spot_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot passed offset test, 1 if it did not.
+Empty spot_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot passed offset test, 1 if it did not.
+Expected X_Cy3 float linear_scale SpecializedQuantitationType Cy3 X coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Expected X_Cy5 float linear_scale SpecializedQuantitationType Cy5 X coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Expected Y_Cy3 float linear_scale SpecializedQuantitationType Cy3 Y coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Expected Y_Cy5 float linear_scale SpecializedQuantitationType Cy5 Y coordinate of expected position of the circle in the grid. Expected position in the grid is computed fitting least square lines to circle centers in every row and column.
+Failed Control_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the control passed all tests, 1 if at least one of the tests failed.
+Failed Control_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the control passed all tests, 1 if at least one of the tests failed.
+Field string_datatype unscaled SpecializedQuantitationType Name of a field where the spot is located.
+Flag boolean unscaled PresentAbsent
+Flag_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Numeric code for the spot (0 - no flag, flag codes 1,...,7).
+Flag_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Numeric code for the spot (0 - no flag, flag codes 1,...,7).
+Gene ID string_datatype unscaled SpecializedQuantitationType Gene ID information for the spot.
+Ignored % failed_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot passed ignored percentage test, 1 if it did not.
+Ignored % failed_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot passed ignored percentage test, 1 if it did not.
+Ignored Area_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Area of ignored regions directly neighboring ("touching") the signal area is computed.
+Ignored Area_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Area of ignored regions directly neighboring ("touching") the signal area is computed.
+Ignored Median_Cy3 float linear_scale SpecializedQuantitationType Cy3 Median pixel intensity computed over the local ignored region.
+Ignored Median_Cy5 float linear_scale SpecializedQuantitationType Cy5 Median pixel intensity computed over the local ignored region.
+Max Diam_Cy3 float linear_scale SpecializedQuantitationType Cy3 Diameter of the circle, the spot's signal region can be inscribed in.
+Max Diam_Cy5 float linear_scale SpecializedQuantitationType Cy5 Diameter of the circle, the spot's signal region can be inscribed in.
+Min Diam_Cy3 float linear_scale SpecializedQuantitationType Cy3 Diameter of the circle inscribed into the spot's signal region.
+Min Diam_Cy5 float linear_scale SpecializedQuantitationType Cy5 Diameter of the circle inscribed into the spot's signal region.
+Negative spot_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 1 if the spot was qualified as negative, 0 if it was not.
+Negative spot_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 1 if the spot was qualified as negative, 0 if it was not.
+Offset X_Cy3 float linear_scale SpecializedQuantitationType Cy3 X Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset X_Cy5 float linear_scale SpecializedQuantitationType Cy5 X Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset Y_Cy3 float linear_scale SpecializedQuantitationType Cy3 Y Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset Y_Cy5 float linear_scale SpecializedQuantitationType Cy5 Y Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Offset failed_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot passed offset test, 1 if it did not.
+Offset failed_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot passed offset test, 1 if it did not.
+Open Perimeter Failed_Cy3 boolean linear_scale SpecializedQuantitationType Cy3
+Open Perimeter Failed_Cy5 boolean linear_scale SpecializedQuantitationType Cy5
+Open Perimeter_Cy3 float linear_scale SpecializedQuantitationType Cy3 Computes the proportion of signal perimeter that touches the border of rectangular snip around the spot.
+Open Perimeter_Cy5 float linear_scale SpecializedQuantitationType Cy5 Computes the proportion of signal perimeter that touches the border of rectangular snip around the spot.
+Open perimeter failed_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot open perimeter test, 1 if it did not.
+Open perimeter failed_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot open perimeter test, 1 if it did not.
+Perim-To-Area_Cy3 boolean linear_scale SpecializedQuantitationType Cy3
+Perim-To-Area_Cy5 boolean linear_scale SpecializedQuantitationType Cy5
+Perim-to-area failed_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot passed perimeter-to-area test, 1 if it did not.
+Perim-to-area failed_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot passed perimeter-to-area test, 1 if it did not.
+Position Offset_Cy3 float linear_scale SpecializedQuantitationType Cy3
+Position Offset_Cy5 float linear_scale SpecializedQuantitationType Cy5
+Position offset_Cy3 float linear_scale SpecializedQuantitationType Cy3 Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Position offset_Cy5 float linear_scale SpecializedQuantitationType Cy5 Offset (in pixels) of the center of the grid circle from the expected position in the grid.
+Saturated spot_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 1 if the spot was qualified as saturated, 0 if it was not.
+Saturated spot_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 1 if the spot was qualified as saturated, 0 if it was not.
+Selected spot_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 1 if the spot was selected in the software view at time of save, 0 if not.
+Selected spot_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 1 if the spot was selected in the software view at time of save, 0 if not.
+Shape Regularity float linear_scale SpecializedQuantitationType
+Shape Regularity Failed_Cy3 boolean linear_scale SpecializedQuantitationType Cy3
+Shape Regularity Failed_Cy5 boolean linear_scale SpecializedQuantitationType Cy5
+Shape Regularity_Cy3 float linear_scale SpecializedQuantitationType Cy3 The signal area of a spot is inscribed into a circle, then the number of non-signal pixels that fall within this circle is computed and divided by area of circle. This ratio is subtracted from 1 to give the Shape Regularity.
+Shape Regularity_Cy5 float linear_scale SpecializedQuantitationType Cy5 The signal area of a spot is inscribed into a circle, then the number of non-signal pixels that fall within this circle is computed and divided by area of circle. This ratio is subtracted from 1 to give the Shape Regularity.
+Shape regularity failed_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot passed shape regularity test, 1 if it did not.
+Shape regularity failed_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot passed shape regularity test, 1 if it did not.
+Signal Area_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Number of pixels in the local signal region.
+Signal Area_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Number of pixels in the local signal region.
+Signal Contamination Present_Cy3 boolean linear_scale SpecializedQuantitationType Cy3
+Signal Contamination Present_Cy5 boolean linear_scale SpecializedQuantitationType Cy5
+Signal Mean float linear_scale MeasuredSignal
+Signal Mean_Cy3 float linear_scale MeasuredSignal Cy3 Pixel intensity averaged over the local signal region.
+Signal Mean_Cy5 float linear_scale MeasuredSignal Cy5 Pixel intensity averaged over the local signal region.
+Signal Median_Cy3 float linear_scale SpecializedQuantitationType Cy3 Median pixel intensity computed over the local signal region.
+Signal Median_Cy5 float linear_scale SpecializedQuantitationType Cy5 Median pixel intensity computed over the local signal region.
+Signal Mode_Cy3 float linear_scale SpecializedQuantitationType Cy3 Mode pixel intensity computed over the local signal region (mode corresponds to the pick location in intensity distribution).
+Signal Mode_Cy5 float linear_scale SpecializedQuantitationType Cy5 Mode pixel intensity computed over the local signal region (mode corresponds to the pick location in intensity distribution).
+Signal Stdev float linear_scale Error Signal Mean
+Signal Stdev_Cy3 float linear_scale Error Signal Mean_Cy3 Cy3 Standard deviation of pixel intensities over the local signal region.
+Signal Stdev_Cy5 float linear_scale Error Signal Mean_Cy5 Cy5 Standard deviation of pixel intensities over the local signal region.
+Signal Total_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Total pixel intensity summed over the local signal region.
+Signal Total_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Total pixel intensity summed over the local signal region.
+Signal contamination present_Cy3 boolean linear_scale SpecializedQuantitationType Cy3 0 if the spot passed signal contamination test, 1 if it did not.
+Signal contamination present_Cy5 boolean linear_scale SpecializedQuantitationType Cy5 0 if the spot passed signal contamination test, 1 if it did not.
+Spot Area_Cy3 integer linear_scale SpecializedQuantitationType Cy3 Signal Area plus Ignored Area.
+Spot Area_Cy5 integer linear_scale SpecializedQuantitationType Cy5 Signal Area plus Ignored Area.
+XCoord float linear_scale SpecializedQuantitationType
+XCoord_Cy3 float linear_scale SpecializedQuantitationType Cy3 X coordinate (in pixels) of grid circle corresponding to the spot.
+XCoord_Cy5 float linear_scale SpecializedQuantitationType Cy5 X coordinate (in pixels) of grid circle corresponding to the spot.
+YCoord float linear_scale SpecializedQuantitationType
+YCoord_Cy3 float linear_scale SpecializedQuantitationType Cy3 Y coordinate (in pixels) of grid circle corresponding to the spot.
+YCoord_Cy5 float linear_scale SpecializedQuantitationType Cy5 Y coordinate (in pixels) of grid circle corresponding to the spot.
+>>>NimbleScan[NimbleGen Systems]
+GENE_EXPR_OPTION string_datatype unscaled SpecializedQuantitationType The CONTAINER name from the design file, if analysis was done by container or WHOLE_ARRAY if all replicate probe sets were combined into a single set. The default analysis is normally by container. CONTAINER names are generally named FORWARD/REVERSE/BLOCK
+IMAGE_ID_CY3 string_datatype unscaled SpecializedQuantitationType Cy3 The name of the image the data was extracted from, minus the .tif extension. For NimbleGen data sets, this will be the array identifier plus any additional information, like wavelength used to scan the array, or photomultiplier tube setting. The array ID
+IMAGE_ID_CY5 string_datatype unscaled SpecializedQuantitationType Cy5 The name of the image the data was extracted from, minus the .tif extension. For NimbleGen data sets, this will be the array identifier plus any additional information, like wavelength used to scan the array, or photomultiplier tube setting. The array ID
+IMAGE_ID string_datatype unscaled SpecializedQuantitationType The name of the image the data was extracted from, minus the .tif extension. For NimbleGen data sets, this will be the array identifier plus any additional information, like wavelength used to scan the array, or photomultiplier tube setting. The array ID
+MATCH_INDEX integer linear_scale SpecializedQuantitationType Integer number that ties probe pairs together. Using the combination of MATCH_INDEX and MISMATCH you can retrieve and distinguish the members of the probe pair. Required for expression arrays with mismatches. Must be unique for each probe pair of a given
+MM_CY3 float linear_scale SpecializedQuantitationType Cy3 The mismatch signal intensity for the probe pair. Will range from 0 to 65536. Will be zero for perfect match only designs.
+MM_CY5 float linear_scale SpecializedQuantitationType Cy5 The mismatch signal intensity for the probe pair. Will range from 0 to 65536. Will be zero for perfect match only designs.
+PM_CY3 float linear_scale MeasuredSignal Cy3 The perfect match signal intensity for the probe pair. Will range from 0 to 65536.
+PM_CY5 float linear_scale MeasuredSignal Cy5 The perfect match signal intensity for the probe pair. Will range from 0 to 65536.
+MM float linear_scale SpecializedQuantitationType The mismatch signal intensity for the probe pair. Will range from 0 to 65536. Will be zero for perfect match only designs.
+PM float linear_scale MeasuredSignal The perfect match signal intensity for the probe pair. Will range from 0 to 65536.
+POSITION integer linear_scale SpecializedQuantitationType Position of the PROBE_SEQUENCE in the sequence/region of interest, starting from the left/5-prime end.
+PROBE_ID string_datatype unscaled SpecializedQuantitationType The NimbleGen probe identifier. Used to identify a probe sequence within a design. Limited to 50 characters. For expression designs, a PROBE_ID is a 17 character string that looks like HSAP00P0001724033. The first four letters (HSAP) are the species code.
+SEQ_ID string_datatype unscaled SpecializedQuantitationType The NimbleGen sequence identifier. Used to group the probe pairs together for determining gene expression summary values. Required - must be unique for each sequence/region of interest. Limited to 50 characters.
+SEQ_URL string_datatype unscaled SpecializedQuantitationType When populated, URL to sequence information for the SEQ_ID.
+>>>QuantArray[PerkinElmer]
+Cy3 Area integer linear_scale SpecializedQuantitationType Cy3 The area of the spot given as the number of pixels (in sq. microns). Used to determine whether the spot is close to the expected size or is an artifact on the slide.
+Cy3 Area Filter boolean linear_scale SpecializedQuantitationType Cy3
+Cy3 Background float linear_scale MeasuredSignal 1 Cy3 Mean pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Std Dev float linear_scale Error 1 Cy3 Background Cy3 Standard deviation of the raw Cy3 background pixel intensity.
+Cy3 Background Subtr 1 Cy3
+Cy3 Bkg. Uniformity float linear_scale SpecializedQuantitationType 1 Cy3 The uniformity of the pixels used in the background intensity calculation.
+Cy3 Bkg. Uniformity Filter boolean linear_scale SpecializedQuantitationType Cy3
+Cy3 Circularity float linear_scale SpecializedQuantitationType Cy3 Reports the degree to which a spot is close to a perfect circle. This measurement is used to determine the print quality of the microarray.
+Cy3 Circularity Filter boolean linear_scale SpecializedQuantitationType Cy3
+Cy3 Confidence integer linear_scale SpecializedQuantitationType Cy3 Unused QuantArray feature, will always = 1.
+Cy3 Diameter float linear_scale SpecializedQuantitationType Cy3 Average diameter of the spot (in microns). Used to determine whether the spot is close to the expected size or is perhaps an artifact on the slide.
+Cy3 Diameter Filter boolean linear_scale SpecializedQuantitationType Cy3
+Cy3 Footprint float linear_scale SpecializedQuantitationType Cy3 The distance between the expected position of a spot and its actual measured position (in microns).
+Cy3 Footprint Filter boolean linear_scale SpecializedQuantitationType Cy3
+Cy3 Intensity float linear_scale MeasuredSignal Cy3 Mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Intensity Std Dev float linear_scale Error Cy3 Intensity Cy3 Standard deviation of the raw Cy3 foreground pixel intensity.
+Cy3 Normalized & BackSubtr 1 Cy3
+Cy3 Normalized & BackSubtr Ratio 1 Cy3
+Cy3 Percent float linear_scale SpecializedQuantitationType Cy3 The portion of the overall spot fluorescence contributed by channel Cy3 (in percent).
+Cy3 Ratio float linear_scale SpecializedQuantitationType Cy3 The portion of the overall spot fluorescence contributed by channel Cy3 (normalized so that channel Cy3 contribution equals 1).
+Cy3 Replicate Filter boolean linear_scale SpecializedQuantitationType Cy3
+Cy3 Signal Noise Ratio float linear_scale Ratio 1 Cy3 The ratio of spot intensity to the standard deviation of the local background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Cy3 Signal Noise Ratio Filter float linear_scale SpecializedQuantitationType Cy3
+Cy3 Spot Uniformity float linear_scale SpecializedQuantitationType Cy3 The uniformity of the pixels used in the spot foreground intensity calculation.This helps in determining the quality of the spot and aids in selecting the correct quantification output method.
+Cy3 Spot Uniformity Filter boolean linear_scale SpecializedQuantitationType Cy3
+Cy5 Area integer linear_scale SpecializedQuantitationType Cy5 The area of the spot given as the number of pixels (in sq. microns). Used to determine whether the spot is close to the expected size or is an artifact on the slide.
+Cy5 Area Filter boolean linear_scale SpecializedQuantitationType Cy5
+Cy5 Background float linear_scale MeasuredSignal 1 Cy5 Mean pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Std Dev float linear_scale Error 1 Cy5 Background Cy5 Standard deviation of the raw Cy5 background pixel intensity.
+Cy5 Background Subtr 1 Cy5
+Cy5 Bkg. Uniformity float linear_scale SpecializedQuantitationType 1 Cy5 The uniformity of the pixels used in the background intensity calculation.
+Cy5 Bkg. Uniformity Filter boolean linear_scale SpecializedQuantitationType Cy5
+Cy5 Circularity float linear_scale SpecializedQuantitationType Cy5 Reports the degree to which a spot is close to a perfect circle. This measurement is used to determine the print quality of the microarray.
+Cy5 Circularity Filter boolean linear_scale SpecializedQuantitationType Cy5
+Cy5 Confidence integer linear_scale SpecializedQuantitationType Cy5 Unused QuantArray feature, will always = 1.
+Cy5 Diameter float linear_scale SpecializedQuantitationType Cy5 Average diameter of the spot (in microns). Used to determine whether the spot is close to the expected size or is perhaps an artifact on the slide.
+Cy5 Diameter Filter boolean linear_scale SpecializedQuantitationType Cy5
+Cy5 Footprint float linear_scale SpecializedQuantitationType Cy5 The distance between the expected position of a spot and its actual measured position (in microns).
+Cy5 Footprint Filter boolean linear_scale SpecializedQuantitationType Cy5
+Cy5 Intensity float linear_scale MeasuredSignal Cy5 Mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Intensity Std Dev float linear_scale Error Cy5 Intensity Cy5 Standard deviation of the raw Cy5 foreground pixel intensity.
+Cy5 Normalized & BackSubtr 1 Cy5
+Cy5 Normalized & BackSubtr Ratio 1 Cy5
+Cy5 Percent float linear_scale SpecializedQuantitationType Cy5 The portion of the overall spot fluorescence contributed by channel Cy5 (in percent).
+Cy5 Ratio float linear_scale SpecializedQuantitationType Cy5 The portion of the overall spot fluorescence contributed by channel Cy5 (normalized so that channel Cy5 contribution equals 1).
+Cy5 Replicate Filter boolean linear_scale SpecializedQuantitationType Cy5
+Cy5 Signal Noise Ratio float linear_scale Ratio 1 Cy5 The ratio of spot intensity to the standard deviation of the local background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Cy5 Signal Noise Ratio Filter float linear_scale SpecializedQuantitationType Cy5
+Cy5 Spot Uniformity float linear_scale SpecializedQuantitationType Cy5 The uniformity of the pixels used in the spot foreground intensity calculation.This helps in determining the quality of the spot and aids in selecting the correct quantification output method.
+Cy5 Spot Uniformity Filter boolean linear_scale SpecializedQuantitationType Cy5
+Ignore Filter integer linear_scale SpecializedQuantitationType Whether the feature quantitation is used in down-stream analysis (flagging feature as valid): 0 = feature invalid (null data); 1 = feature valid.
+Number integer linear_scale SpecializedQuantitationType Unique integer for each spot.
+X Location integer linear_scale SpecializedQuantitationType X pixel coordinate of spot center.
+Y Location integer linear_scale SpecializedQuantitationType Y pixel coordinate of spot center.
+log_normalised float log_base_2 Ratio Ratio = log2 Cy5 - log2 Cy3, for dyeswap ratio *-1
+normalised log(cy3/cy5) ratio float log Ratio 0
+normalised log(cy5/cy3) ratio float log Ratio 0
+Cy3 Background Mean float linear_scale MeasuredSignal 1 Cy3 Mean pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Median integer linear_scale SpecializedQuantitationType 1 Cy3 Median pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Mode float linear_scale SpecializedQuantitationType 1 Cy3 Mode of pixel Cy3 background intensity in Channel Cy3.
+Cy3 Background Total integer linear_scale SpecializedQuantitationType 1 Cy3 Total pixel Cy3 background intensity in Channel Cy3.
+Cy3 Flag integer unscaled SpecializedQuantitationType Cy3
+Cy3 Signal Mean float linear_scale MeasuredSignal Cy3 Mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Signal Median integer linear_scale SpecializedQuantitationType Cy3 Median pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Signal Mode float linear_scale SpecializedQuantitationType Cy3 Mode of pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Signal Total integer linear_scale SpecializedQuantitationType Cy3 Total pixel Cy3 foreground intensity in Channel Cy3.
+Cy5 Background Mean float linear_scale MeasuredSignal 1 Cy5 Mean pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Median integer linear_scale SpecializedQuantitationType 1 Cy5 Median pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Mode float linear_scale SpecializedQuantitationType 1 Cy5 Mode of pixel Cy5 background intensity in Channel Cy5.
+Cy5 Background Total integer linear_scale SpecializedQuantitationType 1 Cy5 Total pixel Cy5 background intensity in Channel Cy5.
+Cy5 Flag integer unscaled SpecializedQuantitationType Cy5
+Cy5 Signal Mean float linear_scale MeasuredSignal Cy5 Mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Signal Median integer linear_scale SpecializedQuantitationType Cy5 Median pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Signal Mode float linear_scale SpecializedQuantitationType Cy5 Mode of pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Signal Total integer linear_scale SpecializedQuantitationType Cy5 Total pixel Cy5 foreground intensity in Channel Cy5.
+>>>ScanAlyze[Stanford University]
+BGPIX integer linear_scale SpecializedQuantitationType 1 Number of background pixels.
+BOT integer linear_scale SpecializedQuantitationType Box bottom: int(((centerX + radius) - Xoffset) / pixelSize).
+CH1AB float linear_scale SpecializedQuantitationType 1 Cy3 Mean intensities of background pixels of Cy3.
+CH1B float linear_scale MeasuredSignal 1 Cy3 Median intensities of background pixels of Cy3.
+CH1EDGEA float linear_scale SpecializedQuantitationType Cy3 Mean magnitude of the horizontal & vertical Sobel edge vectors contained within each spot, for Cy3.
+CH1GTB1 float linear_scale SpecializedQuantitationType Cy3 Fraction of pixels in the spot greater than background (CH1B) of Cy3.
+CH1GTB2 float linear_scale SpecializedQuantitationType Cy3 Fraction of pixels in the spot greater than 1.5x the background (CH1B) for Cy3.
+CH1I float linear_scale MeasuredSignal Cy3 Uncorrected Cy3 mean pixel intensity.
+CH1KSD float linear_scale SpecializedQuantitationType Cy3 Value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy3.
+CH1KSP float linear_scale SpecializedQuantitationType Cy3 Probability value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy3.
+CH2AB float linear_scale SpecializedQuantitationType 1 Cy5 Mean intensities of background pixels of Cy5.
+CH2B float linear_scale MeasuredSignal 1 Cy5 Median intensities of background pixels of Cy5.
+CH2EDGEA float linear_scale SpecializedQuantitationType Cy5 Mean magnitude of the horizontal & vertical Sobel edge vectors contained within each spot, for Cy5.
+CH2GTB1 float linear_scale SpecializedQuantitationType Cy5 Fraction of pixels in the spot greater than background (CH2B) of Cy5.
+CH2GTB2 float linear_scale SpecializedQuantitationType Cy5 Fraction of pixels in the spot greater than 1.5x the background (CH2B) for Cy5.
+CH2I float linear_scale MeasuredSignal Cy5 Uncorrected Cy5 mean pixel intensity.
+CH2KSD float linear_scale SpecializedQuantitationType Cy5 Value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy5.
+CH2KSP float linear_scale SpecializedQuantitationType Cy5 Probability value of Komogorov-Smirnov statistic that assesses the likelihood that the spot pixel intensity is drawn from the background distribution, for Cy5.
+COL integer linear_scale SpecializedQuantitationType The column within the grid that the spot is contained.
+CORR float linear_scale SpecializedQuantitationType The correlation between channel1 (Cy3) & Channel 2 (Cy5) pixels within the spot, and is a useful quality control parameter. Generally, high values imply better fit & good spot quality.
+EDGE integer unscaled SpecializedQuantitationType
+FLAG integer unscaled Failed User defined spot flag (default 0).
+GRID integer linear_scale SpecializedQuantitationType The grid in which the spot is contained.
+LEFT integer linear_scale SpecializedQuantitationType Box left: int(((centerY - radius) - yoffset) / pixelSize).
+LFRAT float linear_scale SpecializedQuantitationType When single pixel intensities on channel 1 (Cy3) are plotted against those in channel 2 (Cy5) they fall on a straight line with a slope equal to the ratio. The least-squares fit of a line to the points, minimizing the sum of the squares shortest distance
+MRAT float linear_scale SpecializedQuantitationType Contains median of Ch2PI-CH2B/Ch1PI-CH1B where Ch1PI & Ch2PI represent single pixel intensities.
+RAT2 float linear_scale Ratio Ratio of the arithmetic mean intensities of each spot for each wavelength, with the median background subtracted. Channel 2/Channel 1 ratio, (CH2I - CH2B)/(CH1 - CH1B) or Red/Green ratio.
+REGR float linear_scale SpecializedQuantitationType When single pixel intensities on channel 1 (Cy3) are plotted against those in channel 2 (Cy5) they fall on a straight line with a slope equal to the ratio. This represents the linear regression of Cy5 on Cy3.
+RIGHT integer linear_scale SpecializedQuantitationType Box right: int(((centerY + radius) - yoffset) / pixelSize).
+SPIX integer linear_scale SpecializedQuantitationType Count of the number of pixels in the spot.
+SPOT integer linear_scale SpecializedQuantitationType Unique index of spot in file - counting starts with grid1, moves along row1 from column1 until the last column, then advances to the next row; after all rows in grid1 are assigned an index, counting proceeds to grid2, etc...
+TOP integer linear_scale SpecializedQuantitationType Box top: int(((centerX - radius) - Xoffset) / pixelSize).
+>>>ScanArray Express[PerkinElmer]
+% > B543+1SD float linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the 543nm channel.
+% > B543+2SD float linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the 543nm channel.
+% > B633+1SD float linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the 633nm channel.
+% > B633+2SD float linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the 633nm channel.
+B Pixels integer linear_scale SpecializedQuantitationType The total number of background pixels.
+B543 Mean float linear_scale MeasuredSignal 1 Cy3 Mean pixel background intensity in 543nm channel.
+B543 Median float linear_scale SpecializedQuantitationType 1 Cy3 Median pixel background intensity in 543nm channel.
+B543 SD float linear_scale Error 1 B543 Median Cy3 Standard deviation of the raw 543nm background pixel intensity.
+B633 Mean float linear_scale MeasuredSignal 1 Cy5 Mean pixel background intensity in 633nm channel.
+B633 Median float linear_scale SpecializedQuantitationType 1 Cy5 Median pixel background intensity in 633nm channel.
+B633 SD float linear_scale Error 1 B633 Median Cy5 Standard deviation of the raw 633nm background pixel intensity.
+Cy3 % > B + 1 SD float linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the Cy3 channel.
+Cy3 % > B + 2 SD float linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the Cy3 channel.
+Cy3 B Mean float linear_scale MeasuredSignal 1 Cy3 Mean pixel Cy3 background intensity in Channel Cy3.
+Cy3 B Median float linear_scale SpecializedQuantitationType 1 Cy3 Median pixel Cy3 background intensity in Channel Cy3.
+Cy3 B SD float linear_scale Error 1 Cy3 B Median Cy3 Standard deviation of the raw Cy3 background pixel intensity.
+Cy3 F % Sat. float linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels in the Cy3 channel that are saturated.
+Cy3 Log Ratio
+Cy3 Mean float linear_scale MeasuredSignal Cy3 Mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Mean - B float linear_scale SpecializedQuantitationType Cy3 The mean feature pixel intensity in the Cy3 channel with the mean background subtracted.
+Cy3 Mean of Ratios
+Cy3 Median float linear_scale SpecializedQuantitationType Cy3 Median pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 Median - B float linear_scale SpecializedQuantitationType Cy3 The median feature pixel intensity in the Cy3 channel with the median background subtracted.
+Cy3 Median of Ratios
+Cy3 N (Mean-B) float linear_scale DerivedSignal Cy3 Mean pixel Cy3 foreground intensity in Channel Cy3 with the mean background subtracted, globally normalized.
+Cy3 N (Median-B) float linear_scale SpecializedQuantitationType Cy3 Median pixel Cy3 foreground intensity in Channel Cy3 with the median background subtracted, globally normalized.
+Cy3 N Log Ratio
+Cy3 N Mean float linear_scale DerivedSignal Cy3 Globally normalized mean pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 N Mean of Ratios
+Cy3 N Median float linear_scale SpecializedQuantitationType Cy3 Globally normalized median pixel Cy3 foreground intensity in Channel Cy3.
+Cy3 N Median of Ratios
+Cy3 N Ratio of Means
+Cy3 N Ratio of Medians
+Cy3 N Rgn Ratio
+Cy3 Ratio of Means
+Cy3 Ratio of Medians
+Cy3 Ratios SD
+Cy3 Rgn Ratio
+Cy3 Rgn R�
+Cy3 SD float linear_scale Error Cy3 Median Cy3 Standard deviation of the raw Cy3 foreground pixel intensity.
+Cy3 SignalNoiseRatio float linear_scale Ratio Cy3 The ratio of spot Cy3 intensity to the standard deviation of the local Cy3 background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Cy5 % > B + 1 SD float linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than one standard deviation above the background pixel intensity, in the Cy5 channel.
+Cy5 % > B + 2 SD float linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels with intensities more than two standard deviations above the background pixel intensity, in the Cy5 channel.
+Cy5 B Mean float linear_scale MeasuredSignal 1 Cy5 Mean pixel Cy5 background intensity in Channel Cy5.
+Cy5 B Median float linear_scale SpecializedQuantitationType 1 Cy5 Median pixel Cy5 background intensity in Channel Cy5.
+Cy5 B SD float linear_scale Error 1 Cy5 B Median Cy5 Standard deviation of the raw Cy5 background pixel intensity.
+Cy5 F % Sat. float linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels in the Cy5 channel that are saturated.
+Cy5 Log Ratio
+Cy5 Mean float linear_scale MeasuredSignal Cy5 Mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Mean - B float linear_scale SpecializedQuantitationType Cy5 The mean feature pixel intensity in the Cy5 channel with the mean background subtracted.
+Cy5 Mean of Ratios
+Cy5 Median float linear_scale SpecializedQuantitationType Cy5 Median pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 Median - B float linear_scale SpecializedQuantitationType Cy5 The median feature pixel intensity in the Cy5 channel with the median background subtracted.
+Cy5 Median of Ratios
+Cy5 N (Mean-B) float linear_scale DerivedSignal Cy5 Mean pixel Cy5 foreground intensity in Channel Cy5 with the mean background subtracted, globally normalized.
+Cy5 N (Median-B) float linear_scale SpecializedQuantitationType Cy5 Median pixel Cy5 foreground intensity in Channel Cy5 with the median background subtracted, globally normalized.
+Cy5 N Log Ratio
+Cy5 N Mean float linear_scale DerivedSignal Cy5 Globally normalized mean pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 N Mean of Ratios
+Cy5 N Median float linear_scale SpecializedQuantitationType Cy5 Globally normalized median pixel Cy5 foreground intensity in Channel Cy5.
+Cy5 N Median of Ratios
+Cy5 N Ratio of Means
+Cy5 N Ratio of Medians
+Cy5 N Rgn Ratio
+Cy5 Ratio of Means
+Cy5 Ratio of Medians
+Cy5 Ratios SD
+Cy5 Rgn Ratio
+Cy5 Rgn R�
+Cy5 SD float linear_scale Error Cy5 Median Cy5 Standard deviation of the raw Cy5 foreground pixel intensity.
+Cy5 SignalNoiseRatio float linear_scale Ratio Cy5 The ratio of spot Cy5 intensity to the standard deviation of the local Cy5 background of all spots in the microarray. Spots with a low Signal/Noise Ratio can be quickly identified for later reviews.
+Dia. integer linear_scale SpecializedQuantitationType The diameter in um of the feature-indicator.
+Diameter integer linear_scale SpecializedQuantitationType The diameter in um of the feature-indicator.
+F Pixels integer linear_scale SpecializedQuantitationType The total number of feature pixels.
+F543 % Sat. float linear_scale SpecializedQuantitationType Cy3 The percentage of feature pixels in the 543nm channel that are saturated.
+F543 Mean float linear_scale MeasuredSignal Cy3 Mean pixel foreground intensity in 543nm channel.
+F543 Mean - B543 float linear_scale SpecializedQuantitationType Cy3 The mean feature pixel intensity in the 543nm channel with the mean background subtracted.
+F543 Median float linear_scale SpecializedQuantitationType Cy3 Median pixel foreground intensity in 543nm channel.
+F543 Median - B543 float linear_scale SpecializedQuantitationType Cy3 The median feature pixel intensity in the 543nm channel with the median background subtracted.
+F543 SD float linear_scale Error F543 Median Cy3 Standard deviation of the raw 543nm foreground pixel intensity.
+F633 % Sat. float linear_scale SpecializedQuantitationType Cy5 The percentage of feature pixels in the 633nm channel that are saturated.
+F633 Mean float linear_scale MeasuredSignal Cy5 Mean pixel foreground intensity in 633nm channel.
+F633 Mean - B633 float linear_scale SpecializedQuantitationType Cy5 The mean feature pixel intensity in the 633nm channel with the mean background subtracted.
+F633 Median float linear_scale SpecializedQuantitationType Cy5 Median pixel foreground intensity in 633nm channel.
+F633 Median - B633 float linear_scale SpecializedQuantitationType Cy5 The median feature pixel intensity in the 633nm channel with the median background subtracted.
+F633 SD float linear_scale Error F633 Median Cy5 Standard deviation of the raw 633nm foreground pixel intensity.
+Flags integer unscaled SpecializedQuantitationType
+Footprint integer linear_scale SpecializedQuantitationType
+ID string_datatype unscaled SpecializedQuantitationType
+Index integer linear_scale SpecializedQuantitationType
+Log Ratio float log_base_2 Ratio
+Log Ratio (633/543) float log_base_2 Ratio
+Mean of Ratios float linear_scale SpecializedQuantitationType The mean of pixel-by-pixel ratios of pixel intensities.
+Mean of Ratios (633/543) float linear_scale SpecializedQuantitationType The mean of pixel-by-pixel ratios of pixel intensities.
+Median of Ratios float linear_scale SpecializedQuantitationType The median of pixel-by-pixel ratios of pixel intensities.
+Median of Ratios (633/543) float linear_scale SpecializedQuantitationType The median of pixel-by-pixel ratios of pixel intensities.
+N Log Ratio float linear_scale SpecializedQuantitationType
+N Mean of Ratios float linear_scale SpecializedQuantitationType
+N Median of Ratios float linear_scale SpecializedQuantitationType
+N Ratio of Means float linear_scale SpecializedQuantitationType
+N Ratio of Medians float linear_scale SpecializedQuantitationType
+N Rgn Ratio float linear_scale SpecializedQuantitationType
+Name string_datatype unscaled SpecializedQuantitationType
+Normalize integer unscaled PresentAbsent The normalization status of the feature (included/not included).
+Ratio of Means float linear_scale SpecializedQuantitationType The ratio of the mean intensities of each feature for each wavelength.
+Ratio of Means (633/543) float linear_scale SpecializedQuantitationType The ratio of the mean intensities of each feature for each wavelength.
+Ratio of Medians float linear_scale SpecializedQuantitationType The ratio of the median intensities of each feature for each wavelength.
+Ratio of Medians (633/543) float linear_scale SpecializedQuantitationType The ratio of the median intensities of each feature for each wavelength.
+Ratios SD float linear_scale SpecializedQuantitationType Median of Ratios
+Ratios SD (633/543) float linear_scale SpecializedQuantitationType Median of Ratios (633/543)
+Rgn R2 float linear_scale SpecializedQuantitationType
+Rgn R2 (633/543) float linear_scale SpecializedQuantitationType
+Rgn Ratio float linear_scale SpecializedQuantitationType
+Rgn Ratio (633/543) float linear_scale SpecializedQuantitationType
+Rgn R� (633/543) float linear_scale SpecializedQuantitationType
+Sum of Means float linear_scale SpecializedQuantitationType The sum of the mean intensities for each wavelength.
+Sum of Medians float linear_scale SpecializedQuantitationType The sum of the median intensities for each wavelength.
+X integer linear_scale SpecializedQuantitationType X coordinate of spot center.
+Y integer linear_scale SpecializedQuantitationType Y coordinate of spot center.
+>>>SpotFinder[TIGR]
+BGA integer linear_scale MeasuredSignal 1 Cy3 Spot background in Cy3 channel.
+BGB integer linear_scale MeasuredSignal 1 Cy5 Spot background in Cy5 channel.
+BkgA integer linear_scale MeasuredSignal 1 Cy3 Spot background in Cy3 channel.
+BkgB integer linear_scale MeasuredSignal 1 Cy5 Spot background in Cy5 channel.
+Flag string_datatype unscaled SpecializedQuantitationType Flag values are generated based on next conditions: A - the number of non-saturated pixels in spot is 0; B- number of non-saturated pixels in spot is between 30 and 50; C- number of non saturated pixels in spot is more then 50; S- fully or partially satur
+Flag A string_datatype unscaled SpecializedQuantitationType Cy3 Spot flag in channel A. This flag is set by QC filter.
+Flag B string_datatype unscaled SpecializedQuantitationType Cy5 Spot flag in channel B. This flag is set by QC filter.
+FlagA string_datatype unscaled SpecializedQuantitationType Cy3 Spot flag in channel A. This flag is set by QC filter.
+FlagB string_datatype unscaled SpecializedQuantitationType Cy5 Spot flag in channel B. This flag is set by QC filter.
+IA integer linear_scale MeasuredSignal Cy3 Spot intensity in Cy3 channel corrected for background.
+IB integer linear_scale MeasuredSignal Cy5 Spot intensity in Cy5 channel corrected for background.
+MNA integer linear_scale SpecializedQuantitationType Cy3 Mean intensity value in Cy3 channel.
+MNB integer linear_scale SpecializedQuantitationType Cy5 Mean intensity value in Cy5 channel.
+MeanR float linear_scale Ratio Spot mean ratio
+MedA integer linear_scale SpecializedQuantitationType Cy3 Median intensity in Cy3 channel.
+MedB integer linear_scale SpecializedQuantitationType Cy5 Median intensity in Cy5 channel.
+MedBkgA integer linear_scale SpecializedQuantitationType Cy3 Median background intensity in Cy3 channel.
+MedBkgB integer linear_scale SpecializedQuantitationType Cy5 Median background intensity in Cy5 channel.
+MedianR float linear_scale Ratio Spot median ratio.
+ModeR float linear_scale Ratio Spot mode ratio.
+PValueA float linear_scale SpecializedQuantitationType Cy3 P-value in Cy3 channel.
+PValueB float linear_scale SpecializedQuantitationType Cy5 P-value in Cy5 channel.
+QC float linear_scale SpecializedQuantitationType Spot total QC score. This is a mean of QC scores in Cy3 and Cy5 channels.
+QCA float linear_scale SpecializedQuantitationType Cy3 Spot QC score in channel A. This is a geometric mean of shape and S/N QC scores in channel A. S/N QC score is calculated as percentage of pixels in a spot with values higher than 2*median(local BKG). Spot shape QC score is defined as ratio of spot area to
+QCB float linear_scale SpecializedQuantitationType Cy5 Spot QC score in channel B. This is a geometric mean of shape and S/N QC scores in channel B. S/N QC score is calculated as percentage of pixels in a spot with values higher than 2*median(local BKG). Spot shape QC score is defined as ratio of spot area to
+QCscore float linear_scale SpecializedQuantitationType
+SA integer linear_scale SpecializedQuantitationType Spot total area in pixels.
+SC integer unscaled SpecializedQuantitationType
+SDA float linear_scale Error IA Cy3 Standard deviation for spot pixels in Cy3 channel.
+SDB float linear_scale Error IB Cy5 Standard deviation for spot pixels in Cy5 channel.
+SDBkgA float linear_scale Error 1 BkgA Cy3 Standard deviation of the background value in Cy3 channel.
+SDBkgB float linear_scale Error 1 BkgB Cy5 Standard deviation of the background value in Cy5 channel.
+SF float linear_scale SpecializedQuantitationType
+SR integer unscaled SpecializedQuantitationType
+Sat float linear_scale SpecializedQuantitationType Spot saturation factor. This measure shows the percentage of non-saturated pixels in the spot used for integration.
+UID integer linear_scale SpecializedQuantitationType Unique identifier for this spot.
+>>>UCSF Spot
+Dapi float linear_scale MeasuredSignal DAPI
+DapiBack float linear_scale MeasuredSignal 1 DAPI
+DapiFore float linear_scale SpecializedQuantitationType DAPI
+Flag integer linear_scale Failed
+Log2MedRat float log_base_2 Ratio
+Log2Rat float log_base_2 Ratio
+Log2Slope float log_base_2 SpecializedQuantitationType
+MeanLog2Rat float log_base_2 SpecializedQuantitationType
+MeanRat float linear_scale SpecializedQuantitationType
+MedianRatio float Ratio
+MinF-B float SpecializedQuantitationType
+OriginDist float SpecializedQuantitationType
+RawRat float linear_scale Ratio
+Ref float linear_scale MeasuredSignal
+RefBack float linear_scale MeasuredSignal 1
+RefBackSD linear_scale Error 1 RefBack
+RefFore float linear_scale SpecializedQuantitationType
+RefForeSD linear_scale Error RefFore
+RefIntercept float SpecializedQuantitationType
+RefZstat PValue
+Slope float linear_scale SpecializedQuantitationType
+SpotCorr float DerivedSignal
+Test float linear_scale MeasuredSignal 1
+TestBack float linear_scale MeasuredSignal 1
+TestBackSD linear_scale Error 1 TestBack
+TestFore float linear_scale SpecializedQuantitationType
+TestForeSD linear_scale Error TestFore
+TestIntercept float SpecializedQuantitationType
+TestZstat PValue
+nback integer linear_scale SpecializedQuantitationType 1
+nfore integer linear_scale SpecializedQuantitationType
+>>>Uni of Toronto in-house analysis software
+B532 Location float linear_scale MeasuredSignal 1 Cy3
+B635 Location float linear_scale MeasuredSignal 1 Cy5
+F532 Location float linear_scale MeasuredSignal Cy3
+F635 Location float linear_scale MeasuredSignal Cy5
+Intensity float log_base_10 SpecializedQuantitationType
+Raw Log Ratio float log_base_10 Ratio 1
diff --git a/lib/ArrayExpress/MAGETAB.pm b/lib/ArrayExpress/MAGETAB.pm
new file mode 100644
index 0000000..42bba6b
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB.pm
@@ -0,0 +1,911 @@
+#!/usr/bin/env perl
+#
+# Core module for ArrayExpress MAGE-TAB to MAGE-ML
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: MAGETAB.pm 2072 2008-06-04 15:08:35Z tfrayner $
+
+package ArrayExpress::MAGETAB;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use File::Spec;
+use File::Path;
+use File::Basename;
+use Text::CSV_XS;
+
+require ArrayExpress::MAGETAB::IDF;
+require ArrayExpress::MAGETAB::SDRF;
+require ArrayExpress::MAGETAB::DataMatrix;
+require ArrayExpress::Datafile::Parser;
+
+use ArrayExpress::Curator::MAGE qw(write_mage unique_identifier postprocess_mage);
+use ArrayExpress::Curator::Common qw(check_linebreaks);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base qw(ArrayExpress::Curator::Logger);
+
+my %magetab_doc : ATTR( :name<magetab_doc>, :default<undef> );
+my %idf : ATTR( :name<idf>, :default<undef> );
+my %sdrfs : ATTR( :get<sdrfs>, :set<sdrfs>, :default<[]> );
+my %data_matrices : ATTR( :get<data_matrices>, :set<data_matrices>, :default<[]> );
+my %output_file : ATTR( :name<output_file>, :default<[]> );
+my %mage : ATTR( :get<mage>, :set<mage>, :default<undef> );
+my %namespace : ATTR( :name<namespace>, :default<q{MAGETabulator}> );
+my %authority : ATTR( :name<authority>, :default<q{ebi.ac.uk}> );
+my %expt_accession : ATTR( :name<expt_accession>, :default<undef> );
+
+# Allow API users to pass in a prot accn service of their own.
+my %prot_accn_service : ATTR( :name<protocol_accession_service>, :default<undef> );
+my %prot_accn_prefix : ATTR( :name<protocol_accession_prefix>, :default<undef> );
+
+my %ded_filehandle : ATTR( :get<ded_filehandle>, :set<ded_filehandle>, :default<undef> );
+my %ded_count : ATTR( :get<ded_count>, :set<ded_count>, :default<undef> );
+my %native_filetypes : ATTR( :get<native_filetypes>, :set<native_filetypes>, :default<undef> );
+my %bags : ATTR( :name<bags>, :default<{}> );
+my %clobber : ATTR( :name<clobber>, :default<0> );
+my %target_directory : ATTR( :name<target_directory>, :init_arg<target_directory>, );
+my %source_directory : ATTR( :get<source_directory>, :init_arg<source_directory>, :default<undef> );
+my %is_standalone : ATTR( :get<is_standalone>, :init_arg<is_standalone>, :default<0> );
+my %qt_filename : ATTR( :get<qt_filename>, :init_arg<qt_filename>, :default<undef> );
+my %keep_all_qts : ATTR( :get<keep_all_qts>, :init_arg<keep_all_qts>, :default<undef> );
+my %keep_protocol_accns : ATTR( :get<keep_protocol_accns>, :init_arg<keep_protocol_accns>, :default<undef> );
+my %include_default_qts : ATTR( :get<include_default_qts>, :init_arg<include_default_qts>, :default<undef> );
+my %reporter_prefix : ATTR( :get<reporter_prefix>, :init_arg<reporter_prefix>, :default<undef> );
+my %compseq_prefix : ATTR( :get<compseq_prefix>, :init_arg<compseq_prefix>, :default<undef> );
+my %use_plain_text : ATTR( :get<use_plain_text>, :init_arg<use_plain_text>, :default<undef> );
+my %skip_datafiles : ATTR( :get<skip_datafiles>, :init_arg<skip_datafiles>, :default<undef> );
+my %in_relaxed_mode : ATTR( :name<in_relaxed_mode>, :default<0> );
+my %ignore_size_limits : ATTR( :name<ignore_size_limits>, :default<undef> );
+
+# Make this visible to users of the module.
+our $VERSION = 1.1;
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ croak("Error: no experiment accession set.")
+ unless defined( $expt_accession{$id} );
+
+ croak("Error: no target directory set.")
+ unless defined( $target_directory{$id} );
+
+ croak("Error: no IDF/MAGE-TAB doc filename set.")
+ unless (defined( $idf{$id} ) || defined( $magetab_doc{$id} ) );
+
+ # Create the target directory, if not already present:
+ mkpath( $target_directory{$id}, undef, oct(777) );
+
+ # If protocol accession service is defined, accession prefix must
+ # be also. Ignore all this if we're not going to be using it
+ # anyway (e.g. during validation).
+ unless ( $self->get_keep_protocol_accns() ) {
+ if ( ( $CONFIG->get_AUTOSUBS_DSN()
+ || $self->get_protocol_accession_service() )
+ && ! defined( $self->get_protocol_accession_prefix() ) ) {
+ croak("Error: protocol accession service is active"
+ . " but no accession prefix set.");
+ }
+ }
+
+ $self->set_progname( 'MAGETAB' );
+ $self->set_version( $VERSION );
+
+ $self->initialize_logfiles();
+
+ return;
+}
+
+sub preprocess_magetab_doc {
+
+ # Class method used to parse linebreaks and split a combined
+ # MAGE-TAB (IDF+SDRF) document into its IDF and SDRF components.
+ my ( $class, $doc ) = @_;
+
+ # Check linebreaks here.
+ my ( $eols, $eol_char ) = check_linebreaks( $doc );
+ unless ( $eol_char ) {
+ croak(
+ sprintf(
+ "Error: Cannot correctly parse linebreaks in file %s"
+ . " (%s unix, %s dos, %s mac)\n",
+ $doc,
+ $eols->{unix},
+ $eols->{dos},
+ $eols->{mac},
+ )
+ );
+ }
+
+ if ( ( $eol_char eq "\015" )
+ && ( $Text::CSV_XS::VERSION < 0.27 ) ) {
+
+ # Mac linebreaks not supported by older versions of Text::CSV_XS.
+ croak("Error: Mac linebreaks not supported by this version"
+ . " of Text::CSV_XS. Please upgrade to version 0.27 or higher.\n");
+ }
+
+ # Split the file here.
+ my $idf_fh = IO::File->new_tmpfile();
+ my $sdrf_fh = IO::File->new_tmpfile();
+ my ($is_idf, $is_sdrf);
+
+ open( my $fh, '<', $doc )
+ or croak(qq{Error: Unable to open MAGETAB document "$doc": $!});
+
+ LINE:
+ while ( my $line = <$fh> ) {
+ if ( $line =~ /\A \"? \[IDF\] /ixms ) {
+ $is_idf = 1;
+ next LINE;
+ }
+ if ( $line =~ /\A \"? \[SDRF\] /ixms ) {
+ $is_sdrf = 1;
+ next LINE;
+ }
+
+ if ( $is_idf && ! $is_sdrf ) {
+ print $idf_fh $line;
+ }
+ elsif ( $is_idf && $is_sdrf ) {
+ print $sdrf_fh $line;
+ }
+ }
+
+ close( $fh ) or croak(qq{Error: Unable to close MAGETAB document "$doc": $!});
+
+ unless ( $is_idf && $is_sdrf ) {
+ croak("Error: Document must contain an [IDF] "
+ . "section and an [SDRF] section, in that order.");
+ }
+ seek($idf_fh, 0, 0) or die("Error rewinding temporary IDF file: $!");
+ seek($sdrf_fh, 0, 0) or die("Error rewinding temporary SDRF file: $!");
+
+ return ($idf_fh, $sdrf_fh, $eol_char);
+}
+
+sub parse_magetab_doc {
+
+ my ( $self ) = @_;
+
+ unless ( $self->get_magetab_doc() ) {
+ croak("Error: MAGETAB::parse_magetab_doc called without setting filename.");
+ }
+
+ $self->logprint('magetab',
+ sprintf("Processing MAGE-TAB doc: %s\n\n", $self->get_magetab_doc()));
+
+ # Split the file into IDF and SDRF, and determine the eol_char.
+ my ( $idf_fh, $sdrf_fh, $eol_char )
+ = __PACKAGE__->preprocess_magetab_doc( $self->get_magetab_doc() );
+
+ # Parse the IDF component.
+ my $idf_parser = ArrayExpress::MAGETAB::IDF->new({
+ idf_filehandle => $idf_fh,
+ eol_char => $eol_char,
+ namespace => $self->get_namespace(),
+ authority => $self->get_authority(),
+ expt_accession => $self->get_expt_accession(),
+ in_relaxed_mode => $self->get_in_relaxed_mode(),
+ source_directory => $self->get_source_directory(),
+ });
+ my $experiment = $self->parse_idf( $idf_parser );
+
+ # Parse the SDRF component.
+ my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+ sdrf_filehandle => $sdrf_fh,
+ eol_char => $eol_char,
+ namespace => $self->get_namespace(),
+ authority => $self->get_authority(),
+ expt_accession => $self->get_expt_accession(),
+ bags => $idf_parser->get_bags(),
+ source_directory => $self->get_source_directory(),
+ in_relaxed_mode => $self->get_in_relaxed_mode(),
+ skip_datafiles => $self->get_skip_datafiles(),
+ });
+
+ my $datafiles = $self->parse_sdrf( $sdrf_parser );
+ $self->add_all_mage_objects( $experiment, $sdrf_parser );
+
+ return wantarray
+ ? ( $datafiles, $self->get_bags(), $self->get_mage() )
+ : $self->get_mage();
+}
+
+sub parse {
+
+ my ( $self ) = @_;
+
+ unless ( $self->get_idf() ) {
+ croak("Error: MAGETAB::parse called without setting IDF filename.");
+ }
+
+ $self->logprint('magetab',
+ sprintf("Processing IDF: %s\n\n", $self->get_idf()));
+
+ my $idf_parser = ArrayExpress::MAGETAB::IDF->new({
+ idf => $self->get_idf(),
+ namespace => $self->get_namespace(),
+ authority => $self->get_authority(),
+ expt_accession => $self->get_expt_accession(),
+ in_relaxed_mode => $self->get_in_relaxed_mode(),
+ source_directory => $self->get_source_directory(),
+ });
+
+ my $experiment = $self->parse_idf( $idf_parser );
+
+ my $sdrfs = $self->get_sdrfs();
+ my $mage = $self->get_mage();
+
+ # FIXME parse the SDRFS etc. here
+ foreach my $sdrf_file (@$sdrfs) {
+
+ # FIXME stitch the SDRFs together here.
+
+ }
+
+ my $datafiles = [];
+ if ( scalar @$sdrfs ) {
+
+ # FIXME only one SDRF supported for now:
+ my $sdrf_file = $sdrfs->[0]; # FIXME delete this line when ready.
+
+ $self->logprint('magetab',
+ sprintf("Processing SDRF: %s\n\n", $sdrf_file));
+
+ my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+ sdrf => $sdrf_file,
+ namespace => $self->get_namespace(),
+ authority => $self->get_authority(),
+ expt_accession => $self->get_expt_accession(),
+ bags => $idf_parser->get_bags(),
+ source_directory => $self->get_source_directory(),
+ in_relaxed_mode => $self->get_in_relaxed_mode(),
+ skip_datafiles => $self->get_skip_datafiles(),
+ });
+
+ $datafiles = $self->parse_sdrf( $sdrf_parser );
+ $self->add_all_mage_objects( $experiment, $sdrf_parser );
+ }
+
+ return wantarray
+ ? ( $datafiles, $self->get_bags(), $self->get_mage() )
+ : $self->get_mage();
+}
+
+sub write_mageml {
+
+ my ( $self ) = @_;
+
+ my $output_file;
+ unless ( $output_file = $self->get_output_file() ) {
+ croak("Error: called write_mageml without setting output_file.");
+ }
+
+ if ( my $target = $self->get_target_directory() ) {
+ $output_file = File::Spec->catfile( $target, basename($output_file) );
+ }
+
+ my $mage;
+ unless ( $mage = $self->get_mage() ) {
+ $mage = $self->parse();
+ }
+
+ # Construct the MAGE structure
+ my $mage_identifier = sprintf(
+ "MAGE:%s:%s v%s:%s",
+ $self->get_authority(),
+ $self->get_namespace(),
+ $VERSION,
+ $self->get_expt_accession(),
+ );
+ $mage->identifier( $mage_identifier );
+
+ # Process any external DesignElementDimension filehandles.
+ my ( $ded_fh, $dummy_id );
+ if ( $self->get_ded_count() ) {
+
+ $ded_fh = $self->get_ded_filehandle();
+
+ # Create a placeholder for the insertion of DEDs
+ $dummy_id = 'DUMMY';
+ my $dummy_FD = Bio::MAGE::BioAssayData::FeatureDimension->new(
+ identifier => $dummy_id,
+ name => $dummy_id,
+ );
+ $mage->add_objects( [$dummy_FD] );
+ }
+ else {
+ print STDOUT ("No DesignElementDimension found.\n");
+ }
+
+ # Write out everything but the DED
+ warn ("Writing out initial MAGE-ML...\n");
+
+ my $mage_version = 1.1;
+ my $data_format = 'tab delimited';
+ my $mage_fh = IO::File->new_tmpfile;
+ write_mage( $mage, $mage_fh, $data_format, $mage_version );
+
+ # Rewind the temporary filehandle
+ seek( $mage_fh, 0, 0 );
+
+ # Fix the DED using classical text processing (lower memory overhead)
+ print STDOUT ("Inserting DesignElementDimension where found...\n");
+ my $output_fh = IO::File->new( $output_file, '>' )
+ or die(
+ sprintf(
+ "Error: cannot open output file %s: $!\n",
+ $output_file,
+ ));
+
+ postprocess_mage(
+ $ded_fh,
+ $mage_fh,
+ $output_fh,
+ $dummy_id,
+ $self->get_native_filetypes(),
+ );
+
+ return;
+}
+
+sub get_id_prefix : RESTRICTED { # FIXME consider ways to combine
+ # this with the same TabFile method.
+
+ my ( $self ) = @_;
+
+ return sprintf( "%s:%s", $self->get_authority(), $self->get_namespace() );
+}
+
+###################
+# Private methods #
+###################
+
+sub initialize_logfiles : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Default to using the IDF filename; only one of these should be
+ # set anyway.
+ my $sourcefile = $self->get_idf() || $self->get_magetab_doc();
+
+ # Sort out our log files.
+ my $logfile_string = File::Spec->catfile(
+ $self->get_target_directory(),
+ basename($sourcefile),
+ );
+ $logfile_string =~ s/\.\w{3,4}$//; # strip off the extension
+ my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+ $self->localize_logfiles({
+ directory => $dir,
+ volume => $vol,
+ });
+
+ return;
+}
+
+sub parse_idf : PRIVATE {
+
+ my ( $self, $idf_parser ) = @_;
+
+ # Add the protocol accession reassignment service if we've got
+ # one.
+ if ( my $service = $self->generate_prot_accn_service() ) {
+ $idf_parser->set_protocol_accession_service( $service );
+ }
+
+ my ( $mage, $experiment, $sdrfs ) = $idf_parser->parse();
+
+ $self->set_sdrfs($sdrfs || []);
+ $self->set_mage($mage);
+
+ return $experiment;
+}
+
+sub parse_sdrf : PRIVATE {
+
+ my ( $self, $sdrf_parser ) = @_;
+
+ my @datafiles = @{ $sdrf_parser->parse() };
+
+ $self->set_bags( $sdrf_parser->get_bags() );
+
+ # Process the datafiles here.
+ if ( $self->get_skip_datafiles() ) {
+ $self->logprint('magetab', "Skipping Data files...\n\n");
+
+ # E.g. CEL, CHP; recognised solely by filename extension.
+ $self->set_native_filetypes(
+ $sdrf_parser->get_native_filetypes(),
+ );
+ }
+ else {
+ $self->logprint('magetab', "Processing Data files...\n\n");
+
+ $self->encode_datafiles( \@datafiles );
+ }
+
+ return \@datafiles;
+}
+
+sub add_all_mage_objects : PRIVATE {
+
+ my ( $self, $experiment, $sdrf_parser ) = @_;
+
+ my $mage = $self->get_mage();
+
+ foreach my $bag ( values %{ $self->get_bags() } ) {
+ $mage->add_objects( $bag->() );
+ }
+
+ $experiment->setBioAssays( $sdrf_parser->get_all_bioassays() );
+ $experiment->setBioAssayData( $sdrf_parser->get_all_bioassaydata() );
+
+ $self->set_mage($mage);
+
+ return;
+}
+
+sub encode_datafiles : PRIVATE {
+
+ my ( $self, $datafiles ) = @_;
+
+ my $parser = ArrayExpress::Datafile::Parser->new({
+ namespace => $self->get_id_prefix() . ":" . $self->get_expt_accession(),
+ include_known_qts => $self->get_include_default_qts(),
+ quantitation_types => $self->get_qt_filename(),
+ use_binary_datafiles => ! $self->get_use_plain_text(),
+ is_standalone => $self->get_is_standalone(),
+ output_directory => $self->get_target_directory(),
+ source_directory => $self->get_source_directory(),
+ error_fh => $self->log_fh('magetab'),
+ allow_undef_qts => $self->get_keep_all_qts(),
+ protocol_bag => $self->get_bags->{protocol},
+ parameter_bag => $self->get_bags->{parameter},
+ reporter_prefix => $self->get_reporter_prefix(),
+ compseq_prefix => $self->get_compseq_prefix(),
+ ignore_size_limits => $self->get_ignore_size_limits(),
+ clobber => $self->get_clobber(),
+ });
+
+ foreach my $file ( @{ $datafiles } ) {
+
+ # Parse the file, generate the DED and extract QTD
+ # info. Output the stripped data file.
+ $parser->parse( $file );
+
+ # If the parser has reset clobber, note it here.
+ $self->set_clobber( $parser->get_clobber() );
+
+ my $data = $file->get_mage_badata();
+
+ # DesignElementDimension.
+ if ( $file->get_ded_type() ) {
+
+ my $dim_class = sprintf("Bio::MAGE::BioAssayData::%sDimension",
+ $file->get_ded_type());
+
+ my $ded;
+ eval {
+ $ded = $dim_class->new(
+ identifier => $file->get_ded_identifier(),
+ );
+ } or croak(
+ sprintf("Error: Unrecognized DED type: %s\n",
+ $file->get_ded_type())
+ );
+ $data->setDesignElementDimension($ded) if $data;
+ }
+
+ # Process data matrices and per-hyb data a little differently.
+ if ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE()
+ || $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ # Process this later, after all the per-hyb objects have
+ # been created.
+ my $identifier_template
+ = sprintf ("%s:%s",
+ $self->get_id_prefix(),
+ $self->get_expt_accession());
+
+ my $dm = ArrayExpress::MAGETAB::DataMatrix->new({
+ datafile => $file,
+ id_template => $identifier_template,
+ mage_helper => $self,
+ });
+ $dm->create_mage();
+ }
+ else {
+
+ # Process file as per-hyb data.
+ # QuantitationType and QuantitationTypeDimension.
+ my ( $qtlist, $qtd_key ) = $self->qts_from_names( $file );
+
+ my $qtd = $self->get_bags->{qtd}->(
+ $qtd_key,
+ { qt_list => $qtlist,
+ identifier_template => sprintf("%s:%s.%s",
+ $self->get_id_prefix(),
+ $self->get_expt_accession(),
+ unique_identifier()),
+ }
+ );
+ $file->set_mage_qtd($qtd);
+ $data->setQuantitationTypeDimension($qtd) if $data;
+ }
+ }
+
+ # Store DesignElementDimension and native file info for later
+ # writing out as MAGE-ML in the postprocessing step.
+ $self->set_ded_filehandle( $parser->get_feature_fh() );
+ $self->set_ded_count( $parser->get_ded_count() );
+ $self->set_native_filetypes( $parser->get_cel_types() );
+
+ return;
+}
+
+sub qts_from_names {
+
+ my ( $self, $file, $qt_names ) = @_;
+
+ my $bag_of = $self->get_bags();
+
+ $qt_names ||= $file->get_column_headings();
+
+ my ( @qtd_qtlist, $qtd_key );
+ my ( $QT_prefix, $software );
+
+ # FIXME maybe either use dm_chip_type or qt_type here to detect
+ # alternative Affy data (e.g. data matrix). At the moment Affy
+ # data matrix QTs are non-standard format.
+ if ( $file->get_format_type() eq 'Affymetrix' ) {
+ $QT_prefix = 'Affymetrix:QuantitationType:';
+ $software = 'Affymetrix';
+ }
+ elsif ( my ( $type, $qt_namespace )
+ = ( $file->get_qt_type =~ m{\A (.*?) \[ (.*) \] \z}xms ) ) {
+ $QT_prefix = "${qt_namespace}:${type}_QuantitationType:";
+ $software = $type;
+ }
+ else {
+ $QT_prefix = sprintf('Unknown:%s_QuantitationType:', $file->get_qt_type());
+ $software = $file->get_qt_type() eq 'Ambiguous'
+ ? 'Unknown'
+ : $file->get_qt_type();
+ }
+
+ foreach my $heading (@$qt_names) {
+
+ my $identifier = $QT_prefix . $heading;
+ my $quantitationtype = $bag_of->{quantitationtype}->(
+ $identifier,
+ { identifier => $identifier,
+ prefix => $QT_prefix,
+ name => $heading,
+ software => $software,
+ data_metrics => $file->get_data_metrics(),
+ container => $bag_of->{quantitationtype},
+ }
+ );
+ push( @qtd_qtlist, $quantitationtype );
+
+ $qtd_key .= $heading;
+
+ }
+
+ # Quick check for sane QTs (This error should no longer be triggered
+ # by missing confidence indicator targets - see MAGE.pm).
+ foreach my $qt ( @{ $bag_of->{quantitationtype}->() } ) {
+ unless ( $qt->getIdentifier ) {
+ print STDERR (<<'END_ERROR');
+ERROR: At least one QuantitationType found lacking an identifier.
+Please check that the confidence indicator targets in your QT file
+are represented in the data files.
+
+END_ERROR
+
+ croak( qq{Error occurred at data file }
+ . $file->get_name()
+ . q{QT name: "}
+ . $qt->getName()
+ . q{"} );
+ }
+ }
+
+ $qtd_key ||= 'NULL'; # In case of no QTs, e.g. missing file
+
+ return ( \@qtd_qtlist, $qtd_key );
+}
+
+sub generate_prot_accn_service : PRIVATE {
+
+ # If configured to do so will return a coderef suitable for
+ # passing to MAGETAB::IDF::protocol_accession_service.
+ my ( $self ) = @_;
+
+ # Skip this if we want to keep our original accessions.
+ return if $self->get_keep_protocol_accns();
+
+ # Use user-specified service if available.
+ if ( my $coderef = $self->get_protocol_accession_service() ) {
+ return $coderef;
+ }
+
+ if ( $CONFIG->get_AUTOSUBS_DSN() ) {
+
+ require ArrayExpress::AutoSubmission::DB::Protocol;
+
+ ArrayExpress::AutoSubmission::DB::Protocol->accession_prefix(
+ $self->get_protocol_accession_prefix(),
+ );
+
+ return sub {
+
+ my ($user_accession, $expt_accession) = @_;
+ warn(
+ sprintf(
+ "Assigning accession for protocol %s using autosubmission system...\n",
+ $user_accession,
+ )
+ );
+
+ # Arguments are user_accession, expt_accession, protocol name.
+ my $new_accession
+ = ArrayExpress::AutoSubmission::DB::Protocol->reassign_protocol(
+ $user_accession,
+ $expt_accession,
+ $user_accession,
+ );
+
+ return $new_accession;
+ }
+ }
+ else {
+ return;
+ }
+}
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../T2M_logo.png"
+ border="0" height="50" alt="Tab2MAGE logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: MAGETAB.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::MAGETAB - A parser class for MAGE-TAB documents.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::MAGETAB;
+ my $magetab = ArrayExpress::MAGETAB->new({
+ idf => $idf_file,
+ target_directory => $dir,
+ expt_accession => $accn,
+ });
+
+ $magetab->write_mageml();
+
+=head1 DESCRIPTION
+
+This module acts as a front end to the MAGE-TAB parsing API. Parser
+objects are instantiated with a number of attributes to control how
+the MAGE-TAB document is parsed. Support is only provided for IDF and
+SDRF documents at present; it is anticipated that the parser will be
+extended to support ADF at a later date.
+
+Currently the parser is built using a MAGEv1.1 object model to store
+the MAGE-TAB metadata. It is envisioned that this dependence on the
+Bio::MAGE modules may be removed once a full MAGE-TAB object model is
+agreed upon by the community.
+
+To simplify the process of data submission, ArrayExpress has
+introduced a new flavour of MAGE-TAB in which the IDF and SDRF
+sections are combined into a single worksheet. This parser supports
+both MAGE-TAB v1.1 documents (with separate IDF and SDRF) and these
+combined documents.
+
+=head1 METHODS
+
+=over 2
+
+=item C<new>
+
+Object constructor. This recognises the following attributes:
+
+=over 4
+
+=item C<idf>
+
+The path of the IDF file with which to start parsing.
+
+=item C<magetab_doc>
+
+The path of a combined IDF+SDRF file to parse.
+
+=item C<output_file>
+
+The name of the output MAGE-ML file.
+
+=item C<namespace>
+
+The namespace to use in MAGE identifier creation.
+
+=item C<authority>
+
+The authority to use in MAGE identifier creation.
+
+=item C<expt_accession>
+
+The accession number assigned to the experiment.
+
+=item C<target_directory>
+
+The directory into which to write the output files.
+
+=item C<source_directory>
+
+The directory which contains all data and SDRF files.
+
+=item C<is_standalone>
+
+A flag indicating whether the script is able to connect to
+ArrayExpress to retrieve array design information. It is sometimes
+desirable to skip these downloads, which can be quite large.
+
+=item C<qt_filename>
+
+QuantitationType file. This option allows you to specify a custom
+QuantitationType definition file to override those defined as part of
+the Tab2MAGE package. See L<ArrayExpress::Datafile::QT_list> for more
+information.
+
+=item C<include_default_qts>
+
+This option can be used in conjunction with C<qt_filename> to indicate
+that the QuantitationType listing from the Tab2MAGE package itself
+should be included in the lists of known QuantitationTypes used in
+data file parsing. The default behaviour is to deactivate these known
+QTs if a custom QT file is to be used.
+
+=item C<keep_all_qts>
+
+A flag indicating whether unrecognised QuantitationTypes in data files
+should be kept or not. The default behaviour is to strip unrecognised
+columns out of the data files.
+
+=item C<reporter_prefix>
+
+The prefix to be used during Reporter identifier construction. This
+prefix is prepended to the identifiers listed in the data files.
+
+=item C<compseq_prefix>
+
+The prefix to be used during CompositeElement (CompositeSequence)
+identifier construction. This prefix is prepended to the identifiers
+listed in the data files.
+
+=item C<protocol_accession_service>
+
+A code reference used to reassign protocol accessions. See
+L<PROTOCOL_ACCESSIONS>, below.
+
+=item C<protocol_accession_prefix>
+
+The prefix to be used for protocol accession creation, when the
+autosubmissions system is in use. See L<PROTOCOL_ACCESSIONS>, below.
+
+=item C<keep_protocol_accns>
+
+A flag indicating whether the protocols in the IDF should be assigned
+new accession numbers. This option overrides C<protocol_accession_service>.
+
+=item C<use_plain_text>
+
+Some file formats are only supported in their native forms by
+ArrayExpress. Nonetheless, this package can parse some of these data
+formats into tab-delimited representations fully encoded in the
+MAGE-ML document (examples include Nimblegen data, Affymetrix CEL
+files).
+
+=item C<skip_datafiles>
+
+This option tells the parser to skip attempting to read the data files
+referenced by a given MAGE-TAB document, and instead attempts to
+generate MAGE in their absence. This option is particularly useful for
+unsupported data file formats.
+
+=item C<ignore_size_limits>
+
+The Tab2MAGE configuration file allows the user to set maximum data
+file sizes for parsing and web download, to provide some protection
+from overloading the system in a production pipeline setting. To
+temporarily ignore these size limits, use this option.
+
+=item C<in_relaxed_mode>
+
+A flag indicating whether to allow minor errors during parsing. At the
+moment the only errors which are ignored by this option are C<Term
+Source REF>, C<Protocol REF>, C<Parameter Value []> and C<Factor Value
+[]> columns which reference Names which have not been defined in the
+IDF.
+
+=item C<clobber>
+
+Flag indicating whether or not to overwrite existing files without
+prompting the user.
+
+=back
+
+=item C<parse>
+
+Starts the MAGE-TAB parse and loads the document into memory.
+
+=item C<write_mageml>
+
+Writes out MAGE-ML corresponding to the input MAGE-TAB document. If
+the MAGE-TAB has not yet been parsed, C<parse()> is called
+automatically.
+
+=back
+
+=head1 PROTOCOL_ACCESSIONS
+
+The parser provides a set of callbacks which can be used to assign
+MAGE-TAB Protocol Names to unique accessions at the point of
+parsing. If the autosubmissions system has been set up and configured,
+then the parser will default to using that mechanism to assign
+protocol accessions. If you wish to use your own service, you may use
+the C<protocol_accession_service> and C<protocol_accession_prefix>
+attributes to control this. The C<protocol_accession_service> should
+point to a code reference which will accept two arguments: (a) the
+Protocol Name as given in the IDF, and (b) the experiment
+accession. The code reference should return a unique accession which
+will then be assigned to the protocol in the output MAGE-ML.
+
+If the autosubmissions system is to be used, the
+C<protocol_accession_prefix> attribute must be set, e.g. to "P-MTAB-".
+
+=head1 AUTHOR
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2008.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/Checker.pm b/lib/ArrayExpress/MAGETAB/Checker.pm
new file mode 100644
index 0000000..7d0962f
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/Checker.pm
@@ -0,0 +1,2034 @@
+#!/usr/bin/env perl
+#
+# Module to provide basic methods used by the expt_check script.
+#
+# Tim Rayner 2005, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: Checker.pm 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+=pod
+
+=begin html
+
+ <div><a name="top"></a>
+ <table class="layout">
+ <tr>
+ <td class="whitetitle" width="100">
+ <a href="../../../index.html">
+ <img src="../../MAGETAB_logo.png"
+ border="0" height="50" alt="MAGE-TAB logo"></td>
+ </a>
+ <td class="pagetitle">Module detail: Checker.pm</td>
+ </tr>
+ </table>
+ </div>
+
+=end html
+
+=head1 NAME
+
+ArrayExpress::MAGETAB::Checker - MAGE-TAB experiment checking.
+
+=head1 SYNOPSIS
+
+ use ArrayExpress::MAGETAB::Checker;
+
+ my $checker = ArrayExpress::MAGETAB::Checker->new({
+ idf => $magetab_idf,
+ });
+
+ $checker->check();
+
+=head1 DESCRIPTION
+
+This module provides the basic operations needed for checking a
+MAGE-TAB document and associated data files.
+
+=head1 OPTIONS
+
+The following options must be used in addition to those provided by the
+parent class (see L<ArrayExpress::Curator::ExperimentChecker>):
+
+=over 2
+
+=item C<idf>
+
+The MAGE-TAB IDF filename to be checked.
+
+=back
+
+=head1 TESTS
+
+The following tests are performed by this module, with output
+printed to the error and/or report logs:
+
+=over 4
+
+=item B<IDF Contents>
+
+Checks that at least one of each of the following items are present in
+the IDF, warning the user if any are missing:
+
+ Experimental Factor
+ Contact
+ Publication
+ Protocol
+ SDRF
+
+=item B<Experiment info>
+
+Warns the user if any of the following pieces of experiment annotation
+are missing:
+
+ Experiment Design
+ Release Date
+ Title
+ Description
+
+=item B<Term Sources>
+
+Checks all instances of Term Source REFs, and warns the user if an
+undeclared Term Source Name has been used. Also notifies the user if a
+Term Source declared in the IDF has not been used in the SDRF.
+
+=item B<Experimental Factors>
+
+Checks all SDRF Factor Value columns, and warns the user if an
+undeclared Experimental Factor Name has been used. Also notifies the
+user if a Experimental Factor declared in the IDF has not been used in
+the SDRF.
+
+=item B<Undeclared Experimental Factors>
+
+Checks all material characteristics against the factors described by
+the document, alerting the user if any such characteristics vary
+during the experiment without having been declared as an experimental
+factor.
+
+=item B<Source Characteristics>
+
+Checks that each Source material has at least some annotation.
+This is not, however, a full test of MIAME compliance.
+
+=item B<PubMed ID>
+
+Warns the user if a non-numeric PubMed ID has been entered.
+
+=item B<Submission and release dates>
+
+Warns the user if experiment or release dates are in an incorrect
+format.
+
+=item B<Submitter>
+
+Checks that a submitter contact has been provided, and that their last
+name and email address have both been included in the IDF.
+
+=item B<Protocols>
+
+Checks that all protocols referenced in the Hybridization spreadsheet
+section are declared in the Protocol section (and vice versa).
+
+=item B<Protocol description length>
+
+Warns if any of the protocol texts seem too brief.
+
+=item B<Protocol usage>
+
+Checks that a protocol has been attached to each step of the
+experiment (biomaterial treatments, hybridization, scanning and normalization).
+
+=item B<Parameters>
+
+Checks that all parameters referenced in the Hybridization spreadsheet
+section are declared in the Protocol section (and vice versa).
+
+=item B<Files>
+
+Confirms that each data file is associated with an array design (i.e.,
+Array Design REF or Array Design File), and at least one experimental
+factor value.
+
+=item B<Experiment Design Graph>
+
+Creates the links between sample, extract, labeled extract and
+hybridization, and writes a biomaterials log file listing the
+numbers of each. If the Graphviz software is installed
+(http://www.graphviz.org) then the script will use the 'dot'
+program to produce a PNG format graph showing how the
+various components relate to each other.
+
+=back
+
+Tim Rayner (rayner at ebi.ac.uk), ArrayExpress team, EBI, 2007.
+
+Acknowledgements go to the ArrayExpress curation team for feature
+requests, bug reports and other valuable comments.
+
+=begin html
+
+<hr>
+<a href="http://sourceforge.net">
+ <img src="http://sourceforge.net/sflogo.php?group_id=120325&type=2"
+ width="125"
+ height="37"
+ border="0"
+ alt="SourceForge.net Logo" />
+</a>
+
+=end html
+
+=cut
+
+package ArrayExpress::MAGETAB::Checker;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+use List::Util qw(first);
+use List::MoreUtils qw(pairwise);
+use File::Spec;
+use File::Basename;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(
+ get_filepath_from_uri
+ $RE_EMPTY_STRING
+ $RE_COMMENTED_STRING
+ $RE_SURROUNDED_BY_WHITESPACE
+);
+
+require ArrayExpress::Datafile;
+require ArrayExpress::MAGETAB;
+require ArrayExpress::MAGETAB::Checker::IDF;
+require ArrayExpress::MAGETAB::SDRF;
+
+use ArrayExpress::Curator::Report qw(
+ format_description
+);
+
+use base qw(ArrayExpress::Curator::ExperimentChecker);
+
+# The entry-point IDF file.
+my %idf : ATTR( :get<idf>, :init_arg<idf>, :default<undef> );
+
+# Attributes used for checking combined IDF/SDRF documents.
+my %magetab_doc : ATTR( :name<magetab_doc>, :default<undef> );
+my %idf_filehandle : ATTR( :name<idf_filehandle>, :default<undef> );
+my %sdrf_filehandle : ATTR( :name<sdrf_filehandle>, :default<undef> );
+my %eol_char : ATTR( :name<eol_char>, :default<undef> );
+
+# Flag indicating we can overwrite files.
+my %clobber : ATTR( :name<clobber>, :default<0> );
+
+# Used to store Names defined in the IDF.
+my %termsources : ATTR( :get<termsources>, :set<termsources>, :default<{}> );
+my %factors : ATTR( :get<factors>, :set<factors>, :default<{}> );
+my %protocols : ATTR( :get<protocols>, :set<protocols>, :default<{}> );
+my %parameters : ATTR( :get<parameters>, :set<parameters>, :default<{}> );
+
+# Used to accumulate SDRF info for passing back to the data file
+# checks.
+my %file_info : ATTR( :get<file_info>, :set<file_info>, :default<[]> );
+my %hybridizations : ATTR( :get<hybridizations>, :set<hybridizations>, :default<{}> );
+my %scans : ATTR( :get<scans>, :set<scans>, :default<{}> );
+my %normalizations : ATTR( :get<normalizations>, :set<normalizations>, :default<{}> );
+
+# Used to store per-row SDRF info.
+my %row_files : ATTR( :get<row_files>, :set<row_files>, :default<[]> );
+my %row_array : ATTR( :get<row_array>, :set<row_array>, :default<undef> );
+my %row_factors : ATTR( :get<row_factors>, :set<row_factors>, :default<[]> );
+
+# Used to check usage of IDF objects in the SDRF.
+my %protocols_used : ATTR( :get<protocols_used>, :set<protocols_used>, :default<{}> );
+my %parameters_used : ATTR( :get<parameters_used>, :set<parameters_used>, :default<{}> );
+my %factors_used : ATTR( :get<factors_used>, :set<factors_used>, :default<{}> );
+my %termsources_used : ATTR( :get<termsources_used>, :set<termsources_used>, :default<{}> );
+
+# Used to track Characteristics for each material, to check for consistency.
+my %current_material : ATTR( :get<current_material>, :set<current_material>, :default<undef> );
+my %char_cache : ATTR( :get<char_cache>, :set<char_cache>, :default<{}> );
+my %reported_cache : ATTR( :get<reported_cache>, :set<reported_cache>, :default<{}> );
+
+# Used to store the MAGE container hashrefs for e.g. visualization.
+my %mage_generator : ATTR( :get<mage_generator>, :set<mage_generator>, :default<undef> );
+
+sub START {
+ my ( $self, $id, $args ) = @_;
+
+ unless ( defined( $idf{$id} || defined( $magetab_doc{$id} ) ) ) {
+ croak("Error: No IDF filename passed to MAGE-TAB checker.");
+ }
+
+ # Localise to CWD, or value of get_log_to_current_dir.
+ my $inputfile = $self->get_idf() || $self->get_magetab_doc();
+ my $logfile_string = File::Spec->rel2abs($inputfile);
+ $logfile_string =~ s/\.\w{3,4}$//; # strip off the extension
+ my ( $vol, $dir, $name ) = File::Spec->splitpath($logfile_string);
+ $self->localize_logfiles({
+ directory => $dir,
+ volume => $vol,
+ name => $name,
+ });
+
+ return;
+}
+
+sub get_files_and_annotation : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ print STDOUT ("Running in MAGE-TAB mode...\n");
+
+ $self->logprint( 'error', "* MAGE-TAB mode *\n\n" );
+ $self->logprint( 'report', "* MAGE-TAB mode *\n\n" );
+
+ # Sort out any combined MAGE-TAB IDF+SDRF documents here.
+ if ( $self->get_magetab_doc() ) {
+ my ( $idf_fh, $sdrf_fh, $eol_char );
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ local $EVAL_ERROR;
+ eval {
+ ( $idf_fh, $sdrf_fh, $eol_char ) =
+ ArrayExpress::MAGETAB->preprocess_magetab_doc(
+ $self->get_magetab_doc(),
+ );
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+ if ( $EVAL_ERROR ) {
+ $self->logprint(
+ 'error',
+ "ERROR: Failed to parse IDF and SDRF section headings: $EVAL_ERROR\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ return;
+ }
+ else {
+ $self->set_idf_filehandle( $idf_fh );
+ $self->set_sdrf_filehandle( $sdrf_fh );
+ $self->set_eol_char( $eol_char );
+ }
+ }
+
+ # Check the passed IDF/SDRF and set up our list of Datafile
+ # objects.
+
+ # Check the document naively before we invoke the main MAGE export
+ # parser, which will most likely crash in any case.
+ $self->logprint_line( 'error', 'Naive parsing START' );
+
+ $self->parse_naively();
+
+ $self->logprint_line( 'error', 'Naive parsing END' );
+
+ # Attempt to read in the document using the parser. This will fail
+ # unless the document is structurally valid.
+ $self->logprint_line( 'error', 'Full MAGE-TAB parsing START' );
+
+ # Check if our error level is at ERROR_PARSEFAIL before attempting
+ # MAGE creation.
+ my ( $filelist, $bags, $mage );
+ if ( $self->get_error() & $CONFIG->get_ERROR_PARSEFAIL() ) {
+ $self->logprint(
+ 'error',
+ "ERROR: Previous checks have determined that a full MAGE-TAB"
+ . " parse will fail. Skipping and attempting to continue.\n",
+ );
+ }
+ else {
+
+ # Attempt to generate MAGE.
+ ( $filelist, $bags, $mage ) = $self->generate_mage();
+ }
+
+ $self->logprint_line( 'error', 'Full MAGE-TAB parsing END' );
+
+ unless ( $mage && $bags ) {
+
+ # If MAGE creation failed, fall back to the naive parse for
+ # files to check; hyb, scan and norm ids.
+ $filelist = $self->get_files_from_naive_parse();
+ }
+
+ # Rewrite Datafile objects which use URIs to reference files.
+ unless ( $self->get_skip_data_checks() ) {
+ foreach my $file ( @$filelist ) {
+ $self->rewrite_datafile_uri( $file );
+ }
+ }
+
+ # Keys used to map FGEM column headings to hybs, scans, norms;
+ # also stores links to the actual objects as values (PBA for hyb,
+ # DBA for norm, BAT for scan; these are checked later for
+ # protocolapps).
+ my $normalizations = $self->get_normalizations();
+ my $scans = $self->get_scans();
+ my $hybridizations = $self->get_hybridizations();
+
+ $self->logprint_line( 'error', 'ADF parsing START' );
+
+ # Populate the $file->get_adf_features() hashrefs within @$filelist
+ if ( $self->get_adf_filename || $self->get_array_accession ) {
+ $self->cache_user_supplied_arrays( $filelist );
+ }
+ else {
+
+ # just populate the keys for now
+ my %array_designs;
+ foreach my $file (@$filelist) {
+ unless ( $file->get_is_exp() ) {
+ $array_designs{ $file->get_array_design_id() }++;
+ }
+ }
+
+ # populate the array designs hashref
+ unless ( $self->get_is_standalone() ) {
+
+ my @accno_table;
+ foreach my $accno ( keys %array_designs ) {
+
+ # ADF retrieval crashes when AE is down; we trap that here.
+ $self->get_ae_arraydesign({
+ accession => $accno,
+ });
+ }
+ }
+ }
+ $self->populate_file_arraydesigns( $filelist );
+
+ $self->logprint_line( 'error', 'ADF parsing END' );
+
+ # FIXME consider returning $scans as well.
+ return ( $filelist, $hybridizations, $normalizations );
+}
+
+sub generate_mage : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my ( $filelist, $bags, $mage, $magetab );
+
+ my %common_opts = (
+ authority => '{AUTHORITY}',
+ namespace => '{NAMESPACE}',
+ expt_accession => '{ACCESSION}',
+ target_directory => ( $self->get_log_to_current_dir() || q{.} ),
+ source_directory => $self->get_source_directory(),
+ is_standalone => $self->get_is_standalone(),
+ qt_filename => $self->get_qt_filename(),
+ include_default_qts => $self->get_include_default_qts(),
+ keep_protocol_accns => 1,
+ skip_datafiles => 1,
+ clobber => $self->get_clobber(),
+ );
+
+ # Don't propagate fatal errors up the call stack.
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ local $EVAL_ERROR;
+
+ # Either one or the other of the following should be valid.
+ if ( my $idf = $self->get_idf() ) {
+ $magetab = ArrayExpress::MAGETAB->new({
+ idf => $idf,
+ %common_opts,
+ });
+
+ # Attempt to generate MAGE.
+ eval {
+ ( $filelist, $bags, $mage ) = $magetab->parse();
+ };
+ }
+ elsif ( my $doc = $self->get_magetab_doc() ) {
+ $magetab = ArrayExpress::MAGETAB->new({
+ magetab_doc => $doc,
+ %common_opts,
+ });
+
+ # Attempt to generate MAGE.
+ eval {
+ ( $filelist, $bags, $mage ) = $magetab->parse_magetab_doc();
+ };
+ }
+ else {
+ croak("ERROR: No IDF or combined MAGE-TAB document to parse.");
+ }
+
+ # Reinstate our DIE sighandler.
+ $SIG{__DIE__} = $sighandler if $sighandler;
+
+ # Reset our clobber value, based on any changes in the magetab
+ # parser value.
+ $self->set_clobber( $magetab->get_clobber() );
+
+ if ( $EVAL_ERROR ) {
+ $self->logprint(
+ 'error',
+ "ERROR: MAGE-TAB document is not structurally valid."
+ . " Parser fails with this error: $EVAL_ERROR\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ else {
+
+ # Store this object for later visualization.
+ $self->set_mage_generator( $magetab );
+
+ # Record hyb, scan and normalization names for later use in
+ # checking DataMatrices.
+ my (%hybridization, %scan, %normalization);
+ foreach my $pba ( @{ $bags->{'pba'}->() } ) {
+ $hybridization{$pba->getName()} = $pba;
+
+ # Single-channel scan BATs. We assume that in such cases
+ # the extra two-color PBAs have been pruned and we can
+ # assume a single BAT which represents the Scan event.
+ if ( scalar @{ $pba->getChannels() || [] } == 1 ) {
+ foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+ $scan{$bat->getName()} = $bat;
+ }
+ }
+ }
+ foreach my $pba ( @{ $bags->{'extended_pba'}->() } ) {
+
+ # Two-channel scan BATs. We look for PBAs pointing to
+ # themselves as indication that this is the last PBA in
+ # the PBA -> PBA -> PBA -> MBA chain.
+ foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+ if ( my $target = $bat->getTarget() ) {
+ if ( $pba->getIdentifier() eq $target->getIdentifier() ) {
+
+ # End of the PBA chain, must therefore be the Scan PBA.
+ $scan{$bat->getName()} = $bat;
+ }
+ }
+ }
+ }
+ foreach my $dba ( @{ $bags->{'dba'}->() } ) {
+ $normalization{$dba->getName()} = $dba;
+ }
+ $self->set_hybridizations(\%hybridization);
+ $self->set_scans(\%scan);
+ $self->set_normalizations(\%normalization);
+
+ # Check Labeled Extract - Label association is present.
+ $self->check_mage_labeledextracts( $bags->{'labeledextract'}->() );
+
+ # Check annotation of the Sources.
+ $self->check_mage_bmchars( $bags->{'source'}->() );
+
+ # Check that all variation in BMCs are also FVs.
+ $self->check_bmchars_against_fvs( $bags );
+
+ # FIXME check protocols attached at various stages.
+ $self->check_protocol_attachments( $bags );
+
+ # Generate SDRF workflow in biomaterials log.
+ $self->print_sdrf_workflow( $bags );
+ }
+
+ # $mage should be undef on failure.
+ return ( $filelist, $bags, $mage );
+}
+
+sub rewrite_datafile_uri : PRIVATE {
+
+ my ( $self, $file ) = @_;
+
+ my $path;
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ local $EVAL_ERROR;
+ eval {
+ $path = get_filepath_from_uri(
+ $file->get_path(),
+ $self->get_source_directory(),
+ );
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+ if ( $EVAL_ERROR ) {
+ $self->logprint(
+ 'error',
+ $EVAL_ERROR,
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ else {
+ $file->set_path( $path );
+ $file->set_name( basename( $path ) );
+ }
+}
+
+sub parse_naively : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # We start with the IDF.
+ my $sdrfs = $self->parse_idf_naively();
+
+ # Check we're not about to read an OS-level file when we've been told not to.
+ foreach my $sdrf ( @{ $sdrfs } ) {
+ if ( File::Spec->file_name_is_absolute($sdrf)
+ && $self->get_safe_filechecks() ) {
+ $self->logprint(
+ 'error',
+ "ERROR: Absolute SDRF path used in safe file checking mode.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ }
+
+ # This also populates the files attribute with a list of Datafile
+ # objects.
+ $self->parse_sdrfs_naively($sdrfs);
+
+ # Rewind any filehandles (not needed for regular IDF/SDRF). Maybe
+ # consider pos/tell here FIXME?
+ if ( my $idf_fh = $self->get_idf_filehandle() ) {
+ seek($idf_fh, 0, 0)
+ or croak("Error rewinding IDF temporary filehandle: $!");
+ }
+ if ( my $sdrf_fh = $self->get_sdrf_filehandle() ) {
+ seek($sdrf_fh, 0, 0)
+ or croak("Error rewinding SDRF temporary filehandle: $!");
+ }
+
+ return;
+}
+
+sub parse_individual_sdrf_naively : PRIVATE {
+
+ my ( $self, $sdrf_parser ) = @_;
+
+ # Check for the presence of the SDRF, or alternatively a filehandle.
+ my $sdrf = $sdrf_parser->get_sdrf();
+
+ # This will crash if the SDRF isn't present, so we eval it.
+ my $sdrf_fh;
+ eval { $sdrf_fh = $sdrf_parser->get_sdrf_filehandle() };
+
+ unless ( $sdrf_fh ) {
+ $self->logprint(
+ 'error',
+ qq{ERROR: Unable read SDRF file "$sdrf".\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+ return;
+ }
+
+ my $BLANK = $sdrf_parser->get_blank_regexp();
+ my $csv_parser = $sdrf_parser->get_csv_parser();
+
+ # Scan past any empty or commented lines to get to the header row.
+ my $headings;
+ my $header_string;
+ HEADERLINE:
+ while ( $headings = $csv_parser->getline( $sdrf_fh ) ) {
+ $header_string = join( q{}, @$headings ) if $headings;
+ last HEADERLINE if ( $header_string
+ && $header_string !~ $RE_EMPTY_STRING
+ && $header_string !~ $RE_COMMENTED_STRING );
+ }
+
+ # Check we've parsed to the end of the file.
+ my ( $error, $mess ) = $csv_parser->error_diag();
+ unless ( $headings || $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_parser->error_input(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ return;
+ }
+
+ if ( $header_string && $headings && scalar @{ $headings } ) {
+ my $rowchecks = $self->check_sdrf_header( $headings );
+
+ # Read through the rest of the file, checking values as appropriate.
+ FILE_LINE:
+ while ( my $row = $csv_parser->getline( $sdrf_fh ) ) {
+
+ # Reset our per-row trackers.
+ $self->set_row_array(undef);
+ $self->set_row_files([]);
+ $self->set_row_factors([]);
+
+ # Skip empty lines.
+ my $line = join( q{}, @$row );
+ next FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+ # Allow hash comments (FIXME NOT IN SPEC).
+ next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+ # Strip surrounding whitespace from each element.
+ foreach my $element ( @$row ) {
+ $element =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+ }
+
+ # Actually run the row-level checks here. FIXME we might
+ # want to run certain tests even if the value is blank;
+ # e.g. characteristics consistency checking.
+ for ( my $i = 0; $i < scalar @$row; $i++ ) {
+ unless ( $row->[$i] =~ $BLANK ) {
+ my $sub = $rowchecks->[$i];
+ if ( ref $sub eq 'CODE' ) {
+ $sub->( $row->[$i], $headings->[$i] );
+ }
+ }
+ }
+
+ $self->check_file_info();
+ }
+
+ # Check we've parsed to the end of the file.
+ my ( $error, $mess ) = $csv_parser->error_diag();
+ unless ( $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_parser->error_input(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ return;
+ }
+ }
+ else {
+ my $sdrf = $sdrf_parser->get_sdrf() || 'section';
+ $self->logprint(
+ 'error',
+ qq{ERROR: Unable to parse header line of SDRF $sdrf.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ return;
+}
+
+sub parse_sdrfs_naively : PRIVATE {
+
+ my ( $self, $sdrfs ) = @_;
+
+ $self->logprint_line( 'error', ' Naive SDRF parsing START ' );
+
+ if ( $sdrfs && scalar @{ $sdrfs } ) {
+ foreach my $sdrf ( @{ $sdrfs } ) {
+ my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+ sdrf => $sdrf,
+ source_directory => $self->get_source_directory(),
+ });
+ $self->parse_individual_sdrf_naively( $sdrf_parser );
+ }
+ }
+ elsif ( my $sdrf_fh = $self->get_sdrf_filehandle() ) {
+ my $sdrf_parser = ArrayExpress::MAGETAB::SDRF->new({
+ sdrf_filehandle => $sdrf_fh,
+ eol_char => $self->get_eol_char(),
+ source_directory => $self->get_source_directory(),
+ });
+ $self->parse_individual_sdrf_naively( $sdrf_parser );
+ }
+ else {
+ $self->logprint(
+ 'error',
+ "ERROR: No SDRF files or annotation available.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ $self->check_idf_term_usage();
+
+ $self->logprint_line( 'error', ' Naive SDRF parsing END ' );
+
+ return;
+}
+
+sub check_char_against_cache : PRIVATE {
+
+ my ( $self, $value, $heading ) = @_;
+
+ my $cache = $self->get_char_cache();
+ my $reported = $self->get_reported_cache();
+ my $material = $self->get_current_material();
+
+ # Drop out if there's no material for these characteristics (this
+ # is a separate error, and will be reported elsewhere).
+ return unless defined($material);
+
+ # Beware autovivification here (of the $material keys).
+ if ( exists $cache->{ $material }{ $heading }
+ && ! exists $reported->{ $material }{ $heading } ){
+
+ unless ( $cache->{ $material }{ $heading } eq $value ) {
+ $self->logprint(
+ 'error',
+ qq{ERROR: The material "$material" has inconsistent }
+ . qq{characteristics ("$heading") between SDRF rows.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD );
+ $reported->{ $material }{ $heading } = $value;
+ }
+ }
+ else {
+ $cache->{ $material }{ $heading } = $value;
+ }
+
+ $self->set_char_cache( $cache );
+ $self->set_reported_cache( $reported );
+
+ return;
+}
+
+sub check_sdrf_protocol : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ # Check we have a declared protocol.
+ unless ( $self->is_valid_protocol( $name ) ) {
+ $self->logprint(
+ 'error',
+ qq{Warning: Unknown Protocol REF in SDRF: "$name" (must be declared in IDF).\n},
+ );
+
+ # FIXME this should really be PARSEFAIL, but for Protocol REF
+ # with associated Term Source REF this would lead to false
+ # positives. PARSEFAIL will be set if the main parser fails.
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+
+ # Record the protocol usage for later.
+ $self->get_protocols_used()->{$name}++;
+
+ return;
+}
+
+sub check_sdrf_termsource : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ # Check this is a valid term source.
+ unless ( $self->is_valid_termsource( $name ) ) {
+ $self->logprint(
+ 'error',
+ qq{ERROR: Unknown Term Source REF in SDRF: "$name" (must be declared in IDF).\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Record the termsource usage for later.
+ $self->get_termsources_used()->{$name}++;
+
+ return;
+}
+
+sub check_sdrf_factorvalue : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ # Check this is a valid factor name.
+ unless ( $self->is_valid_factor( $name ) ) {
+ $self->logprint(
+ 'error',
+ qq{ERROR: Unknown Experimental Factor in SDRF: "$name" (must be declared in IDF).\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Record the factor usage for later.
+ $self->get_factors_used()->{$name}++;
+
+ return;
+}
+
+sub check_sdrf_parameter : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ # Check this is a valid parameter.
+ unless ( $self->is_valid_parameter( $name ) ) {
+ $self->logprint(
+ 'error',
+ qq{ERROR: Unknown Parameter in SDRF: "$name" (must be declared in IDF).\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Record the parameter usage for later.
+ $self->get_parameters_used()->{$name}++;
+
+ return;
+}
+
+sub add_hybridization : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ $hybridizations{ident $self}{$name}++;
+
+ return;
+}
+
+sub add_scan : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ $scans{ident $self}{$name}++;
+
+ return;
+}
+
+sub add_normalization : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ $normalizations{ident $self}{$name}++;
+
+ return;
+}
+
+sub check_sdrf_header : PRIVATE {
+
+ my ( $self, $headings ) = @_;
+
+ # Used in multiple sets of tests, we define these regexps here only once.
+ my $characteristics_re = qr/ Characteristics? # heading prefix
+ [ ]*
+ \[ [ ]* ([^\]]+?) [ ]* \] # bracketed term
+ /ixms;
+ my $factorvalue_re = qr/ Factor [ ]* Values? # heading prefix
+ [ ]*
+ \[ [ ]* ([^\]]+?) [ ]* \] # bracketed term
+ (
+ [ ]*
+ \( [ ]* ([^\)]+?) [ ]* \) # optional parens term
+ )?
+ /ixms;
+ my $parameter_re = qr/ Parameter [ ]* Values? # heading prefix
+ [ ]*
+ \[ [ ]* ([^\]]+?) [ ]* \] # bracketed term
+ /ixms;
+ my $unit_re = qr/ Unit # heading prefix
+ [ ]*
+ \[ [ ]* ([^\]]+?) [ ]* \] # bracketed term
+ /ixms;
+
+ # Define the tests to be run on header values. Any subs here will
+ # be passed match values $1 and $2 from the key regexps. FIXME
+ # consider validating the ontology terms held in Characteristics[]
+ # and Unit[] column headers.
+ my %header_test = (
+ $characteristics_re => 1,
+ $factorvalue_re => sub { $self->check_sdrf_factorvalue(@_) },
+ $parameter_re => sub { $self->check_sdrf_parameter(@_) },
+ $unit_re => 1,
+ );
+
+ # Define any tests and/or aggregation methods to be run on the
+ # column values. Any subs here will be passed each value from the
+ # column in turn, and the full column header itself.
+ my %recognized = (
+
+ qr/ Source [ ]* Names? /ixms
+ => sub { $self->set_current_material(shift) },
+
+ qr/ Sample [ ]* Names? /ixms
+ => sub { $self->set_current_material(shift) },
+
+ qr/ Extract [ ]* Names? /ixms
+ => sub { $self->set_current_material(shift) },
+
+ qr/ Labell?ed [ ]* Extract [ ]* Names? /ixms
+ => sub { $self->set_current_material(shift) },
+
+ qr/ Term [ ]* Accession [ ]* Numbers? /ixms => 1,
+ qr/ Providers? /ixms => 1,
+ qr/ Material [ ]* Types? /ixms => 1,
+ qr/ Technology [ ]* Types? /ixms => 1,
+ qr/ Labels? /ixms => 1,
+ qr/ Descriptions? /ixms => 1,
+ qr/ Performers? /ixms => 1,
+ qr/ Dates? /ixms => 1,
+ qr/ Image [ ]* Files? /ixms => 1,
+
+ qr/ Hybridi[sz]ation [ ]* Names? /ixms
+ => sub { $self->add_hybridization(@_) },
+
+ qr/ Assay [ ]* Names? /ixms
+ => sub { $self->add_hybridization(@_) },
+
+ qr/ Scan [ ]* Names? /ixms
+ => sub { $self->add_scan(@_) },
+
+ qr/ Normali[sz]ation [ ]* Names? /ixms
+ => sub { $self->add_normalization(@_) },
+
+ qr/ Array [ ]* Design [ ]* Files? /ixms
+ => sub { $self->protest_unsupported('Array Design File') },
+
+ qr/ Array [ ]* Data [ ]* Files? /ixms
+ => sub { $self->add_row_file('raw', @_) },
+
+ qr/ Derived [ ]* Array [ ]* Data [ ]* Files? /ixms
+ => sub { $self->add_row_file('normalized', @_) },
+
+ qr/ Array [ ]* Data [ ]* Matrix [ ]* Files? /ixms
+ => sub { $self->add_row_matrix_file($CONFIG->get_RAW_DM_FILE_TYPE(), @_) },
+
+ qr/ Derived [ ]* Array [ ]* Data [ ]* Matrix [ ]* Files? /ixms
+ => sub { $self->add_row_matrix_file($CONFIG->get_FGEM_FILE_TYPE(), @_) },
+
+ # Only check Term Source REF if the optional namespace is omitted.
+ qr/ Term [ ]* Source [ ]* REFs? # main heading only
+ /ixms
+ => sub { $self->check_sdrf_termsource(@_) },
+
+ qr/ Term [ ]* Source [ ]* REFs? # main heading, and
+ [ ]* :[^\t\"]+ # optional namespace
+ /ixms => 1,
+
+ # Only check Protocol REF if the optional namespace is omitted.
+ qr/ Protocol [ ]* REFs? # main heading only
+ /ixms
+ => sub { $self->check_sdrf_protocol(@_) },
+
+ qr/ Protocol [ ]* REFs? # main heading, and
+ [ ]* :[^\t\"]+ # optional namespace
+ /ixms => 1,
+
+ qr/ Array [ ]* Design [ ]* REFs? # main heading
+ ( [ ]* :[^\t\"]+ )? # optional namespace
+ /ixms
+ => sub { $self->set_row_array(shift) },
+
+ qr/ Comments? # heading prefix
+ [ ]*
+ \[ [ ]* ([^\]]+?) [ ]* \] # bracketed term
+ /ixms => 1,
+
+
+ # See above for these regexps.
+ $characteristics_re => sub { $self->check_char_against_cache(@_) },
+ $factorvalue_re => sub { $self->add_row_factor(@_) },
+ $parameter_re => 1,
+ $unit_re => 1,
+ );
+
+ # An array of either CODE refs to run on a given column, or
+ # other true scalar values.
+ my @rowchecks;
+
+ foreach my $col ( @{ $headings || [] } ) {
+ if ( my $result = first { $col =~ m/\A [ ]* $_ [ ]* \z/xms } keys %recognized ) {
+ push @rowchecks, $recognized{$result};
+
+ # Check Parameter Value, Factor Value headings here.
+ my @args;
+ if ( my $key = first { @args = ($col =~ $_) } keys %header_test ) {
+ my $test = $header_test{$key};
+ if ( ref $test eq 'CODE' ) {
+ $test->(@args);
+ }
+ }
+ }
+ else {
+ push @rowchecks, 0;
+ $self->logprint(
+ 'error',
+ qq{ERROR: Unrecognized SDRF column heading: "$col".\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+ }
+
+ return \@rowchecks;
+}
+
+sub parse_idf_naively : PRIVATE {
+
+ my ( $self ) = @_;
+
+ $self->logprint_line( 'error', ' Naive IDF parsing START ' );
+
+ my $idf_checker = ArrayExpress::MAGETAB::Checker::IDF->new({
+ idf => $self->get_idf(),
+ checker => $self,
+ source_directory => $self->get_source_directory(),
+
+ # magetab_doc-specific options. These should be undef for
+ # regular IDFs.
+ idf_filehandle => $self->get_idf_filehandle(),
+ eol_char => $self->get_eol_char(),
+
+ expt_accession => 'DUMMY',
+ });
+
+ # Read the file in, performing some basic checks on structure.
+ eval {
+ $idf_checker->read_file();
+ };
+ if ( $EVAL_ERROR ) {
+ $self->logprint(
+ 'error',
+ "Error reading IDF format: $EVAL_ERROR",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # Start the checks. These should now be mainly content, not
+ # structure.
+
+ # First, simply check on the existence of required annotation.
+ $self->check_required_idf_annotation($idf_checker);
+
+ # Capture the Term Source Names for later checking (this obviously
+ # needs to happen before any such checking takes place).
+ $self->populate_termsources($idf_checker);
+
+ # Experiment designs, dates, description.
+ $self->check_experiment_annotation($idf_checker);
+
+ # Publications.
+ $self->check_publications($idf_checker);
+
+ # Submitters.
+ $self->check_submitters($idf_checker);
+
+ # Protocols.
+ $self->check_protocols($idf_checker);
+
+ # Experimental Factors.
+ $self->check_factors($idf_checker);
+
+ $self->logprint_line( 'error', ' Naive IDF parsing END ' );
+
+ return $idf_checker->get_sdrfs();
+}
+
+sub check_required_idf_annotation : PRIVATE {
+
+ my ( $self, $idf_checker ) = @_;
+
+ my %required_list = (
+ 'Experimental Factors' => 'get_factors',
+ 'Contacts (Persons)' => 'get_people',
+ 'Publications' => 'get_publications',
+ 'Protocols' => 'get_protocols',
+ );
+ while ( my ( $needed_items, $method ) = each %required_list ) {
+ unless ( scalar( @{ $idf_checker->$method } ) ) {
+ $self->logprint(
+ 'error',
+ "Warning: IDF provides no $needed_items.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+
+ # SDRFs are a special case - they're a real problem if missing,
+ # but not if this is a combined IDF+SDRF document.
+ unless ( scalar( @{ $idf_checker->get_sdrfs() } )
+ || $self->get_magetab_doc() ) {
+ $self->logprint(
+ 'error',
+ "ERROR: IDF references no SDRF documents.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ }
+
+ # FIXME these hashes should perhaps instead be lightweight
+ # objects? (re-implement in MAGETAB::IDF superclass).
+ my $expt_hash = $idf_checker->get_experiment();
+
+ $self->check_required_expt_info( $expt_hash );
+
+ my $people = $idf_checker->get_people();
+ if ( scalar @{ $people } ) {
+ $self->check_required_person_info( $people );
+ }
+
+ return;
+}
+
+sub check_required_expt_info : PRIVATE {
+
+ my ( $self, $expt_hash ) = @_;
+
+ my %required_expt_info = (
+ 'Release Date' => 'releasedate',
+ 'Title' => 'title',
+ 'Description' => 'description',
+ );
+ while ( my ( $needed_info, $key ) = each %required_expt_info ) {
+ if ( ! defined( $expt_hash->{ $key } ) ) {
+ $self->logprint(
+ 'error',
+ "Warning: IDF provides no Experiment $needed_info.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ elsif ( my ($type) = ($needed_info =~ /\A (\w*) [ ]* date \z/ixms) ) {
+ $self->check_date_format( $expt_hash->{ $key }, $type );
+ }
+ }
+
+ return;
+}
+
+sub check_required_person_info : PRIVATE {
+
+ my ( $self, $people ) = @_;
+
+ # Info required for all contacts. N.B. submitter-specific checks
+ # are made later, in check_submitters().
+ my %required_info = (
+ 'Last Name' => 'lastname',
+ 'Affiliation' => 'affiliation',
+ );
+
+ foreach my $person ( @{ $people } ) {
+
+ # Find something to use to identify the Person.
+ my $name = $person->{'lastname'}
+ || $person->{'email'}
+ || '**UNNAMED CONTACT**';
+
+ # Check required info.
+ while ( my ( $needed_info, $key ) = each %required_info ) {
+ if ( ! defined( $person->{ $key } ) ) {
+ $self->logprint(
+ 'error',
+ qq{Warning: IDF provides no $needed_info for Person "$name".\n},
+ );
+
+ # FIXME - we may want to upgrade this if truly required
+ # info is being checked for; currently any requirement
+ # for Affiliation is a bit wishy-washy.
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+ }
+
+ return;
+}
+
+sub populate_termsources : PRIVATE {
+
+ my ( $self, $idf_checker ) = @_;
+
+ my %termsourcename;
+ foreach my $termsource ( @{ $idf_checker->get_termsources() } ) {
+
+ $termsourcename{$termsource->{'name'}}++
+ if defined($termsource->{'name'});
+
+ # Term Source without file risks creating meaningless MAGE
+ # Database objects.
+ unless ( $termsource->{'file'} ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ "Warning: Term Source %s has no File information.\n",
+ ($termsource->{'name'} || q{}),
+ ),
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+ }
+ }
+ $self->set_termsources(\%termsourcename);
+
+ return;
+}
+
+sub check_experiment_annotation : PRIVATE {
+
+ my ( $self, $idf_checker ) = @_;
+
+ # Experiment Designs is an arrayref.
+ my $expt_hash = $idf_checker->get_experiment();
+ my $designs = $expt_hash->{'design'};
+ if ( defined $designs && scalar @{ $designs || [] } ) {
+
+ # Collect ExperimentDesignType terms for DW checking.
+ $self->add_expt_designs( @{ $designs } );
+ }
+ else {
+ $self->logprint(
+ 'error',
+ "Warning: IDF provides no Experimental Design.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ # Design term sources.
+ my $design_termsources = $expt_hash->{'termsource'};
+ if ( defined $design_termsources ) {
+ foreach my $ts ( @{ $design_termsources || [] } ) {
+ unless( $self->is_valid_termsource( $ts ) ) {
+ $self->logprint(
+ 'error',
+ qq{Error: Experimental Design Term Source "$ts" is not declared in the IDF.\n},
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+ }
+ }
+ }
+
+ # Experiment date format also needs checking.
+ if ( defined ( $expt_hash->{'experimentdate'} ) ) {
+ $self->check_date_format( $expt_hash->{'experimentdate'}, q{} );
+ }
+
+ # Print the experiment description to the report log.
+ if ( defined ( $expt_hash->{'description'} ) ) {
+ format_description(
+ $expt_hash->{'description'},
+ $self->log_fh('report'),
+ );
+ }
+
+ my %typemap = (
+ 'Quality Control Type' => 'get_qualitycontroltypes',
+ 'Replicate Type' => 'get_replicatetypes',
+ 'Normalization Type' => 'get_normalizationtypes',
+ );
+ while ( my ( $typeclass, $method ) = each %typemap ) {
+ foreach my $type ( @{ $idf_checker->$method } ) {
+ unless ( $type->{'type'} ) {
+ $self->logprint(
+ 'error',
+ "Warning: $typeclass missing value in IDF.\n",
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+ }
+ if ( my $ts = $type->{'termsource'} ) {
+ unless( $self->is_valid_termsource( $ts ) ) {
+ $self->logprint(
+ 'error',
+ qq{Error: $typeclass Term Source "$ts" is not declared in the IDF.\n},
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+sub check_publications : PRIVATE {
+
+ my ( $self, $idf_checker ) = @_;
+
+ # Check for numeric PubMed ID.
+ foreach my $publication ( @{ $idf_checker->get_publications() } ) {
+ if ( defined ($publication->{'pubmedid'})
+ && $publication->{'pubmedid'} =~ m/\D/ ) {
+ $self->logprint(
+ 'error',
+ "Warning: PubMed ID contains non-numeric characters: ",
+ $publication->{'pubmedid'}, "\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ if ( my $ts = $publication->{'termsource'} ) {
+ unless( $self->is_valid_termsource( $ts ) ) {
+ $self->logprint(
+ 'error',
+ qq{Error: Publication Status Term Source "$ts" is not declared in the IDF.\n},
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+ }
+ }
+ }
+
+ return;
+}
+
+sub check_submitters : PRIVATE {
+
+ my ( $self, $idf_checker ) = @_;
+
+ my $submitters_found;
+ foreach my $contact ( @{ $idf_checker->get_people() } ) {
+ if ( defined($contact->{'roles'})
+ && $contact->{'roles'} =~ /\b submitter \b/xms ) {
+
+ $submitters_found++;
+ unless ( $contact->{'lastname'} ) {
+ $self->logprint(
+ 'error',
+ "Warning: Experiment submitter has no last name.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ unless ( $contact->{'email'} ) {
+ $self->logprint(
+ 'error',
+ "Warning: Submitter email address is missing.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+ }
+ if ( my $ts = $contact->{'termsource'} ) {
+ unless( $self->is_valid_termsource( $ts ) ) {
+ $self->logprint(
+ 'error',
+ qq{Error: Person Roles Term Source "$ts" is not declared in the IDF.\n},
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+ }
+ }
+ }
+ unless ( $submitters_found ) {
+ $self->logprint(
+ 'error',
+ "Warning: No submitters provided in IDF.\n",
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ return;
+}
+
+sub check_protocols : PRIVATE {
+
+ my ( $self, $idf_checker ) = @_;
+
+ # Check protocol descriptions.
+ my (%protocolname, %parametername);
+ foreach my $protocol ( @{ $idf_checker->get_protocols() } ) {
+
+ $protocolname{$protocol->{'name'}}++ if defined($protocol->{'name'});
+
+ if ( $protocol->{'description'} ) {
+ unless ( length( $protocol->{'description'} ) > 50 ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Description for %s protocol "%s" is too short.\n},
+ ( $protocol->{'type'} || q{} ),
+ ( $protocol->{'name'} || q{} ),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+ else {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Description is missing for %s protocol "%s".\n},
+ ( $protocol->{'type'} || q{} ),
+ ( $protocol->{'name'} || q{} ),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ if ( $protocol->{'parameters'} ) {
+ foreach my $name ( split /\s*;\s*/, $protocol->{'parameters'} ) {
+
+ # We store the protocol name if we can, so that we can
+ # try and match parameters to protocols later. NOT
+ # IMPLEMENTED FIXME. N.B. this won't work for
+ # identically named parameters in separate protocols
+ # anyway (which is legal). FIXME.
+ $parametername{$name} = ($protocol->{'name'} || 1)
+ if defined $name;
+ }
+ }
+ if ( my $ts = $protocol->{'termsource'} ) {
+ unless ( $self->is_valid_termsource( $ts ) ) {
+ $self->logprint(
+ 'error',
+ qq{Error: Protocol Term Source "$ts" is not declared in the IDF.\n},
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+ }
+ }
+ }
+ $self->set_protocols(\%protocolname);
+ $self->set_parameters(\%parametername);
+
+ return;
+}
+
+sub check_factors : PRIVATE {
+
+ my ( $self, $idf_checker ) = @_;
+
+ my %factorname;
+ foreach my $factor ( @{ $idf_checker->get_factors() } ) {
+
+ $factorname{$factor->{'name'}}++ if defined($factor->{'name'});
+
+ unless ( $factor->{'type'} ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Experimental Factor "%s" has no Type.\n},
+ ( $factor->{'name'} || q{} ),
+ ),
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEBAD());
+ }
+ if ( my $ts = $factor->{'termsource'} ) {
+ unless ( $self->is_valid_termsource( $ts ) ) {
+ $self->logprint(
+ 'error',
+ qq{Error: Experimental Factor Term Source "$ts" is not declared in the IDF.\n},
+ );
+ $self->add_error($CONFIG->get_ERROR_PARSEFAIL());
+ }
+ }
+ }
+ $self->set_factors(\%factorname);
+
+ return;
+}
+
+sub is_valid_termsource : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ return $termsources{ident $self}{$name} ? 1 : 0;
+}
+
+sub is_valid_factor : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ return $factors{ident $self}{$name} ? 1 : 0;
+}
+
+sub is_valid_protocol : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ return $protocols{ident $self}{$name} ? 1 : 0;
+}
+
+sub is_valid_parameter : PRIVATE {
+
+ my ( $self, $name ) = @_;
+
+ return $parameters{ident $self}{$name} ? 1 : 0;
+}
+
+sub add_row_file : PRIVATE {
+
+ my ( $self, $type, $name ) = @_;
+
+ my %files = map { $_->{'name'} => $_ } @{ $self->get_row_files() };
+
+ $files{$name} = { 'name' => $name, 'type' => $type };
+
+ $self->set_row_files( [ values %files ] );
+
+ return;
+}
+
+sub add_row_matrix_file : PRIVATE {
+
+ my ( $self, $type, $name ) = @_;
+
+ my %files = map { $_->{'name'} => $_ } @{ $self->get_row_files() };
+
+ my $mage_ba_data;
+
+ if ( $type eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+ require Bio::MAGE::BioAssayData::DerivedBioAssayData;
+ $mage_ba_data = Bio::MAGE::BioAssayData::DerivedBioAssayData->new();
+ }
+ elsif ( $type eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+ require Bio::MAGE::BioAssayData::MeasuredBioAssayData;
+ $mage_ba_data = Bio::MAGE::BioAssayData::MeasuredBioAssayData->new();
+ }
+ else {
+ croak("Error: unrecognized file type: $type.");
+ }
+
+ $files{$name} = {
+ 'name' => $name,
+ 'type' => $type,
+ 'ba_data' => $mage_ba_data,
+ };
+
+ $self->set_row_files( [ values %files ] );
+
+ return;
+}
+
+sub add_row_factor : PRIVATE {
+
+ my ( $self, $value ) = @_;
+
+ my %factors = map { $_ => 1 } @{ $self->get_row_factors() };
+
+ $factors{$value} = 1;
+
+ $self->set_row_factors( [ keys %factors ] );
+
+ return;
+}
+
+sub get_files_from_naive_parse : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my @filelist;
+ foreach my $info ( @{ $self->get_file_info() } ) {
+
+ my $path;
+ if ( my $dir = $self->get_source_directory() ) {
+ $path = File::Spec->catfile( $dir, $info->{'name'} );
+ }
+ else {
+ $path = $info->{'name'};
+ }
+
+ # N.B. this will get rewritten later to handle URIs.
+ my $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => $info->{'name'},
+ data_type => $info->{'type'},
+ array_design_id => ($info->{'array'} || 'Unknown'),
+ });
+
+ foreach my $factor ( @{ $info->{'factors'} || [] } ) {
+
+ # FIXME category not easily determined at this point.
+ $file->add_factor_value('DUMMY', $factor);
+ }
+
+ # Data matrices are distinguished from ordinary FGEMs by
+ # having a mage_badata attribute. FIXME consider adding CDF
+ # info here as an NVT of the badata object.
+ if ( my $obj = $info->{'ba_data'} ) {
+ $file->set_mage_badata( $obj );
+ }
+ push @filelist, $file;
+ }
+
+ return \@filelist;
+}
+
+sub check_file_info : PRIVATE {
+
+ # Read the data from row_files, arrays, factors, check for
+ # consistency, and store it in %fileinfo for later.
+
+ my ( $self ) = @_;
+
+ foreach my $info ( @{ $self->get_row_files() } ) {
+
+ if ( my $array = $self->get_row_array() ) {
+ $info->{'array'} = $array;
+ }
+ else {
+ $self->logprint(
+ 'error',
+ qq{WARNING: Data file "$info->{name}" not associated with any array designs.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEBAD() );
+ }
+
+ my @factors = @{ $self->get_row_factors() };
+ if ( scalar @factors ) {
+ $info->{'factors'} = \@factors;
+ }
+ else {
+ $self->logprint(
+ 'error',
+ qq{Warning: Data file "$info->{name}" not associated with any factor values.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+
+ $self->add_file_info( $info );
+ }
+
+ return;
+}
+
+sub add_file_info : PRIVATE {
+
+ my ( $self, $info ) = @_;
+
+ my %files = map { $_->{'name'} => $_ } @{ $self->get_file_info() };
+
+ $files{$info->{'name'}} = $info;
+
+ $self->set_file_info( [ values %files ] );
+
+ return;
+}
+
+sub check_idf_term_usage : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Check that Term Sources, Experimental Factors, Protocols and
+ # Parameters declared in the IDF are all used in the SDRF.
+
+ my %check = (
+ 'Term Source' => { 'idf' => 'get_termsources',
+ 'sdrf' => 'get_termsources_used', },
+ 'Experimental Factor' => { 'idf' => 'get_factors',
+ 'sdrf' => 'get_factors_used', },
+ 'Protocol' => { 'idf' => 'get_protocols',
+ 'sdrf' => 'get_protocols_used', },
+ 'Parameter' => { 'idf' => 'get_parameters',
+ 'sdrf' => 'get_parameters_used', },
+ );
+
+ while ( my ($item, $methods) = each %check ) {
+ my $idf_terms = $methods->{idf};
+ my $sdrf_terms = $methods->{sdrf};
+ foreach my $term ( keys %{ $self->$idf_terms } ) {
+ unless ( $self->$sdrf_terms()->{$term} ) {
+ $self->logprint(
+ 'error',
+ qq{Warning: $item Name "$term" is declared in the IDF but not used in the SDRF.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_INNOCENT() );
+ }
+ }
+ }
+
+ return;
+}
+
+sub protest_unsupported : PRIVATE {
+
+ my ( $self, $feature ) = @_;
+
+ $self->logprint(
+ 'error',
+ qq{ERROR: The feature "$feature" is unsupported at this time.\n},
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+ return;
+}
+
+sub print_sdrf_workflow : PRIVATE {
+
+ my ( $self, $bags ) = @_;
+
+ # keys are from Tab2MAGE::create_bags()
+ my @classes = (
+ { 'source' => 'Sources' },
+ { 'sample' => 'Samples' },
+ { 'extract' => 'Extracts' },
+ { 'labeledextract' => 'Labeled extracts' },
+ { 'pba' => 'Hybridizations' },
+ { 'mbad' => 'Raw data files' },
+ { 'dba' => 'Normalizations' },
+ { 'dbad' => 'Normalized data files' },
+ );
+
+ $self->generate_workflow_from_bags( $bags, [ @classes ] );
+
+ return;
+}
+
+sub check_protocol_attachments : PRIVATE {
+
+ my ( $self, $bags ) = @_;
+
+ foreach my $bm_type qw(sample extract labeledextract) {
+ $self->check_mage_treatments_protapps(
+ $bags->{$bm_type}->(),
+ $bm_type,
+ );
+ }
+
+ $self->check_mage_hyb_protapps( $bags->{'pba'}->() );
+
+ # This needs to support one and two-color scans, but we've already
+ # gathered all the relevant BATs while generating MAGE.
+ $self->check_mage_scan_protapps( [ values %{ $self->get_scans() } ] );
+ $self->check_mage_norm_protapps( $bags->{'dbad'}->() );
+
+ return;
+}
+
+sub check_mage_scan_protapps : PRIVATE {
+
+ # Check all passed BioAssayTreatments have protocol apps attached
+ # to them. This is MAGE-TAB specific, and we're only using it here
+ # to check on Scan protocols.
+ my ( $self, $bats ) = @_;
+
+ foreach my $bat ( @$bats ) {
+ my $found;
+ foreach my $pa ( @{ $bat->getProtocolApplications() || [] } ) {
+ $found++ if $pa->getProtocol();
+ }
+ unless ( $found ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Scan "%s" lacks a scanning protocol.\n},
+ $bat->getName(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+
+ return;
+}
+
+sub check_mage_norm_protapps : PRIVATE {
+
+ # Check all DBADs have a protocol attached to them. This is
+ # MAGE-TAB specific.
+ my ( $self, $dbads ) = @_;
+
+ foreach my $dbad ( @$dbads ) {
+ my $found;
+ if ( my $txn = $dbad->getProducerTransformation() ) {
+ foreach my $pa ( @{ $txn->getProtocolApplications() || [] } ) {
+ $found++ if $pa->getProtocol();
+ }
+ }
+ unless ( $found ) {
+ $self->logprint(
+ 'error',
+ sprintf(
+ qq{Warning: Derived Array Data "%s" lacks a normalization protocol.\n},
+ $dbad->getName(),
+ ),
+ );
+ $self->add_error( $CONFIG->get_ERROR_MIAME() );
+ }
+ }
+
+ return;
+}
+
+sub check_bmchars_against_fvs : PRIVATE {
+
+ my ( $self, $bags ) = @_;
+
+ # Generate a list of FV OE category => value pairs
+ my @fv_catvals;
+ foreach my $ef ( @{ $bags->{'factor'}->() } ) {
+ foreach my $fv ( @{ $ef->getFactorValues() || [] } ) {
+ my ( $category, $value );
+ if ( my $fvvalue = $fv->getValue() ) {
+
+ # Regular FV Value OE.
+ $category = $fvvalue->getCategory();
+ $value = $fvvalue->getValue();
+ }
+ elsif ( my $measurement = $fv->getMeasurement() ) {
+
+ # FV Measurements.
+ my $efc = $ef->getCategory();
+ $category = $efc->getValue();
+
+ # CamelCase the term.
+ $category =~ s/^(.)/uc($1)/e;
+ $category =~ s/_(.)/uc($1)/ge;
+ $category =~ s/_//;
+
+ $value = $measurement->getValue();
+ }
+ push @fv_catvals, { $category => $value };
+ }
+ }
+
+ # Generate a list of BMC OE category => value pairs.
+ foreach my $type ( qw(source sample extract labeledextract) ) {
+ my @bmc_catvals;
+ foreach my $material ( @{ $bags->{$type}->() || [] } ) {
+ foreach my $char (
+ @{ $material->getCharacteristics() || [] } ) {
+ my ( $category, $value );
+ if ( $char->getCategory() eq $char->getValue() ) {
+
+ # Handle nested OEs (e.g. BMC measurements).
+ my $flattened = $self->flatten_nested_oe($char);
+ $category = $flattened->getCategory();
+ $value = $flattened->getValue();
+ }
+ else {
+
+ # Regular BMC OEs.
+ $category = $char->getCategory();
+ $value = $char->getValue();
+ }
+ push @bmc_catvals, { $category => $value };
+ }
+ }
+ $self->compare_fvs_to_bmcs(\@fv_catvals, \@bmc_catvals);
+ }
+
+ return;
+}
+
+sub flatten_nested_oe : PRIVATE {
+
+ my ( $self, $oe ) = @_;
+
+ # Reduce a nested OE to a flattened form in which everything but
+ # the value is discarded. Relies on the value being coded as a
+ # has_value property.
+
+ croak("Error: OntologyEntry is not nested")
+ unless ( $oe->getCategory() eq $oe->getValue() );
+
+ my $category = $oe->getCategory();
+ my $value = $self->find_nested_oe_value( $oe );
+
+ my $flattened;
+ if ( defined $value ) {
+ require Bio::MAGE::Description::OntologyEntry;
+ $flattened = Bio::MAGE::Description::OntologyEntry->new(
+ category => $category,
+ value => $value,
+ );
+ }
+
+ return $flattened;
+}
+
+sub find_nested_oe_value : PRIVATE {
+
+ my ( $self, $oe ) = @_;
+
+ # Given a nested OE, return the first has_value value encountered
+ # by recursing through the tree.
+
+ # Beware this is very MO-specific FIXME?
+ return $oe->getValue() if ( $oe->getCategory() eq 'has_value'
+ && $oe->getCategory() ne $oe->getValue() );
+
+ foreach my $child ( @{ $oe->getAssociations() || [] } ) {
+ if ( my $value = $self->find_nested_oe_value( $child ) ) {
+ return $value;
+ }
+ }
+
+ return;
+}
+
+sub create_data_matrix_mage : PRIVATE {
+
+ my ( $self, $filelist, $magetab ) = @_;
+
+ FILE:
+ foreach my $file ( @{ $filelist || [] } ) {
+ if ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE()
+ || $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+ # Sort out any data matrix links here. These are processed
+ # after all the per-hyb objects have been created.
+ require ArrayExpress::MAGETAB::DataMatrix;
+
+ my $dm = ArrayExpress::MAGETAB::DataMatrix->new({
+ datafile => $file,
+ id_template => 'DUMMY',
+ mage_helper => $magetab,
+ });
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+ local $EVAL_ERROR;
+ eval{
+ $dm->create_mage();
+ };
+ $SIG{__DIE__} = $sighandler if $sighandler;
+ if ($EVAL_ERROR) {
+ my $err_message = sprintf(
+ "Error parsing data matrix %s (%s); graph visualization will omit this file.\n",
+ $file->get_name(),
+ $EVAL_ERROR,
+ );
+ warn($err_message);
+ $self->logprint(
+ 'error',
+ $err_message,
+ );
+ $self->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+ last FILE;
+ }
+ }
+ }
+}
+
+sub visualize_experiment : RESTRICTED {
+
+ my ( $self, $filelist ) = @_;
+
+ my $magetab = $self->get_mage_generator();
+
+ unless ( defined($magetab) && $magetab->can('get_bags') ) {
+ $self->logprint(
+ 'error',
+ "Warning: MAGE objects unavailable for visualization. Skipping.\n",
+ );
+ return;
+ }
+
+ my $bags = $magetab->get_bags();
+
+ # Parse any data matrix headers into MAGE objects.
+ unless ( $self->get_skip_data_checks() ) {
+ $self->create_data_matrix_mage( $filelist, $magetab );
+ }
+
+ # Create the dotfile (and PNG if GraphViz installed)
+ my $output_base = $self->get_idf() || $self->get_magetab_doc();
+ $output_base =~ s/\.\w{3}$// if $output_base;
+ $output_base ||= 'magetab'; # Just in case
+
+ if ( defined($self->get_log_to_current_dir) ) {
+ my $output_dir = $self->get_log_to_current_dir || q{.};
+ my ($vol, $dir, $name) = File::Spec->splitpath($output_base);
+ $output_base = File::Spec->catfile($output_dir, $name);
+ }
+
+ # we'll just use the default font here.
+ require ArrayExpress::Curator::Visualize;
+ ArrayExpress::Curator::Visualize->import(
+ qw(dot_and_png)
+ );
+ my $classes = {
+ 'source' => 'Source',
+ 'sample' => 'Sample',
+ 'extract' => 'Extract',
+ 'labeledextract' => 'Labeled Extract',
+ 'pba' => 'Hybridization',
+ 'mba' => 'Feature Extraction',
+ 'datamatrix_mba' => 'Array Data',
+ 'dba' => 'Normalization',
+ 'datamatrix_dba' => 'Array Data',
+ };
+ $self->set_clobber(
+ dot_and_png(
+ $bags,
+ $output_base,
+ undef,
+ $self->get_clobber(),
+ $classes,
+ )
+ );
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/Checker/IDF.pm b/lib/ArrayExpress/MAGETAB/Checker/IDF.pm
new file mode 100644
index 0000000..c05d4bb
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/Checker/IDF.pm
@@ -0,0 +1,76 @@
+#!/usr/bin/env perl
+#
+# $Id: IDF.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::MAGETAB::Checker::IDF;
+
+use Class::Std;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use base qw(ArrayExpress::MAGETAB::IDF);
+
+my %checker : ATTR( :name<checker>, :default<\*STDOUT> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $checker{$id}
+ && $checker{$id}->isa('ArrayExpress::Curator::ExperimentChecker') ) {
+ die("Error: Invalid or absent checker object.");
+ }
+
+ return;
+}
+
+sub read_file {
+
+ my ( $self ) = @_;
+
+ # Parse the IDF file into memory here.
+ my $array_of_rows = $self->read_as_arrayref();
+
+ # Check tags for duplicates, make sure that tags are recognized.
+ my $idf_hash = $self->validate_arrayref_tags( $array_of_rows );
+
+ # Populate the internal data store with whatever we can recognise;
+ # discard the rest, as it has already been reported.
+ while ( my ( $tag, $values ) = each %{ $idf_hash } ) {
+ if ( my $sub = $self->retrieve_sub( $tag ) ) {
+
+ # Recognised tag.
+ $sub->( @$values );
+ }
+ }
+
+ return;
+}
+
+# This method overrides that in the superclass, allowing what would
+# normally be fatal parse errors to be recorded in the logs.
+sub raise_error : RESTRICTED {
+
+ my ( $self, @messages ) = @_;
+
+ my $checker = $self->get_checker();
+
+ my $message = join(q{}, @messages);
+
+ # Make sure the message ends in a single newline.
+ $message =~ s/[\r\n]* \z/\n/xms;
+
+ $checker->logprint(
+ 'error',
+ $message,
+ );
+
+ $checker->add_error( $CONFIG->get_ERROR_PARSEFAIL() );
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/DataMatrix.pm b/lib/ArrayExpress/MAGETAB/DataMatrix.pm
new file mode 100644
index 0000000..d7ab077
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/DataMatrix.pm
@@ -0,0 +1,548 @@
+#!/usr/bin/env perl
+#
+# Module to help in the generation of MAGETAB DataMatrix MAGE objects.
+#
+# Tim Rayner 2007, ArrayExpress team, European Bioinformatics Institute
+#
+# $Id: DataMatrix.pm 1852 2007-12-13 10:14:27Z tfrayner $
+#
+
+package ArrayExpress::MAGETAB::DataMatrix;
+
+use strict;
+use warnings;
+
+use Carp;
+use English qw( -no_match_vars );
+use Class::Std;
+
+use ArrayExpress::Curator::Config qw( $CONFIG );
+use ArrayExpress::Curator::MAGE qw(unique_identifier);
+
+use base 'ArrayExpress::Datafile::DataMatrix';
+
+my %id_template : ATTR( :name<id_template>, :default<undef> );
+my %datafile : ATTR( :name<datafile>, :default<undef> );
+my %mage_helper : ATTR( :name<mage_helper>, :default<undef> );
+my %dm_id_template : ATTR( :name<dm_id_template>, :default<undef> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $self->get_datafile() ) {
+ croak("Error: datafile attribute not set.");
+ }
+ unless ( $self->get_id_template() ) {
+ croak("Error: id_template attribute not set.");
+ }
+ unless ( $self->get_mage_helper() ) {
+ croak("Error: mage_helper attribute not set.");
+ }
+
+ # This is a per-file template.
+ $self->set_dm_id_template(
+ sprintf(
+ "%s.%s.DataMatrix",
+ $self->get_id_template(),
+ unique_identifier(),
+ )
+ );
+
+ return;
+}
+
+sub create_mage {
+
+ my ( $self ) = @_;
+
+ # Figure out BADim, QTDim and data cube order. Generate MAGE for
+ # QTD and add to data object ($file->get_mage_badata). Map BADim
+ # to bioassays as specified by $file->get_sdrf_id_column.
+
+ my $file = $self->get_datafile();
+ my $badata = $file->get_mage_badata();
+ my $dm_hybs = $file->get_heading_hybs();
+ my $dm_qts = $file->get_heading_qts();
+
+ my $magetab = $self->get_mage_helper();
+ my $bag_of = $magetab->get_bags();
+
+ # Construct the dimensions
+ my ( $ba_namerefs, $qt_names, $order )
+ = $self->get_dimension_lists( $dm_hybs, $dm_qts );
+
+ # Internal consistency check; order should always be set.
+ croak("Error: Order of Data Matrix data cube is indeterminate.\n")
+ unless $order;
+
+ # Warn on unsupported cube order
+ if ( $order eq 'DQB' ) {
+ warn(
+ qq{WARNING: A BioDataCube order of "$order" is not }
+ . qq{supported by the ArrayExpress MAGE-ML loader.\n}
+ );
+ }
+ my $bdc = $badata->getBioDataValues();
+ $bdc->setOrder( $order );
+
+ # Create the actual qtd here
+ my ( $qt_list, $qtd_key ) = $magetab->qts_from_names( $file, $qt_names );
+
+ my $qtd = $bag_of->{qtd}->(
+ $qtd_key,
+ {
+ qt_list => $qt_list,
+ identifier_template => $self->get_dm_id_template(),
+ }
+ );
+ $file->set_mage_qtd($qtd);
+ $badata->setQuantitationTypeDimension($qtd);
+
+ # Store the old MBAs from the BioAssayDimension for later
+ # repointing to a new DBA/DBAD if this is an Array Data File
+ # converted to Matrix (e.g. Illumina).
+ my @old_mbas;
+ if ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+ if ( my $ded = $badata->getBioAssayDimension() ) {
+ foreach my $old ( @{ $ded->getBioAssays() || [] } ) {
+ my $name = $old->getName();
+ push @old_mbas, $old;
+ $bag_of->{mba}->( $name, 'delete' )
+ or die("Error deleting unwanted MBA: $name");
+ }
+ }
+ }
+
+ # Find or create list of BioAssay objects given the $ba_namerefs
+ # arrayref of arrayrefs.
+ my $mapped_bioassays = $self->retrieve_mapped_bioassays($ba_namerefs);
+
+ # Sort out the BioAssayMaps here.
+ $self->add_bioassays_to_bioassaymaps( $mapped_bioassays, \@old_mbas );
+
+ # Create and link the BioAssayDimension here.
+ my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+ identifier => $self->get_dm_id_template() . '.MeasuredBioAssayDimension',
+ bioAssays => $mapped_bioassays,
+ );
+ $badata->setBioAssayDimension( $bad );
+
+ # FIXME figure out how to add MBA protocol applications? These are
+ # FeatureExtraction protocols, not yet supported by SDRF; this is
+ # not an issue for Derived data, which attaches
+ # ProtocolApplication to ProducerTransformation.
+
+ return;
+}
+
+sub repoint_mba_to_dba : PRIVATE {
+
+ # This is pretty much Illumina-only at the moment.
+
+ my ( $self, $dba, $old_mbas, $file, $bags ) = @_;
+
+ my @bams = @{ $bags->{bam}->() };
+
+ foreach my $old ( @$old_mbas ) {
+ my $old_name = $old->getName();
+ foreach my $bam ( @bams ) {
+ my @new;
+ foreach my $source ( @{ $bam->getSourceBioAssays() || [] } ) {
+ if ( $source->isa('Bio::MAGE::BioAssay::MeasuredBioAssay')
+ && $source->getName() eq $old_name ) {
+
+ # Replace the old MBA with the new DBA.
+ push @new, $dba;
+
+ # Make DBAD from MBAD, repoint Dimensions and
+ # BioDataCube. FIXME this only remaps the
+ # first MBAD, not a general solution.
+ if ( my $mbad = shift @{ $source->getMeasuredBioAssayData() || [] } ) {
+ my $dbad = $bags->{dbad}->(
+ $mbad->getName(),
+ {
+ identifier_template => $self->get_dm_id_template(),
+ de_dimension => $mbad->getDesignElementDimension(),
+ qt_dimension => $mbad->getQuantitationTypeDimension(),
+ filename => $mbad->getBioDataValues()->getCube(),
+ }
+ );
+ $dba->setDerivedBioAssayData( [ $dbad ] );
+ $bags->{mbad}->( $mbad->getName(), 'delete' );
+ $file->set_mage_badata( $dbad );
+ }
+
+ my @fvs = @{ $source->getBioAssayFactorValues() || [] };
+ $dba->setBioAssayFactorValues( \@fvs );
+
+ # FIXME also switch around FeatureExtraction
+ # ProtocolApps here?
+ }
+ else {
+ push @new, $source;
+ }
+ }
+ $bam->setSourceBioAssays( \@new );
+ }
+ }
+
+ return;
+}
+
+sub add_bioassays_to_bioassaymaps : PRIVATE {
+
+ my ( $self, $mapped_bioassays, $old_mbas ) = @_;
+
+ my $file = $self->get_datafile();
+ my $badata = $file->get_mage_badata();
+ my $magetab = $self->get_mage_helper();
+
+ my $maps = [];
+ if ( $file->get_data_type() eq $CONFIG->get_RAW_DM_FILE_TYPE() ) {
+
+ # Create new DBA if this is an Array Data Matrix, link to the
+ # BioAssay list using $magetab->bam_bag(). Note that we can't
+ # add it to the MBAData because ProducerTransformation is only
+ # for DBAData.
+ my $name = $file->get_name();
+ my $dba = $magetab->get_bags()->{datamatrix_dba}->(
+ $name,
+ {
+ identifier_template => $self->get_dm_id_template(),
+ name => $name,
+ }
+ );
+ $maps = [
+ $magetab->get_bags()->{bam}->(
+ $name,
+ {
+ identifier_template => $self->get_dm_id_template(),
+ target_bioassay => $dba,
+ }
+ ),
+ ];
+ $dba->setDerivedBioAssayMap( $maps );
+
+ # Remap old MBA links (generated during SDRF parsing) to the
+ # newly-created DBA.
+ my $bags = $magetab->get_bags();
+ $self->repoint_mba_to_dba( $dba, $old_mbas, $file, $bags );
+
+ # FIXME Array Data Matrix BioAssay lacks FVs at the moment
+ # (Illumina is okay though).
+
+ }
+ elsif ( $file->get_data_type() eq $CONFIG->get_FGEM_FILE_TYPE() ) {
+
+ # Use the BioAssayMaps from $badata.
+ my $txn = $badata->getProducerTransformation()
+ or croak("Error: DBAD has no ProducerTransformation.");
+
+ my $mapping = $txn->getBioAssayMapping()
+ or croak("Error: DBAD ProducerTransformation has no BioAssayMapping.");
+
+ $maps = $mapping->getBioAssayMaps();
+
+ # Since we're here, add the QuantitationTypeMapping.
+ $self->add_qt_mapping(
+ $txn,
+ $file->get_mage_qtd(),
+ $mapped_bioassays,
+ $magetab->get_bags()->{qtm},
+ );
+ }
+ else {
+ croak("Error: unrecognized data type: " . $file->get_data_type() );
+ }
+
+ unless ( scalar @$maps ) {
+ croak("Error: BioAssayData cannot be mapped to source BioAssays.");
+ }
+
+ foreach my $map ( @$maps ) {
+ $map->setSourceBioAssays( $mapped_bioassays );
+ }
+
+ return;
+}
+
+sub retrieve_mapped_bioassays : PRIVATE {
+
+ my ( $self, $ba_namerefs ) = @_;
+
+ my $bags = $self->get_mage_helper()->get_bags();
+ my $id_column = $self->get_datafile->get_sdrf_id_column();
+
+ my $bioassays;
+ if ( $id_column =~ /(?:Hybridi[zs]ation|Assay)(?: *Name)?(?: *REF)?/i ) {
+ $bioassays = $self->map_physical_bioassays(
+ $ba_namerefs,
+ $bags->{pba},
+ $bags->{mba},
+ $bags->{datamatrix_dba},
+ $bags->{bam},
+ $id_column,
+ );
+ }
+ elsif ( $id_column =~ /Scan(?: *Name)?(?: *REF)?/i ) {
+ $bioassays = $self->map_physical_bioassays(
+ $ba_namerefs,
+ $bags->{extended_pba},
+ $bags->{mba},
+ $bags->{datamatrix_dba},
+ $bags->{bam},
+ $id_column,
+ $bags->{pba},
+ );
+ }
+ elsif ( $id_column =~ /Normali[zs]ation(?: *Name)?(?: *REF)?/i ) {
+ $bioassays = $self->map_derived_bioassays(
+ $ba_namerefs,
+ $bags->{dba},
+ $bags->{datamatrix_dba},
+ $bags->{bam},
+ $id_column,
+ );
+ }
+ else {
+ croak("Error: Unrecognized/unsupported column: $id_column\n");
+ }
+
+ return $bioassays;
+}
+
+sub map_derived_bioassays : PRIVATE {
+
+ # Data matrices mapped to Normalization allows us to query just for
+ # Normalization Names.
+
+ my ( $self,
+ $ba_namerefs,
+ $dba_bag,
+ $dm_dba_bag,
+ $bam_bag,
+ $id_column ) = @_;
+
+ my @ba_list;
+ foreach my $ba_names ( @$ba_namerefs ) {
+
+ my @column_bas;
+ foreach my $ba_name ( @$ba_names ) {
+ my $col_ba = $self->retrieve_bioassay($ba_name, $dba_bag, $id_column);
+ push @column_bas, $col_ba;
+ }
+
+ my $bioassay = $self->find_or_create_per_column_bioassay(
+ \@column_bas,
+ $self->get_dm_id_template(),
+ $dm_dba_bag,
+ $bam_bag,
+ );
+ push @ba_list, $bioassay;
+ }
+
+ return \@ba_list;
+}
+
+sub map_physical_bioassays : PRIVATE {
+
+ # Data matrices mapped to Scan or Hybridization need MBAs
+ # generated for each PBA (hyb/scan). Grouping DBAs are created as
+ # necessary.
+
+ my ( $self,
+ $ba_namerefs,
+ $pba_bag,
+ $mba_bag,
+ $dm_dba_bag,
+ $bam_bag,
+ $id_column,
+ $fallback_bag ) = @_;
+
+ my @ba_list;
+ foreach my $ba_names ( @$ba_namerefs ) {
+
+ my @column_bas;
+ foreach my $ba_name ( @$ba_names ) {
+
+ # Scan Name can be used to look up PBAs in
+ # extended_pba_bag, but note that this won't work for
+ # single-channel data, which needs a suitable fallback to
+ # hyb PBA having ImageAcquisition with name=Scan Name.
+ my $col_pba = $self->retrieve_bioassay(
+ $ba_name,
+ $pba_bag,
+ $id_column,
+ $fallback_bag,
+ );
+
+ # Follow the BioAssayTreatments to the end of the PBA
+ # chain.
+ $col_pba = $self->find_last_pba($col_pba);
+
+ # Find or create the MBA here.
+ my $col_mba = $self->find_or_create_mba(
+ $col_pba,
+ $mba_bag,
+ );
+
+ push @column_bas, $col_mba;
+ }
+
+ my $bioassay = $self->find_or_create_per_column_bioassay(
+ \@column_bas,
+ $self->get_dm_id_template(),
+ $dm_dba_bag,
+ $bam_bag,
+ );
+ push @ba_list, $bioassay;
+ }
+
+ return \@ba_list;
+}
+
+sub find_last_pba : PRIVATE {
+
+ my ( $self, $pba ) = @_;
+
+ # If no treatments, give up immediately.
+ my $bats = $pba->getBioAssayTreatments()
+ or return $pba;
+
+ BAT:
+ foreach my $bat ( @$bats ) {
+ my $new_pba = $bat->getTarget() or next BAT;
+
+ if ( $new_pba->getIdentifier() eq $pba->getIdentifier() ) {
+
+ # Recursion endpoint.
+ return $new_pba;
+ }
+ else {
+
+ # Return the PBA linked to the first Target PBA we find.
+ return $self->find_last_pba($new_pba);
+ }
+ }
+
+ # If no BATs with Target, return our original PBA.
+ return $pba;
+}
+
+sub find_or_create_mba : PRIVATE {
+
+ my ( $self, $pba, $mba_bag ) = @_;
+
+ my $pba_id = $pba->getIdentifier();
+
+ MBA:
+ foreach my $mba ( @{ $mba_bag->() } ) {
+
+ my $fext = $mba->getFeatureExtraction()
+ or next MBA;
+
+ my $mba_pba = $fext->getPhysicalBioAssaySource()
+ or next MBA;
+
+ if ( $mba_pba->getIdentifier() eq $pba_id ) {
+
+ # Success; return the MBA
+ return $mba;
+ }
+ }
+
+ # Not found; create a new MBA.
+ my $name = $pba->getName();
+ my $mba = $mba_bag->(
+ $name,
+ {
+ name => $name,
+ physical_bioassay => $pba,
+ identifier_template => $self->get_id_template() . ".$name",
+ }
+ );
+
+ # Propagate factor values.
+ $self->update_bioassay_fvs(
+ $mba,
+ $pba->getBioAssayFactorValues()
+ );
+
+ return $mba;
+}
+
+sub add_qt_mapping : PRIVATE {
+
+ my ( $self, $txn, $mage_qtd, $source_bioassays, $qtm_bag ) = @_;
+
+ $self->add_target_qts_to_mapping( $txn, $mage_qtd, $qtm_bag );
+
+ $self->add_source_qts_to_mapping( $txn, $source_bioassays, $qtm_bag );
+
+ return;
+}
+
+sub add_target_qts_to_mapping : PRIVATE {
+
+ my ( $self, $txn, $mage_qtd, $qtm_bag ) = @_;
+
+ my $qtmapping;
+ unless ( $qtmapping = $txn->getQuantitationTypeMapping ) {
+ $qtmapping = Bio::MAGE::BioAssayData::QuantitationTypeMapping->new();
+ $txn->setQuantitationTypeMapping( $qtmapping );
+ }
+
+ my %found;
+ foreach my $qtmap ( @{ $qtmapping->getQuantitationTypeMaps() || [] } ) {
+ if ( my $oldqt = $qtmap->getTargetQuantitationType() ) {
+ $found{ $oldqt->getIdentifier() }++;
+ }
+ }
+ foreach my $newqt ( @{ $mage_qtd->getQuantitationTypes() || [] } ) {
+ unless ( $found{ $newqt->getIdentifier() } ) {
+ my $qtmap = $qtm_bag->(
+ $newqt->getName(),
+ {
+ target_qt => $newqt,
+ identifier_template => $self->get_id_template(),
+ }
+ );
+ $qtmapping->addQuantitationTypeMaps( $qtmap );
+ }
+ }
+
+ return;
+}
+
+sub add_source_qts_to_mapping : PRIVATE {
+
+ my ( $self, $txn, $source_bioassays, $qtm_bag ) = @_;
+
+ my ($source_qts, $source_data) = $self->get_bioassay_qts( $source_bioassays );
+
+ $txn->setBioAssayDataSources( $source_data );
+
+ my $qtmapping;
+ unless ( $qtmapping = $txn->getQuantitationTypeMapping() ) {
+ carp("Warning: No QuantitationTypeMapping for Transformation object "
+ . $txn->getIdentifier() );
+ return;
+ }
+
+ foreach my $qtmap ( @{ $qtmapping->getQuantitationTypeMaps() || [] } ) {
+ my %found;
+ foreach my $oldqt ( @{ $qtmap->getSourcesQuantitationType() || [] } ) {
+ $found{ $oldqt->getIdentifier() }++;
+ }
+ foreach my $newqt ( @$source_qts ) {
+ unless ( $found{ $newqt->getIdentifier() } ) {
+ $qtmap->addSourcesQuantitationType( $newqt );
+ }
+ }
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/IDF.pm b/lib/ArrayExpress/MAGETAB/IDF.pm
new file mode 100644
index 0000000..4b04795
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/IDF.pm
@@ -0,0 +1,881 @@
+#!/usr/bin/env perl
+#
+# Module providing IDF parsing functions to the ArrayExpress MAGE-TAB
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: IDF.pm 2069 2008-06-04 14:33:52Z tfrayner $
+
+package ArrayExpress::MAGETAB::IDF;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use List::Util qw(first);
+use Bio::MAGE qw(:ALL);
+use Readonly;
+
+use ArrayExpress::Curator::MAGE qw(
+ unique_identifier
+ make_container
+);
+use ArrayExpress::Curator::Common qw(
+ get_filepath_from_uri
+ $RE_EMPTY_STRING
+ $RE_COMMENTED_STRING
+ $RE_SURROUNDED_BY_WHITESPACE
+);
+
+use base qw(ArrayExpress::MAGETAB::TabFile);
+
+# Accessor attributes.
+my %idf : ATTR( :name<idf>, :default<undef> );
+my %idf_filehandle : ATTR( :init_arg<idf_filehandle>, :default<undef> );
+my %dispatch : ATTR( :name<dispatch>, :default<{}> );
+my %sdrfs : ATTR( :name<sdrfs>, :default<[]> );
+my %protocol_accession_service : ATTR( :name<protocol_accession_service>, :default<undef> );
+
+# Singleton data.
+my %experiment : ATTR( :get<experiment>, :default<{}> );
+
+# Grouped data.
+my %factor : ATTR( :get<factors>, :default<[]> );
+my %person : ATTR( :get<people>, :default<[]> );
+my %publication : ATTR( :get<publications>, :default<[]> );
+my %protocol : ATTR( :get<protocols>, :default<[]> );
+my %termsource : ATTR( :get<termsources>, :default<[]> );
+my %qualitycontrol : ATTR( :get<qualitycontroltypes>, :default<[]> );
+my %replicate : ATTR( :get<replicatetypes>, :default<[]> );
+my %normalization : ATTR( :get<normalizationtypes>, :default<[]> );
+my %comment : ATTR( :get<comments>, :default<{}> );
+
+Readonly my $COMMENT_TAG => qr/\A \s* Comment \s* \[ ([^\]]+) \] \s* \z/ixms;
+
+#########################
+# Object initialization #
+#########################
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ # If we have a filename, great. If all we have is a filehandle
+ # then eol_char *must* be set because check_linebreaks can't be
+ # used. Linebreak checking must be the responsibility of the
+ # caller when filehandles are used.
+ unless ( $self->get_idf()
+ || ( $self->get_idf_filehandle() && $self->get_eol_char() ) ) {
+ confess("Error: MAGETAB::IDF object requires either filename or (filehandle and eol_char) attributes.");
+ }
+
+ unless ( $self->get_expt_accession() ) {
+ confess("Error: MAGETAB::IDF object requires expt_accession to be set.");
+ }
+
+ # This dispatch table is used to validate IDF tags and to populate
+ # internal data structures prior to MAGE generation.
+ $self->set_dispatch({
+ qr/Investigation *Title/i
+ => sub{ $self->add_singleton_datum(\%experiment, 'title', @_) },
+ qr/Date *Of *Experiment/i
+ => sub{ $self->add_singleton_datum(\%experiment, 'experimentdate', @_) },
+ qr/Public *Release *Date/i
+ => sub{ $self->add_singleton_datum(\%experiment, 'releasedate', @_) },
+ qr/Experiment *Description/i
+ => sub{ $self->add_singleton_datum(\%experiment, 'description', @_) },
+
+ qr/Experimental *Designs?/i
+ => sub{ $self->add_singleton_data(\%experiment, 'design', @_) },
+ qr/Experimental *Designs? *Term *Source *REF/i
+ => sub{ $self->add_singleton_data(\%experiment, 'termsource', @_) },
+ qr/Experimental *Designs? *Term *Accession *Numbers?/i
+ => sub{ $self->add_singleton_data(\%experiment, 'termaccession', @_) },
+
+ qr/Experimental *Factor *Names?/i
+ => sub{ $self->add_grouped_data(\%factor, 'name', @_) },
+ qr/Experimental *Factor *Types?/i
+ => sub{ $self->add_grouped_data(\%factor, 'type', @_) },
+ qr/Experimental *Factor *(Types?)? *Term *Source *REF/i
+ => sub{ $self->add_grouped_data(\%factor, 'termsource', @_) },
+ qr/Experimental *Factor *(Types?)? *Term *Accession *Numbers?/i
+ => sub{ $self->add_grouped_data(\%factor, 'termaccession', @_) },
+
+ qr/Person *Last *Names?/i
+ => sub{ $self->add_grouped_data(\%person, 'lastname', @_) },
+ qr/Person *First *Names?/i
+ => sub{ $self->add_grouped_data(\%person, 'firstname', @_) },
+ qr/Person *Mid *Initials?/i
+ => sub{ $self->add_grouped_data(\%person, 'midinitials', @_) },
+ qr/Person *Emails?/i
+ => sub{ $self->add_grouped_data(\%person, 'email', @_) },
+ qr/Person *Phones?/i
+ => sub{ $self->add_grouped_data(\%person, 'phone', @_) },
+ qr/Person *Fax(es)?/i
+ => sub{ $self->add_grouped_data(\%person, 'fax', @_) },
+ qr/Person *Address(es)?/i
+ => sub{ $self->add_grouped_data(\%person, 'address', @_) },
+ qr/Person *Affiliations?/i
+ => sub{ $self->add_grouped_data(\%person, 'affiliation', @_) },
+ qr/Person *Roles?/i
+ => sub{ $self->add_grouped_data(\%person, 'roles', @_) },
+ qr/Person *Roles? *Term *Source *REF/i
+ => sub{ $self->add_grouped_data(\%person, 'termsource', @_) },
+ qr/Person *Roles? *Term *Accession *Numbers?/i
+ => sub{ $self->add_grouped_data(\%person, 'termaccession', @_) },
+
+ qr/Quality *Control *Types?/i
+ => sub{ $self->add_grouped_data(\%qualitycontrol, 'type', @_) },
+ qr/Quality *Control *(Types?)? *Term *Source *REF/i
+ => sub{ $self->add_grouped_data(\%qualitycontrol, 'termsource', @_) },
+ qr/Quality *Control *(Types?)? *Term *Accession *Numbers?/i
+ => sub{ $self->add_grouped_data(\%qualitycontrol, 'termaccession', @_) },
+ qr/Replicate *Types?/i
+ => sub{ $self->add_grouped_data(\%replicate, 'type', @_) },
+ qr/Replicate *(Types?)? *Term *Source *REF/i
+ => sub{ $self->add_grouped_data(\%replicate, 'termsource', @_) },
+ qr/Replicate *(Types?)? *Term *Accession *Numbers?/i
+ => sub{ $self->add_grouped_data(\%replicate, 'termaccession', @_) },
+ qr/Normali[sz]ation *Types?/i
+ => sub{ $self->add_grouped_data(\%normalization, 'type', @_) },
+ qr/Normali[sz]ation *(Types?)? *Term *Source *REF/i
+ => sub{ $self->add_grouped_data(\%normalization, 'termsource', @_) },
+ qr/Normali[sz]ation *(Types?)? *Term *Accession *Numbers?/i
+ => sub{ $self->add_grouped_data(\%normalization, 'termaccession', @_) },
+
+ qr/PubMed *IDs?/i
+ => sub{ $self->add_grouped_data(\%publication, 'pubmedid', @_) },
+ qr/Publication *DOIs?/i
+ => sub{ $self->add_grouped_data(\%publication, 'doi', @_) },
+ qr/Publication *Authors? *Lists?/i
+ => sub{ $self->add_grouped_data(\%publication, 'authorlist', @_) },
+ qr/Publication *Titles?/i
+ => sub{ $self->add_grouped_data(\%publication, 'title', @_) },
+ qr/Publication *Status/i
+ => sub{ $self->add_grouped_data(\%publication, 'status', @_) },
+ qr/Publication *Status *Term *Source *REF/i
+ => sub{ $self->add_grouped_data(\%publication, 'termsource', @_) },
+ qr/Publication *Status *Term *Accession *Numbers?/i
+ => sub{ $self->add_grouped_data(\%publication, 'termaccession', @_) },
+
+ qr/Protocol *Names?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'name', @_) },
+ qr/Protocol *Types?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'type', @_) },
+ qr/Protocol *Descriptions?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'description', @_) },
+ qr/Protocol *Parameters?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'parameters', @_) },
+ qr/Protocol *Hardwares?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'hardware', @_) },
+ qr/Protocol *Softwares?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'software', @_) },
+ qr/Protocol *Contacts?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'contact', @_) },
+ qr/Protocol *(Types?)? *Term *Source *REF/i
+ => sub{ $self->add_grouped_data(\%protocol, 'termsource', @_) },
+ qr/Protocol *(Types?)? *Term *Accession *Numbers?/i
+ => sub{ $self->add_grouped_data(\%protocol, 'termaccession', @_) },
+
+ qr/Term *Source *Names?/i
+ => sub{ $self->add_grouped_data(\%termsource, 'name', @_) },
+ qr/Term *Source *Files?/i
+ => sub{ $self->add_grouped_data(\%termsource, 'file', @_) },
+ qr/Term *Source *Versions?/i
+ => sub{ $self->add_grouped_data(\%termsource, 'version', @_) },
+
+ qr/SDRF *Files?/i
+ => sub{ $self->add_sdrfs( @_) },
+ });
+
+ # ArrayExpress-specific tweak; AE wants these NVTs added
+ # regardless of whether they're specified or not. This is a
+ # departure from the MAGE-TAB specification, but can be easily
+ # deactivated by deleting the following two lines:
+ $self->add_comment('AEExperimentDisplayName', q{});
+ $self->add_comment('SecondaryAccession', q{});
+
+ return;
+}
+
+##################
+# Public methods #
+##################
+
+sub parse {
+
+ my ( $self ) = @_;
+
+ # Parse the IDF file into memory here.
+ my $array_of_rows = $self->read_as_arrayref();
+
+ # Check tags for duplicates, make sure that tags are recognized.
+ my $idf_hash = $self->validate_arrayref_tags($array_of_rows);
+
+ # Populate the IDF object's internal data structures.
+ while ( my ($tag, $values) = each %$idf_hash ) {
+ $self->dispatch($tag, @$values);
+ }
+
+ # FIXME store this somewhere?
+ my ( $mage, $experiment ) = $self->generate_mage();
+
+ return ( $mage, $experiment, $self->get_sdrfs() );
+}
+
+###################
+# Private methods #
+###################
+
+sub generate_mage : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Databases.
+ $self->create_termsources();
+
+ # Protocols.
+ foreach my $protocol_data ( @{ $self->get_protocols() } ) {
+ $self->create_protocol($protocol_data);
+ }
+
+ my $experiment = $self->create_experiment();
+
+ # MAGE top-level object.
+ my $mage = Bio::MAGE->new();
+
+ $mage->add_objects( [ $experiment ] );
+
+ foreach my $obj_type ( qw(termsource protocol people) ) {
+ my $method = "${obj_type}_bag";
+ $mage->add_objects( $self->$method() );
+ }
+
+ return ( $mage, $experiment );
+}
+
+sub create_termsources : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my @termsources;
+ TS_DATA:
+ foreach my $ts_data ( @{ $self->get_termsources() } ) {
+
+ # FIXME the Database authority:namespace tag hard-coded at the
+ # moment.
+ unless( defined( $ts_data->{name} ) ) {
+ if ( scalar( grep { defined $ts_data->{$_} } qw(version file) ) ) {
+ $self->raise_error("Error: Found a Term Source without a Name.\n");
+ }
+ else {
+ next TS_DATA;
+ }
+ }
+ my $identifier =
+ sprintf("%s:%s",
+ 'ebi.ac.uk:Database',
+ $ts_data->{name});
+ my $termsource = $self->termsource_bag(
+ $ts_data->{name},
+ {
+ identifier => $identifier,
+ name => $ts_data->{name},
+ version => $ts_data->{version},
+ uri => $ts_data->{file},
+ }
+ );
+ push @termsources, $termsource;
+ }
+
+ return \@termsources;
+}
+
+sub create_experiment : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my $factors = $self->create_factors();
+ my $people = $self->create_people();
+ my $publications = $self->create_publications();
+ my $design = $self->create_design($factors);
+
+ my $description = Bio::MAGE::Description::Description->new(
+ text => $self->get_experiment()->{description},
+ bibliographicReferences => $publications,
+ );
+
+ my $comments = $self->create_comments();
+ my $dates = $self->create_dates();
+
+ my $experiment = Bio::MAGE::Experiment::Experiment->new(
+ name => $self->get_experiment()->{title},
+ identifier => $self->get_expt_accession(),
+ experimentDesigns => [$design],
+ providers => $people,
+ descriptions => [$description],
+ propertySets => [@{ $comments }, @{ $dates }],
+ );
+
+ return $experiment;
+}
+
+sub create_comments : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my @comments;
+ while (my ($name, $value) = each %{ $self->get_comments() } ) {
+ my $comment = Bio::MAGE::NameValueType->new(
+ name => $name,
+ value => $value,
+ );
+ push @comments, $comment;
+ }
+
+ return \@comments;
+}
+
+sub create_dates : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Validate the dates here FIXME.
+ # NVT names are hard-coded for now FIXME.
+ my @dates;
+ if ( my $date = $self->get_experiment()->{releasedate} ) {
+ push @dates, Bio::MAGE::NameValueType->new(
+ name => 'ArrayExpressReleaseDate',
+ value => $date,
+ );
+ }
+ if ( my $date = $self->get_experiment()->{experimentdate} ) {
+ push @dates, Bio::MAGE::NameValueType->new(
+ name => 'ExperimentDate',
+ value => $date,
+ );
+ }
+
+ return \@dates;
+}
+
+sub create_design : PRIVATE {
+
+ my ( $self, $factors ) = @_;
+
+ my @types;
+ foreach my $value ( @{ $self->get_experiment()->{design} } ) {
+ my $termsource = shift ( @{ $self->get_experiment()->{termsource} } );
+ my $termaccno = shift ( @{ $self->get_experiment()->{termaccession} } );
+ my $oe = $self->create_ontologyentry(
+ 'ExperimentDesignType',
+ $value,
+ $termsource,
+ $termaccno,
+ );
+ push @types, $oe;
+ }
+
+ # Quality Control, Normalization and Replicate Types.
+ my $qc_description = $self->create_exptdescriptiontype(
+ 'QualityControlDescriptionType',
+ $self->get_qualitycontroltypes(),
+ );
+ my $norm_description = $self->create_exptdescriptiontype(
+ 'NormalizationDescriptionType',
+ $self->get_normalizationtypes(),
+ );
+ my $repl_description = $self->create_exptdescriptiontype(
+ 'ReplicateDescriptionType',
+ $self->get_replicatetypes(),
+ );
+
+ my $design = Bio::MAGE::Experiment::ExperimentDesign->new(
+ types => \@types,
+ experimentalFactors => $factors,
+ );
+
+ $design->setQualityControlDescription( $qc_description )
+ if $qc_description;
+ $design->setNormalizationDescription( $norm_description )
+ if $norm_description;
+ $design->setReplicateDescription( $repl_description )
+ if $repl_description;
+
+ return $design;
+}
+
+sub create_exptdescriptiontype : PRIVATE {
+
+ # Used to generate OE-containing Descriptions for QC,
+ # normalization and replicate types to attach to ExperimentDesign.
+ my ( $self, $category, $typelist ) = @_;
+
+ my @types;
+ foreach my $type_data ( @{ $typelist } ) {
+ my $type = $self->create_ontologyentry(
+ $category,
+ $type_data->{type},
+ $type_data->{termsource},
+ $type_data->{termaccession},
+ );
+ push @types, $type;
+ }
+
+ my $description;
+ if ( scalar @types ) {
+ $description = Bio::MAGE::Description::Description->new(
+ annotations => \@types,
+ );
+ }
+
+ return $description;
+}
+
+sub create_factors : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my @factors;
+
+ FACTOR_DATA:
+ foreach my $factor_data ( @{ $self->get_factors() } ) {
+
+ unless( defined( $factor_data->{name} ) ) {
+ if ( scalar( grep { defined $factor_data->{$_} }
+ qw(type termsource termaccession) ) ) {
+ $self->raise_error("Error: Found an Experimental Factor without a Name.\n");
+ }
+ else {
+ next FACTOR_DATA;
+ }
+ }
+ my $factor = $self->factor_bag(
+ $factor_data->{name}, $factor_data
+ );
+ push @factors, $factor;
+ }
+
+ return \@factors;
+}
+
+sub create_people : PRIVATE {
+
+ my ( $self ) = @_;
+
+ my @people;
+ foreach my $p_data ( @{ $self->get_people() } ) {
+
+ my @nameparts;
+ foreach my $field qw(firstname midinitials lastname) {
+ push @nameparts, $p_data->{$field} if $p_data->{$field};
+ }
+ unless( scalar @nameparts ) {
+ croak(
+ "Error: Found a Person without First Name, Last Name or Mid Initials.\n"
+ );
+ }
+ $p_data->{identifier} = sprintf("%s:%s.Person",
+ $self->get_id_prefix(),
+ join(q{ }, @nameparts));
+
+ my $person = $self->people_bag(
+ $p_data->{identifier}, $p_data
+ );
+
+ push @people, $person;
+ }
+
+ return \@people;
+}
+
+sub create_publications : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # FIXME PubMed database is hard-coded for the moment.
+ my $pubmed_db = Bio::MAGE::Description::Database->new(
+ name => 'Entrez PubMed',
+ identifier => 'ebi.ac.uk:Database:pubmed',
+ URI => 'http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=PubMed',
+ );
+
+ my @publications;
+ foreach my $article_data ( @{ $self->get_publications() } ) {
+
+ my (@parameters, @nvts, @pubmed_accns);
+
+ # FIXME MAGE-TAB v1.0 does not capture publication type
+ # (e.g. journal_article). Deferred until a later release.
+
+ if ( $article_data->{pubmedid} ) {
+ push @pubmed_accns, Bio::MAGE::Description::DatabaseEntry->new(
+ accession => $article_data->{pubmedid},
+ database => $pubmed_db,
+ );
+ }
+
+ # Nowhere to specify this in MAGE-TAB, but MAGE requires at
+ # least one parameter so we add this.
+ my $pubtype = $self->create_ontologyentry(
+ 'PublicationType',
+ 'journal_article',
+ );
+ push @parameters, $pubtype;
+
+ # FIXME "PublicationStatus" not in MO yet.
+ if ( $article_data->{status} ) {
+ my $status = $self->create_ontologyentry(
+ 'PublicationStatus',
+ $article_data->{status},
+ $article_data->{termsource},
+ $article_data->{termaccession},
+ );
+ push @parameters, $status;
+ }
+
+ if ( $article_data->{doi} ) {
+ my $doi = Bio::MAGE::NameValueType->new(
+ name => 'DOI',
+ value => $article_data->{doi},
+ );
+ push @nvts, $doi;
+ }
+
+ my $publication = Bio::MAGE::BQS::BibliographicReference->new(
+ authors => $article_data->{authorlist},
+ title => $article_data->{title},
+ accessions => \@pubmed_accns,
+ parameters => \@parameters,
+ propertySets => \@nvts,
+ );
+
+ push @publications, $publication;
+ }
+
+ return \@publications;
+}
+
+sub create_protocol : PRIVATE {
+
+ my ( $self, $protocol_data ) = @_;
+
+ unless( defined( $protocol_data->{name} ) ) {
+ if ( scalar( grep { defined $protocol_data->{$_} }
+ qw(type
+ description
+ parameters
+ hardware
+ software
+ contact
+ termsource
+ termaccession) ) ) {
+ $self->raise_error("Error: Found a Protocol without a Name.\n");
+ }
+ else {
+ return;
+ }
+ }
+
+ # First, give the protocol_accession_service callback the
+ # opportunity to reassign protocol accession.
+ $protocol_data->{accession} = $self->get_protocol_accession(
+ $protocol_data->{name},
+ );
+
+ # Then generate the protocol. This also handles parameters,
+ # software and hardware.
+ my $protocol = $self->protocol_bag(
+ $protocol_data->{name}, $protocol_data
+ );
+
+ return $protocol;
+}
+
+sub get_protocol_accession : PRIVATE {
+
+ my ( $self, $accession ) = @_;
+
+ if ( my $service = $self->get_protocol_accession_service() ) {
+ $accession = $service->(
+ $accession,
+ $self->get_expt_accession(),
+ );
+ }
+
+ return $accession;
+}
+
+sub read_as_arrayref : RESTRICTED {
+
+ # Method to parse the IDF object file into an array of
+ # arrayrefs. This method uses Text::CSV_XS to parse tab-delimited
+ # text.
+
+ my ( $self ) = @_;
+
+ # First, determine the file linebreak type and generate a CSV
+ # parser object.
+ my $csv_parser = $self->get_csv_parser();
+
+ # This is still required for Text::CSV_XS.
+ local $/ = $self->calculate_eol_char();
+
+ # Open the file
+ my $fh = $self->get_idf_filehandle();
+
+ my (@rows, $larry);
+
+ FILE_LINE:
+ while ( $larry = $csv_parser->getline($fh) ) {
+
+ # Skip empty lines.
+ my $line = join( q{}, @$larry );
+ next FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+ # Allow hash comments (FIXME NOT IN SPEC).
+ next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+ # Strip surrounding whitespace from each element.
+ foreach my $element ( @$larry ) {
+ $element =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+ }
+
+ # Strip off empty trailing values.
+ my $end_value;
+ until ( defined($end_value) && $end_value !~ /\A \s* \z/xms ) {
+ $end_value = pop(@$larry);
+ }
+ push @$larry, $end_value;
+
+ # Reset empty strings to undefs.
+ foreach my $value ( @$larry ) {
+ undef($value) if ( defined($value) && $value eq q{} );
+ }
+
+ push @rows, $larry;
+ }
+
+ # Check we've parsed to the end of the file.
+ my ( $error, $mess ) = $csv_parser->error_diag();
+ unless ( $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ croak(
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_parser->error_input(),
+ ),
+ );
+ }
+
+ return \@rows;
+}
+
+sub normalize_tag : PRIVATE {
+
+ # Takes a string, returns the lowercase, whitespace-stripped
+ # version.
+ my ( $self, $tag ) = @_;
+
+ $tag =~ s/\s+//g;
+ $tag = lc($tag);
+
+ return $tag
+}
+
+sub validate_arrayref_tags : RESTRICTED {
+
+ # Method to check the return value from read_as_arrayref to check
+ # for (a) duplicate tags, and (b) unrecognised tags. Returns a
+ # hash with keys corresponding to IDF tags and value arrayrefs
+ # containing the IDF annotation.
+
+ my ($self, $array_of_rows) = @_;
+
+ # Duplicate tag check. This is somewhat primitive at the moment,
+ # and can be fooled FIXME.
+ my (%seen);
+ foreach my $row ( @$array_of_rows ) {
+
+ # Two-dimensional hash; normalized then actual IDF tags.
+ my $normtag = $self->normalize_tag( $row->[0] );
+ $seen{ $normtag }{ $row->[0] } ++;
+ }
+ while ( my ($norm_tag, $idf_tags) = each %seen ) {
+
+ # Differently typed but identical tags.
+ if ( scalar (grep { defined $_ } values %$idf_tags ) > 1 ) {
+ my $tagstring = join(", ", keys %$idf_tags);
+ $self->raise_error(qq{Error: duplicated IDF tag(s): "$tagstring"});
+ }
+
+ # Identically typed duplicate tags.
+ while ( my ($idf_tag, $count) = each %$idf_tags ) {
+ if ( $count > 1 ) {
+ $self->raise_error(qq{Error: duplicated IDF tag: "$idf_tag"});
+ }
+ }
+ }
+
+ # Hash of row tag keys with rest-of-row arrayref values.
+ my %idf_hash = map
+ { $_->[0] => [ @{ $_ }[1 .. $#$_] ] }
+ @{ $array_of_rows };
+
+ # A list of acceptable tags, expressed as qr//
+ my @acceptable = keys %{ $self->get_dispatch() };
+ while ( my ( $tag, $values ) = each %idf_hash ) {
+
+ # N.B. acceptable tag REs may contain whitespace; no x option
+ # here.
+ next if $tag =~ /\A\s*$COMMENT_TAG\s*\z/ms;
+
+ # Check for recognised tags here.
+ unless ( first { $tag =~ /\A\s*$_\s*\z/ms } @acceptable ) {
+ $self->raise_error(qq{Error: unrecognized IDF tag(s): "$tag"});
+ }
+
+ # Empty Name tags are invalid and will cause fatal crashes
+ # later; we check for them here.
+ if ( $tag =~ m/name \s* \z/ixms ) {
+ foreach my $value ( @$values ) {
+ warn(
+ qq{Warning: IDF Name attribute "$tag" is empty.\n}
+ ) unless $value;
+ }
+ }
+ }
+
+ return \%idf_hash;
+}
+
+sub add_grouped_data : PRIVATE {
+
+ # Create an ordered set of data groups indexed by $i.
+ my ( $self, $group, $tag, @args ) = @_;
+
+ for ( my $i = 0; $i <= $#args; $i++ ) {
+ $group->{ident $self}[$i]{$tag} = $args[$i];
+ }
+
+ return;
+}
+
+sub add_singleton_data : PRIVATE {
+
+ # Record a 1:n object:args relationship.
+ my ( $self, $object, $tag, @args ) = @_;
+
+ # Make a copy of @args, just in case.
+ $object->{ident $self}{$tag} = [ @args ];
+
+ return;
+}
+
+sub add_singleton_datum : PRIVATE {
+
+ # Record a 1:1 object:arg relationship.
+ my ( $self, $object, $tag, $arg ) = @_;
+
+ $object->{ident $self}{$tag} = $arg;
+
+ return;
+}
+
+sub add_comment : PRIVATE {
+
+ # Comments are currently processed at the level of experiment
+ # only.
+ my ( $self, $name, $value ) = @_;
+
+ $comment{ident $self}{$name} = $value;
+
+ return;
+}
+
+sub add_sdrfs : PRIVATE {
+
+ # Store the list of SDRF files.
+ my ( $self, @sdrf_uri_strings ) = @_;
+
+ my @sdrfs = map { get_filepath_from_uri(
+ $_, $self->get_source_directory(),
+ ) } @sdrf_uri_strings;
+
+ $self->set_sdrfs(\@sdrfs);
+
+ return;
+}
+
+sub retrieve_sub : RESTRICTED {
+
+ my ( $self, $tag ) = @_;
+
+ my $rc;
+
+ while ( my ( $key, $sub ) = each %{ $self->get_dispatch() } ) {
+
+ # $key may contain whitespace, no x option here.
+ if ( $tag =~ /\A\s*$key\s*\z/ms ) {
+ $rc = $sub;
+ }
+
+ # Have to loop through the rest of the list to reset while()
+ # on the hash.
+ }
+
+ return $rc;
+}
+
+sub dispatch : PRIVATE {
+
+ my ( $self, $tag, @args ) = @_;
+
+ unless (defined $tag) {
+ confess("Error: get_dispatch needs a defined tag name.");
+ }
+
+ my $sub = $self->retrieve_sub( $tag );
+ unless (defined $sub && ref $sub eq 'CODE') {
+ if ( my ( $commentname ) = ( $tag =~ /\A\s*$COMMENT_TAG\s*\z/ms ) ) {
+ $self->add_comment($commentname, @args);
+ }
+ else {
+
+ # This should have been caught in validate_arrayref_tags
+ croak(qq{Error: Cannot parse the IDF tag: "$tag".});
+ }
+ }
+
+ return $sub ? $sub->(@args) : undef;
+}
+
+sub get_filename : RESTRICTED {
+
+ # Method to pass the IDF filename to check_linebreaks, called from
+ # SUPER->calculate_eol_char()
+ my ( $self ) = @_;
+
+ return $self->get_idf();
+}
+
+sub get_idf_filehandle : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ unless ( $idf_filehandle{ident $self} ) {
+ if ( my $file = $self->get_idf() ) {
+ open (my $fh, '<', $file)
+ or croak("Error opening IDF for reading: $!\n");
+ $idf_filehandle{ident $self} = $fh;
+ }
+ else {
+ confess("Error: No IDF filename given.");
+ }
+ }
+ return $idf_filehandle{ident $self};
+}
+
+1;
diff --git a/lib/ArrayExpress/MAGETAB/SDRF.pm b/lib/ArrayExpress/MAGETAB/SDRF.pm
new file mode 100644
index 0000000..512aaed
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/SDRF.pm
@@ -0,0 +1,3486 @@
+#!/usr/bin/env perl
+#
+# Module providing SDRF parsing functions to the ArrayExpress MAGE-TAB
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: SDRF.pm 2069 2008-06-04 14:33:52Z tfrayner $
+
+package ArrayExpress::MAGETAB::SDRF;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use English qw( -no_match_vars );
+use Parse::RecDescent;
+use Storable qw(dclone);
+use List::Util qw(first);
+use Readonly;
+use File::Basename;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(
+ get_filepath_from_uri
+ $RE_EMPTY_STRING
+ $RE_COMMENTED_STRING
+ $RE_SURROUNDED_BY_WHITESPACE
+);
+use ArrayExpress::Curator::MAGE qw(unique_identifier);
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $AE_LABELCOMPOUND_PREFIX
+ $AE_CHANNEL_PREFIX
+);
+use ArrayExpress::Datafile;
+
+use base qw(ArrayExpress::MAGETAB::TabFile);
+
+Readonly my $BLANK => qr/\A [ ]* \z/xms;
+
+# The Parse::RecDescent grammar is stored in the __DATA__ section, below.
+Readonly my $GRAMMAR => join("\n", <DATA> );
+
+my %sdrf : ATTR( :name<sdrf>, :default<undef> );
+my %sdrf_filehandle : ATTR( :init_arg<sdrf_filehandle>, :default<undef> );
+my %datafiles_cache : ATTR( :default<{}> );
+my %row_parser : ATTR( :default<undef> );
+my %skip_datafiles : ATTR( :name<skip_datafiles>, :default<undef> );
+my %native_filetypes : ATTR( :get<native_filetypes>, :default<{}> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ # If we have a filename, great. If all we have is a filehandle
+ # then eol_char *must* be set because check_linebreaks can't be
+ # used. Linebreak checking must be the responsibility of the
+ # caller when filehandles are used.
+ unless ( $self->get_sdrf()
+ || ( $self->get_sdrf_filehandle() && $self->get_eol_char() ) ) {
+ croak("Error: MAGETAB::SDRF object requires either filename or (filehandle and eol_char) attributes.");
+ }
+
+ return;
+}
+
+sub parse {
+
+ my ( $self ) = @_;
+
+ # This has to be set for Text::CSV_XS.
+ local $/ = $self->calculate_eol_char();
+
+ my $row_parser = $self->parse_header();
+ my $sdrf_fh = $self->get_sdrf_filehandle();
+ my $csv_parser = $self->get_csv_parser();
+
+ my $larry;
+
+ # Run through the rest of the file with the row-level parser.
+ FILE_LINE:
+ while ( $larry = $csv_parser->getline($sdrf_fh) ) {
+
+ # Skip empty lines.
+ my $line = join( q{}, @$larry );
+ next FILE_LINE if ( $line =~ $RE_EMPTY_STRING );
+
+ # Allow hash comments (FIXME NOT IN SPEC).
+ next FILE_LINE if ( $line =~ $RE_COMMENTED_STRING );
+
+ # Strip surrounding whitespace from each element.
+ foreach my $element ( @$larry ) {
+ $element =~ s/$RE_SURROUNDED_BY_WHITESPACE/$1/xms;
+ }
+
+ # FIXME some error handling wouldn't go amiss here.
+ $row_parser->(@$larry);
+ }
+
+ # Check we've parsed to the end of the file.
+ my ( $error, $mess ) = $csv_parser->error_diag();
+ unless ( $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ croak(
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_parser->error_input(),
+ ),
+ );
+ }
+
+ $self->simplify_single_channels();
+
+ $self->propagate_fvs_to_datafiles();
+
+ return $self->get_datafiles();
+}
+
+sub parse_header {
+
+ # Generates a row-level parser function based on the first line in the SDRF.
+
+ # $::sdrf is a qualified $self, used below in the Parse::RecDescent grammar.
+ ( $::sdrf ) = @_;
+
+ # Globals to record the previous material and event (bioassay) in
+ # the chain.
+ our ($previous_material,
+ $previous_event,
+ $previous_data,
+ $array_accession,
+ $channel);
+
+ # FIXME add support for REF:namespaces (currently accepted, but
+ # discarded for termsource).
+
+ # Check linebreaks; get first line as $header_string and generate
+ # row-level parser.
+ my $csv_parser = $::sdrf->get_csv_parser();
+ my $sdrf_fh = $::sdrf->get_sdrf_filehandle();
+
+ # Get the header line - the first non-empty, non-comment line in the file.
+ my ( $header_string, $harry );
+ HEADERLINE:
+ while ( $harry = $csv_parser->getline($sdrf_fh) ) {
+
+ $header_string = join( qq{\x{0}}, @$harry );
+
+ # Skip empty and commented lines.
+ if ( $header_string
+ && $header_string !~ $RE_EMPTY_STRING
+ && $header_string !~ $RE_COMMENTED_STRING ) {
+
+ # We've found the header line. Add a starting skip
+ # character for the benefit of the parser.
+ $header_string = qq{\x{0}} . $header_string;
+ last HEADERLINE;
+ }
+ }
+
+ # Check we have no CSV parse errors.
+ my ( $error, $mess ) = $csv_parser->error_diag();
+ unless ( $harry || $error == 2012 ) { # 2012 is the Text::CSV_XS EOF code.
+ croak(
+ sprintf(
+ "Error in tab-delimited format: %s. Bad input was:\n\n%s\n",
+ $mess,
+ $csv_parser->error_input(),
+ ),
+ );
+ }
+
+ # N.B. MAGE-TAB 1.1 SDRFs can actually be empty, so an empty
+ # $header_string is valid at this point.
+
+ # FIXME This next line is an egregious hack; it reopens an
+ # (*undocumented*) filehandle in the Parse::RecDescent module to a
+ # local variable so we can capture STDERR. I tried doing this the
+ # correct way (open(local *STDERR, '>', \$parse_error)) but
+ # couldn't get it to work properly. We'll want to revisit this at
+ # some point.
+ open( *Parse::RecDescent::ERROR, '>', \(my $parse_error) )
+ or croak("Error: unable to redirect STDERR for MAGE-TAB header parsing.");
+
+ # Set the token separator character.
+ $Parse::RecDescent::skip = ' *\x{0} *';
+
+ # FIXME most of these are removable once development is advanced.
+ $::RD_ERRORS++; # unless undefined, report fatal errors
+ $::RD_WARN++; # unless undefined, also report non-fatal problems
+ $::RD_HINT++; # if defined, also suggestion remedies
+
+ # Generate the grammar parser first.
+ my $parser = Parse::RecDescent->new($GRAMMAR) or die("Bad grammar!");
+
+ # The parser should return a function which can process each SDRF
+ # row as an array (row-level parser).
+ my $row_parser = $parser->header($header_string);
+
+ # Typically the grammar will generate some error messages before
+ # we get here. N.B. $! doesn't really give a useful error here so
+ # we don't use it.
+ unless (defined $row_parser) {
+ die(
+ qq{\nERROR parsing header line:\n} . $parse_error
+ );
+ }
+
+ return $row_parser;
+}
+
+sub generate_id_template {
+
+ my ( $self, $name ) = @_;
+
+ # FIXME consider unique_identifier here.
+ my $identifier_template = sprintf(
+ "%s:%s.%s",
+ $self->get_id_prefix(),
+ $self->get_expt_accession(),
+ $name,
+ );
+
+ return $identifier_template;
+}
+
+sub create_source {
+
+ my ( $self, $name ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( $name );
+
+ my $source = $self->source_bag(
+ $name,
+ {
+ name => $name,
+ identifier_template => $identifier_template,
+ material_type => 'unknown',
+ },
+ );
+
+ return $source;
+}
+
+sub create_sample {
+
+ my ( $self, $name, $previous, $protocolapps ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( $name );
+
+ my $sample = $self->sample_bag(
+ $name,
+ {
+ name => $name,
+ identifier_template => $identifier_template,
+ material_type => 'unknown',
+ },
+ );
+
+ if ( ( $protocolapps && scalar @{ $protocolapps } ) || $previous ) {
+ my $treatments = $self->create_material_treatments(
+ $protocolapps,
+ $previous,
+ 'specified_biomaterial_action',
+ );
+ $self->add_treatments_to_material( $treatments, $sample );
+ }
+
+ return $sample;
+}
+
+sub create_extract {
+
+ my ( $self, $name, $previous, $protocolapps ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( $name );
+
+ my $extract = $self->extract_bag(
+ $name,
+ {
+ name => $name,
+ identifier_template => $identifier_template,
+ material_type => 'unknown',
+ },
+ );
+
+ if ( ( $protocolapps && scalar @{ $protocolapps } ) || $previous ) {
+ my $treatments = $self->create_material_treatments(
+ $protocolapps,
+ $previous,
+ 'nucleic_acid_extraction',
+ );
+ $self->add_treatments_to_material( $treatments, $extract );
+ }
+
+ return $extract;
+}
+
+sub create_labeled_extract {
+
+ my ( $self, $name, $previous, $protocolapps ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( $name );
+
+ my $le = $self->labeledextract_bag(
+ $name,
+ {
+ name => $name,
+ identifier_template => $identifier_template,
+ material_type => 'unknown',
+ },
+ );
+
+ if ( ( $protocolapps && scalar @{ $protocolapps } ) || $previous ) {
+ my $treatments = $self->create_material_treatments(
+ $protocolapps,
+ $previous,
+ 'labeling',
+ );
+ $self->add_treatments_to_material( $treatments, $le );
+ }
+
+ return $le;
+}
+
+sub create_label {
+
+ my ( $self, $dyename, $termsource, $accession, $le ) = @_;
+
+ return if ( $dyename =~ $BLANK );
+
+ my $oe;
+ if ( $termsource ) {
+ $oe = $self->create_ontologyentry(
+ 'LabelCompound',
+ $dyename,
+ $termsource,
+ $accession,
+ );
+ }
+
+ my $label = $self->label_bag(
+ $dyename,
+ {
+ dye => $dyename,
+ indices => $oe ? [ $oe ] : undef,
+ },
+ );
+
+ $self->add_label_to_le( $label, $le ) if $le;
+
+ return $label;
+}
+
+sub create_protocolapplication {
+
+ my ( $self, $name, $namespace, $termsource, $accession ) = @_;
+
+ # FIXME do something more with Term Source, accession?
+
+ return if ( $name =~ $BLANK );
+
+ $name = ( $namespace || q{} ) . $name;
+
+ my $args;
+ if ( $self->get_in_relaxed_mode() ) {
+ $args = { accession => $name };
+ }
+
+ my $protocol = $self->protocol_bag( $name, $args );
+
+ # Protocol not found;
+ unless ( $protocol ) {
+
+ # If we have a valid namespace or termsource, let it through.
+ if ( $namespace ) {
+ $protocol = $self->protocol_bag(
+ $name, { accession => $name },
+ )
+ }
+ elsif ( $termsource ) {
+
+ my $args;
+ if ( $self->get_in_relaxed_mode() ) {
+ $args = {name => $termsource,
+ identifier => "ebi.ac.uk:Database:$termsource"};
+ }
+
+ if ( $namespace || $self->termsource_bag( $termsource, $args ) ) {
+ $protocol = $self->protocol_bag(
+ $name, { accession => $accession || $name }, # FIXME not sure about $accession here.
+ )
+ }
+ else {
+ croak(qq{Error: Protocol Name "$name" not found,}
+ . qq{ and Term Source Name "$termsource" is invalid.\n});
+ }
+ }
+ else {
+ croak("Error: Protocol Name not found: $name\n");
+ };
+ }
+
+ # FIXME add Hardware, SoftwareApplications, since we have the
+ # protocol.
+ my $protocol_app
+ = Bio::MAGE::Protocol::ProtocolApplication->new(
+ protocol => $protocol,
+ activityDate => 'n/a',
+ );
+
+ return $protocol_app;
+}
+
+sub create_parametervalue {
+
+ my ( $self, $paramname, $value, $protocolapp ) = @_;
+
+ return if ( $value =~ $BLANK );
+
+ my $protocol = $protocolapp->getProtocol();
+ my $protname = $protocol->getName() || q{};
+
+ # Unless running in "relaxed" mode, this requires that all
+ # parameters be declared in the IDF FIXME.
+ my $args;
+ if ( $self->get_in_relaxed_mode() ) {
+ $args = { identifier => $paramname };
+ }
+ my $parameter = $self->parameter_bag( "$paramname.$protname", $args );
+
+ unless ( $parameter ) {
+ croak(
+ qq{Error: Parameter Name "$paramname" not found for protocol "$protname"\n}
+ );
+ }
+
+ my $parameterval
+ = Bio::MAGE::Protocol::ParameterValue->new(
+ parameterType => $parameter,
+ value => $value,
+ );
+
+ $self->add_parameterval_to_protocolapp(
+ $parameterval,
+ $protocolapp,
+ ) if $protocolapp;
+
+ return $parameterval;
+}
+
+sub add_value_to_parameter {
+
+ my ( $self, $parameter, $termsource, $accession ) = @_;
+
+ return if ( $termsource =~ $BLANK || ! $parameter );
+
+ # Pre-check that the termsource is valid. We don't actually use
+ # the database object returned.
+ my $args;
+ if ( $self->get_in_relaxed_mode() ) {
+ $args = {name => $termsource,
+ identifier => "ebi.ac.uk:Database:$termsource"};
+ }
+ my $database = $self->termsource_bag( $termsource, $args )
+ or croak(qq{Error: cannot find Term Source Name "$termsource"\n});
+
+ # Create NVTs; this is the best we can do in MAGE.
+ my $nvt = $self->create_nvt(
+ 'Term Source Name',
+ $termsource,
+ undef,
+ $parameter,
+ );
+ if ( defined( $accession ) && $accession !~ $BLANK ) {
+ $self->create_nvt(
+ 'Term Accession Number',
+ $accession,
+ undef,
+ $nvt,
+ );
+ }
+
+ return;
+}
+
+sub create_unit {
+
+ my ( $self, $type, $name, $termsource, $accession ) = @_;
+
+ # FIXME maybe do something with Term Source ?
+
+ return if ( $name =~ $BLANK );
+
+ my $unit;
+
+ # Don't propagate errors here to any signal handler.
+ my $sighandler = $SIG{__DIE__};
+ delete $SIG{__DIE__};
+
+ { # Create the Unit, or fall back to QuantityUnit.
+ local $EVAL_ERROR;
+
+ UNITCLASS:
+ until ( ! $EVAL_ERROR && $unit ) {
+ eval {
+ $unit = ("Bio::MAGE::Measurement::$type")->new();
+ };
+ if ( $EVAL_ERROR ) {
+
+ # Fall-through default is generic QuantityUnit
+ warn (
+ "Warning: $type is not a MAGE Unit subclass."
+ . " Creating generic QuantityUnit.\n"
+ );
+
+ $type = 'QuantityUnit';
+ next UNITCLASS;
+ }
+ }
+ }
+ { # Attempt to set the UnitNameCV.
+ local $EVAL_ERROR;
+ eval {
+ $unit->setUnitNameCV($name);
+ };
+ if ( $EVAL_ERROR ) {
+ warn (
+ "Warning: $name is not a valid MAGE UnitNameCV here."
+ . " Adding term as UnitName instead.\n"
+ );
+ $unit->setUnitName($name);
+ $unit->setUnitNameCV('other');
+ }
+ }
+
+ # Reinstate signal handler.
+ $SIG{__DIE__} = $sighandler if $sighandler;
+
+ return $unit;
+}
+
+sub create_nested_oe_measurement {
+
+ # Create a nested OE measurement value, return the top-level OE
+ # and the Measurement-level OE. Units are handled elsewhere.
+
+ my ( $self, $category, $value ) = @_;
+
+ return if ( $value =~ $BLANK );
+
+ my $oe_meas_val = Bio::MAGE::Description::OntologyEntry->new(
+ category => 'has_value',
+ value => $value,
+ );
+
+ my $oe_meas_has_val = Bio::MAGE::Description::OntologyEntry->new(
+ category => 'has_value',
+ value => 'has_value',
+ associations => [$oe_meas_val],
+ );
+
+ my $oe_meas = Bio::MAGE::Description::OntologyEntry->new(
+ category => 'Measurement',
+ value => 'Measurement',
+ associations => [$oe_meas_has_val],
+ );
+
+ my $oe_has_meas = Bio::MAGE::Description::OntologyEntry->new(
+ category => 'has_measurement',
+ value => 'has_measurement',
+ associations => [$oe_meas],
+ );
+
+ my $oe = Bio::MAGE::Description::OntologyEntry->new(
+ category => $category,
+ value => $category,
+ associations => [$oe_has_meas],
+ );
+
+ return ($oe, $oe_meas);
+}
+
+sub create_nested_oe_unit {
+
+ my ( $self, $type, $name, $termsource, $accession, $oe_meas ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $oe_unit_subclass_val = $self->create_ontologyentry(
+ $type,
+ $name,
+ $termsource,
+ $accession,
+ );
+
+ my $oe_unit_subclass = Bio::MAGE::Description::OntologyEntry->new(
+ category => $type,
+ value => $type,
+ associations => [$oe_unit_subclass_val],
+ );
+
+ my $oe_unit = Bio::MAGE::Description::OntologyEntry->new(
+ category => 'Unit',
+ value => 'Unit',
+ associations => [$oe_unit_subclass],
+ );
+
+ my $oe_has_units = Bio::MAGE::Description::OntologyEntry->new(
+ category => 'has_units',
+ value => 'has_units',
+ associations => [$oe_unit],
+ );
+
+ $oe_meas->addAssociations($oe_has_units);
+
+ return $oe_meas;
+}
+
+sub create_performer {
+
+ my ( $self, $performername, $protocolapp ) = @_;
+
+ return if ( $performername =~ $BLANK );
+
+ # This is very basic at the moment. Maybe FIXME to fully parse
+ # performer name?
+ my $identifier = sprintf("%s:%s.Person",
+ $self->get_id_prefix(),
+ $performername);
+
+ my $performer = $self->people_bag(
+ $performername, {
+ identifier => $identifier,
+ name => $performername,
+ roles => 'investigator',
+ },
+ );
+
+ $self->add_performer_to_protocolapp(
+ $performer,
+ $protocolapp,
+ ) if $protocolapp;
+
+ return $performer;
+}
+
+sub create_provider {
+
+ my ( $self, $providername, $source ) = @_;
+
+ return if ( $providername =~ $BLANK );
+
+ # This is very basic at the moment. Maybe FIXME to fully parse
+ # provider name?
+ my $identifier = sprintf("%s:%s.Person",
+ $self->get_id_prefix(),
+ $providername);
+
+ my $provider = $self->people_bag(
+ $providername, {
+ identifier => $identifier,
+ name => $providername,
+ roles => 'biomaterial_provider',
+ },
+ );
+
+ $self->add_provider_to_source(
+ $provider,
+ $source,
+ ) if $source;
+
+ return $provider;
+}
+
+sub extract_protocolapps_by_type : PRIVATE {
+
+ my ( $self, $protocolapps, $desired_types ) = @_;
+
+ my ( @wanted_apps, @other_apps );
+
+ # FIXME? at the moment we just look at protocol type for
+ # disambiguation.
+ foreach my $app ( @{ $protocolapps || [] } ) {
+ my $protocol = $app->getProtocol();
+ my $oe = $protocol->getType();
+ my $is_hyb;
+ if ( $oe ) {
+ my $type = $oe->getValue();
+ if ( first { $type eq $_ } @{ $desired_types || [] } ) {
+ push (@wanted_apps, $app);
+ $is_hyb++;
+ }
+ }
+ unless ( $is_hyb ) {
+ push (@other_apps, $app);
+ }
+ }
+
+ return ( \@wanted_apps, \@other_apps );
+}
+
+sub create_hybridization {
+
+ my ( $self, $name, $previous, $protocolapps, $channel ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( "hyb.$name" );
+
+ $channel ||= 'Unknown';
+ my $label = $self->label_bag( $channel, {dye => $channel} );
+
+ my $hybridization = $self->pba_bag(
+ $name,
+ {
+ name => $name,
+ derived_from => $previous,
+ label => $label,
+ identifier_template => $identifier_template,
+ hyb_protocolapps => [ @{ $protocolapps } ], # needs to be a copy.
+ },
+ );
+
+ return $hybridization;
+}
+
+sub create_assay {
+
+ my ( $self, $name, $previous, $protocolapps, $channel ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( "assay.$name" );
+
+ $channel ||= 'Unknown';
+ my $label = $self->label_bag( $channel, {dye => $channel} );
+
+ my $assay = $self->pba_bag(
+ $name,
+ {
+ name => $name,
+ derived_from => $previous,
+ label => $label,
+ identifier_template => $identifier_template,
+ hyb_protocolapps => [ @{ $protocolapps } ], # needs to be a copy.
+ is_assay => 1,
+ },
+ );
+
+ return $assay;
+}
+
+sub create_array {
+
+ my ( $self, $accession, $namespace, $termsource, $alt_accession, $pba ) = @_;
+
+ # accession is the term in the Array Design REF column;
+ # alt_accession is the optional contents of the Term Accession
+ # Number column; we use it preferentially where given.
+
+ return if ( $accession =~ $BLANK );
+
+ $accession = $alt_accession || ( ( $namespace || q{} ) . $accession );
+
+ my $arraydesign = $self->arraydesign_bag(
+ $accession,
+ {
+ accession => $accession,
+ termsource => $termsource,
+ },
+ );
+
+ my $hybname = $pba->getName();
+
+ my $identifier_template = $self->generate_id_template( $hybname );
+
+ my $array = $self->array_bag(
+ $hybname,
+ {
+ identifier_template => $identifier_template,
+ arraydesign => $arraydesign,
+ armanuf_bag => $self->get_bags->{armanuf},
+ },
+ );
+
+ my $hyb = $pba->getBioAssayCreation();
+
+ $hyb->setArray( $array );
+
+ return $array;
+}
+
+sub create_scan {
+
+ my ( $self, $name, $hyb_pba, $protocolapps, $channel, $previous_material ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ # Unless $hyb_pba is a true hyb-level PBA, make a new one.
+ my @old_pbas;
+ unless ( $hyb_pba
+ && $hyb_pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay')
+ && $hyb_pba->getBioAssayCreation() ) {
+
+ my $hybname;
+ if ( $hyb_pba
+ && $hyb_pba->can('getName')
+ && $hyb_pba->getName() ) {
+ $hybname = $hyb_pba->getName();
+ }
+ my $hyb_protoapps;
+ ( $hyb_protoapps, $protocolapps )
+ = $self->extract_protocolapps_by_type(
+ $protocolapps,
+ [ 'hybridization' ],
+ );
+ $hyb_pba = $self->create_hybridization(
+ $hybname || "hyb $name",
+ $previous_material,
+ $hyb_protoapps,
+ $channel,
+ );
+ @old_pbas = $hyb_pba;
+ }
+
+ unless ( $hyb_pba && $hyb_pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay') ) {
+ croak("Error: PBA is incorrect type: " . ref $hyb_pba);
+ }
+
+ # If this is truly a virgin $hyb_pba, we need to remove the
+ # original BioAssayTreatment generated by create_hybridization.
+ my @cleaned_bats;
+ foreach my $bat ( @{ $hyb_pba->getBioAssayTreatments || [] } ) {
+ unless ( $bat->getTarget()->getIdentifier eq $hyb_pba->getIdentifier() ) {
+ push @cleaned_bats, $bat
+ }
+ }
+ $hyb_pba->setBioAssayTreatments(\@cleaned_bats);
+
+ my $identifier_template = $self->generate_id_template( "scan.$name" );
+
+ # Note the hashref below *should* be unnecessary, but it's
+ # included here just in case.
+ $channel ||= 'Unknown';
+ my $label = $self->label_bag( $channel, {dye => $channel} );
+
+ # Create a channel-specific PBA.
+ my $channel_pba = $self->create_perchannel_pba(
+ $identifier_template,
+ $channel,
+ $label,
+ $hyb_pba,
+ $name,
+ );
+
+ # Create/update the merged PBA here. Note that the .merged suffix
+ # convention is assumed in other modules for mapping the hyb PBA
+ # to the merged PBA and vice versa.
+ my $merged_pba = $self->extended_pba_bag(
+ $name,
+ {
+ identifier_template => $identifier_template,
+ name => $name,
+ label => $label,
+ scan_protocolapps => [ @{ $protocolapps } ], # needs to be a copy.
+ },
+ );
+
+ # Link the merged and channel-specific PBAs.
+ my $scan_id_template = "$identifier_template.$channel.merged";
+ $channel_pba->setBioAssayTreatments(
+ [
+ $self->new_scan(
+ {
+ identifier_template => $scan_id_template,
+ name => $name,
+ pba => $merged_pba,
+ target => $merged_pba,
+ },
+ )
+ ]
+ );
+
+ return wantarray
+ ? ( $merged_pba, $channel_pba, @old_pbas )
+ : $merged_pba;
+}
+
+sub create_image {
+
+ my ( $self, $imagefile, $pba, $protocolapps, $channel, $previous_material ) = @_;
+
+ return if ( $imagefile =~ $BLANK );
+
+ # Unless $pba is a true scan-level PBA, make a new one.
+ # ( Scanning PBAs have no BAC; FIXME check that the BAT target is $pba? ).
+ my @old_pbas;
+ unless ( $pba
+ && $pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay')
+ && ! $pba->getBioAssayCreation() ) {
+
+ my $scanname;
+ if ( $pba
+ && $pba->can('getName')
+ && $pba->getName() ) {
+ $scanname = $pba->getName();
+ }
+ my $channel_pba;
+ ($pba, $channel_pba, @old_pbas) = $self->create_scan(
+ $scanname || $imagefile,
+ $pba,
+ $protocolapps,
+ $channel,
+ $previous_material,
+ );
+ push( @old_pbas, $pba, $channel_pba );
+ }
+
+ my $identifier_template = $self->generate_id_template( $imagefile );
+
+ # FIXME no support for ImageFormat OEs as yet (not in
+ # specification). We attempt here to derive it from the image
+ # filename extension.
+ my %known = (
+ tif => 'TIFF',
+ tiff => 'TIFF',
+ jpg => 'JPEG',
+ jpeg => 'JPEG',
+ png => 'PNG',
+ gif => 'GIF',
+ );
+
+ my $format = 'unknown';
+ if ( my ( $ext ) = ( $imagefile =~ m/\.(\w{3,4})$/ ) ) {
+ if ( my $term = $known{lc($ext)} ) {
+ $format = $term;
+ }
+ }
+
+ my $image = $self->image_bag(
+ $imagefile,
+ {
+ identifier_template => $identifier_template,
+ uri => $imagefile,
+ format => $format,
+ },
+ );
+
+ $self->add_images_to_pba( [ $image ], $pba )
+ if $pba;
+
+ return wantarray
+ ? ( $image, @old_pbas )
+ : $image;
+}
+
+sub create_raw_data_file {
+
+ my ( $self,
+ $name,
+ $pba,
+ $protocolapps,
+ $array_accession,
+ $channel,
+ $previous_material, ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( $name );
+ my $filename = "$name.proc";
+
+ # Allow Affy CEL to be coded as native when skip_datafiles is
+ # used.
+ if ( $self->get_skip_datafiles() ) {
+ $filename = $name;
+ if ( $name =~ /\.CEL \z/ixms ) {
+ $self->add_native_filetypes( $filename, 'CEL' );
+ }
+ }
+
+ my $mbad = $self->mbad_bag(
+ $name,
+ {
+ name => $name,
+ filename => $filename,
+ identifier_template => $identifier_template,
+ }
+ );
+
+ my ( $mba, @old_pbas) = $self->create_feature_extraction(
+ $name,
+ $pba,
+ $protocolapps,
+ $channel,
+ $previous_material,
+ $mbad,
+ );
+
+ unless ( $mbad->getBioAssayDimension() ) {
+ my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+ identifier => "$identifier_template.MeasuredBioAssayDimension",
+ bioAssays => [ $mba ],
+ );
+ $mbad->setBioAssayDimension($bad);
+ }
+
+ my $datafile = $self->create_datafile(
+ $name,
+ 'raw',
+ $mbad,
+ $array_accession,
+ );
+
+ $self->add_datafiles( $datafile );
+
+ return wantarray ? ( $mba, $mbad, @old_pbas ) : $mba;
+}
+
+sub create_feature_extraction : PRIVATE {
+
+ my ( $self,
+ $name,
+ $pba,
+ $protocolapps,
+ $channel,
+ $previous_material,
+ $mbad, ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ # Unless $pba is a true scan-level PBA, make a new one.
+ # ( Scanning PBAs have no BAC; FIXME check that the BAT target is $pba? ).
+ my @old_pbas;
+ unless ( $pba
+ && $pba->isa('Bio::MAGE::BioAssay::PhysicalBioAssay')
+ && ! $pba->getBioAssayCreation() ) {
+
+ my $scan_protoapps;
+ ( $scan_protoapps, $protocolapps )
+ = $self->extract_protocolapps_by_type(
+ $protocolapps,
+ [ 'image_acquisition', 'hybridization' ]
+ );
+
+ # Scan name incorporates hyb name if available; this will help
+ # when creating Illumina scan objects.
+ my $scanname;
+ if ( $pba
+ && $pba->can('getName')
+ && $pba->getName() ) {
+ $scanname = $pba->getName();
+ }
+ my $channel_pba;
+ ($pba, $channel_pba, @old_pbas) = $self->create_scan(
+ $scanname || $name,
+ $pba,
+ $scan_protoapps,
+ $channel,
+ $previous_material,
+ );
+ push( @old_pbas, $pba, $channel_pba );
+ }
+
+ my $identifier_template = $self->generate_id_template( $name );
+
+ my $mba = $self->mba_bag(
+ $name,
+ {
+ identifier_template => $identifier_template,
+ name => $name,
+ physical_bioassay => $pba,
+ protocolapps => [ @{ $protocolapps } ], # needs to be a copy.
+ measured_bioassay_data => $mbad,
+ },
+ );
+
+ return wantarray ? ( $mba, @old_pbas ) : $mba;
+}
+
+sub create_normalization {
+
+ my ( $self,
+ $name,
+ $previous_event,
+ $protocolapps,
+ $channel,
+ $previous_material, ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ # Unless $previous_event is at least MBA or DBA, make a new MBA.
+ my @old_bioassays;
+ unless ( $previous_event
+ && ( $previous_event->isa('Bio::MAGE::BioAssay::MeasuredBioAssay')
+ || $previous_event->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) ) {
+
+ my $fext_protoapps;
+ ( $fext_protoapps, $protocolapps )
+ = $self->extract_protocolapps_by_type(
+ $protocolapps,
+ [ 'feature_extraction', 'image_acquisition', 'hybridization' ]
+ );
+
+ my $fextname;
+ if ( $previous_event
+ && $previous_event->can('getName')
+ && $previous_event->getName() ) {
+ $fextname = $previous_event->getName();
+ }
+ ($previous_event, @old_bioassays) = $self->create_feature_extraction(
+ $fextname || $name,
+ $previous_event,
+ $fext_protoapps,
+ $channel,
+ $previous_material,
+ );
+ push( @old_bioassays, $previous_event );
+ }
+
+ my $identifier_template = $self->generate_id_template( $name );
+
+ my $dba = $self->dba_bag(
+ $name,
+ {
+ identifier_template => $identifier_template,
+ name => $name,
+ source_bioassays => [ $previous_event ],
+ bioassaymap_bag => $self->get_bags()->{bam},
+ },
+ );
+
+ return ( $dba, $protocolapps, @old_bioassays );
+}
+
+sub create_normalized_data {
+
+ my ( $self,
+ $name,
+ $dba,
+ $protocolapps,
+ $previous_data,
+ $array_accession,
+ $channel,
+ $previous_material, ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ # Unless $dba is an actual DBA, make a new DBA.
+ my @old_bioassays;
+ unless ( $dba
+ && $dba->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) {
+ my $fext_protoapps;
+ ( $fext_protoapps, $protocolapps )
+ = $self->extract_protocolapps_by_type(
+ $protocolapps,
+ [ 'feature_extraction', 'image_acquisition', 'hybridization' ]
+ );
+
+ # Here we just default to the file name as internal identifier.
+ my $exhausted_fext_apps;
+ ($dba, $exhausted_fext_apps, @old_bioassays) = $self->create_normalization(
+ $name,
+ $dba,
+ $fext_protoapps,
+ $channel,
+ $previous_material,
+ );
+
+ # N.B. $exhausted_fext_apps should be empty here.
+ unless ( ! scalar( @{ $exhausted_fext_apps || [] } ) ) {
+ die("Internal error: Some feature extraction protocol apps unused.");
+ }
+
+ push( @old_bioassays, $dba );
+ }
+
+ if ( ! defined($dba) ) {
+ warn("Undefined DBA object (no Normalization Name?); will not create DBAData $name.\n");
+ return;
+ }
+
+ my $identifier_template = $self->generate_id_template( $name );
+ my $filename = "$name.proc";
+
+ # Allow Affy CHP to be coded as native when skip_datafiles is
+ # used.
+ if ( $self->get_skip_datafiles() ) {
+ $filename = $name;
+ if ( $name =~ /\.CHP \z/ixms ) {
+ $self->add_native_filetypes( $filename, 'CHP' );
+ }
+ }
+
+ my $dbad = $self->dbad_bag(
+ $name,
+ {
+ name => $name,
+ filename => $filename,
+ identifier_template => $identifier_template,
+ protocolapps => [ @{ $protocolapps } ], # needs to be a copy.
+ source_bioassay_dataset => $previous_data ? [ $previous_data ] : undef,
+ bioassay_map => $dba->getDerivedBioAssayMap(),
+ }
+ );
+
+ # Add DBAD to DBA.
+ $self->add_dbad_to_dba($dbad, $dba);
+
+ unless ( $dbad->getBioAssayDimension() ) {
+ my $bad = Bio::MAGE::BioAssayData::BioAssayDimension->new(
+ identifier => "$identifier_template.DerivedBioAssayDimension",
+ bioAssays => [ $dba ],
+ );
+ $dbad->setBioAssayDimension($bad);
+ }
+
+ my $datafile = $self->create_datafile(
+ $name,
+ 'normalized',
+ $dbad,
+ $array_accession,
+ );
+
+ $self->add_datafiles( $datafile );
+
+ return ($dbad, @old_bioassays);
+}
+
+sub create_raw_data_matrix {
+
+ my ( $self, $name, $protocolapps, $array_accession ) = @_;
+
+ # FIXME nothing done with $protocolapps yet; since this is a
+ # feature extraction protocol, and MeasuredBioAssayData doesn't
+ # have ProducerTransformation, they must be attached to the
+ # MBA. We don't create those until we've read the file, however.
+
+ # In effect, this boils down to a requirement that Scan Name be
+ # used in conjunction with Array Data Matrix File if the hybs are
+ # multi-channel, or if there's a scan protocol (since otherwise
+ # the scan PBAs are not created either). Feature extraction
+ # protocol cannot be supported on Array Data Matrix File at this
+ # time. All these BioAssay objects would also have to be inferred
+ # not only here but also in the data matrix management code. Since
+ # Array Data Matrix is an edge use-case (Illumina, by contrast, is
+ # handled as Array Data File).
+
+ return if ( $name =~ $BLANK );
+
+ my $identifier_template = $self->generate_id_template( $name );
+ my $filename = "$name.proc";
+
+ # Allow files to be coded as native when skip_datafiles is
+ # used.
+ if ( $self->get_skip_datafiles() ) {
+ $filename = $name;
+ }
+
+ my $mbad = $self->mbad_bag(
+ $name,
+ {
+ name => $name,
+ filename => $filename,
+ identifier_template => $identifier_template,
+ }
+ );
+
+ my $datafile = $self->create_datafile(
+ $name,
+ $CONFIG->get_RAW_DM_FILE_TYPE(),
+ $mbad,
+ $array_accession,
+ );
+
+ $self->add_datafiles( $datafile );
+
+ return $mbad;
+}
+
+sub create_norm_data_matrix {
+
+ my ( $self,
+ $name,
+ $protocolapps,
+ $array_accession,
+ $dba,
+ $channel,
+ $previous_material, ) = @_;
+
+ return if ( $name =~ $BLANK );
+
+ # Unless $dba is an actual DBA, make a new DBA.
+ my @old_bioassays;
+ unless ( $dba
+ && $dba->isa('Bio::MAGE::BioAssay::DerivedBioAssay') ) {
+ my $fext_protoapps;
+ ( $fext_protoapps, $protocolapps )
+ = $self->extract_protocolapps_by_type(
+ $protocolapps,
+ [ 'feature_extraction', 'image_acquisition', 'hybridization' ]
+ );
+
+ my $exhausted_fext_apps;
+ ($dba, $exhausted_fext_apps, @old_bioassays) = $self->create_normalization(
+ $name,
+ $dba,
+ $fext_protoapps,
+ $channel,
+ $previous_material,
+ );
+
+ # N.B. $exhausted_fext_apps should be empty here.
+ unless ( ! scalar( @{ $exhausted_fext_apps || [] } ) ) {
+ die("Internal error: Some feature extraction protocol apps unused.");
+ }
+
+ push( @old_bioassays, $dba );
+ }
+
+ my $identifier_template = $self->generate_id_template( $name );
+ my $filename = "$name.proc";
+
+ # Allow files to be coded as native when skip_datafiles is
+ # used.
+ if ( $self->get_skip_datafiles() ) {
+ $filename = $name;
+ }
+
+ my $dbad = $self->dbad_bag(
+ $name,
+ {
+ name => $name,
+ filename => $filename,
+ identifier_template => $identifier_template,
+ protocolapps => [ @{ $protocolapps } ], # needs to be a copy.
+ }
+ );
+
+ # Add DBAD to DBA.
+ $self->add_dbad_to_dba($dbad, $dba);
+
+ my $txn;
+ unless ( $txn = $dbad->getProducerTransformation() ) {
+ $txn = Bio::MAGE::BioAssayData::Transformation->new(
+ identifier => "$identifier_template.Transformation",
+ name => $name,
+ );
+ $dbad->setProducerTransformation( $txn );
+ }
+
+ my $mapping;
+ unless ( $mapping = $txn->getBioAssayMapping() ) {
+ $mapping = Bio::MAGE::BioAssayData::BioAssayMapping->new();
+ $txn->setBioAssayMapping( $mapping );
+ }
+
+ my $found;
+ foreach my $map ( @{ $mapping->getBioAssayMaps() || [] } ) {
+ if ( my $target = $map->getBioAssayMapTarget() ) {
+ if ( $target->getIdentifier() eq $dba->getIdentifier() ) {
+ $found++;
+ }
+ }
+ }
+ unless ( $found ) {
+ my $map = $self->bam_bag(
+ $dba->getName(),
+ {
+ identifier_template => $identifier_template,
+ target_bioassay => $dba,
+ },
+ );
+ $mapping->addBioAssayMaps( $map );
+ }
+
+ my $datafile = $self->create_datafile(
+ $name,
+ $CONFIG->get_FGEM_FILE_TYPE(),
+ $dbad,
+ $array_accession,
+ );
+
+ $self->add_datafiles( $datafile );
+
+ return $dbad;
+}
+
+sub create_datafile {
+
+ my ( $self, $filename, $datatype, $bad, $array_accession ) = @_;
+
+ unless ( defined $array_accession ) {
+ warn("Error: Datafile $filename needs an array accession number.\n");
+ $array_accession = 'Unknown';
+ }
+
+ my ( $path, $name );
+ if ( $self->get_skip_datafiles() ) {
+ $path = $filename;
+ $name = $filename;
+ }
+ else {
+ $path = get_filepath_from_uri(
+ $filename,
+ $self->get_source_directory(),
+ );
+ $name = basename( $path );
+ }
+
+ my $file = ArrayExpress::Datafile->new({
+ path => $path,
+ name => $name,
+ data_type => $datatype,
+ mage_badata => $bad,
+ array_design_id => $array_accession,
+ });
+
+ return $file;
+}
+
+sub create_factorvalue_value {
+
+ my ( $self, $factorname, $altcategory, $value, $termsource, $accession ) = @_;
+
+ return if ( $value =~ $BLANK );
+
+ my ($factorvalue, $exp_factor)
+ = $self->create_generic_factorvalue( $factorname, $value, $altcategory );
+
+ my $category;
+ if ( $altcategory ) {
+
+ # If we're given a category in parentheses, use it.
+ $category = $altcategory;
+ }
+ else {
+
+ # Otherwise, derive the category from the EF term:
+ my $ef_oe = $exp_factor->getCategory()
+ or croak("Error: Experimental Factor $factorname has no type.");
+
+ my @ef_catparts = split /_/, $ef_oe->getValue();
+
+ $category = join(q{}, map{ ucfirst($_) } @ef_catparts);
+ }
+
+ my $oe = $self->create_ontologyentry( $category, $value, $termsource, $accession );
+
+ $factorvalue->setValue( $oe );
+
+ $self->add_factorvalue_to_factor( $factorvalue, $exp_factor );
+
+ return $factorvalue;
+}
+
+sub create_factorvalue_measurement {
+
+ my ( $self, $factorname, $altcategory, $value, $unit ) = @_;
+
+ return if ( $value =~ $BLANK );
+
+ # Note: $altcategory is ignored for measurement.
+
+ my $unitname;
+ if ( $unit ) {
+ $unitname = $self->get_unitname_for_unit( $unit );
+ }
+
+ # Here we're retasking the altcategory slot for unitname; this is
+ # a bit naughty. FIXME.
+ my ($factorvalue, $exp_factor) = $self->create_generic_factorvalue(
+ $factorname,
+ $value,
+ $unitname,
+ );
+
+ my $measurement = Bio::MAGE::Measurement::Measurement->new(
+ value => $value,
+ unit => $unit,
+ );
+
+ $factorvalue->setMeasurement( $measurement );
+
+ # This has to be done *after* adding the unit to the term. It's
+ # now handled in the relevant grammar action.
+# $self->add_factorvalue_to_factor( $factorvalue, $exp_factor );
+
+ return wantarray ? ($factorvalue, $exp_factor) : $factorvalue;
+}
+
+sub create_generic_factorvalue : PRIVATE {
+
+ my ( $self, $factorname, $value, $altcategory ) = @_;
+
+ my $args;
+ if ( $self->get_in_relaxed_mode() ) {
+ $args = {name => $factorname, type => $factorname};
+ }
+
+ my $exp_factor = $self->factor_bag( $factorname, $args )
+ or croak(qq{Error: Experimental Factor Name not found: "$factorname"\n});
+
+ my $identifier_template = $self->generate_id_template(
+ $altcategory ? "$factorname.$altcategory.$value" : "$factorname.$value"
+ );
+
+ my $name = defined $altcategory ? "$value $altcategory" : $value;
+ my $factorvalue = $self->factorvalue_bag(
+ "$factorname.$name",
+ {
+ identifier_template => $identifier_template,
+ name => $name,
+ },
+ );
+
+ return wantarray
+ ? ( $factorvalue, $exp_factor )
+ : $factorvalue;
+}
+
+sub create_description {
+
+ my ( $self, $text, $describable ) = @_;
+
+ return if ( $text =~ $BLANK );
+
+ my $description = Bio::MAGE::Description::Description->new(
+ text => $text,
+ );
+
+ $self->add_description_to_describable( $description, $describable)
+ if $describable;
+
+ return $description;
+}
+
+sub create_nvt {
+
+ my ( $self, $name, $value, $type, $extendable ) = @_;
+
+ return if ( $value =~ $BLANK );
+
+ my $nvt = Bio::MAGE::NameValueType->new(
+ name => $name,
+ value => $value,
+ type => $type,
+ );
+
+ $self->add_nvt_to_extendable( $nvt, $extendable )
+ if $extendable;
+
+ return $nvt;
+}
+
+sub add_char_to_material {
+
+ my ( $self, $char, $material ) = @_;
+
+ return unless ( $material && $char );
+
+ my $found;
+ if ( my $preexisting_chars = $material->getCharacteristics() ) {
+
+ my $new_category = $char->getCategory();
+ my $new_value = $char->getValue();
+ $found = first {
+ $_->getCategory() eq $new_category
+ && $_->getValue() eq $new_value;
+ } @$preexisting_chars;
+ }
+ $material->addCharacteristics($char) unless $found;
+
+ return;
+}
+
+sub set_material_type {
+
+ my ( $self, $material, $type ) = @_;
+
+ return unless ( $material && $type );
+
+ $material->setMaterialType($type);
+
+ return;
+}
+
+sub set_technology_type {
+
+ my ( $self, $assay, $type ) = @_;
+
+ return unless ( $assay && $type );
+
+ # Look through descriptions for a matching OE, or a description
+ # holding an OE with the same category.
+ my ( $found, $desc );
+ if ( my $descs = $assay->getDescriptions() ) {
+ foreach my $old ( @$descs ) {
+ if ( my $oes = $old->getAnnotations() ) {
+ foreach my $oe ( @$oes ) {
+
+ # Same category; record the description object.
+ if ( $oe->getCategory() eq $type->getCategory() ) {
+ $desc = $old;
+
+ # Same value; we're done here.
+ if ( $oe->getValue() eq $type->getValue() ) {
+ $found++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ unless ( $found ) {
+ unless ( $desc ) {
+ $desc = Bio::MAGE::Description::Description->new();
+ $assay->addDescriptions( $desc );
+ }
+ $desc->addAnnotations( $type );
+ }
+
+ return;
+}
+
+sub add_factorvals_to_bioassays {
+
+ my ( $self, $factorvals, $bioassays ) = @_;
+
+ # Generate a uniqued list of bioassays.
+ my %unique_ba = map { $_->getIdentifier() => $_ } @$bioassays;
+
+ # Takes a list of FactorValues and BioAssays, links the former to
+ # the latter, checking for duplicates.
+
+ # Add the factors if not already linked.
+ foreach my $ba ( values %unique_ba ) {
+ foreach my $fv ( @$factorvals ) {
+ $self->add_factorval_to_bioassay( $fv, $ba );
+ }
+ }
+
+ return;
+}
+
+sub add_performer_to_protocolapp : PRIVATE {
+
+ my ( $self, $performer, $protocolapp ) = @_;
+
+ my $found;
+ if ( my $preexisting_people = $protocolapp->getPerformers() ) {
+
+ my $new_id = $performer->getIdentifier();
+ $found = first {
+ $_->getIdentifier() eq $new_id
+ } @$preexisting_people;
+ }
+ $protocolapp->addPerformers($performer) unless $found;
+
+ return;
+}
+
+sub add_date_to_protocolapp {
+
+ my ( $self, $date, $protocolapp ) = @_;
+
+ return if ( $date =~ $BLANK );
+
+ $protocolapp->setActivityDate( $date ) if $protocolapp;
+
+ return;
+}
+
+sub add_provider_to_source : PRIVATE {
+
+ my ( $self, $provider, $source ) = @_;
+
+ my $found;
+ if ( my $preexisting_people = $source->getSourceContact() ) {
+
+ my $new_id = $provider->getIdentifier();
+ $found = first {
+ $_->getIdentifier() eq $new_id
+ } @$preexisting_people;
+ }
+ $source->addSourceContact($provider) unless $found;
+
+ return;
+}
+
+sub add_description_to_describable : PRIVATE {
+
+ my ( $self, $description, $describable ) = @_;
+
+ my $found;
+ if ( my $preexisting_descs = $describable->getDescriptions() ) {
+
+ my $new_text = $description->getText();
+ $found = first {
+ $_->getText() eq $new_text
+ } @$preexisting_descs;
+ }
+ $describable->addDescriptions($description) unless $found;
+
+ return;
+}
+
+sub add_nvt_to_extendable : PRIVATE {
+
+ my ( $self, $nvt, $extendable ) = @_;
+
+ my $found;
+ if ( my $preexisting_nvts = $extendable->getPropertySets() ) {
+
+ my $new_name = $nvt->getName();
+ my $new_value = $nvt->getValue();
+ $found = first {
+ $_->getName() eq $new_name
+ && $_->getValue() eq $new_value;
+ } @$preexisting_nvts;
+ }
+ $extendable->addPropertySets($nvt) unless $found;
+
+ return;
+}
+
+sub add_label_to_le : PRIVATE {
+
+ my ( $self, $label, $le ) = @_;
+
+ my $found;
+ if ( my $preexisting_labels = $le->getLabels() ) {
+
+ my $new_name = $label->getName();
+ $found = first {
+ $_->getName() eq $new_name
+ } @$preexisting_labels;
+ }
+ $le->addLabels($label) unless $found;
+
+ return;
+}
+
+sub add_parameterval_to_protocolapp : PRIVATE {
+
+ my ( $self, $parameterval, $protocolapp ) = @_;
+
+ my $found;
+ if ( my $preexisting_values = $protocolapp->getParameterValues() ) {
+
+ my $parameter = $parameterval->getParameterType();
+ my $new_id = $parameter->getIdentifier();
+ $found = first {
+ my $oldparam = $_->getParameterType();
+ $oldparam->getIdentifier() eq $new_id
+ } @$preexisting_values;
+ }
+ $protocolapp->addParameterValues($parameterval) unless $found;
+
+ return;
+}
+
+sub add_factorvalue_to_factor {
+
+ my ( $self, $fv, $factor ) = @_;
+
+ my $found;
+ my $name = $fv->getName();
+ if ( my $preexisting_fvs = $factor->getFactorValues() ) {
+ if ( my $value = $fv->getValue() ) {
+ my $new_cat = $value->getCategory();
+ my $new_val = $value->getValue();
+ $found = first {
+ my $oldvalue = $_->getValue;
+ $oldvalue
+ && ($oldvalue->getCategory() eq $new_cat )
+ && ($oldvalue->getValue() eq $new_val )
+ } @$preexisting_fvs;
+ }
+ elsif ( my $measurement = $fv->getMeasurement() ) {
+ my $new_val = $measurement->getValue();
+ my $new_unit = $self->get_unitname_from_measure($measurement);
+ $found = first {
+ my $oldmeas = $_->getMeasurement();
+ $oldmeas
+ && $oldmeas->getValue() eq $new_val
+ && ($self->get_unitname_from_measure($oldmeas) eq $new_unit)
+ } @$preexisting_fvs;
+ }
+ elsif ( defined $name ) {
+ $found = first {
+ $_->getName() eq $name
+ } @$preexisting_fvs;
+ }
+ else {
+ croak("Error: FactorValue has no value, measurement or name.");
+ }
+ }
+
+ $factor->addFactorValues($fv) unless $found;
+
+ return;
+}
+
+sub get_unitname_from_measure : PRIVATE {
+
+ my ( $self, $measurement ) = @_;
+
+ my $unitname;
+ if ( my $unit = $measurement->getUnit() ) {
+ $unitname = $self->get_unitname_for_unit( $unit );
+ }
+
+ # Return empty string on failure; this value will be used in
+ # string comparisons so should not be undef.
+ return defined $unitname ? $unitname : q{};
+}
+
+sub add_unit_to_thing {
+
+ my ( $self, $unit, $thing ) = @_;
+
+ return unless ( $unit && $thing );
+
+ # We attempt to make this smart: $thing can be a ParameterValue
+ # (NOTE: may need to create a new Parameter, attach to the
+ # Protocol if the old and new units don't match), or a Measurement
+ # (in which case just attach directly).
+ if ( $thing->isa('Bio::MAGE::Protocol::ParameterValue') ) {
+ $self->add_unit_to_parameterval($unit, $thing);
+ }
+ elsif ( $thing->isa('Bio::MAGE::Measurement::Measurement') ) {
+ $thing->setUnit($unit);
+ }
+ else {
+ croak("Error: Cannot process argument: $thing (" . ref($thing) .")");
+ }
+
+ return;
+}
+
+sub get_unit_for_param : PRIVATE {
+
+ my ( $self, $parameter ) = @_;
+
+ return unless $parameter;
+
+ my $defaultval = $parameter->getDefaultValue();
+
+ if ($defaultval) {
+ return $defaultval->getUnit();
+ }
+
+ return;
+}
+
+sub get_unitname_for_unit : PRIVATE {
+
+ my ( $self, $unit ) = @_;
+
+ return unless $unit;
+
+ my $unitname = $unit->getUnitNameCV();
+ if ( ! $unitname || $unitname eq 'other' ) {
+ $unitname = $unit->getUnitName() || 'other';
+ }
+
+ return $unitname;
+}
+
+sub add_unit_to_parameterval : PRIVATE {
+
+ my ( $self, $unit, $paramval ) = @_;
+
+ my $unitname = $self->get_unitname_for_unit($unit);
+ my $parameter = $paramval->getParameterType();
+
+ if ( my $oldunit = $self->get_unit_for_param($parameter) ) {
+
+ # Check the unit; if different create a new parameter and
+ # add it to the relevant protocol. Note that the last
+ # entry in @::protocolapp_list should point to the protocol
+ # of interest.
+ my $oldunitname = $self->get_unitname_for_unit($oldunit);
+ unless ( $oldunitname eq $unitname ) {
+
+ # Find the associated protocol.
+ unless ( scalar @::protocolapp_list ) {
+ die("Error: no protocolapps available.");
+ }
+ my $protocol = $::protocolapp_list[$#::protocolapp_list]->getProtocol();
+ my $paramname = $parameter->getName();
+ unless ($protocol) {
+ croak(
+ "Error: cannot create a new parameter ($paramname,"
+ . " unit $unitname) without a protocol.\n"
+ );
+ }
+
+ # Check through the other parameters linked to this
+ # protocol. Match by param name and unit.
+ my $preexisting;
+ OLDPARAM:
+ foreach my $old ( @{ $protocol->getParameterTypes() || [] } ) {
+ if ( $paramname eq $old->getName() ) {
+ my $preex_unit = $self->get_unit_for_param($old);
+ my $preex_unitname = $self->get_unitname_for_unit($preex_unit);
+ if ( $preex_unitname eq $unitname ) {
+ $preexisting = $old;
+ last OLDPARAM;
+ }
+ }
+ }
+
+ if ( $preexisting ) {
+
+ # Repoint $paramval to the preexisting ParameterType.
+ $paramval->setParameterType( $preexisting );
+ $parameter = $preexisting;
+ }
+ else {
+
+ # Completely new parameter.
+ my $param_id = sprintf(
+ "%s:%s.%s.Parameter.%s",
+ $self->get_id_prefix(),
+ $protocol->getIdentifier(),
+ $paramname,
+ $unitname,
+ );
+ my $newparam = $self->parameter_bag(
+ "$paramname.$unitname." . $protocol->getName(),
+ {
+ name => $paramname,
+ identifier => $param_id,
+ unit => $unit,
+ },
+ );
+
+ # Link everything together.
+ $protocol->addParameterTypes( $newparam );
+ $paramval->setParameterType( $newparam );
+ $parameter = $newparam;
+ }
+ }
+ }
+ else {
+
+ # Add the unit to the preexisting parameter.
+ my $newdefault = Bio::MAGE::Measurement::Measurement->new(
+ unit => $unit,
+ );
+ $parameter->setDefaultValue($newdefault);
+ }
+
+ # Fix the parameter identifier to add $unitname suffix if it's not
+ # already there.
+ my $param_id = $parameter->getIdentifier();
+ unless( $param_id =~ /$unitname \z/xms ) {
+ $param_id .= ".$unitname";
+ $parameter->setIdentifier($param_id);
+ }
+
+ return;
+}
+
+sub create_single_treatment : PRIVATE {
+
+ my ( $self, $app, $bmm, $order, $default_action ) = @_;
+
+ my $action;
+ if ( $app ) {
+
+ # Use Storable::dclone to copy the ProtocolType OE and reset
+ # the category to Action.
+ my $protocol = $app->getProtocol();
+ if ( my $type = $protocol->getType() ) {
+ $action = dclone($protocol->getType());
+ $action->setCategory('Action');
+ }
+ }
+
+ unless ( $action ) {
+
+ # Fall back to a generic default.
+ $action = $self->create_ontologyentry(
+ 'Action',
+ ($default_action || 'specified_biomaterial_action'),
+ );
+ }
+
+ # Quick and dirty, we may want to revisit this FIXME
+ my $identifier = $self->generate_id_template(
+ unique_identifier() . '.Treatment',
+ );
+
+ my $treatment = Bio::MAGE::BioMaterial::Treatment->new(
+ identifier => $identifier,
+ action => $action,
+ order => $order,
+ sourceBioMaterialMeasurements => [ $bmm ],
+ );
+
+ $treatment->setProtocolApplications( [ $app ] ) if $app;
+
+ return $treatment;
+}
+
+sub create_material_treatments : PRIVATE {
+
+ my ( $self, $protocolapps, $previous, $default_action ) = @_;
+
+ my @treatments;
+
+ # One BioMaterialMeasurement used for all Treatments.
+ my $bmm;
+ if ( $previous ) {
+ $bmm = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+ bioMaterial => $previous,
+ );
+ }
+
+ # If ProtocolApps supplied, generate a treatment for each.
+ my $order = 1;
+ foreach my $app ( @{ $protocolapps } ) {
+
+ my $treatment = $self->create_single_treatment(
+ $app,
+ $bmm,
+ $order,
+ );
+ $order++;
+ push @treatments, $treatment;
+ }
+
+ # Fall back to a single treatment if there's no protocol
+ # applications, but there is a source material.
+ if ( ! scalar( @treatments ) && $bmm ) {
+ my $treatment = $self->create_single_treatment(
+ undef,
+ $bmm,
+ 1,
+ $default_action,
+ );
+ push @treatments, $treatment;
+ }
+
+ return \@treatments;
+}
+
+sub add_treatments_to_material : PRIVATE {
+
+ my ( $self, $treatments, $material ) = @_;
+
+ # FIXME compare old source materials with the new ones, add and
+ # maybe renumber the treatment order if possible?
+
+ # Lookup table of treatments by order (NB this is *not* infallible
+ # FIXME).
+ my %old_treatments = map { $_->getOrder() => $_ }
+ @{ $material->getTreatments() || [] };
+
+ foreach my $new ( @$treatments ) {
+
+ # See if we've seen this treatment before. This relies on the
+ # object having the same order attribute FIXME.
+ if ( my $old = $old_treatments{ $new->getOrder() } ) {
+ $self->update_treatment_info( $old, $new );
+ }
+ else{
+ $material->addTreatments($new);
+ }
+ }
+
+ return;
+}
+
+sub update_treatment_info : PRIVATE {
+
+ my ( $self, $old, $new ) = @_;
+
+ if ( my $bmms = $new->getSourceBioMaterialMeasurements() ) {
+ foreach my $bmm ( @{ $bmms } ) {
+ $self->add_source_to_treatment( $bmm, $old );
+ }
+ }
+
+ if ( my $apps = $new->getProtocolApplications() ) {
+ foreach my $app ( @{ $apps } ) {
+ $self->add_protocolapp_to_treatment( $app, $old );
+ }
+ }
+
+ return;
+}
+
+sub add_source_to_treatment : PRIVATE {
+
+ my ( $self, $source_bmm, $treatment ) = @_;
+
+ my $found;
+ if ( my $preexisting_bmms
+ = $treatment->getSourceBioMaterialMeasurements() ) {
+ my $new_id = $source_bmm->getBioMaterial()->getIdentifier();
+ $found = first {
+ $_->getBioMaterial()->getIdentifier() eq $new_id;
+ } @$preexisting_bmms;
+ }
+ $treatment->addSourceBioMaterialMeasurements($source_bmm)
+ unless $found;
+
+ return;
+}
+
+sub add_protocolapp_to_treatment : PRIVATE {
+
+ my ( $self, $protoapp, $treatment ) = @_;
+
+ my $found;
+ if ( my $preexisting_apps
+ = $treatment->getProtocolApplications() ) {
+ my $new_id = $protoapp->getProtocol()->getIdentifier();
+ $found = first {
+ $_->getProtocol()->getIdentifier() eq $new_id;
+ } @$preexisting_apps;
+ }
+ $treatment->addProtocolApplications($protoapp)
+ unless $found;
+
+ return;
+}
+
+sub add_images_to_pba : PRIVATE {
+
+ my ( $self, $images, $pba ) = @_;
+
+ my @image_acquisitions;
+
+ # Capture all the ImageAcquisitions attached to $pba
+ foreach my $bat ( @{ $pba->getBioAssayTreatments() || [] } ) {
+ if ( $bat->isa('Bio::MAGE::BioAssay::ImageAcquisition') ) {
+ push @image_acquisitions, $bat;
+ }
+ }
+
+ # Create ImageAcquisition if necessary, then add $images to all
+ # IAs in the PBA.
+ unless ( scalar @image_acquisitions ) {
+ my $ia_identifier = $pba->getIdentifier();
+ $ia_identifier =~ s/PhysicalBioAssay \z/ImageAcquisition/xms;
+ my $ia = Bio::MAGE::BioAssay::ImageAcquisition->new(
+ identifier => $ia_identifier,
+ target => $pba,
+ physicalBioAssay => $pba,
+ );
+ push @image_acquisitions, $ia;
+ }
+
+ foreach my $ia ( @image_acquisitions ) {
+ foreach my $image ( @$images ) {
+ my $found = first {
+ $_->getURI() eq $image->getURI()
+ } @{ $ia->getImages() || [] };
+ $ia->addImages( $image ) unless $found;
+ }
+ }
+
+ foreach my $image ( @$images ) {
+ my $found = first {
+ $_->getURI() eq $image->getURI()
+ } @{ $pba->getPhysicalBioAssayData() || [] };
+ $pba->addPhysicalBioAssayData( $image ) unless $found;
+ }
+
+ return;
+}
+
+sub add_basource_to_map : PRIVATE {
+
+ my ( $self, $bioassay, $map ) = @_;
+
+ my $found;
+ if ( my $sources = $map->getSourceBioAssays() ) {
+ my $new_id = $bioassay->getIdentifier();
+ $found = first {
+ $_->getIdentifier() eq $new_id
+ } @$sources;
+ }
+ $map->addSourceBioAssays( $bioassay ) unless $found;
+
+ return;
+}
+
+sub create_perchannel_pba : PRIVATE {
+
+ my ( $self, $identifier_template, $channelname, $label, $hyb_pba, $scanname ) = @_;
+
+ $channelname ||= 'Unknown';
+ my $new_id_template = "$identifier_template.$channelname";
+
+ # The following convention is assumed when adding FactorValues to
+ # all the BioAssays in a row.
+ my $channel_pba_name = "$scanname.$channelname";
+
+ # We don't pass $args->{derived_from} down to the extended PBA
+ # generation; this is used as a flag to indicate hyb-level PBAs
+ # only.
+ my $channel_pba = $self->extended_pba_bag(
+ $channel_pba_name,
+ {
+ identifier_template => $new_id_template,
+ name => $channel_pba_name,
+ label => $label,
+ },
+ );
+
+ # Add a BioAssayTreatment pointing from $hyb_pba to channel PBAs
+ # here.
+ my %preexisting;
+ if ( my $old_treats = $hyb_pba->getBioAssayTreatments() ) {
+ foreach my $treat ( @$old_treats ) {
+ if ( my $old_pba = $treat->getPhysicalBioAssay() ) {
+ $preexisting{ $old_pba->getIdentifier() }++;
+ }
+ }
+ }
+ unless ( $preexisting{ $channel_pba->getIdentifier() } ) {
+ $hyb_pba->addBioAssayTreatments(
+ $self->new_scan(
+ {
+ identifier_template => $new_id_template,
+ name => $channel_pba_name,
+ pba => $channel_pba,
+ target => $channel_pba,
+ },
+ )
+ );
+ }
+
+ return $channel_pba;
+}
+
+sub add_dbad_to_dba : PRIVATE {
+
+ my ( $self, $dbad, $dba ) = @_;
+
+ my $found;
+ if ( my $preexisting_dbads = $dba->getDerivedBioAssayData() ) {
+ my $new_id = $dbad->getIdentifier();
+ $found = first { $_->getIdentifier() eq $new_id }
+ @$preexisting_dbads;
+ }
+ $dba->addDerivedBioAssayData( $dbad ) unless $found;
+
+ return;
+}
+
+sub add_datafiles : PRIVATE {
+
+ my ( $self, @datafiles ) = @_;
+
+ foreach my $file ( @datafiles ) {
+ $datafiles_cache{ ident $self }{ $file->get_path() } = $file;
+ }
+
+ return;
+}
+
+sub get_datafiles {
+
+ my ( $self ) = @_;
+
+ # Return sorted alphabetically by name for the moment FIXME.
+ return [
+ map { $datafiles_cache{ ident $self }{$_} }
+ sort keys %{ $datafiles_cache{ ident $self } }
+ ];
+}
+
+sub get_sdrf_filehandle {
+
+ my ( $self ) = @_;
+
+ unless ( $sdrf_filehandle{ident $self} ) {
+ if ( my $file = $self->get_sdrf() ) {
+ open (my $fh, '<', $file)
+ or croak("Error opening SDRF for reading: $!\n");
+ $sdrf_filehandle{ident $self} = $fh;
+ }
+ else {
+ confess("Error: No SDRF filename given.");
+ }
+ }
+ return $sdrf_filehandle{ident $self};
+}
+
+sub get_row_parser : PRIVATE {
+
+ my ( $self ) = @_;
+
+ unless ( $row_parser{ident $self} ) {
+ $row_parser{ident $self} = $self->parse_header()
+ or die("Error: Unable to generate row-level parser.");
+ }
+ return $row_parser{ident $self}
+}
+
+sub get_filename : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ return $self->get_sdrf();
+}
+
+sub simplify_single_channels : PRIVATE {
+
+ # Snip out the extra PBAs used for two-colour coding for the
+ # single-channel hybs.
+ my ( $self ) = @_;
+
+ foreach my $hyb_pba ( @{ $self->pba_bag() } ) {
+
+ next unless ( scalar( @{ $hyb_pba->getChannels() || [] } ) == 1 );
+
+ BAT:
+ foreach my $bat ( @{ $hyb_pba->getBioAssayTreatments() || [] } ) {
+ my $ch_pba = $bat->getTarget();
+
+ # If scans were never generated, we could have just the hyb PBA.
+ next BAT if ( $ch_pba->getIdentifier() eq $hyb_pba->getIdentifier()
+ && $ch_pba->getBioAssayCreation() );
+
+ my $mrg_pba;
+ if ( my $ch_bats = $ch_pba->getBioAssayTreatments() ) {
+ warn ("WARNING: discarding all but first BioAssayTreatment.")
+ if (scalar(@$ch_bats) > 1 );
+ if ( scalar(@$ch_bats) ) {
+ $mrg_pba = $ch_bats->[0]->getTarget();
+ }
+ }
+
+ # Repoint the merged PBA data to the hyb PBA. Note that
+ # this overwrites any BATs associated with $hyb_pba; this
+ # was necessary to allow clean deletion of $ch_pba (don't
+ # ask me why; I assume a reference needs weakening
+ # somewhere, presumably in the $hyb_pba BAT).
+ if ( $mrg_pba ) {
+ my ($images, $protocolapps, $mbas, $dbas)
+ = $self->strip_pba_for_repointing($mrg_pba);
+ $self->repoint_pba_data(
+ $images,
+ $protocolapps,
+ $mbas,
+ $dbas,
+ $hyb_pba,
+ $mrg_pba->getName(),
+ );
+ }
+
+ # Also strip the channel PBA. This appears to remove
+ # references to $mrg_pba.
+ $self->strip_pba_for_repointing($ch_pba);
+
+ # Delete the old PBA objects from the bags.
+ $self->extended_pba_bag($ch_pba->getName(), 'delete');
+# or die("Error: Unable to delete unwanted channel PBA: "
+# . $ch_pba->getName());
+ if ( $mrg_pba ) {
+ $self->extended_pba_bag($mrg_pba->getName(), 'delete');
+# or die("Error: Unable to delete unwanted merging PBA: "
+# . $mrg_pba->getName());
+ }
+
+ # We remove the identifiers; this will cause an exception
+ # if there's a problem with these objects hanging around
+ # later (better that than silent failure).
+ $ch_pba->setIdentifier("");
+ $mrg_pba->setIdentifier("") if $mrg_pba;
+ }
+ }
+
+ return;
+}
+
+sub strip_pba_for_repointing : PRIVATE {
+
+ my ( $self, $pba ) = @_;
+
+ my (%oldimages, %oldprotocolapps, %oldmbas, %olddbas);
+
+ # Gather associations from one hyb to be transferred to another.
+
+ # Images first.
+ foreach my $image ( @{ $pba->getPhysicalBioAssayData() || [] } ) {
+ $oldimages{ $image->getIdentifier() } = $image;
+ }
+ $pba->setPhysicalBioAssayData([]);
+
+ # Process Images associated with the BAT (should be
+ # the same, but you never know).
+ my $bat = $pba->getBioAssayTreatments()->[0];
+ if ( $bat ) {
+ if ( $bat->isa('Bio::MAGE::BioAssay::ImageAcquisition') ) {
+ foreach my $image ( @{ $bat->getImages() || [] } ) {
+ $oldimages{ $image->getIdentifier() } = $image;
+ }
+ }
+
+ # ProtocolApps.
+ foreach my $pa ( @{ $bat->getProtocolApplications() || [] } ) {
+ $oldprotocolapps{ $pa } = $pa;
+ }
+ }
+
+ $pba->setBioAssayTreatments([]);
+
+ # MBAs.
+ foreach my $mba ( @{ $self->mba_bag() } ) {
+ if ( my $fext = $mba->getFeatureExtraction() ) {
+ if ( my $pba_ref = $fext->getPhysicalBioAssaySource() ) {
+ if ( $pba_ref->getIdentifier() eq $pba->getIdentifier() ) {
+ $oldmbas{ $mba->getIdentifier() } = $mba;
+ }
+ }
+ }
+ }
+
+ # DBAs (e.g. in the absence of raw data this can happen).
+ foreach my $dba ( @{ $self->dba_bag() } ) {
+ if ( my $maps = $dba->getDerivedBioAssayMap() ) {
+ foreach my $map ( @$maps ) {
+ my @new_sources;
+ if ( my $bas = $map->getSourceBioAssays() ) {
+ foreach my $ba ( @$bas ) {
+ if ( $ba->getIdentifier() eq $pba->getIdentifier() ) {
+ $olddbas{ $dba->getIdentifier() } = $dba;
+ }
+ else {
+ push @new_sources, $ba;
+ }
+ }
+ }
+
+ # Break the link between $pba and map.
+ $map->setSourceBioAssays(\@new_sources);
+ }
+ }
+ }
+
+ $pba->setBioAssayFactorValues([]);
+ $pba->setChannels([]);
+
+ return (
+ [values %oldimages],
+ [values %oldprotocolapps],
+ [values %oldmbas],
+ [values %olddbas],
+ );
+}
+
+sub repoint_pba_data : PRIVATE {
+
+ my ( $self,
+ $oldimages,
+ $oldprotocolapps,
+ $oldmbas,
+ $olddbas,
+ $pba,
+ $name ) = @_;
+
+ my $identifier = $self->generate_id_template($name) . '.ImageAcquisition';
+ my $bat = Bio::MAGE::BioAssay::ImageAcquisition->new(
+ identifier => $identifier,
+ target => $pba,
+ name => $name,
+# physicalBioAssay => $pba,
+ );
+
+ # Reattach any Images moved from old BAT.
+ if ( scalar ( @{ $oldimages || [] } ) ) {
+ $self->add_images_to_pba( $oldimages, $pba );
+ }
+
+ # Reattach old protocolapps from old BAT.
+ foreach my $pa ( @{ $oldprotocolapps || [] } ) {
+ $self->add_protocolapp_to_treatment( $pa, $bat );
+ }
+
+ # Repoint any MBAs previously pointing to $hyb_pba.
+ if ( scalar ( @{ $oldmbas || [] } ) ) {
+ foreach my $mba ( @$oldmbas ) {
+ my $fext = $mba->getFeatureExtraction();
+ $fext->setPhysicalBioAssaySource($pba);
+ }
+ }
+
+ # Repoint any DBAs previously pointing to $hyb_pba.
+ if ( scalar ( @{ $olddbas || [] } ) ) {
+ foreach my $dba ( @$olddbas ) {
+ my $maps = $dba->getDerivedBioAssayMap();
+ foreach my $map ( @$maps ) {
+ $self->add_basource_to_map( $pba, $map );
+ }
+ }
+ }
+
+ $pba->setBioAssayTreatments( [ $bat ] );
+
+ return;
+}
+
+sub propagate_fvs_to_datafiles : PRIVATE {
+
+ my ( $self ) = @_;
+
+ FILE:
+ foreach my $file ( @{ $self->get_datafiles || [] } ) {
+
+ my $badata;
+ next FILE unless ( $badata = $file->get_mage_badata() );
+
+ my $badim;
+ next FILE unless ( $badim = $badata->getBioAssayDimension() );
+
+ foreach my $bioassay ( @{ $badim->getBioAssays() || [] } ) {
+
+ FACTORVALUE:
+ foreach my $fv ( @{ $bioassay->getBioAssayFactorValues() || [] } ) {
+ my ( $category, $value );
+ if ( my $oe = $fv->getValue() ) {
+ $category = $oe->getCategory();
+ $value = $oe->getValue();
+ }
+ elsif ( my $measurement = $fv->getMeasurement() ) {
+ my $unit = $measurement->getUnit();
+ $category = $unit
+ ? ( $unit->getUnitNameCV() || $unit->getUnitName() )
+ : 'UNKNOWN';
+ $value = $measurement->getValue();
+ }
+ else {
+ next FACTORVALUE;
+ }
+ $file->add_factor_value( $category, $value );
+ }
+ }
+ }
+
+ return;
+}
+
+sub add_native_filetypes : PRIVATE {
+ my ( $self, %type ) = @_;
+
+ foreach my $filename ( keys %type ) {
+ $native_filetypes{ident $self}{$filename} = $type{$filename};
+ }
+ return $native_filetypes{ident $self};
+}
+
+sub get_blank_regexp {
+
+ # Allow access to the RE we're using to detect blanks.
+ my ( $self ) = @_;
+
+ return $BLANK;
+}
+
+1;
+
+__DATA__
+
+ header: material_section(?)
+ edge(s?)
+ assay_or_hyb(?)
+ edge(s?)
+ data_section(?)
+ factor_value(s?)
+ end_of_line
+
+ { $return = sub{
+
+ my @objects;
+
+ # Reset some global variables.
+ $::channel = 'Unknown';
+ $::array_accession = undef;
+ $::previous_material = undef;
+ $::previous_event = undef;
+ $::previous_data = undef;
+ @::protocolapp_list = ();
+
+ # Generate the objects.
+ foreach my $sub (@{$item[1][0]},
+ @{$item[2]},
+ @{$item[3]},
+ @{$item[4]},
+ @{$item[5][0]},
+ @{$item[6]}){
+ if (ref $sub eq 'CODE') {
+ my @obj = &{ $sub };
+ push @objects, @obj;
+ }
+ else {
+ die("Error: Grammar rule return value not a CODE ref: $sub");
+ }
+ }
+
+ if ( scalar @_ ) {
+ die("Error: SDRF row not completely parsed: " . join("\n", @_));
+ }
+
+ # Post-process BioAssays and FactorValues.
+ my @bioassays
+ = grep { $_ && $_->isa('Bio::MAGE::BioAssay::BioAssay') }
+ @objects;
+ my @factorvals
+ = grep { $_ && $_->isa('Bio::MAGE::Experiment::FactorValue') }
+ @objects;
+ $::sdrf->add_factorvals_to_bioassays(
+ \@factorvals,
+ \@bioassays,
+ );
+
+ return \@objects;
+ };
+ }
+
+ | <error: Invalid header; unparseable sequence starts here: $text>
+
+ end_of_line: <skip:'[ \x{0}\r]*'> /\Z/
+
+ material_section: material edge_and_material(s?)
+
+ { $return = [$item[1], map { @{ $_ } } @{$item[2]}] }
+
+ data_section: assay_or_data edge_and_assay_or_data(s?)
+
+ { $return = [$item[1], map { @{ $_ } } @{$item[2]}] }
+
+ edge_and_material: edge(s?) material { $return = [ @{ $item[1] }, $item[2] ] }
+
+ edge_and_assay_or_data: edge(s?) assay_or_data { $return = [ @{ $item[1] }, $item[2] ] }
+
+ assay_or_data: event
+ | data
+
+ edge: factor_value
+ | protocol
+
+ material: source
+ | sample
+ | extract
+ | labeled_extract
+
+ event: scan
+ | normalization
+
+ data: raw_data
+ | derived_data
+
+ source_name: /Source *Names?/i
+
+ source: source_name source_attribute(s?)
+
+ { $return = sub{
+ my $name = shift;
+ my $obj = $::sdrf->create_source($name);
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_material = $obj if $obj;
+ return $obj;
+ };
+ }
+
+ sample_name: /Sample *Names?/i
+
+ sample: sample_name material_attribute(s?)
+
+ { $return = sub{
+ my $name = shift;
+ my $obj = $::sdrf->create_sample(
+ $name,
+ $::previous_material,
+ \@::protocolapp_list,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_material = $obj if $obj;
+ return $obj;
+ };
+ }
+
+ extract_name: /Extract *Names?/i
+
+ extract: extract_name material_attribute(s?)
+
+ { $return = sub{
+ my $name = shift;
+ my $obj = $::sdrf->create_extract(
+ $name,
+ $::previous_material,
+ \@::protocolapp_list,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_material = $obj if $obj;
+ return $obj;
+ };
+ }
+
+ labeled_extract_name: /Labell?ed *Extract *Names?/i
+
+ labeled_extract: labeled_extract_name labeled_extract_attribute(s?)
+
+ { $return = sub{
+ my $name = shift;
+ my $obj = $::sdrf->create_labeled_extract(
+ $name,
+ $::previous_material,
+ \@::protocolapp_list,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_material = $obj if $obj;
+ return $obj;
+ };
+ }
+
+ source_attribute: material_attribute
+ | provider
+
+ labeled_extract_attribute: material_attribute
+ | label
+
+ material_attribute: characteristic
+ | materialtype
+ | description
+ | comment
+
+ characteristic_heading: /Characteristics?/i
+
+ characteristic: characteristic_heading
+ <skip:' *'> bracket_term
+ <skip:' *\x{0} *'> char_fv_attribute(?)
+
+ { $return = sub {
+ my $material = shift;
+
+ my $char;
+ if ( ref $item[5][0] eq 'CODE' ) {
+
+ # Add a unit (nested OE) to the material.
+ my $oe_meas;
+ ( $char, $oe_meas )
+ = $::sdrf->create_nested_oe_measurement($item[3], shift);
+ unshift( @_, $oe_meas );
+ my $unit = &{ $item[5][0] };
+ $::sdrf->add_char_to_material($char, $material);
+ }
+ else {
+
+ # Value
+ my @args = shift;
+
+ if ( ref $item[5][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[5][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+
+ $char = $::sdrf->create_ontologyentry($item[3], @args);
+ $::sdrf->add_char_to_material($char, $material);
+ }
+ return $char;
+ };
+ }
+
+ factor_value_heading: /Factor *Values?/i
+
+ factor_value: factor_value_heading
+ <skip:' *'> bracket_term parens_term(?)
+ <skip:' *\x{0} *'> char_fv_attribute(?)
+
+ { $return = sub {
+ my ($fv, $ef);
+ if ( ref $item[6][0] eq 'CODE' ) {
+ my $value = shift;
+
+ # Attach the unit to the measurement.
+ unshift( @_, undef );
+ my $unit = &{ $item[6][0] };
+
+ ($fv, $ef) = $::sdrf->create_factorvalue_measurement(
+ $item[3],
+ $item[4][0],
+ $value,
+ $unit,
+ );
+
+ # This has to be done after adding the unit.
+ $::sdrf->add_factorvalue_to_factor( $fv, $ef ) if $fv;
+ }
+ else {
+
+ # Value
+ my @args = shift;
+
+ if ( ref $item[6][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[6][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+ $fv = $::sdrf->create_factorvalue_value(
+ $item[3],
+ $item[4][0],
+ @args,
+ );
+ }
+ return $fv;
+ };
+ }
+
+ char_fv_attribute: unit
+ | termsource
+
+ bracket_term: /\A \[ [ ]* ([^\x{0}\]]+?) [ ]* \]/xms
+
+ { $return = $1 }
+
+ parens_term: /\A \( [ ]* ([^\x{0}\)]+?) [ ]* \)/xms
+
+ { $return = $1 }
+
+ namespace_term: /\A : ([^\x{0}]+)/xms
+
+ { $return = $1 }
+
+ term_source_ref: /Term *Source *REFs?/i
+
+ termsource: term_source_ref
+ <skip:' *'> namespace_term(?)
+ <skip:' *\x{0} *'> term_accession(?)
+
+ { $return = [ $item[3][0], $item[5][0] ] } # FIXME add namespace_term support
+
+ term_accession: /Term *Accession *Numbers?/i
+
+ { $return = 'term_accession'; }
+
+ provider_heading: /Providers?/i
+
+ provider: provider_heading comment(s?)
+
+ { $return = sub {
+ my $source = shift;
+ my @names = split /\s*;\s*/, shift;
+
+ # At least one entry needed to consume @_ correctly.
+ @names = q{} unless scalar @names;
+
+ # Temporary store for multiple uses of @_
+ my @args = @_;
+
+ my @obj_list;
+ foreach my $name ( @names ) {
+ my $obj = $::sdrf->create_provider(
+ $name,
+ $source,
+ );
+ push @obj_list, $obj;
+ @_ = @args;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ }
+ return @obj_list;
+ };
+ }
+
+ materialtype_heading: /Material *Types?/i
+
+ materialtype: materialtype_heading termsource(?)
+
+ { $return = sub {
+ my $material = shift;
+
+ # Value
+ my @args = shift;
+
+ if ( ref $item[2][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[2][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+ my $type = $::sdrf->create_ontologyentry('MaterialType', @args);
+ $::sdrf->set_material_type( $material, $type ) if ($material && $type);
+ return $type;
+ };
+ }
+
+ label_heading: /Labels?/i
+
+ label: label_heading termsource(?)
+
+ { $return = sub {
+ my $labeled_extract = shift;
+ $::channel = shift;
+
+ my @args;
+
+ if ( ref $item[2][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[2][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+ my $label = $::sdrf->create_label($::channel, @args, $labeled_extract);
+ return $label;
+ };
+ }
+
+ description: /Descriptions?/i
+
+ { $return = sub {
+ my $describable = shift;
+ return $::sdrf->create_description(shift, $describable);
+ };
+ }
+
+ comment_heading: /Comments?/i
+
+ comment: comment_heading <skip:' *'> bracket_term
+
+ { $return = sub {
+ my $extendable = shift;
+ return $::sdrf->create_nvt($item[3], shift, undef, $extendable);
+ };
+ }
+
+ protocol_ref: /Protocol *REFs?/i
+
+ protocol: protocol_ref
+ <skip:' *'> namespace_term(?)
+ <skip:' *\x{0} *'> termsource(?)
+ protocol_attributes(s?)
+
+ { $return = sub{
+
+ # Name, namespace_term
+ my @args = (shift, $item[3][0]);
+
+ if ( ref $item[5][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[5][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+
+ my $obj = $::sdrf->create_protocolapplication(@args);
+
+ # Add to the global ProtApp list immediately so that
+ # parameter units can be sorted out without
+ # having to pass $obj through.
+ push(@::protocolapp_list, $obj) if $obj;
+
+ foreach my $sub (@{$item[6]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ return $obj;
+ };
+ }
+
+ protocol_attributes: parameter
+ | performer
+ | date
+ | comment
+
+ parameter_heading: /Parameter *Values?/i
+
+ parameter: parameter_heading
+ <skip:' *'> bracket_term
+ <skip:' *\x{0} *'> parameter_attributes(s?)
+
+ { $return = sub {
+ my $protocolapp = shift;
+ my $value = shift;
+ my $obj = $::sdrf->create_parametervalue($item[3], $value, $protocolapp);
+ foreach my $sub (@{$item[5]}){
+ if ( ref $sub eq 'CODE' ) {
+
+ # Measurement, Comment
+ unshift( @_, $obj );
+ my $attr = &{ $sub };
+
+ if ( defined $attr
+ && $attr->isa('Bio::MAGE::Measurement::Unit') ) {
+ $::sdrf->add_unit_to_thing( $attr, $obj );
+ }
+ }
+ elsif ( ref $sub eq 'ARRAY' ) {
+
+ # Value
+ my @args;
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($sub->[1] eq 'term_accession')
+ ? shift
+ : undef;
+
+ $::sdrf->add_value_to_parameter(
+ $obj,
+ @args,
+ );
+ }
+ }
+ return $obj;
+ };
+ }
+
+ parameter_attributes: unit
+ | termsource
+ | comment
+
+ unit_heading: /Unit/i
+
+ unit: unit_heading
+ <skip:' *'> bracket_term
+ <skip:' *\x{0} *'> termsource(?)
+
+ { $return = sub {
+ my $value_having_unit = shift;
+
+ # Unit name
+ my @args = shift;
+
+ if ( ref $item[5][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[5][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+
+ my $unit;
+ if ( defined($value_having_unit)
+ && $value_having_unit->isa('Bio::MAGE::Description::OntologyEntry') ) {
+ $unit = $::sdrf->create_nested_oe_unit(
+ $item[3],
+ @args,
+ $value_having_unit,
+ );
+ }
+ else {
+ $unit = $::sdrf->create_unit(
+ $item[3],
+ @args,
+ );
+ }
+ return $unit;
+ };
+ }
+
+ performer_heading: /Performers?/i
+
+ performer: performer_heading comment(s?)
+
+ { $return = sub {
+ my $protocolapp = shift;
+ my @names = split /\s*;\s*/, shift;
+
+ # At least one entry needed to consume @_ correctly.
+ @names = q{} unless scalar @names;
+
+ # Temporary store for multiple uses of @_
+ my @args = @_;
+
+ my @obj_list;
+ foreach my $name ( @names ) {
+ my $obj = $::sdrf->create_performer(
+ $name,
+ $protocolapp,
+ );
+ push @obj_list, $obj;
+ @_ = @args;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ }
+ return @obj_list;
+ };
+ }
+
+ date: /Dates?/i
+
+ { $return = sub {
+ my $protocolapp = shift;
+ my $date = shift;
+ $::sdrf->add_date_to_protocolapp( $date, $protocolapp );
+ return $date;
+ };
+ }
+
+ array_design: array_design_file
+ | array_design_ref
+
+ array_design_file_heading: /Array *Design *Files?/i
+
+ array_design_file: array_design_file_heading comment(s?)
+
+ { die "ADFs not yet supported" }
+
+ array_design_ref_heading: /Array *Design *REFs?/i
+
+ array_design_ref: array_design_ref_heading
+ <skip:' *'> namespace_term(?)
+ <skip:' *\x{0} *'> termsource(?)
+ comment(s?)
+
+ { $return = sub {
+ my $hybridization = shift;
+ $::array_accession = shift;
+
+ # array_accession, namespace_term
+ my @args = ( $::array_accession, $item[3][0] );
+
+ if ( ref $item[5][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[5][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+
+ my $obj = $::sdrf->create_array(@args, $hybridization);
+ foreach my $sub (@{$item[6]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ return $obj;
+ };
+ }
+
+ assay_or_hyb: assay
+ | hybridization
+
+ hybridization_name: /Hybridi[sz]ation *Names?/i
+
+ hybridization: hybridization_name hybrid_attribute(s?)
+
+ { $return = sub {
+ my $name = shift;
+ my $obj = $::sdrf->create_hybridization(
+ $name,
+ $::previous_material,
+ \@::protocolapp_list,
+ $::channel,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_event = $obj if $obj;
+ return $obj;
+ };
+ }
+
+ hybrid_attribute: array_design
+ | technology_type
+ | comment
+
+ assay_name: /Assay *Names?/i
+
+ assay: assay_name technology_type comment(?)
+
+ { $return = sub {
+ my $name = shift;
+ my $obj = $::sdrf->create_assay(
+ $name,
+ $::previous_material,
+ \@::protocolapp_list,
+ $::channel,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub ($item[2], $item[3][0]){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+
+ $::previous_event = $obj if $obj;
+ return $obj;
+ };
+ }
+
+ technol_type_heading: /Technology *Types?/i
+
+ technology_type: technol_type_heading termsource(?)
+
+ { $return = sub {
+ my $assay = shift;
+
+ # Value
+ my @args = shift;
+
+ if ( ref $item[2][0] eq 'ARRAY' ) {
+
+ # Term Source
+ push @args, shift;
+
+ # Accession
+ push @args, ($item[2][0][1] eq 'term_accession')
+ ? shift
+ : undef;
+ }
+ else {
+
+ # No term source given
+ push @args, undef, undef;
+ }
+ my $type = $::sdrf->create_ontologyentry('TechnologyType', @args);
+ $::sdrf->set_technology_type($assay, $type) if ($assay && $type);
+ return $type;
+ };
+ }
+
+ scan_name: /Scan *Names?/i
+
+ scan: scan_name scan_attribute(s?)
+
+ { $return = sub {
+ my $name = shift;
+ my ($obj, $channel_obj, @ret_obj) = $::sdrf->create_scan(
+ $name,
+ $::previous_event,
+ \@::protocolapp_list,
+ $::channel,
+ $::previous_material,
+ );
+ @::protocolapp_list = () if $obj;
+ $::previous_event = $obj if $obj;
+ push @ret_obj, $obj if $obj;
+ push @ret_obj, $channel_obj if $channel_obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ push (@ret_obj, &{ $sub }) if (ref $sub eq 'CODE');
+ }
+ return @ret_obj; # Includes channel and merged PBAs; hybs where autogenerated.
+ };
+ }
+
+ scan_attribute: comment
+
+ normalization_name: /Normali[sz]ation *Names?/i
+
+ normalization: normalization_name norm_attribute(s?)
+
+ { $return = sub {
+ my ( $obj, $apps, @old_bioassays ) = $::sdrf->create_normalization(
+ shift,
+ $::previous_event,
+ \@::protocolapp_list,
+ $::channel,
+ $::previous_material,
+ );
+ @::protocolapp_list = @{ $apps || [] } if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_event = $obj if $obj;
+ return ( $obj, @old_bioassays ); # DBA; MBA, PBAs if autogenerated.
+ };
+ }
+
+ norm_attribute: comment
+
+ raw_data: image
+ | array_data
+ | array_data_matrix
+
+ derived_data: derived_array_data
+ | derived_array_data_matrix
+
+ array_data_file: /Array *Data *Files?/i
+
+ array_data: array_data_file comment(s?)
+
+ { $return = sub {
+ my ($obj, $data, @oldpbas) = $::sdrf->create_raw_data_file(
+ shift,
+ $::previous_event,
+ \@::protocolapp_list,
+ $::array_accession,
+ $::channel,
+ $::previous_material,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_event = $obj if $obj;
+ $::previous_data = $data if $data;
+ return ($obj, @oldpbas); # MBA; PBAs if autogenerated.
+ };
+ }
+
+ derived_array_data_file: /Derived *Array *Data *Files?/i
+
+ derived_array_data: derived_array_data_file comment(s?)
+
+ { $return = sub {
+ my ($obj, @old_bioassays) = $::sdrf->create_normalized_data(
+ shift,
+ $::previous_event,
+ \@::protocolapp_list,
+ $::previous_data,
+ $::array_accession,
+ $::channel,
+ $::previous_material,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_data = $obj if $obj;
+ return ($obj, @old_bioassays); # DBAData
+ };
+ }
+
+ array_data_matrix_file: /Array *Data *Matrix *Files?/i
+
+ array_data_matrix: array_data_matrix_file comment(s?)
+
+ { $return = sub {
+ my ($obj, @old_pbas) = $::sdrf->create_raw_data_matrix(
+ shift,
+ \@::protocolapp_list,
+ $::array_accession,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_data = $obj if $obj;
+ return ($obj, @old_pbas); # MBAData; PBAs if autogenerated.
+ };
+ }
+
+ derived_array_data_matrix_file: /Derived *Array *Data *Matrix *Files?/i
+
+ derived_array_data_matrix: derived_array_data_matrix_file comment(s?)
+
+ { $return = sub {
+ my ($obj, @old_bioassays) = $::sdrf->create_norm_data_matrix(
+ shift,
+ \@::protocolapp_list,
+ $::array_accession,
+ $::previous_event,
+ $::channel,
+ $::previous_material,
+ );
+ @::protocolapp_list = () if $obj;
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ $::previous_data = $obj if $obj;
+ return ($obj, @old_bioassays); # DBAData
+ };
+ }
+
+ image_file: /Image *Files?/i
+
+ image: image_file comment(s?)
+
+ { $return = sub {
+ my ( $obj, @old_pbas ) = $::sdrf->create_image(
+ shift,
+ $::previous_event,
+ \@::protocolapp_list,
+ $::channel,
+ $::previous_material,
+ );
+ foreach my $sub (@{$item[2]}){
+ unshift( @_, $obj ) and
+ &{ $sub } if (ref $sub eq 'CODE');
+ }
+ return ( $obj, @old_pbas ); # Image; hyb PBA if autogenerated.
+ };
+ }
+
diff --git a/lib/ArrayExpress/MAGETAB/TabFile.pm b/lib/ArrayExpress/MAGETAB/TabFile.pm
new file mode 100644
index 0000000..b55ffa3
--- /dev/null
+++ b/lib/ArrayExpress/MAGETAB/TabFile.pm
@@ -0,0 +1,926 @@
+#!/usr/bin/env perl
+#
+# Module providing IDF parsing functions to the ArrayExpress MAGE-TAB
+# implementation.
+#
+# Tim Rayner, 2007, EMBL-EBI Microarray Informatics Team
+#
+# $Id: TabFile.pm 2017 2008-04-01 21:24:23Z tfrayner $
+
+package ArrayExpress::MAGETAB::TabFile;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+use List::Util qw(first);
+use Bio::MAGE qw(:ALL);
+use Text::CSV_XS;
+use Readonly;
+
+use ArrayExpress::Curator::Common qw(check_linebreaks);
+use ArrayExpress::Curator::MAGE qw(
+ make_container
+ unique_identifier
+
+ new_armanuf
+ new_array
+ new_image
+ new_biosource
+ new_biosample
+ new_extract
+ new_labeledextract
+ new_labelcompound
+ new_measuredbioassay
+ new_derivedbioassay
+ new_measuredbioassaydata
+ new_derivedbioassaydata
+ new_bioassaymap
+ new_quantitationtypemap
+ new_quantitationtype
+ new_qtd
+);
+
+use ArrayExpress::Curator::MAGE::Definitions qw(
+ $AE_LABELCOMPOUND_PREFIX
+ $AE_CHANNEL_PREFIX
+);
+
+Readonly my $BLANK => qr/\A (->)? \z/xms;
+
+## Accessor attributes.
+my %bags : ATTR( :name<bags>, :default<undef> );
+my %namespace : ATTR( :name<namespace>, :default<MAGETabulator> );
+my %authority : ATTR( :name<authority>, :default<ebi.ac.uk> );
+my %expt_accession : ATTR( :name<expt_accession>, :default<undef> );
+my %in_relaxed_mode : ATTR( :name<in_relaxed_mode>, :default<undef> );
+my %tabfile_eol_char : ATTR( :name<eol_char>, :default<undef> );
+my %source_directory : ATTR( :name<source_directory>, :default<undef> );
+
+#########################
+# Object initialization #
+#########################
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ # Constructor calls may pass in previously-used bags (e.g. when
+ # transferring IDF MAGE to the SDRF parser).
+ if ( defined $self->get_bags() ) {
+ $self->generate_bag_methods();
+ }
+ else{
+ $self->initialize_bags();
+ }
+
+ return;
+}
+
+##################
+# Public methods #
+##################
+
+###################
+# Private methods #
+###################
+
+sub initialize_bags : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ my %bag_of;
+
+ # These differ from the tab2mage versions of the same, implemented
+ # here as OO with an eye to migrating everything to OO design in
+ # the future. Currently these methods must comply with the
+ # conventions laid down in ArrayExpress::Curator::MAGE.
+
+ # These are used in both IDF and SDRF.
+ $bag_of{termsource} = make_container( sub{ $self->new_termsource(@_) } );
+ $bag_of{factor} = make_container( sub{ $self->new_factor(@_) } );
+ $bag_of{people} = make_container( sub{ $self->new_person(@_) } );
+ $bag_of{places} = make_container( sub{ $self->new_organization(@_) } );
+ $bag_of{protocol} = make_container( sub{ $self->new_protocol(@_) } );
+ $bag_of{parameter} = make_container( sub{ $self->new_parameter(@_) } );
+ $bag_of{software} = make_container( sub{ $self->new_protocol_ware(@_) } );
+ $bag_of{hardware} = make_container( sub{ $self->new_protocol_ware(@_) } );
+
+ # These are SDRF only.
+ $bag_of{armanuf} = make_container( \&new_armanuf );
+ $bag_of{array} = make_container( \&new_array );
+ $bag_of{arraydesign} = make_container( sub{ $self->new_arraydesign(@_) } );
+ $bag_of{factorvalue} = make_container( sub{ $self->new_factorvalue(@_) } );
+ $bag_of{source} = make_container( \&new_biosource );
+ $bag_of{sample} = make_container( \&new_biosample, 1 );
+ $bag_of{extract} = make_container( \&new_extract, 1 );
+ $bag_of{labeledextract} = make_container( \&new_labeledextract, 1 );
+ $bag_of{label} = make_container( \&new_labelcompound );
+ $bag_of{image} = make_container( \&new_image );
+ $bag_of{pba} = make_container( sub{ $self->new_physicalbioassay(@_) } );
+ $bag_of{mba} = make_container( \&new_measuredbioassay, 1 );
+ $bag_of{dba} = make_container( \&new_derivedbioassay, 1 );
+ $bag_of{extended_pba} = make_container( sub{ $self->new_physicalbioassay(@_) } );
+ $bag_of{datamatrix_mba} = make_container( \&new_measuredbioassay, 1 );
+ $bag_of{datamatrix_dba} = make_container( \&new_derivedbioassay, 1 );
+ $bag_of{mbad} = make_container( \&new_measuredbioassaydata );
+ $bag_of{dbad} = make_container( \&new_derivedbioassaydata );
+ $bag_of{datamatrix_mbad} = make_container( \&new_measuredbioassaydata );
+ $bag_of{datamatrix_dbad} = make_container( \&new_derivedbioassaydata );
+ $bag_of{bam} = make_container( \&new_bioassaymap );
+ $bag_of{qtm} = make_container( \&new_quantitationtypemap );
+ $bag_of{quantitationtype} = make_container( \&new_quantitationtype );
+ $bag_of{qtd} = make_container( \&new_qtd );
+
+ $self->set_bags( \%bag_of );
+
+ $self->generate_bag_methods();
+
+ return \%bag_of;
+}
+
+sub generate_bag_methods : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ my $bag = $self->get_bags();
+
+ { # Create convenience methods for bag access.
+ ## no critic ProhibitNoStrict ProhibitNoWarnings
+ no strict qw(refs);
+ no warnings qw(redefine);
+ ## use critic ProhibitNoStrict ProhibitNoWarnings
+ while ( my ( $bagname, $sub ) = each %$bag ) {
+ my $method = "${bagname}_bag";
+
+ # The shift here corrects the @_ argument array.
+ *{$method} = sub{ my $id = shift; $sub->(@_) };
+ }
+ }
+
+ return;
+}
+
+sub get_id_prefix : RESTRICTED {
+
+ my ( $self ) = @_;
+
+ return sprintf( "%s:%s", $self->get_authority(), $self->get_namespace() );
+}
+
+sub get_all_bioassays {
+
+ # Method to retrieve all bioassays of any type.
+ my ( $self ) = @_;
+
+ my @bioassays;
+
+ # Add references to all BioAssays
+ foreach my $type ( qw(pba
+ mba
+ dba
+ extended_pba
+ datamatrix_mba
+ datamatrix_dba) ) {
+
+ push @bioassays, @{ $self->get_bags->{ $type }->() };
+ }
+
+ return(\@bioassays);
+}
+
+sub get_all_bioassaydata {
+
+ # Method to retrieve all bioassaydata of any type.
+ my ( $self ) = @_;
+
+ my @bioassaydata;
+
+ # Add references to all BioAssayData
+ foreach my $type ( qw(mbad
+ dbad
+ datamatrix_mbad
+ datamatrix_dbad) ) {
+
+ push @bioassaydata, @{ $self->get_bags->{ $type }->() };
+ }
+
+ return(\@bioassaydata);
+}
+
+sub create_ontologyentry {
+
+ # Method takes two mandatory arguments, $category and $value, and
+ # four optional arguments ($termsource, $accession, $uri and
+ # $db). Returns an OE object. If termsource defined but db is not,
+ # termsource is used to look up a suitable db.
+ my ( $self, $category, $value, $termsource, $accession, $uri, $db ) = @_;
+
+ return if ( $value =~ $BLANK );
+
+ my $oe = Bio::MAGE::Description::OntologyEntry->new(
+ category => $category,
+ value => $value,
+ );
+
+ my $database;
+ if ( $db ) {
+ $database = $db;
+ }
+ elsif ( $termsource ) {
+
+ # We need an accession in some form for the OE to be valid. We
+ # don't just want to drop the termsource so we fake it
+ # here. N.B. this line actually holds for MO in its current form.
+ $accession ||= $value;
+
+ # If the Term Source is detectably MO, use some defaults.
+ if ( lc($termsource) eq 'mo' || $termsource =~ m/MGED ?Ontology/i ) {
+ $uri ||= sprintf(
+ "http://mged.sourceforge.net/ontologies/MGEDontology.php#%s",
+ $value
+# FIXME use the following if the OWLdocs ever go production-ready.
+# "http://mged.sourceforge.net/ontologies/MOhtml/%s.html",
+# lc($value)
+ );
+ }
+
+ my $args;
+ if ( $self->get_in_relaxed_mode() ) {
+ $args = {name => $termsource,
+ identifier => "ebi.ac.uk:Database:$termsource"};
+ }
+
+ $database = $self->termsource_bag( $termsource, $args )
+ or croak(qq{Error: cannot find Term Source Name "$termsource"\n});
+ }
+
+ if ( $database ) {
+
+ # Create the actual DB entry.
+ my $dbentry = Bio::MAGE::Description::DatabaseEntry->new(
+ accession => $accession,
+ URI => $uri,
+ database => $database,
+ );
+ $oe->setOntologyReference( $dbentry );
+ }
+
+ return $oe;
+}
+
+sub new_termsource : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Description::Database->new(
+ identifier => $args->{identifier},
+ ) unless $obj;
+
+ if ( $args->{name} && ! $obj->getName() ) {
+ $obj->setName( $args->{name} );
+ }
+
+ if ( $args->{version} && ! $obj->getVersion() ) {
+ $obj->setVersion( $args->{version} );
+ }
+
+ if ( $args->{uri} && ! $obj->getURI() ) {
+ $obj->setURI( $args->{uri} );
+ }
+
+ return $obj;
+}
+
+sub new_factor : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Experiment::ExperimentalFactor->new() unless $obj;
+
+ # Identifier.
+ unless ( $obj->getIdentifier() ) {
+ my $identifier
+ = sprintf("%s:%s.%s.%s.ExperimentalFactor",
+ $self->get_id_prefix(),
+ $self->get_expt_accession(),
+ unique_identifier(),
+ $args->{type});
+ $obj->setIdentifier($identifier);
+ }
+
+ # Name.
+ $obj->setName( $args->{name} ) unless $obj->getName();
+
+ # Category OE.
+ my $oe = $self->create_ontologyentry(
+ 'ExperimentalFactorCategory',
+ $args->{type},
+ $args->{termsource},
+ $args->{termaccession},
+ );
+ $obj->setCategory($oe) unless $obj->getCategory;
+
+ return $obj;
+}
+
+sub new_person : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::AuditAndSecurity::Person->new() unless $obj;
+
+ $obj->setIdentifier( $args->{identifier} ) if $args->{identifier};
+ $obj->setName( $args->{name} ) if $args->{name};
+ $obj->setFirstName( $args->{firstname} ) if $args->{firstname};
+ $obj->setMidInitials( $args->{midinitials} ) if $args->{midinitials};
+ $obj->setLastName( $args->{lastname} ) if $args->{lastname};
+ $obj->setPhone( $args->{phone} ) if $args->{phone};
+ $obj->setEmail( $args->{email} ) if $args->{email};
+ $obj->setFax( $args->{fax} ) if $args->{fax};
+ $obj->setAddress( $args->{address} ) if $args->{address};
+
+ if ( $args->{roles} ) {
+ my @newroles;
+ my @oldroles = @{ $obj->getRoles() || [] };
+ foreach my $value (split /\s*;\s*/, $args->{roles} ) {
+
+ my $found = first {
+ $_->getValue() eq $value;
+ } @oldroles;
+ unless ( $found ) {
+ my $role = $self->create_ontologyentry(
+ 'Roles',
+ $value,
+ $args->{termsource},
+ $args->{termaccession},
+ );
+ push @newroles, $role;
+ }
+ }
+ $obj->addRoles( @newroles ) if scalar(@newroles);
+ }
+
+ my $affiliation;
+ if ( $args->{affiliation} ) {
+ $affiliation = $self->places_bag(
+ $args->{affiliation},
+ {
+ name => $args->{affiliation},
+ },
+ );
+ $obj->setAffiliation( $affiliation );
+ }
+
+ return $obj;
+}
+
+sub new_organization : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::AuditAndSecurity::Organization->new() unless $obj;
+
+ unless ( $obj->getIdentifier() ) {
+
+ # Only first 50 characters used in identifier creation to
+ # avoid ArrayExpress schema problems with over-long ids.
+ my $identifier = sprintf(
+ "%s:%s.Organization",
+ $self->get_id_prefix(),
+ substr( $args->{name}, 0, 50 ),
+ );
+
+ $obj->setIdentifier($identifier);
+ }
+
+ $obj->setName($args->{name}) unless $obj->getName();
+
+ return $obj;
+}
+
+sub new_protocol : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Protocol::Protocol->new unless $obj;
+
+ # We allow the SDRF to re-identify protocols if Term Source and
+ # Accession are used.
+ $obj->setIdentifier($args->{accession}) if $args->{accession};
+
+ if ( $args->{type} && ! $obj->getType() ) {
+ my $type = $self->create_ontologyentry(
+ 'ProtocolType',
+ $args->{type},
+ $args->{termsource},
+ $args->{termaccession},
+ );
+ $obj->setType($type);
+ }
+ if ( $args->{name} && ! $obj->getName() ) {
+ $obj->setName( $args->{name} );
+ }
+ if ( $args->{description} && ! $obj->getText() ) {
+ $obj->setText( $args->{description} );
+ }
+
+ # FIXME more granular update for parameters here?
+ if ( $args->{parameters} && ! $obj->getParameterTypes() ) {
+
+ my $parameters = $self->create_parameters(
+ $args->{parameters},
+ $args->{accession},
+ $args->{name},
+ );
+
+ $obj->setParameterTypes( $parameters );
+ }
+
+ # Create software, hardware, attach to protocol and each other.
+ my ( $software, $hardware ) = $self->create_and_add_protocol_wares(
+ $obj,
+ $args->{software},
+ $args->{hardware},
+ );
+
+ # FIXME contact is implemented as a NVT association for now.
+ if ( $args->{contact} ) {
+ my $contact = Bio::MAGE::NameValueType->new(
+ name => 'Protocol Contact',
+ value => $args->{contact},
+ );
+ $obj->addPropertySets($contact);
+ }
+
+ return $obj;
+
+}
+
+sub create_parameters : RESTRICTED {
+
+ # We need to control protocol accession, authority and namespace
+ # at this point, use them in the parameter identifiers. Note that
+ # we don't have parameter units at this point; we need to modify
+ # these later at the SDRF parsing stage.
+ my ( $self, $parameters, $prot_accession, $prot_name ) = @_;
+
+ my $param_namespace =
+ sprintf("%s:%s",
+ $self->get_id_prefix(),
+ $prot_accession);
+ my @parameters;
+ foreach my $param_name ( split /\s*;\s*/, $parameters ) {
+ my $param_id = sprintf("%s.%s.Parameter",
+ $param_namespace,
+ $param_name);
+ push(
+ @parameters,
+ $self->parameter_bag(
+ "$param_name.$prot_name",
+ {
+ identifier => $param_id,
+ name => $param_name,
+ }
+ )
+ );
+ }
+
+ return \@parameters;
+}
+
+sub new_parameter : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::Protocol::Parameter->new unless $obj;
+
+ $obj->setIdentifier($args->{identifier}) unless $obj->getIdentifier();
+
+ if ( $args->{name} && ! $obj->getName() ) {
+ $obj->setName( $args->{name} );
+ }
+
+ # Units are also added at the SDRF parsing stage.
+ if ( $args->{unit} ) {
+ my $defaultval = Bio::MAGE::Measurement::Measurement->new(
+ unit => $args->{unit},
+ );
+ $obj->setDefaultValue( $defaultval );
+ }
+
+ return $obj;
+}
+
+sub new_protocol_ware : RESTRICTED {
+
+ # Create a Software or Hardware object; $class is set during the
+ # creation of the %bags attribute hash (the bag knows what it contains).
+ my ( $self, $args, $obj ) = @_;
+
+ croak("Error: must supply a 'class' attribute to new_protocol_ware.")
+ unless $args->{class};
+
+ my $mageclass = "Bio::MAGE::Protocol::$args->{class}";
+
+ $obj = $mageclass->new unless $obj;
+
+ # Identifier
+ $obj->setIdentifier( $args->{identifier} ) unless $obj->getIdentifier();
+
+ # Type (not actually supported by IDF yet; FIXME maybe by
+ # inferring from ProtocolType (questionable). This is included for later.
+ if ( $args->{type} && ! $obj->getType() ) {
+ my $type = $self->create_ontologyentry(
+ $args->{category} || "$args->{class}Type",
+ $args->{type},
+ );
+ $obj->setType($type);
+ }
+
+ # Name
+ $obj->setName( $args->{name} ) unless $obj->getName;
+
+ return $obj;
+}
+
+sub create_and_add_protocol_wares : RESTRICTED {
+
+ my ( $self, $protocol, $sw_name, $hw_name) = @_;
+
+ my ( $software, $hardware );
+
+ my $ware_namespace =
+ sprintf("%s:%s.%s",
+ $self->get_id_prefix(),
+ $self->get_expt_accession(),
+ unique_identifier());
+
+ # FIXME software and hardware type not currently captured by
+ # MAGE-TAB specification (as of v1.0).
+ if ( $sw_name ) {
+ $software = $self->software_bag(
+ $sw_name,
+ {
+ identifier => "$ware_namespace.Software",
+ name => $sw_name,
+ class => 'Software',
+ }
+ );
+ $self->update_object_wares_assnlist( $protocol, $software );
+ }
+ if ( $hw_name ) {
+ $hardware = $self->hardware_bag(
+ $hw_name,
+ {
+ identifier => "$ware_namespace.Hardware",
+ name => $hw_name,
+ class => 'Hardware',
+ }
+ );
+ $self->update_object_wares_assnlist( $protocol, $hardware );
+ }
+
+ # Link sw and hw here (checking that they're not already linked).
+ if ( $hardware && $software ) {
+
+ # HW to SW is 1:n
+ $software->setHardware($hardware) unless $software->getHardware();
+ $self->update_object_wares_assnlist( $hardware, $software );
+ }
+
+ return ( $software, $hardware );
+}
+
+sub update_object_wares_assnlist : RESTRICTED {
+
+ # Update the list of hardware or software associated with an
+ # object (protocol or hardware, or conceivably software (to other
+ # softwares)) to contain a new entry.
+ my ( $self, $object, $ware ) = @_;
+
+ # Figure out which list we're updating from the $ware object
+ # passed in.
+ my $attribute = ref $ware;
+ $attribute =~ s/^.*:://;
+
+ my $getter = "get${attribute}s";
+ my $adder = "add${attribute}s";
+
+ my $found;
+ if ( my $preexisting_wares = $object->$getter ) {
+ my $new_identifier = $ware->getIdentifier();
+ $found = first { $_->getIdentifier() eq $new_identifier }
+ @$preexisting_wares;
+ }
+
+ $object->$adder($ware) unless $found;
+
+ return;
+}
+
+sub new_arraydesign : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::ArrayDesign::PhysicalArrayDesign->new()
+ unless $obj;
+
+ if ( defined( $args->{accession} ) ) {
+ $obj->setIdentifier( $args->{accession} )
+ unless $obj->getIdentifier();
+ }
+
+ if ( defined( $args->{termsource} ) ) {
+
+ my $ts_args;
+ if ( $self->get_in_relaxed_mode() ) {
+ $ts_args = {name => $args->{termsource},
+ identifier => "ebi.ac.uk:Database:$args->{termsource}"};
+ }
+
+ my $database = $self->termsource_bag( $args->{termsource}, $ts_args )
+ or croak(qq{Error: cannot find Term Source Name "$args->{termsource}"\n});
+
+ my $dbentry = Bio::MAGE::Description::DatabaseEntry->new(
+ accession => $args->{accession},
+ database => $database,
+ );
+ my $desc = Bio::MAGE::Description::Description->new(
+ databaseReferences => [ $dbentry ],
+ );
+ $obj->setDescriptions( [ $desc ] );
+ }
+
+ return $obj;
+}
+
+sub new_physicalbioassay : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::PhysicalBioAssay->new unless $obj;
+
+ # Identifier
+ $args->{identifier_template} && do {
+ my $identifier = "$args->{identifier_template}.PhysicalBioAssay";
+ $obj->setIdentifier($identifier) unless $obj->getIdentifier;
+ };
+
+ # Name
+ $obj->setName($args->{name}) unless $obj->getName();
+
+ # Channels
+ # Construct hash listing of old label ids
+ my $old_label_ids;
+ if ( my $old_channels = $obj->getChannels() ) {
+ foreach my $old_ch ( @{ $old_channels } ) {
+ if ( my $old_labels = $old_ch->getLabels() ) {
+ foreach my $old_l ( @{ $old_labels } ) {
+ $old_label_ids->{ $old_l->getIdentifier() }++;
+ }
+ }
+ }
+ }
+
+ # N.B. only one label per LE supported here (although LEs can have
+ # more than one label, and new_labeledextract does support
+ # this).
+ if ( $args->{label} ) {
+ my $dyename;
+
+ # Convert LabelCompound into Channel
+ my $dye_id = $args->{label}->getIdentifier();
+ unless ( ( $dyename )
+ = ( $dye_id =~ m/^$AE_LABELCOMPOUND_PREFIX(.*)$/ ) ) {
+ $dyename = 'Unknown';
+ }
+
+ # Don't add the channel if it's already present.
+ unless ( $old_label_ids->{ $dye_id } ) {
+ my $channel = Bio::MAGE::BioAssay::Channel->new(
+ identifier => $AE_CHANNEL_PREFIX . $dyename,
+ name => $dyename,
+ labels => [ $args->{label} ],
+ );
+ $obj->addChannels($channel);
+ }
+ }
+
+ # This is only the case for hyb-level PBAs.
+ if ( my $new_bm = $args->{derived_from} ) {
+
+ my $hyb;
+ unless ( $hyb = $obj->getBioAssayCreation() ) {
+ $hyb = $self->new_hybridization(
+ {
+ identifier_template => $args->{identifier_template},
+ name => $args->{name},
+ array => $args->{array},
+ target => $obj,
+ protocolapps => $args->{hyb_protocolapps},
+ is_assay => $args->{is_assay},
+ },
+ );
+ $obj->setBioAssayCreation( $hyb );
+ }
+
+ # SourceBioMaterialMeasurements - added where the BioMaterials
+ # differ (e.g. Cy3, Cy5 labeled extracts).
+ my $preexisting_bmms = $hyb->getSourceBioMaterialMeasurements();
+ my $new_bm_id = $new_bm->getIdentifier;
+ my $found = first {
+ $_->getBioMaterial()->getIdentifier() eq $new_bm_id
+ } @$preexisting_bmms;
+
+ unless ($found) {
+ my $bmm = Bio::MAGE::BioMaterial::BioMaterialMeasurement->new(
+ bioMaterial => $new_bm,
+ );
+ $hyb->addSourceBioMaterialMeasurements($bmm);
+ }
+ }
+
+ if ( my $factorvals = $args->{factor_values} ) {
+ foreach my $fv ( @$factorvals ) {
+ $self->add_factorval_to_bioassay( $fv, $obj );
+ }
+ }
+
+ # BioAssayTreatments; initial PBA creation just creates a dummy
+ # BAT (see scan, data for per-channel BATs). *Don't* update here
+ # if there are preexisting BATs.
+ unless ( $obj->getBioAssayTreatments() ) {
+ $obj->setBioAssayTreatments(
+ [
+ $self->new_scan(
+ {
+ identifier_template => $args->{identifier_template},
+ name => $args->{name},
+ pba => $obj,
+ target => $obj,
+ protocolapps => $args->{scan_protocolapps},
+ }
+ )
+ ]
+ );
+ }
+
+ return $obj;
+}
+
+sub new_factorvalue : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ # Generate an empty FV object; Value_assn, Measurement_assn will
+ # be added elsewhere.
+ unless ( $obj ) {
+ $obj = Bio::MAGE::Experiment::FactorValue->new();
+ }
+
+ if ( $args->{identifier_template} ) {
+ $obj->setIdentifier("$args->{identifier_template}.FactorValue")
+ unless $obj->getIdentifier();
+ }
+ $obj->setName( $args->{name} ) unless $obj->getName;
+
+ return $obj;
+}
+
+sub add_factorval_to_bioassay : RESTRICTED {
+
+ my ( $self, $factorval, $bioassay ) = @_;
+
+ # FIXME we should probably be comparing category, value, unit
+ # etc. here.
+ my $found = first {
+ $_->getIdentifier() eq $factorval->getIdentifier()
+ } @{ $bioassay->getBioAssayFactorValues() || [] };
+ $bioassay->addBioAssayFactorValues( $factorval ) unless $found;
+
+ return;
+}
+
+sub new_hybridization : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ my $identifier;
+ unless ( $obj ) {
+ if ( $args->{is_assay} ) {
+ $obj = Bio::MAGE::BioAssay::BioAssayCreation->new();
+ $identifier = "$args->{identifier_template}.Assay";
+ }
+ else {
+ $obj = Bio::MAGE::BioAssay::Hybridization->new();
+ $identifier = "$args->{identifier_template}.Hybridization";
+ }
+ }
+
+ if ( $args->{identifier_template} ) {
+ $obj->setIdentifier( $identifier )
+ unless $obj->getIdentifier();
+ }
+
+ $obj->setName( $args->{name} ) if $args->{name};
+ $obj->setArray( $args->{array} ) if $args->{array};
+ $obj->setPhysicalBioAssayTarget( $args->{target} ) if $args->{target};
+
+ if ( $args->{protocolapps} && scalar( @{ $args->{protocolapps} } ) ) {
+ $obj->setProtocolApplications( $args->{protocolapps} );
+ }
+
+ return $obj;
+}
+
+sub new_scan : RESTRICTED {
+
+ my ( $self, $args, $obj ) = @_;
+
+ $obj = Bio::MAGE::BioAssay::ImageAcquisition->new() unless $obj;
+
+ if ( $args->{identifier_template} ) {
+ $obj->setIdentifier("$args->{identifier_template}.ImageAcquisition")
+ unless $obj->getIdentifier();
+ }
+
+ $obj->setName( $args->{name} ) if $args->{name};
+ $obj->setPhysicalBioAssay( $args->{pba} ) if $args->{pba};
+ $obj->setTarget( $args->{target} ) if $args->{target};
+
+ if ( $args->{protocolapps} && scalar( @{ $args->{protocolapps} } ) ) {
+ $obj->setProtocolApplications( $args->{protocolapps} );
+ }
+
+ return $obj;
+}
+
+sub calculate_eol_char : RESTRICTED {
+
+ my ( $self, $file ) = @_;
+
+ unless ( $self->get_eol_char() ) {
+ my ($eols, $eol_char) = check_linebreaks( $self->get_filename() );
+ unless ( $eol_char ) {
+ croak(
+ sprintf(
+ "Error: Cannot correctly parse linebreaks in file %s"
+ . " (%s unix, %s dos, %s mac)\n",
+ $self->get_filename(),
+ $eols->{unix},
+ $eols->{dos},
+ $eols->{mac},
+ )
+ );
+ }
+ $self->set_eol_char( $eol_char );
+ }
+
+ if ( ( $self->get_eol_char eq "\015" )
+ && ( $Text::CSV_XS::VERSION < 0.27 ) ) {
+
+ # Mac linebreaks not supported by older versions of Text::CSV_XS.
+ croak("Error: Mac linebreaks not supported by this version"
+ . " of Text::CSV_XS. Please upgrade to version 0.27 or higher.\n");
+ }
+
+ return $self->get_eol_char();
+}
+
+sub get_csv_parser {
+
+ my ( $self ) = @_;
+
+ # FIXME consider caching this in a private attribute?
+ my $csv_parser = Text::CSV_XS->new(
+ { sep_char => qq{\t},
+ quote_char => qq{"}, # default
+ escape_char => qq{"}, # default
+ binary => 1,
+ eol => ( $self->calculate_eol_char() || "\n" ),
+ allow_loose_quotes => 1,
+ }
+ );
+
+ return $csv_parser;
+}
+
+sub raise_error : RESTRICTED {
+
+ my ( $self, @messages ) = @_;
+
+ croak(@messages);
+}
+
+sub raise_warning : RESTRICTED {
+
+ my ( $self, @messages ) = @_;
+
+ carp(@messages);
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Tracking/CelQC.pm b/lib/ArrayExpress/Tracking/CelQC.pm
new file mode 100644
index 0000000..8a06b09
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/CelQC.pm
@@ -0,0 +1,171 @@
+#!/usr/bin/env perl
+#
+# $Id: CelQC.pm 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Tracking::CelQC;
+
+use Class::Std;
+use File::Temp qw(tempfile);
+use Carp;
+use POSIX;
+
+my %input : ATTR( :get<input>, :init_arg<input>, :default<undef> );
+my %quiet : ATTR( :get<quiet>, :init_arg<quiet>, :default<undef> );
+my %stdout : ATTR( :name<stdout>, :default<undef> );
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( defined $self->get_input() ) {
+ confess(qq{Error: Input file not set.\n});
+ }
+ unless ( -r $self->get_input() ) {
+ croak(qq{Error: Input file does not exist or is unreadable.\n});
+ }
+
+ if ( $self->get_quiet() ) {
+ $self->set_stdout('/dev/null');
+ }
+ else {
+ $self->set_stdout(POSIX::ctermid());
+ }
+
+ return;
+}
+
+sub create_r_script : PRIVATE {
+
+ my ( $self, $reference_fh ) = @_;
+
+ # Set up our temporary R script.
+ my ( $r_fh, $r_script ) = tempfile();
+ unless ( $r_fh && $r_script ) {
+ croak(qq{Error: Unable to open temporary R script file "$r_script": $!\n});
+ }
+ while( <$reference_fh> ) {
+ print $r_fh $_;
+ }
+ close( $r_fh )
+ or croak(qq{Error: Unable to close temporary R script filehandle: $!\n});
+
+ return $r_script;
+}
+
+sub call_r_script : PRIVATE {
+
+ my ( $self, $output ) = @_;
+
+ # Set up our temporary R script.
+ my $r_script = $self->create_r_script( \*DATA );
+
+ my $input = $self->get_input();
+ my $stdout = $self->get_stdout();
+
+ # Call the R script and do the interesting stuff.
+ my $syscall = "R CMD BATCH --slave --no-save --no-restore"
+ . " -input=$input -output=$output $r_script $stdout";
+
+ # Quote some legal characters which would otherwise confuse the shell.
+ $syscall =~ s/([(){}\[\]])/\\$1/g;
+
+ system($syscall) == 0
+ or croak("Error executing R code: $?");
+
+ unlink( $r_script )
+ or croak(qq{Error: Unable to delete temporary R script file "$r_script": $!\n});
+
+ return;
+}
+
+sub run_metrics {
+
+ my ( $self ) = @_;
+
+ # Set up our temporary output file.
+ my $result_file;
+ (undef, $result_file) = tempfile();
+
+ $self->call_r_script( $result_file );
+
+ # Parse the results file into a hash.
+ open( my $result_fh, '<', $result_file )
+ or croak(qq{Error: Unable to open results file "$result_file": $!\n});
+ my %result;
+ while ( my $line = <$result_fh> ) {
+ my ( $key, $value ) = ( $line =~ m/"([^\"]+)" \s* "([^\"]+)" \s* \z/xms );
+ $result{$key} = $value;
+ }
+
+ close( $result_fh )
+ or croak(qq{Error closing results filehandle: $!\n});
+
+ unlink( $result_file )
+ or croak(qq{Error deleting temporary results file: $!\n});
+
+ return \%result;
+}
+
+1;
+
+__DATA__
+
+# Actual R code follows; this now works, and is used to generate a
+# temporary R script which is run on the input CEL file.
+#
+# Run using:
+# DYLD_LIBRARY_PATH='' R CMD BATCH -input=<cel file> -output=<output file> <this script file>.
+input<-FALSE;
+output<-FALSE;
+
+# This is a fairly generic options parsing loop, modified to store
+# -input and -output values. Taken from
+# https://sws.stat.iastate.edu/resources/programmingExamples/R/R-Command-Line.html
+for (e in commandArgs()) {
+ ta = strsplit(e,"=",fixed=TRUE);
+ if(! is.na(ta[[1]][2])) {
+ temp = ta[[1]][2];
+ if(substr(ta[[1]][1],nchar(ta[[1]][1]),nchar(ta[[1]][1])) == "I") {
+ temp = as.integer(temp);
+ }
+ if(substr(ta[[1]][1],nchar(ta[[1]][1]),nchar(ta[[1]][1])) == "N") {
+ temp = as.numeric(temp);
+ }
+ assign(ta[[1]][1],temp);
+ if ( ta[[1]][1] == "-input" ) {
+ input = temp;
+ }
+ if ( ta[[1]][1] == "-output" ) {
+ output = temp;
+ }
+# cat("assigned ",ta[[1]][1]," the value of |",temp,"|\n");
+ } else {
+ assign(ta[[1]][1],TRUE);
+# cat("assigned ",ta[[1]][1]," the value of TRUE\n");
+ }
+}
+
+library('simpleaffy');
+scorecel <- function(infile, outfile) {
+ celdata<-ReadAffy(filenames=infile);
+ celqc<-qc(celdata);
+ RNAdeg=AffyRNAdeg(celdata);
+ celavgbg=avbg(celqc);
+ perc=percent.present(celqc);
+# Each tag here will correspond to a qc type in the tracking DB. The
+# exception is "platform" which has its own table.
+ capture.output(c("platform",cdfName(celdata)),
+ file=outfile, append=FALSE);
+ capture.output(c("affy_average_background",prettyNum(as.double(celavgbg))),
+ file=outfile, append=TRUE);
+ capture.output(c("affy_scale_factor",prettyNum(sfs(celqc))),
+ file=outfile, append=TRUE);
+ capture.output(c("affy_percent_present",prettyNum(as.double(perc))),
+ file=outfile, append=TRUE);
+ capture.output(c("affy_RNAdeg_slope",prettyNum(RNAdeg$slope)),
+ file=outfile, append=TRUE);
+}
+scorecel(infile=input, outfile=output);
diff --git a/lib/ArrayExpress/Tracking/Event.pm b/lib/ArrayExpress/Tracking/Event.pm
new file mode 100644
index 0000000..f641906
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/Event.pm
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+#
+# $Id: Event.pm 1853 2007-12-13 17:53:43Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Tracking::Event;
+
+# Class used as a bridge between the AE database query objects and the
+# tracking database.
+
+use Class::Std;
+
+my %event_type : ATTR( :name<event_type>, :default<undef> );
+my %success : ATTR( :name<success>, :default<undef> );
+my %source_db : ATTR( :name<source_db>, :default<undef> );
+my %target_db : ATTR( :name<target_db>, :default<undef> );
+my %starttime : ATTR( :name<starttime>, :default<undef> );
+my %endtime : ATTR( :name<endtime>, :default<undef> );
+my %machine : ATTR( :name<machine>, :default<undef> );
+my %operator : ATTR( :name<operator>, :default<undef> );
+my %log_file : ATTR( :name<log_file>, :default<undef> );
+my %jobregister_dbid : ATTR( :name<jobregister_dbid>, :default<undef> );
+my %comment : ATTR( :name<comment>, :default<undef> );
+
+1;
diff --git a/lib/ArrayExpress/Tracking/QCJobManager.pm b/lib/ArrayExpress/Tracking/QCJobManager.pm
new file mode 100644
index 0000000..d911ed6
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/QCJobManager.pm
@@ -0,0 +1,180 @@
+# $Id: QCJobManager.pm 1978 2008-02-28 12:14:14Z tfrayner $
+
+package ArrayExpress::Tracking::QCJobManager;
+
+use strict;
+use warnings;
+
+use Class::Std;
+use Carp;
+
+use ArrayExpress::AutoSubmission::DB;
+
+# Attributes.
+my %ae_sth : ATTR( :name<ae_sth>, :default<undef> );
+my %ae_dbh : ATTR( :name<ae_dbh>, :default<undef> );
+my %num_procs : ATTR( :name<num_procs>, :default<1> );
+
+my $CHILD_SCRIPT = 'cel_qcstats.pl';
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ unless ( $self->get_ae_sth() ) {
+ confess("Error: no ae_sth set in " . __PACKAGE__);
+ }
+ unless ( $self->get_ae_dbh() ) {
+ confess("Error: no ae_dbh set in " . __PACKAGE__);
+ }
+
+ return;
+}
+
+sub run {
+
+ # Main bjob management event loop.
+
+ my ( $self ) = @_;
+
+ # Start an initial run of $self->get_num_procs();
+ $self->start_new_set();
+
+ # Approx 30 seconds per process.
+ my $sleep_period = 30;
+ sleep( $sleep_period );
+
+ while ( 1 ) {
+
+ # Loop ends when there are no more rows to process.
+ $self->start_new_set() or return;
+ sleep( $sleep_period );
+ }
+
+ return;
+}
+
+sub count_submitted_jobs : PRIVATE {
+
+ my ( $self ) = @_;
+
+ open ( my $bjobs, '-|', 'bjobs -w 2>&1' )
+ or croak("Error setting up bjobs handle: $!");
+
+ my $count = 0;
+ while ( my $out = <$bjobs> ) {
+ $count++ if ( $out =~ m/$CHILD_SCRIPT/ );
+ }
+
+ close( $bjobs ) or carp( $! ? "Error closing bjobs pipe: $!"
+ : "Exit status $? from bjobs." );
+
+ return $count;
+}
+
+sub get_old_hashed_data {
+
+ print( q{Caching data identifiers...} );
+
+ my $hashed_data = {};
+
+ my $dbh = ArrayExpress::AutoSubmission::DB->db_Main();
+
+ my $sth = $dbh->prepare(<<'QUERY');
+select d.identifier, d.needs_metrics_calculation
+from loaded_data d
+where is_deleted=0
+QUERY
+
+ $sth->execute() or die( $sth->errstr() );
+
+ while ( my $row = $sth->fetchrow_hashref( 'NAME_lc' ) ) {
+ $hashed_data->{ $row->{'identifier'} }
+ = $row->{'needs_metrics_calculation'};
+ }
+
+ $sth->finish();
+
+ print( qq{ done.\n} );
+
+ return $hashed_data;
+}
+
+{
+ # Hash of identifier => needs_metrics_calculation
+ my $cached_query;
+
+sub start_new_set : PRIVATE {
+
+ my ( $self ) = @_;
+
+ # Quick look to see if the data has been already hashed.
+ $cached_query ||= get_old_hashed_data();
+
+ my $sth = $self->get_ae_sth();
+
+ # Double-check that the statement handle has been executed.
+ unless ( $sth->{'Executed'} ) {
+ $sth->execute() or die( $sth->errstr() );
+ }
+
+ my $prev = $self->count_submitted_jobs();
+
+ PROC:
+ for ( my $i = $prev; $i < $self->get_num_procs(); $i++ ) {
+ my $hashref = $sth->fetchrow_hashref('NAME_lc');
+
+ # This would be the endpoint.
+ return unless $hashref;
+
+ # Skip those rows which don't need calculations.
+ redo PROC unless $cached_query->{ $hashref->{'identifier'} };
+
+ $self->process_row( $hashref );
+ }
+
+ return 1;
+}
+
+}
+
+sub process_row : PRIVATE {
+
+ my ( $self, $hashref ) = @_;
+
+ my $ae_dbh = $self->get_ae_dbh();
+
+ # Get the CEL file, dispatch the subprocess FIXME.
+ my $lob_locator = $hashref->{'lob'};
+ my $length = $ae_dbh->ora_lob_length( $lob_locator );
+ my $chunksize = 65536;
+
+ # Read in the LOB and write it to a file.
+ my $filename = $hashref->{'dbid'} . '.cel';
+ open( my $fh, '>', $filename )
+ or croak("Error: unable to open file for writing: $!");
+ for ( my $i = 1; $i <= $length; $i += $chunksize ) {
+ my $chunk = $ae_dbh->ora_lob_read( $lob_locator, $i, $chunksize );
+ syswrite( $fh, $chunk ) or croak("Error writing to file: $!");
+ }
+ close( $fh ) or croak("Error closing filehandle: $!");
+
+ # Make sure our log file at least exists, otherwise we get a whole
+ # lot of silent failure from the oh-so-wonderful LSF system.
+ my $logfile = 'legacy_qc.log';
+ unless ( -e $logfile ) {
+ open( my $fh, '>', $logfile )
+ or croak("Error initialising log file: $!\n");
+ close( $fh );
+ }
+
+ # The subprocess will need to delete the file when done, hence the -d option.
+ system(
+ qq{bsub -q production -o "$logfile" }
+ . qq{'$CHILD_SCRIPT -i "$hashref->{identifier}" -f $filename -q -d'}
+ ) == 0 or croak("Error submitting LSF process.");
+
+ return;
+}
+
+1;
diff --git a/lib/ArrayExpress/Tracking/QueryHandler.pm b/lib/ArrayExpress/Tracking/QueryHandler.pm
new file mode 100644
index 0000000..e58ebfa
--- /dev/null
+++ b/lib/ArrayExpress/Tracking/QueryHandler.pm
@@ -0,0 +1,769 @@
+#!/usr/bin/env perl
+#
+# $Id: QueryHandler.pm 2077 2008-06-10 09:39:46Z tfrayner $
+
+use strict;
+use warnings;
+
+package ArrayExpress::Tracking::QueryHandler;
+
+use Class::Std;
+use Carp;
+use Date::Manip qw(ParseDate UnixDate);
+use Readonly;
+
+use ArrayExpress::Curator::Database qw(
+ get_ae_dbh
+ get_aedw_dbh
+);
+
+my %dbhandle : ATTR( :name<dbhandle>, :default<{}> );
+my %event_cache : ATTR( :name<event_cache>, :default<undef> );
+my %last_jobid : ATTR( :name<last_jobid>, :default<{}> );
+my %cached_sth : ATTR( :name<cached_sth>, :default<{}> );
+
+Readonly my $AEREP_DB => 'AEPUB1';
+Readonly my $AEDW_DB => 'AEDWDEV';
+
+sub BUILD {
+
+ my ( $self, $id, $args ) = @_;
+
+ $dbhandle{ident $self}{ $AEREP_DB } = get_ae_dbh()
+ or croak("Error: Unable to connect to AE repository DB.");
+
+ $dbhandle{ident $self}{ $AEDW_DB } = get_aedw_dbh()
+ or croak("Error: Unable to connect to AE warehouse DB.");
+
+ # Long values are trimmed at 1000 chars.
+ $dbhandle{ident $self}->{ $AEREP_DB }{LongReadLen} = 1000;
+ $dbhandle{ident $self}->{ $AEREP_DB }{LongTruncOk} = 1;
+ $dbhandle{ident $self}->{ $AEDW_DB }{LongReadLen} = 1000;
+ $dbhandle{ident $self}->{ $AEDW_DB }{LongTruncOk} = 1;
+
+ return;
+}
+
+sub START {
+
+ my ( $self, $id, $args ) = @_;
+
+ # Set the default last job id to zero for each DB instance.
+ foreach my $instance ( @{ $self->get_instances() } ) {
+ $self->get_last_jobid()->{ $instance } ||= 0;
+ }
+
+ # Create our cached statement handles.
+ $self->cache_statement_handles();
+
+ return;
+}
+
+sub cache_statement_handles : PRIVATE {
+
+ my ( $self ) = @_;
+
+ print STDOUT "Caching statement handles...\n";
+
+ my $dbh = $self->get_dbhandle()->{ $AEREP_DB };
+
+ $cached_sth{ident $self}{expt_species} = $dbh->prepare(<<"QUERY");
+select unique o.value
+from tt_experiment e,
+tt_biomaterials_experiments eb,
+tt_poly_biomaterial pb,
+tt_characteris_t_biomateri bso,
+tt_ontologyentry o,
+tt_identifiable i
+where i.identifier = ?
+and e.id = eb.experiments_id
+and i.id = eb.experiments_id
+and eb.biomaterials_id = pb.id
+and pb.t_biosource_id is not null
+and pb.t_biosource_id = bso.t_biomaterial_id
+and bso.characteristics_id = o.id
+and lower(o.category) = 'organism'
+QUERY
+
+ $cached_sth{ident $self}{array_species} = $dbh->prepare(<<"QUERY");
+select unique oe.value
+from tt_arraydesign ad,
+tt_reportergro_t_arraydesi rg,
+tt_designelementgroup de,
+tt_ontologyentry oe,
+tt_identifiable i
+where i.identifier = ?
+and ad.id = i.id
+and rg.t_arraydesign_id = ad.id
+and de.id = rg.reportergroups_id
+and de.species_id = oe.id
+and lower(oe.category) = 'organism'
+QUERY
+
+ $cached_sth{ident $self}{expt_arrays} = $dbh->prepare(<<"QUERY");
+select unique iden.identifier
+from tt_bioassays_t_experiment bt,
+tt_poly_bioassay poly_b,
+tt_physicalbioassay pba,
+tt_bioassaycreation bc,
+tt_array ar,
+tt_physicalarraydesign ard,
+tt_identifiable iden,
+tt_identifiable i
+where i.identifier = ?
+and i.id = bt.t_experiment_id
+and poly_b.t_physicalbioassay_id = bt.bioassays_id
+and pba.id = poly_b.t_physicalbioassay_id
+and pba.bioassaycreation_id = bc.id
+and bc.array_id = ar.id
+and ar.arraydesign_id = ard.id
+and ard.id = iden.id
+QUERY
+
+ $cached_sth{ident $self}{num_samples} = $dbh->prepare(<<"QUERY");
+select count (unique pb.t_biosource_id) as samples
+from tt_biomaterials_experiments be,
+tt_biomaterial bm,
+tt_poly_biomaterial pb,
+tt_biosample bs,
+tt_identifiable i
+where i.identifier = ?
+and be.experiments_id = i.id
+and be.biomaterials_id = bm.id
+and bm.id = pb.t_biosource_id
+QUERY
+
+ $cached_sth{ident $self}{num_hybridizations} = $dbh->prepare(<<"QUERY");
+select count( cp.t_hybridization_id ) as hybs
+from tt_experiment e,
+tt_bioassays_t_experiment eb,
+tt_poly_bioassay bp,
+tt_physicalbioassay pba,
+tt_poly_bioassaycreation cp,
+tt_identifiable i
+where i.identifier = ?
+and e.id = i.id
+and e.id = eb.t_experiment_id
+and eb.bioassays_id = bp.t_physicalbioassay_id
+and bp.t_physicalbioassay_id = pba.id
+and pba.bioassaycreation_id = cp.id
+and cp.t_hybridization_id is not null
+QUERY
+
+ $cached_sth{ident $self}{has_raw_data} = $dbh->prepare(<<"QUERY");
+select count(*)
+from tt_identifiable i,
+tt_bioassaydatagroup bg,
+tt_bioassaydat_bioassaydat bb,
+tt_poly_bioassaydata pb
+where i.identifier = ?
+and i.id = bg.experiment_id
+and bg.id=bb.bioassaydatagroups_id
+and bb.bioassaydatas_id = pb.t_measuredbioassaydata_id
+QUERY
+
+ $cached_sth{ident $self}{has_processed_data} = $dbh->prepare(<<"QUERY");
+select count(*)
+from tt_identifiable i,
+tt_bioassaydatagroup bg,
+tt_bioassaydat_bioassaydat bb,
+tt_poly_bioassaydata pb
+where i.identifier = ?
+and i.id = bg.experiment_id
+and bg.id=bb.bioassaydatagroups_id
+and bb.bioassaydatas_id = pb.t_derivedbioassaydata_id
+QUERY
+
+ $cached_sth{ident $self}{expt_factors} = $dbh->prepare(<<"QUERY");
+select oe.value as value
+from tt_experimentdesign ed,
+tt_experimentalfactor ef,
+tt_ontologyentry oe,
+tt_identifiable i
+where i.identifier = ?
+and ed.t_experiment_id = i.id
+and ef.t_experimentdesign_id = ed.id
+and oe.id = ef.category_id
+QUERY
+
+ $cached_sth{ident $self}{expt_qts} = $dbh->prepare(<<"QUERY");
+select unique iden.name as name
+from tt_bioassaydatagroup bg,
+tt_bioassaydat_bioassaydat bb,
+tt_bioassaydata ba,
+tt_quantitationtypedimension qtd,
+tt_quantitatio_t_quantitat qtq,
+tt_quantitationtype qt,
+tt_identifiable iden,
+tt_identifiable i
+where i.identifier = ?
+and bg.experiment_id = i.id
+and bg.id=bb.bioassaydatagroups_id
+and ba.id = bb.bioassaydatas_id
+and qtd.id = ba.quantitationtypedimension_id
+and qtq.t_quantitationtypedimension_id = ba.quantitationtypedimension_id
+and qt.id = qtq.quantitationtypes_id
+and iden.id = qt.id
+QUERY
+
+ $cached_sth{ident $self}{is_released} = $dbh->prepare(<<"QUERY");
+select usr.name
+from pl_visibility vi,
+pl_label la,
+tt_identifiable i,
+pl_user usr
+where i.identifier = ?
+and i.identifier = la.mainobj_name
+and la.id = vi.label_id
+and vi.user_id = usr.id
+and usr.name = 'guest'
+QUERY
+
+ $cached_sth{ident $self}{is_loaded} = $dbh->prepare(<<"QUERY");
+select count(*)
+from pl_label la,
+tt_identifiable i
+where i.identifier = ?
+and i.identifier = la.mainobj_name
+QUERY
+
+ $cached_sth{ident $self}{release_date} = $dbh->prepare(<<"QUERY");
+select nvt.value
+from tt_namevaluetype nvt,
+tt_identifiable i
+where i.identifier = ?
+and nvt.t_extendable_id = i.id
+and nvt.name ='ArrayExpressReleaseDate'
+QUERY
+
+ $cached_sth{ident $self}{ae_miame_score} = $dbh->prepare(<<"QUERY");
+select nvt.value
+from tt_namevaluetype nvt,
+tt_identifiable i
+where i.identifier = ?
+and nvt.t_extendable_id = i.id
+and nvt.name ='AEMIAMESCORE'
+QUERY
+
+ $cached_sth{ident $self}{curated_name} = $dbh->prepare(<<"QUERY");
+select nvt.value
+from tt_namevaluetype nvt,
+tt_identifiable i
+where i.identifier = ?
+and nvt.t_extendable_id = i.id
+and nvt.name ='AEExperimentDisplayName'
+and nvt.value != i.name
+QUERY
+
+ $cached_sth{ident $self}{submitter_description} = $dbh->prepare(<<"QUERY");
+select d.text
+from tt_description d,
+tt_identifiable i
+where i.identifier = ?
+and d.t_describable_id = i.id
+and d.text not like '(Generated description)%'
+and length(d.text) > 0
+QUERY
+
+ return;
+}
+
+sub get_instances {
+
+ my ( $self ) = @_;
+
+ return [ keys %{ $self->get_dbhandle() } ];
+}
+
+sub get_experiments {
+
+ my ( $self ) = @_;
+
+ # FIXME we might want to extend this to include the AEDW.
+ my $dbh = $self->get_dbhandle()->{ $AEREP_DB };
+
+ my $sth = $dbh->prepare(<<"QUERY");
+select i.identifier
+from tt_identifiable i,
+tt_experiment e
+where e.id = i.id
+QUERY
+
+ $sth->execute() or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results }
+ ? [ map { $_->[0] } @{ $results } ]
+ : [];
+}
+
+sub get_array_designs {
+
+ # FIXME we might want to extend this to include the AEDW.
+ my ( $self ) = @_;
+
+ my $dbh = $self->get_dbhandle()->{ $AEREP_DB };
+
+ my $sth = $dbh->prepare(<<"QUERY");
+select i.identifier
+from tt_identifiable i,
+tt_physicalarraydesign a
+where a.id = i.id
+QUERY
+
+ $sth->execute() or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results }
+ ? [ map { $_->[0] } @{ $results } ]
+ : [];
+}
+
+sub get_array_species {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns Organism OE values.
+ my $sth = $self->get_cached_sth()->{array_species}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results }
+ ? [ map { $_->[0] } @{ $results } ]
+ : [];
+}
+
+sub get_expt_species {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns Organism OE values.
+ my $sth = $self->get_cached_sth()->{expt_species}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results }
+ ? [ map { $_->[0] } @{ $results } ]
+ : [];
+}
+
+sub get_expt_arrays {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns array accessions.
+ my $sth = $self->get_cached_sth()->{expt_arrays}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results }
+ ? [ map { $_->[0] } @{ $results } ]
+ : [];
+}
+
+sub get_expt_factors {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns ExperimentalFactorCategory OE values.
+ my $sth = $self->get_cached_sth()->{expt_factors}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results }
+ ? [ map { $_->[0] } @{ $results } ]
+ : [];
+}
+
+sub get_expt_qts {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns QT names.
+ my $sth = $self->get_cached_sth()->{expt_qts}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results }
+ ? [ map { $_->[0] } @{ $results } ]
+ : [];
+}
+
+sub get_submitter_description {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns Description texts.
+ my $sth = $self->get_cached_sth()->{submitter_description}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0];
+}
+
+sub get_curated_name {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns the curated name (AEExperimentDisplayName NVT).
+ my $sth = $self->get_cached_sth()->{curated_name}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0];
+}
+
+sub get_num_samples {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns count of samples.
+ my $sth = $self->get_cached_sth()->{num_samples}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0];
+}
+
+sub get_num_hybridizations {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns count of samples.
+ my $sth = $self->get_cached_sth()->{num_hybridizations}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0];
+}
+
+sub get_has_raw_data {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns count of MBADatas.
+ my $sth = $self->get_cached_sth()->{has_raw_data}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0] ? 1 : 0;
+}
+
+sub get_has_processed_data {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns count of DBADatas.
+ my $sth = $self->get_cached_sth()->{has_processed_data}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0] ? 1 : 0;
+}
+
+sub get_release_date {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns the release date (ArrayExpressReleaseDate NVT).
+ my $sth = $self->get_cached_sth()->{release_date}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0];
+}
+
+sub get_is_released {
+
+ my ( $self, $accession ) = @_;
+
+ # If query returns anything, object is public.
+ my $sth = $self->get_cached_sth()->{is_released}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar @{ $results } ? 1 : 0;
+}
+
+sub get_is_loaded {
+
+ my ( $self, $accession ) = @_;
+
+ # If query returns > 0, object is loaded.
+ my $sth = $self->get_cached_sth()->{is_loaded}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0];
+}
+
+sub get_expt_in_data_warehouse {
+
+ my ( $self, $accession ) = @_;
+
+ my $dbh = $self->get_dbhandle()->{ $AEDW_DB };
+
+ # If query returns > 0, object is loaded.
+ my $sth = $dbh->prepare(<<"QUERY");
+select experiment_identifier
+from ae1__experiment__main
+where experiment_identifier = ?
+QUERY
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar( @$results );
+}
+
+sub get_array_in_data_warehouse {
+
+ my ( $self, $accession ) = @_;
+
+ my $dbh = $self->get_dbhandle()->{ $AEDW_DB };
+
+ # If query returns > 0, object is loaded.
+ my $sth = $dbh->prepare(<<"QUERY");
+select arraydesign_identifier
+from ae2__arraydesign
+where arraydesign_identifier = ?
+QUERY
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return scalar( @$results );
+}
+
+sub get_ae_miame_score {
+
+ my ( $self, $accession ) = @_;
+
+ # Query returns the AE-computed MIAME score (AEMIAMESCORE NVT).
+ my $sth = $self->get_cached_sth()->{ae_miame_score}
+ or die("Error: Undefined statement handle.");
+
+ $sth->execute( $accession ) or die( $sth->errstr() );
+
+ my $results = $sth->fetchall_arrayref();
+
+ return $results->[0][0];
+}
+
+sub get_ae_data_warehouse_score {
+
+ my ( $self, $accession ) = @_;
+
+ confess("Not implemented yet"); # FIXME once this has been implemented in AE.
+}
+
+sub get_events {
+
+ my ( $self, $accession ) = @_;
+
+ my $events = $self->get_event_cache();
+
+ unless ( $events ) {
+
+ # Cache our jobregister events.
+ $self->cache_aerep_events();
+ $self->cache_aedw_events();
+ }
+
+ return $self->get_event_cache()->{ $accession } || [];
+}
+
+sub get_updated_event_data {
+
+ my ( $self, $dbid, $instance ) = @_;
+
+ my $dbh = $self->get_dbhandle()->{ $instance }
+ or confess("Error: unrecognized database instance $instance.");
+
+ my $sth = $dbh->prepare(<<"QUERY");
+select ENDTIME, PHASE
+from jobregister
+where id = ?
+and starttime is not null
+QUERY
+
+ $sth->execute( $dbid ) or die( $sth->errstr );
+
+ # This query is by primary key, so we can rely on only one row
+ # returned.
+ my $results = $sth->fetchrow_hashref();
+ my $success = $self->parse_job_phase( $results->{'PHASE'} );
+ my $endtime;
+ if ( defined( $results->{'ENDTIME'} ) ) {
+ my $date = ParseDate($results->{'ENDTIME'});
+ $endtime = UnixDate($date, "%Y-%m-%d %T");
+ }
+
+ return ( $endtime, $success );
+}
+
+sub cache_aerep_events : PRIVATE {
+
+ my ( $self ) = @_;
+
+ print STDOUT "Caching AE repository events...\n";
+
+ $self->cache_events( $AEREP_DB );
+
+ return;
+}
+
+sub cache_aedw_events : PRIVATE {
+
+ my ( $self ) = @_;
+
+ print STDOUT "Caching AE warehouse events...\n";
+
+ $self->cache_events( $AEDW_DB );
+
+ return;
+}
+
+sub cache_events : PRIVATE {
+
+ my ( $self, $instance ) = @_;
+
+ my $dbh = $self->get_dbhandle()->{ $instance }
+ or confess("Error: Undefined database handle ($instance).");
+
+ # Note that currently the AEDW and AEREP jobregister schemas are
+ # identical, so we use this code for both. We may need to change
+ # this if these schemas change.
+
+ # Populate the event_cache with ArrayExpress::Tracking::Event
+ # objects here.
+ my $sth = $dbh->prepare(<<"QUERY");
+select distinct ID, USERNAME, DIRNAME, JOBTYPE, STARTTIME, ENDTIME, PHASE
+from jobregister
+where id > ?
+QUERY
+
+ $sth->execute( $self->get_last_jobid()->{ $instance } || 0 )
+ or die( $sth->errstr() );
+
+ my $event = $self->get_event_cache() || {};
+
+ JOB:
+ while ( my $hashref = $sth->fetchrow_hashref() ) {
+
+ # FIXME this regexp will miss some accessions (e.g. E-TABM-145b).
+ my ( $accession )
+ = ( $hashref->{'DIRNAME'} =~ m/\/([AE]-[A-Z]{4}-[0-9]+)/ );
+ next JOB unless $accession;
+
+ my $obj = $self->make_event_object( $hashref, $instance );
+ push( @{ $event->{$accession} }, $obj );
+ }
+
+ $self->set_event_cache($event);
+
+ return;
+}
+
+sub make_event_object : PRIVATE {
+
+ my ( $self, $hashref, $instance ) = @_;
+
+ # N.B. currently the AEREP and AEDW jobregister tables have
+ # identical columns; we may need to make database-specific
+ # versions of this method if that ever changes.
+
+ my $success = $self->parse_job_phase( $hashref->{'PHASE'} );
+
+ # Here we map the AEREP column names to our events. Attributes not
+ # set: source_db, machine, log_file, comment. Note that we need to
+ # parse the returned date format here.
+ foreach my $key qw(STARTTIME ENDTIME) {
+ my $date = ParseDate($hashref->{$key});
+ $hashref->{$key} = UnixDate($date, "%Y-%m-%d %T");
+ }
+
+ my $obj = ArrayExpress::Tracking::Event->new({
+ event_type => $hashref->{'JOBTYPE'},
+ success => $success,
+ target_db => $instance,
+ starttime => $hashref->{'STARTTIME'},
+ endtime => $hashref->{'ENDTIME'},
+ operator => $hashref->{'USERNAME'},
+ jobregister_dbid => $hashref->{'ID'},
+ });
+
+ return $obj;
+}
+
+sub parse_job_phase : PRIVATE {
+
+ my ( $self, $phase ) = @_;
+
+ # Return 1 if finished, 0 if crashed, or undef if unknown (assumed
+ # elsewhere to mean unfinished).
+
+ if ( $phase && $phase =~ /\b (?: crashed | error | dead ) \b/ixms ) {
+
+ # Failure.
+ return 0;
+ }
+ elsif ( $phase && $phase =~ /\b (?: finish | finished ) \b/ixms ) {
+
+ # Success.
+ return 1;
+ }
+ else{
+
+ # Unfinished?
+ return;
+ }
+}
+
+1;
diff --git a/t/affy_calvin.t b/t/affy_calvin.t
new file mode 100644
index 0000000..bb9ef83
--- /dev/null
+++ b/t/affy_calvin.t
@@ -0,0 +1,205 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 22;
+
+use File::Spec;
+
+BEGIN {
+ use_ok ('ArrayExpress::Datafile::Affymetrix');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Binary');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Parameter');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataColumn');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Component');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataHeader');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataSet');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::DataGroup');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Generic');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::Facade');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::CHP');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::Calvin::CEL');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $chp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test_calvin.CHP' ) );
+
+# Create the instance
+ok( defined $chp, 'new() returned a value' );
+ok( $chp->isa('ArrayExpress::Datafile::Affymetrix::Calvin::CHP'),
+ 'of the correct class' );
+
+# Check our return values
+is( $chp->get_chip_type, 'HG-U133A', 'chip type' );
+is( $chp->get_algorithm, 'ExpressionStat', 'algorithm' );
+is( $chp->get_version, '1', 'version' );
+
+# Parameters
+my $expected_params = {
+ 'Alpha1' => '0.05000',
+ 'Alpha2' => '0.06500',
+ 'BG' => '2',
+ 'HZ' => '4',
+ 'NF' => '1.00000',
+ 'SF' => '1.91583',
+ 'ScaleMask' => 'All',
+ 'TGT' => '500',
+ 'Tau' => '0.01500',
+ 'VZ' => '4',
+};
+
+is_deeply( $chp->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+ '#A' => '10615',
+ '#M' => '363',
+ '#P' => '11305',
+ '#Probe Sets Exceeding Probe Pair Threshold' => '22283',
+ '%A' => '47.63721',
+ '%M' => '1.62904',
+ '%P' => '50.73374',
+ 'BG Avg' => '117.28572',
+ 'BG Max' => '124.08419',
+ 'BG Min' => '106.84966',
+ 'BG Std' => '3.34962',
+ 'Central- Avg' => '21585',
+ 'Central- Count' => '9',
+ 'Control Direction' => 'Antisense',
+ 'Corner+ Avg' => '157',
+ 'Corner+ Count' => '32',
+ 'Corner- Avg' => '22492',
+ 'Corner- Count' => '32',
+ 'Housekeeping_AFFX-HSAC07/X00351_3-5-ratio' => '1.11354',
+ 'Housekeeping_AFFX-HSAC07/X00351_3_detection' => 'P',
+ 'Housekeeping_AFFX-HSAC07/X00351_3_signal' => '17738.18555',
+ 'Housekeeping_AFFX-HSAC07/X00351_5_detection' => 'P',
+ 'Housekeeping_AFFX-HSAC07/X00351_5_signal' => '15929.51465',
+ 'Housekeeping_AFFX-HSAC07/X00351_M_detection' => 'P',
+ 'Housekeeping_AFFX-HSAC07/X00351_M_signal' => '20702.16602',
+ 'Housekeeping_AFFX-HSAC07/X00351_avg-signal' => '18123.28906',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_3-5-ratio' => '1.53856',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_3_detection' => 'P',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_3_signal' => '13372.21094',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_5_detection' => 'P',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_5_signal' => '8691.37793',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_M_detection' => 'P',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_M_signal' => '9520.40723',
+ 'Housekeeping_AFFX-HUMGAPDH/M33197_avg-signal' => '10527.99902',
+ 'Noise Avg' => '6.49794',
+ 'Noise Max' => '7.35717',
+ 'Noise Min' => '5.61606',
+ 'Noise Std' => '0.29578',
+ 'Probe Pair Threshold' => '1',
+ 'RawQ' => '5.36501',
+ 'Signal(A)' => '65.11629',
+ 'Signal(All)' => '728.75806',
+ 'Signal(M)' => '211.16080',
+ 'Signal(P)' => '1368.51440',
+ 'Spike_AFFX-r2-Bs-dap_3-5-ratio' => '0.79929',
+ 'Spike_AFFX-r2-Bs-dap_3_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-dap_3_signal' => '2.09471',
+ 'Spike_AFFX-r2-Bs-dap_5_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-dap_5_signal' => '2.62071',
+ 'Spike_AFFX-r2-Bs-dap_M_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-dap_M_signal' => '25.02802',
+ 'Spike_AFFX-r2-Bs-dap_avg-signal' => '9.91448',
+ 'Spike_AFFX-r2-Bs-lys_3-5-ratio' => '2.06401',
+ 'Spike_AFFX-r2-Bs-lys_3_detection' => 'P',
+ 'Spike_AFFX-r2-Bs-lys_3_signal' => '49.26870',
+ 'Spike_AFFX-r2-Bs-lys_5_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-lys_5_signal' => '23.87037',
+ 'Spike_AFFX-r2-Bs-lys_M_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-lys_M_signal' => '8.82265',
+ 'Spike_AFFX-r2-Bs-lys_avg-signal' => '27.32057',
+ 'Spike_AFFX-r2-Bs-phe_3-5-ratio' => '8.54006',
+ 'Spike_AFFX-r2-Bs-phe_3_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-phe_3_signal' => '44.97870',
+ 'Spike_AFFX-r2-Bs-phe_5_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-phe_5_signal' => '5.26679',
+ 'Spike_AFFX-r2-Bs-phe_M_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-phe_M_signal' => '18.66685',
+ 'Spike_AFFX-r2-Bs-phe_avg-signal' => '22.97078',
+ 'Spike_AFFX-r2-Bs-thr_3-5-ratio' => '0.10473',
+ 'Spike_AFFX-r2-Bs-thr_3_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-thr_3_signal' => '5.12869',
+ 'Spike_AFFX-r2-Bs-thr_5_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-thr_5_signal' => '48.96920',
+ 'Spike_AFFX-r2-Bs-thr_M_detection' => 'A',
+ 'Spike_AFFX-r2-Bs-thr_M_signal' => '77.69570',
+ 'Spike_AFFX-r2-Bs-thr_avg-signal' => '43.93119',
+ 'Spike_AFFX-r2-Ec-bioB_3-5-ratio' => '0.91620',
+ 'Spike_AFFX-r2-Ec-bioB_3_detection' => 'P',
+ 'Spike_AFFX-r2-Ec-bioB_3_signal' => '103.24037',
+ 'Spike_AFFX-r2-Ec-bioB_5_detection' => 'P',
+ 'Spike_AFFX-r2-Ec-bioB_5_signal' => '112.68278',
+ 'Spike_AFFX-r2-Ec-bioB_M_detection' => 'P',
+ 'Spike_AFFX-r2-Ec-bioB_M_signal' => '151.41463',
+ 'Spike_AFFX-r2-Ec-bioB_avg-signal' => '122.44592',
+ 'Spike_AFFX-r2-Ec-bioC_3-5-ratio' => '1.31688',
+ 'Spike_AFFX-r2-Ec-bioC_3_detection' => 'P',
+ 'Spike_AFFX-r2-Ec-bioC_3_signal' => '580.93274',
+ 'Spike_AFFX-r2-Ec-bioC_5_detection' => 'P',
+ 'Spike_AFFX-r2-Ec-bioC_5_signal' => '441.14432',
+ 'Spike_AFFX-r2-Ec-bioC_avg-signal' => '511.03851',
+ 'Spike_AFFX-r2-Ec-bioD_3-5-ratio' => '1.29075',
+ 'Spike_AFFX-r2-Ec-bioD_3_detection' => 'P',
+ 'Spike_AFFX-r2-Ec-bioD_3_signal' => '4272.42627',
+ 'Spike_AFFX-r2-Ec-bioD_5_detection' => 'P',
+ 'Spike_AFFX-r2-Ec-bioD_5_signal' => '3310.04663',
+ 'Spike_AFFX-r2-Ec-bioD_avg-signal' => '3791.23633',
+ 'Spike_AFFX-r2-P1-cre_3-5-ratio' => '1.06699',
+ 'Spike_AFFX-r2-P1-cre_3_detection' => 'P',
+ 'Spike_AFFX-r2-P1-cre_3_signal' => '7970.32422',
+ 'Spike_AFFX-r2-P1-cre_5_detection' => 'P',
+ 'Spike_AFFX-r2-P1-cre_5_signal' => '7469.91846',
+ 'Spike_AFFX-r2-P1-cre_avg-signal' => '7720.12109',
+};
+
+is_deeply( $chp->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+ 'Probe Set Name',
+ 'Detection',
+ 'Detection p-value',
+ 'Signal',
+ 'Number of Pairs',
+ 'Number of Pairs Used',
+];
+
+is_deeply( $chp->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+ 'Affymetrix:QuantitationType:Probe Set Name',
+ 'Affymetrix:QuantitationType:Detection',
+ 'Affymetrix:QuantitationType:Detection p-value',
+ 'Affymetrix:QuantitationType:Signal',
+ 'Affymetrix:QuantitationType:Number of Pairs',
+ 'Affymetrix:QuantitationType:Number of Pairs Used',
+];
+
+is_deeply( $chp->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioB-5_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioB-M_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioB-3_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioC-5_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioC-3_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioDn-5_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-BioDn-3_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-CreX-5_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-CreX-3_at',
+ 'Affymetrix:CompositeSequence:HG-U133A:AFFX-DapX-5_at',
+];
+
+is_deeply( $chp->get_ded(), $expected_features,
+ 'design element (compseq) identifiers' );
diff --git a/t/affy_cdf.t b/t/affy_cdf.t
new file mode 100644
index 0000000..3049ff7
--- /dev/null
+++ b/t/affy_cdf.t
@@ -0,0 +1,51 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1799 2007-10-31 17:39:45Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+
+use File::Spec;
+BEGIN {
+ use_ok ('ArrayExpress::Datafile::Affymetrix');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $cdf = $fac->make_parser( File::Spec->catfile( 't', 'data', 'dummy.CDF' ) );
+
+# Create the instance
+ok( defined $cdf, 'new() returned a value' );
+ok( $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF::GDAC_CDF'),
+ 'of the correct class' );
+
+# Parse the test file
+$cdf->parse();
+
+is( $cdf->get_version, 'GC3.0', 'version' );
+is( $cdf->get_chip_type, 'YG_S98', 'chip type' );
+is( $cdf->get_num_rows, '534', 'number of rows' );
+is( $cdf->get_num_columns, '534', 'number of columns' );
+is( $cdf->get_num_cells, '10', 'expected number of cells' );
+is( $cdf->get_num_qc_cells, '0', 'expected number of qc cells' );
+
+my $expected_ids =
+ [qw(
+ AFFX-MurIL2_at
+ AFFX-MurIL10_at
+ AFFX-MurIL4_at
+ AFFX-MurFAS_at
+ AFFX-BioB-5_at
+ AFFX-BioB-M_at
+ AFFX-BioB-3_at
+ AFFX-BioC-5_at
+ AFFX-BioC-3_at
+ AFFX-BioDn-5_at
+ )];
+
+is_deeply( $cdf->get_probeset_ids, $expected_ids, 'probeset id list' );
diff --git a/t/affy_cel.t b/t/affy_cel.t
new file mode 100644
index 0000000..59006b8
--- /dev/null
+++ b/t/affy_cel.t
@@ -0,0 +1,108 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 18;
+
+use File::Spec;
+
+BEGIN {
+ use_ok ('ArrayExpress::Datafile::Affymetrix');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::CEL::CELv3');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $cel = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test.CEL' ) );
+
+# Create the instance
+ok( defined $cel, 'new() returned a value' );
+ok( $cel->isa('ArrayExpress::Datafile::Affymetrix::CEL::CELv3'),
+ 'of the correct class' );
+
+# Parse the test file
+$cel->parse();
+
+# Check our return values
+is( $cel->get_chip_type, 'Test', 'chip type' );
+is( $cel->get_algorithm, 'Percentile', 'algorithm' );
+is( $cel->get_version, '3', 'version' );
+is( $cel->get_num_cells, '10', 'expected number of cells' );
+is( $cel->get_num_rows, '1', 'number of rows' );
+is( $cel->get_num_columns, '10', 'number of columns' );
+is( $cel->get_num_masked, '0', 'number of masked cells' );
+is( $cel->get_num_outliers, '3', 'number of outlier cells' );
+is( $cel->get_num_modified, '0', 'number of modified cells' );
+
+# Parameters
+my $expected_params = {
+ 'Percentile' => '75',
+ 'CellMargin' => '2',
+ 'OutlierHigh' => '1.500',
+ 'OutlierLow' => '1.004',
+};
+
+is_deeply( $cel->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+ 'Number of Cells' => '10',
+ 'Rows' => '1',
+ 'Columns' => '10',
+ 'Number Cells Masked' => '0',
+ 'Number Outlier Cells' => '3',
+ 'Number Cells Modified' => '0',
+};
+
+is_deeply( $cel->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+ qw(
+ CELX
+ CELY
+ CELIntensity
+ CELIntensityStdev
+ CELPixels
+ CELOutlier
+ CELMask
+ )
+];
+
+is_deeply( $cel->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+ qw(
+ Affymetrix:QuantitationType:CELX
+ Affymetrix:QuantitationType:CELY
+ Affymetrix:QuantitationType:CELIntensity
+ Affymetrix:QuantitationType:CELIntensityStdev
+ Affymetrix:QuantitationType:CELPixels
+ Affymetrix:QuantitationType:CELOutlier
+ Affymetrix:QuantitationType:CELMask
+ )
+];
+
+is_deeply( $cel->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+ 'Affymetrix:Feature:Test:Probe(0,0)',
+ 'Affymetrix:Feature:Test:Probe(1,0)',
+ 'Affymetrix:Feature:Test:Probe(2,0)',
+ 'Affymetrix:Feature:Test:Probe(3,0)',
+ 'Affymetrix:Feature:Test:Probe(4,0)',
+ 'Affymetrix:Feature:Test:Probe(5,0)',
+ 'Affymetrix:Feature:Test:Probe(6,0)',
+ 'Affymetrix:Feature:Test:Probe(7,0)',
+ 'Affymetrix:Feature:Test:Probe(8,0)',
+ 'Affymetrix:Feature:Test:Probe(9,0)',
+];
+
+is_deeply( $cel->get_ded, $expected_features,
+ 'design element (feature) identifiers' );
diff --git a/t/affy_chp.t b/t/affy_chp.t
new file mode 100644
index 0000000..907a5e2
--- /dev/null
+++ b/t/affy_chp.t
@@ -0,0 +1,118 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_chp.t 1988 2008-03-10 10:43:48Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 17;
+
+use File::Spec;
+
+BEGIN {
+ use_ok('ArrayExpress::Datafile::Affymetrix');
+ use_ok('ArrayExpress::Datafile::Affymetrix::CHP::CHPv8');
+ use_ok('ArrayExpress::Datafile::Affymetrix::CHP::CHPv12');
+ use_ok('ArrayExpress::Datafile::Affymetrix::CHP::CHPv13');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $chp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test.CHP' ) );
+
+# Create the instance
+ok( defined $chp, 'new() returned a value' );
+ok( $chp->isa('ArrayExpress::Datafile::Affymetrix::CHP::GDAC_CHP'),
+ 'of the correct class' );
+
+# Parse the test file
+$chp->parse();
+
+# Check our return values
+is( $chp->get_chip_type, 'YG_S98', 'chip type' );
+is( $chp->get_algorithm, 'ExpressionStat', 'algorithm' );
+is( $chp->get_version, '12', 'version' );
+is( $chp->get_num_cells, '10', 'expected number of cells' );
+is( $chp->get_num_rows, '534', 'number of rows' );
+is( $chp->get_num_columns, '534', 'number of columns' );
+
+# Parameters
+my $expected_params = {
+ 'Alpha1' => '0.04',
+ 'Alpha2' => '0.06',
+ 'Tau' => '0.015',
+ 'Gamma1H' => '0.0025',
+ 'Gamma1L' => '0.0025',
+ 'Gamma2H' => '0.003',
+ 'Gamma2L' => '0.003',
+ 'Perturbation' => '1.1',
+ 'TGT' => '100',
+ 'NF' => '1.000000',
+ 'SF' => '0.822926',
+ 'SFGene' => 'All',
+};
+
+is_deeply( $chp->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+ 'Background Avg' => '60.39',
+ 'Background Stdev' => '1.34',
+ 'Background Max' => '65.5',
+ 'Background Min' => '58.0',
+ 'Noise Avg' => '1.42',
+ 'Noise Stdev' => '0.05',
+ 'Noise Max' => '1.6',
+ 'Noise Min' => '1.3',
+ 'RawQ' => '1.86',
+};
+
+is_deeply( $chp->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+ qw(
+ ProbeSetName
+ CHPPairs
+ CHPPairsUsed
+ CHPSignal
+ CHPDetection
+ CHPDetectionPvalue
+ )
+];
+
+is_deeply( $chp->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+ qw(
+ Affymetrix:QuantitationType:ProbeSetName
+ Affymetrix:QuantitationType:CHPPairs
+ Affymetrix:QuantitationType:CHPPairsUsed
+ Affymetrix:QuantitationType:CHPSignal
+ Affymetrix:QuantitationType:CHPDetection
+ Affymetrix:QuantitationType:CHPDetectionPvalue
+ )
+];
+
+is_deeply( $chp->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-MurIL2_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-MurIL10_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-MurIL4_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-MurFAS_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-BioB-5_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-BioB-M_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-BioB-3_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-BioC-5_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-BioC-3_at',
+ 'Affymetrix:CompositeSequence:YG_S98:AFFX-BioDn-5_at',
+];
+
+my $cdf = $fac->make_parser( File::Spec->catfile( 't', 'data', 'dummy.CDF' ) );
+$cdf->parse();
+is_deeply( $chp->get_ded($cdf), $expected_features,
+ 'design element (compseq) identifiers' );
diff --git a/t/affy_exp.t b/t/affy_exp.t
new file mode 100644
index 0000000..0321337
--- /dev/null
+++ b/t/affy_exp.t
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_exp.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 21;
+
+use File::Spec;
+
+BEGIN {
+ use_ok ('ArrayExpress::Datafile::Affymetrix');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::EXP');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $exp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test.EXP' ) );
+
+# Create the instance
+ok( defined $exp, 'new() returned a value' );
+ok( $exp->isa('ArrayExpress::Datafile::Affymetrix::EXP'),
+ 'of the correct class' );
+
+# Parse a test file
+$exp->parse();
+
+# Check our return values
+is( $exp->get_chip_type, 'HG-U133A', 'chip type' );
+is( $exp->get_chip_lot, '12345678', 'chip lot' );
+is( $exp->get_version, '1', 'version' );
+is( $exp->get_operator, 'Gandalf', 'operator' );
+is( $exp->get_protocol, 'EukGE-WS2v4', 'protocol' );
+is( $exp->get_station, '2', 'station' );
+is( $exp->get_module, '1', 'module' );
+is( $exp->get_hyb_date, 'Sep 20 2003 01:10PM', 'hyb_date' );
+is( $exp->get_pixel_size, '3', 'pixel_size' );
+is( $exp->get_filter, '570', 'filter' );
+is( $exp->get_scan_temp, '30', 'scan_temp' );
+is( $exp->get_scan_date, 'Sep 20 2003 01:25PM', 'scan_date' );
+is( $exp->get_scanner_id, '87654321', 'scanner_id' );
+is( $exp->get_num_scans, '2', 'num_scans' );
+is( $exp->get_scanner_type, 'HP', 'scanner_type' );
+
+# Parameters, ordered and named
+my $expected_hyb_params = [
+ { 'Wash A1 Recovery Mixes' => '0' },
+ { 'Wash A1 Temperature (C)' => '25' },
+ { 'Number of Wash A1 Cycles' => '10' },
+ { 'Mixes per Wash A1 Cycle' => '2' },
+ { 'Wash B Recovery Mixes' => '0' },
+ { 'Wash B Temperature (C)' => '50' },
+ { 'Number of Wash B Cycles' => '4' },
+ { 'Mixes per Wash B Cycle' => '15' },
+ { 'Stain Temperature (C)' => '25' },
+ { 'First Stain Time (seconds)' => '600' },
+ { 'Wash A2 Recovery Mixes' => '0' },
+ { 'Wash A2 Temperature (C)' => '25' },
+ { 'Number of Wash A2 Cycles' => '10' },
+ { 'Mixes per Wash A2 Cycle' => '4' },
+ { 'Second Stain Time (seconds)' => '600' },
+ { 'Third Stain Time (seconds)' => '600' },
+ { 'Wash A3 Recovery Mixes' => '0' },
+ { 'Wash A3 Temperature (C)' => '30' },
+ { 'Number of Wash A3 Cycles' => '15' },
+ { 'Mixes per Wash A3 Cycle' => '4' },
+ { 'Holding Temperature (C)' => '25' },
+];
+
+is_deeply( $exp->get_hyb_parameters, $expected_hyb_params,
+ 'named hybridization parameters' );
+
+# Parameters, named as in MAGE-ML
+my $expected_params = {
+ 'HybridizationStep0-EukGE-WS2v4' => '0',
+ 'HybridizationStep1-EukGE-WS2v4' => '25',
+ 'HybridizationStep2-EukGE-WS2v4' => '10',
+ 'HybridizationStep3-EukGE-WS2v4' => '2',
+ 'HybridizationStep4-EukGE-WS2v4' => '0',
+ 'HybridizationStep5-EukGE-WS2v4' => '50',
+ 'HybridizationStep6-EukGE-WS2v4' => '4',
+ 'HybridizationStep7-EukGE-WS2v4' => '15',
+ 'HybridizationStep8-EukGE-WS2v4' => '25',
+ 'HybridizationStep9-EukGE-WS2v4' => '600',
+ 'HybridizationStep10-EukGE-WS2v4' => '0',
+ 'HybridizationStep11-EukGE-WS2v4' => '25',
+ 'HybridizationStep12-EukGE-WS2v4' => '10',
+ 'HybridizationStep13-EukGE-WS2v4' => '4',
+ 'HybridizationStep14-EukGE-WS2v4' => '600',
+ 'HybridizationStep15-EukGE-WS2v4' => '600',
+ 'HybridizationStep16-EukGE-WS2v4' => '0',
+ 'HybridizationStep17-EukGE-WS2v4' => '30',
+ 'HybridizationStep18-EukGE-WS2v4' => '15',
+ 'HybridizationStep19-EukGE-WS2v4' => '4',
+ 'HybridizationStep20-EukGE-WS2v4' => '25',
+};
+
+is_deeply( $exp->get_parameters, $expected_params,
+ 'numbered hybridization parameters' );
+
diff --git a/t/affy_xda_cdf.t b/t/affy_xda_cdf.t
new file mode 100644
index 0000000..d328670
--- /dev/null
+++ b/t/affy_xda_cdf.t
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_cel.t 1799 2007-10-31 17:39:45Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+
+use File::Spec;
+BEGIN {
+ use_ok ('ArrayExpress::Datafile::Affymetrix');
+ use_ok ('ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF');
+}
+
+# Very basic testing until such time as we get a good small test XDA
+# CDF file.
+my $cdf = ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF->new({
+ input => 'not a file',
+});
+
+# Create the instance
+ok( defined $cdf, 'new() returned a value' );
+ok( $cdf->isa('ArrayExpress::Datafile::Affymetrix::CDF::XDA_CDF'),
+ 'of the correct class' );
+
diff --git a/t/affy_xda_cel.t b/t/affy_xda_cel.t
new file mode 100644
index 0000000..a606a4b
--- /dev/null
+++ b/t/affy_xda_cel.t
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_xda_cel.t 1811 2007-11-02 10:34:42Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 17;
+
+use File::Spec;
+BEGIN {
+ use_ok('ArrayExpress::Datafile::Affymetrix');
+ use_ok('ArrayExpress::Datafile::Affymetrix::CEL::CELv4');
+}
+
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $cel = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test_xda.CEL' ) );
+
+# Create the instance
+ok( defined $cel, 'new() returned a value' );
+ok( $cel->isa('ArrayExpress::Datafile::Affymetrix::CEL::CELv4'),
+ 'of the correct class' );
+
+# Parse a test file
+$cel->parse();
+
+# Check our return values
+is( $cel->get_chip_type, 'HG-U133A', 'chip type' );
+is( $cel->get_algorithm, 'Percentile', 'algorithm' );
+is( $cel->get_version, '4', 'version' );
+is( $cel->get_num_cells, '10', 'expected number of cells' );
+is( $cel->get_num_rows, '1', 'number of rows' );
+is( $cel->get_num_columns, '10', 'number of columns' );
+is( $cel->get_num_masked, '0', 'number of masked cells' );
+is( $cel->get_num_outliers, '3', 'number of outlier cells' );
+
+# Parameters
+my $expected_params = {
+ 'Percentile' => '75',
+ 'CellMargin' => '2',
+ 'OutlierHigh' => '1.500',
+ 'OutlierLow' => '1.004',
+};
+
+is_deeply( $cel->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+ 'Number of Cells' => '10',
+ 'Rows' => '1',
+ 'Columns' => '10',
+ 'Number Cells Masked' => '0',
+ 'Number Outlier Cells' => '3',
+};
+
+is_deeply( $cel->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+ qw(
+ CELX
+ CELY
+ CELIntensity
+ CELIntensityStdev
+ CELPixels
+ CELOutlier
+ CELMask
+ )
+];
+
+is_deeply( $cel->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+ qw(
+ Affymetrix:QuantitationType:CELX
+ Affymetrix:QuantitationType:CELY
+ Affymetrix:QuantitationType:CELIntensity
+ Affymetrix:QuantitationType:CELIntensityStdev
+ Affymetrix:QuantitationType:CELPixels
+ Affymetrix:QuantitationType:CELOutlier
+ Affymetrix:QuantitationType:CELMask
+ )
+];
+
+is_deeply( $cel->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+ 'Affymetrix:Feature:HG-U133A:Probe(0,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(1,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(2,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(3,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(4,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(5,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(6,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(7,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(8,0)',
+ 'Affymetrix:Feature:HG-U133A:Probe(9,0)',
+];
+
+is_deeply( $cel->get_ded, $expected_features,
+ 'design element (feature) identifiers' );
diff --git a/t/affy_xda_chp.t b/t/affy_xda_chp.t
new file mode 100644
index 0000000..11645d9
--- /dev/null
+++ b/t/affy_xda_chp.t
@@ -0,0 +1,127 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: affy_xda_chp.t 1988 2008-03-10 10:43:48Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+
+use File::Spec;
+
+BEGIN {
+ use_ok('ArrayExpress::Datafile::Affymetrix');
+ use_ok('ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP');
+}
+
+# Use the factory class to generate a parser, hopefully of the right
+# type.
+my $fac = ArrayExpress::Datafile::Affymetrix->new();
+
+my $chp = $fac->make_parser( File::Spec->catfile( 't', 'data', 'test_xda.CHP' ) );
+
+# Create the instance
+ok( defined $chp, 'new() returned a value' );
+ok( $chp->isa('ArrayExpress::Datafile::Affymetrix::CHP::XDA_CHP'),
+ 'of the correct class' );
+
+# Parse the test file
+$chp->parse();
+
+# Check our return values
+is( $chp->get_chip_type, 'ATH1-121501', 'chip type' );
+is( $chp->get_algorithm, 'ExpressionStat', 'algorithm' );
+is( $chp->get_version, '1', 'version' );
+is( $chp->get_num_cells, '10', 'expected number of cells' );
+is( $chp->get_num_rows, '712', 'number of rows' );
+is( $chp->get_num_columns, '712', 'number of columns' );
+
+# Parameters
+my $expected_params = {
+ 'VZ' => '4',
+ 'Gamma1H' => '0.0045',
+ 'Gamma2L' => '0.006',
+ 'Epsilon' => '0.5',
+ 'TGT' => '200',
+ 'Perturbation' => '1.1',
+ 'Alpha2' => '0.065',
+ 'BG' => '4',
+ 'Gamma2H' => '0.006',
+ 'SF' => '0.944273293018',
+ 'Alpha1' => '0.05',
+ 'SFGene' => 'All',
+ 'HZ' => '4',
+ 'NF' => '1.000000000000',
+ 'SmoothFactorBG' => '100',
+ 'Gamma1L' => '0.0045',
+ 'Tau' => '0.015',
+};
+
+is_deeply( $chp->get_parameters, $expected_params, 'parameters' );
+
+# Statistics
+my $expected_stats = {
+ 'Corner+ Count' => '32',
+ 'Background Avg' => '55.63',
+ 'Central- Avg' => '13530',
+ 'Noise Max' => '3.1',
+ 'Noise Stdev' => '0.08',
+ 'Central- Count' => '9',
+ 'RawQ' => '2.32',
+ 'Corner+ Avg' => '140',
+ 'Noise Min' => '2.6',
+ 'Background Min' => '54.1',
+ 'Background Stdev' => '0.67',
+ 'Corner- Avg' => '15007',
+ 'Background Max' => '58.3',
+ 'Noise Avg' => '2.76',
+ 'Corner- Count' => '32',
+};
+
+is_deeply( $chp->get_stats, $expected_stats, 'statistics' );
+
+# QTD
+my $expected_headings = [
+ qw(
+ ProbeSetName
+ CHPPairs
+ CHPPairsUsed
+ CHPSignal
+ CHPDetection
+ CHPDetectionPvalue
+ )
+];
+
+is_deeply( $chp->get_headings, $expected_headings, 'column headings' );
+
+my $expected_QTs = [
+ qw(
+ Affymetrix:QuantitationType:ProbeSetName
+ Affymetrix:QuantitationType:CHPPairs
+ Affymetrix:QuantitationType:CHPPairsUsed
+ Affymetrix:QuantitationType:CHPSignal
+ Affymetrix:QuantitationType:CHPDetection
+ Affymetrix:QuantitationType:CHPDetectionPvalue
+ )
+];
+
+is_deeply( $chp->get_qtd, $expected_QTs, 'quantitation type identifiers' );
+
+# DED
+my $expected_features = [
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurIL2_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurIL10_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurIL4_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-MurFAS_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioB-5_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioB-M_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioB-3_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioC-5_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioC-3_at',
+ 'Affymetrix:CompositeSequence:ATH1-121501:AFFX-BioDn-5_at',
+];
+
+my $cdf = $fac->make_parser( File::Spec->catfile( 't', 'data', 'dummy.CDF' ) );
+$cdf->parse();
+is_deeply( $chp->get_ded($cdf), $expected_features,
+ 'design element (compseq) identifiers' );
diff --git a/t/array_design.t b/t/array_design.t
new file mode 100644
index 0000000..db2757e
--- /dev/null
+++ b/t/array_design.t
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: database.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 9;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::ArrayDesign' ); }
+
+my $array = ArrayExpress::Datafile::ArrayDesign->new();
+ok( defined $array, 'new() returned a value' );
+ok( $array->isa('ArrayExpress::Datafile::ArrayDesign'), 'of the correct class' );
+
+# Simple hashref attributes.
+my %testhash = (one => 1, two => 'two', three => 'III');
+foreach my $attr qw(features
+ reporters
+ compseqs) {
+ my $getter = "get_$attr";
+ is_deeply($array->$getter(), {}, "$attr initial value");
+
+ my $setter = "set_$attr";
+ $array->$setter(\%testhash);
+ is_deeply($array->$getter(), \%testhash, "$attr set value");
+}
+
diff --git a/t/arraymage.t b/t/arraymage.t
new file mode 100644
index 0000000..b14fd9e
--- /dev/null
+++ b/t/arraymage.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: arraymage.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::ArrayMAGE' ); }
+
diff --git a/t/binary.t b/t/binary.t
new file mode 100644
index 0000000..8feca0c
--- /dev/null
+++ b/t/binary.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: binary.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN {
+ use_ok(
+ 'ArrayExpress::Datafile::Binary',
+ qw(
+ get_integer
+ get_DWORD
+ get_unsigned_short
+ get_unsigned_char
+ get_signed_char
+ get_float
+ get_ascii
+ get_hexadecimal
+ ));
+}
diff --git a/t/common.t b/t/common.t
new file mode 100644
index 0000000..f6bafcd
--- /dev/null
+++ b/t/common.t
@@ -0,0 +1,123 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: common.t 1273 2006-12-11 16:40:34Z tfrayner $
+
+use strict;
+use warnings;
+use charnames qw(:full);
+
+use File::Spec;
+use Test::More tests => 22;
+
+BEGIN {
+ use_ok(
+ 'ArrayExpress::Curator::Common',
+ qw(get_indexcol
+ strip_discards
+ round date_now
+ check_linebreaks
+ clean_hash
+ )
+ );
+ use_ok(
+ 'ArrayExpress::Curator::Config',
+ qw($CONFIG)
+ );
+}
+
+my @test_array = qw(One Two Three Four);
+
+##################
+# get_indexcol() #
+##################
+my @get_indexcol_data = (
+ { query => 'Two', result => 1 },
+ { query => 'Three', result => 2 },
+ { query => 'Too', result => -1 }
+);
+
+foreach my $test (@get_indexcol_data) {
+ is( get_indexcol( \@test_array, $test->{query} ),
+ $test->{result}, 'get_indexcol() matches' );
+}
+
+####################
+# strip_discards() #
+####################
+my @strip_discards_data = (
+ { query => [0], result => [qw(Two Three Four)] },
+ { query => [ 0, 1 ], result => [qw(Three Four)] },
+ { query => [ 1, 2 ], result => [qw(One Four)] },
+ { query => [ 1, 3 ], result => [qw(One Three)] }
+);
+
+foreach my $test (@strip_discards_data) {
+ is_deeply( strip_discards( $test->{query}, \@test_array ),
+ $test->{result}, 'strip_discards() matches' );
+}
+
+###########
+# round() #
+###########
+my @round_data = (
+ { query => [ 1, 1 ], result => 1 },
+ { query => [ 1.0, 1 ], result => 1 },
+ { query => [ 1.123, 0 ], result => 1 },
+ { query => [ 1.125, 2 ], result => 1.13 },
+ { query => [ 1.123456789, 5 ], result => 1.12346 },
+ { query => [ 1.123, -1 ], result => 0 },
+ { query => [ 1234.123, -2 ], result => 1200 },
+);
+
+foreach my $test (@round_data) {
+ is( round( @{ $test->{query} } ), $test->{result}, 'round()' );
+}
+
+##############
+# date_now() #
+##############
+# Just check the basic format returned.
+ok( date_now() =~ m{\A \d{4}-\d{2}-\d{2} T \d{2}:\d{2}:\d{2} Z \z}xms,
+ 'date_now() returns correct format' );
+
+######################
+# check_linebreaks() #
+######################
+
+# DOS
+my ( $counts, $line_ending ) =
+ check_linebreaks( File::Spec->catfile( 't', 'data', 'test.CEL' ) );
+is(
+ $line_ending,
+ "\N{CARRIAGE RETURN}\N{LINE FEED}",
+ 'check_linebreaks() recognises DOS file'
+);
+is_deeply(
+ $counts,
+ { dos => 49, unix => 0, mac => 0 },
+ 'check_linebreaks() returns correct line counts'
+);
+
+# Unix
+( $counts, $line_ending ) =
+ check_linebreaks( File::Spec->catfile( 't', 'data', 'test.txt' ) );
+is( $line_ending, "\N{LINE FEED}", 'check_linebreaks() recognises Unix file' );
+is_deeply(
+ $counts,
+ { dos => 0, unix => 8, mac => 0 },
+ 'check_linebreaks() returns correct line counts'
+);
+
+# TODO: Mac
+
+################
+# clean_hash() #
+################
+is_deeply(
+ clean_hash( { one => 1, two => '', three => undef } ),
+ { one => 1 },
+ 'clean_hash()'
+);
+
+# TODO: all the regexp patterns.
+
diff --git a/t/config.t b/t/config.t
new file mode 100644
index 0000000..c825762
--- /dev/null
+++ b/t/config.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: config.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Config' ); }
+
diff --git a/t/creator.t b/t/creator.t
new file mode 100644
index 0000000..5795702
--- /dev/null
+++ b/t/creator.t
@@ -0,0 +1,20 @@
+#!/usr/bin/env perl -wT
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval {
+ require Class::DBI;
+ };
+
+ skip 'Class::DBI not installed',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::Creator' );
+
+}
+
diff --git a/t/daemon.t b/t/daemon.t
new file mode 100644
index 0000000..1cb0d45
--- /dev/null
+++ b/t/daemon.t
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 2069 2008-06-04 14:33:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval {
+ require Class::DBI;
+ require MIME::Lite;
+ require Archive::Tar;
+ require Archive::Zip;
+ require Proc::Daemon;
+ };
+
+ skip 'MIME::Lite, Archive::Tar, Archive::Zip, Proc::Daemon or Class::DBI not installed',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon' );
+
+}
+
diff --git a/t/daemon_checker.t b/t/daemon_checker.t
new file mode 100644
index 0000000..b02434d
--- /dev/null
+++ b/t/daemon_checker.t
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+SKIP: {
+
+ eval {
+ require ArrayExpress::AutoSubmission::Daemon;
+ };
+
+ skip 'Parent class ArrayExpress::AutoSubmission::Daemon not available.',
+ 3 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::Checker' );
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::T2MChecker' );
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::MAGETABChecker' );
+
+}
+
diff --git a/t/daemon_exporter.t b/t/daemon_exporter.t
new file mode 100644
index 0000000..a842979
--- /dev/null
+++ b/t/daemon_exporter.t
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+SKIP: {
+
+ eval {
+ require ArrayExpress::AutoSubmission::Daemon;
+ };
+
+ skip 'Parent class ArrayExpress::AutoSubmission::Daemon not available.',
+ 3 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::Exporter' );
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::MAGETABExporter' );
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::T2MExporter' );
+
+}
+
diff --git a/t/daemon_mxchecker.t b/t/daemon_mxchecker.t
new file mode 100644
index 0000000..e2e8fab
--- /dev/null
+++ b/t/daemon_mxchecker.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval {
+ require ArrayExpress::AutoSubmission::Daemon;
+ require DBD::mysql;
+ };
+
+ skip 'DBD::mysql not installed, or ArrayExpress::AutoSubmission::Daemon superclass unavailable.',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::MXChecker' );
+
+}
+
diff --git a/t/daemon_mxexporter.t b/t/daemon_mxexporter.t
new file mode 100644
index 0000000..98a86b4
--- /dev/null
+++ b/t/daemon_mxexporter.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: daemon.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval {
+ require ArrayExpress::AutoSubmission::Daemon;
+ require DBD::mysql;
+ };
+
+ skip 'DBD::mysql not installed, or ArrayExpress::AutoSubmission::Daemon superclass unavailable.',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::Daemon::MXExporter' );
+
+}
+
diff --git a/t/data/dummy.CDF b/t/data/dummy.CDF
new file mode 100644
index 0000000..a6d4b85
--- /dev/null
+++ b/t/data/dummy.CDF
@@ -0,0 +1,192 @@
+[CDF]
+Version=GC3.0
+
+[Chip]
+Name=YG_S98
+Rows=534
+Cols=534
+NumberOfUnits=10
+MaxUnit=9375
+NumQCUnits=0
+ChipReference=
+
+[Unit1]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=1
+UnitType=3
+NumberBlocks=1
+
+[Unit1_Block1]
+Name=AFFX-MurIL2_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit2]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=2
+UnitType=3
+NumberBlocks=1
+
+[Unit2_Block1]
+Name=AFFX-MurIL10_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit3]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=3
+UnitType=3
+NumberBlocks=1
+
+[Unit3_Block1]
+Name=AFFX-MurIL4_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit4]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=4
+UnitType=3
+NumberBlocks=1
+
+[Unit4_Block1]
+Name=AFFX-MurFAS_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit5]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=5
+UnitType=3
+NumberBlocks=1
+
+[Unit5_Block1]
+Name=AFFX-BioB-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit6]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=6
+UnitType=3
+NumberBlocks=1
+
+[Unit6_Block1]
+Name=AFFX-BioB-M_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit7]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=7
+UnitType=3
+NumberBlocks=1
+
+[Unit7_Block1]
+Name=AFFX-BioB-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit8]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=8
+UnitType=3
+NumberBlocks=1
+
+[Unit8_Block1]
+Name=AFFX-BioC-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit9]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=9
+UnitType=3
+NumberBlocks=1
+
+[Unit9_Block1]
+Name=AFFX-BioC-3_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
+[Unit10]
+Name=NONE
+Direction=2
+NumAtoms=20
+NumCells=40
+UnitNumber=10
+UnitType=3
+NumberBlocks=1
+
+[Unit10_Block1]
+Name=AFFX-BioDn-5_at
+BlockNumber=1
+NumAtoms=20
+NumCells=40
+StartPosition=1
+StopPosition=20
+
+
diff --git a/t/data/test.CEL b/t/data/test.CEL
new file mode 100644
index 0000000..368eedf
--- /dev/null
+++ b/t/data/test.CEL
@@ -0,0 +1,49 @@
+[CEL]
+Version=3
+
+[HEADER]
+Cols=10
+Rows=1
+TotalX=10
+TotalY=1
+OffsetX=0
+OffsetY=0
+GridCornerUL=233 236
+GridCornerUR=4500 251
+GridCornerLR=4487 4516
+GridCornerLL=221 4501
+Axis-invertX=0
+AxisInvertY=0
+swapXY=0
+DatHeader=[0..46093] test 1:CLS=4733 RWS=4733 XIN=3 YIN=3 VE=17 2.0 03/27/03 23:53:16 Test.1sq 6
+Algorithm=Percentile
+AlgorithmParameters=Percentile:75;CellMargin:2;OutlierHigh:1.500;OutlierLow:1.004
+
+[INTENSITY]
+NumberCells=10
+CellHeader=X Y MEAN STDV NPIXELS
+ 0 0 158.0 28.9 36
+ 1 0 8875.5 1390.6 36
+ 2 0 160.0 25.3 36
+ 3 0 8552.5 1027.3 36
+ 4 0 78.8 15.6 36
+ 5 0 138.8 25.5 36
+ 6 0 8124.0 1124.9 36
+ 7 0 146.3 22.9 36
+ 8 0 7531.3 1038.5 36
+ 9 0 132.3 21.7 36
+
+[MASKS]
+NumberCells=0
+CellHeader=X Y
+
+[OUTLIERS]
+NumberCells=3
+CellHeader=X Y
+3 0
+6 0
+9 0
+
+[MODIFIED]
+NumberCells=0
+CellHeader=X Y ORIGMEAN
diff --git a/t/data/test.CHP b/t/data/test.CHP
new file mode 100644
index 0000000..7df0f68
Binary files /dev/null and b/t/data/test.CHP differ
diff --git a/t/data/test.EXP b/t/data/test.EXP
new file mode 100644
index 0000000..8c93765
--- /dev/null
+++ b/t/data/test.EXP
@@ -0,0 +1,49 @@
+Affymetrix GeneChip Experiment Information
+Version 1
+
+[Sample Info]
+Chip Type HG-U133A
+Chip Lot 12345678
+Operator Gandalf
+Sample Type
+Description
+Project Test
+Comments
+Solution Type
+Solution Lot
+
+[Fluidics]
+Protocol EukGE-WS2v4
+Wash A1 Recovery Mixes 0
+Wash A1 Temperature (C) 25
+Number of Wash A1 Cycles 10
+Mixes per Wash A1 Cycle 2
+Wash B Recovery Mixes 0
+Wash B Temperature (C) 50
+Number of Wash B Cycles 4
+Mixes per Wash B Cycle 15
+Stain Temperature (C) 25
+First Stain Time (seconds) 600
+Wash A2 Recovery Mixes 0
+Wash A2 Temperature (C) 25
+Number of Wash A2 Cycles 10
+Mixes per Wash A2 Cycle 4
+Second Stain Time (seconds) 600
+Third Stain Time (seconds) 600
+Wash A3 Recovery Mixes 0
+Wash A3 Temperature (C) 30
+Number of Wash A3 Cycles 15
+Mixes per Wash A3 Cycle 4
+Holding Temperature (C) 25
+Station 2
+Module 1
+Hybridize Date Sep 20 2003 01:10PM
+
+[Scanner]
+Pixel Size 3
+Filter 570
+Scan Temperature 30
+Scan Date Sep 20 2003 01:25PM
+Scanner ID 87654321
+Number of Scans 2
+Scanner Type HP
diff --git a/t/data/test.gpr b/t/data/test.gpr
new file mode 100644
index 0000000..79564e8
--- /dev/null
+++ b/t/data/test.gpr
@@ -0,0 +1,37 @@
+ATF 1.0
+24 43
+"Type=GenePix Results 1.2"
+"DateTime=2003/01/23 11:27:59"
+"Settings=E:\settings files\array.gps"
+"GalFile="
+"Scanner=GenePix 4000A"
+"Comment="
+"PixelSize=10"
+"ImageName=635 nm 532 nm"
+"FileName=\\Server\Data1.tif"
+"PMTVolts=600 590"
+"NormalizationFactor:RatioOfMedians=1.90064"
+"NormalizationFactor:RatioOfMeans=1.90384"
+"NormalizationFactor:MedianOfRatios=1.86554"
+"NormalizationFactor:MeanOfRatios=1.23878"
+"NormalizationFactor:RegressionRatio=2.45261"
+"JpegImage=\\Server\Data1.jpg"
+"RatioFormulation=W1/W2 (635 nm/532 nm)"
+"Barcode="
+"ImageOrigin=1240, 6320"
+"JpegOrigin=1840, 7160"
+"Creator=GenePix Pro 3.0.0.98"
+"Temperature=1.41243"
+"LaserPower=2.19092 0.863674"
+"LaserOnTime=86000 85977"
+"Block" "Column" "Row" "Name" "ID" "X" "Y" "Dia." "F635 Median" "F635 Mean" "F635 SD" "B635 Median" "B635 Mean" "B635 SD" "% > B635+1SD" "% > B635+2SD" "F635 % Sat." "F532 Median" "F532 Mean" "F532 SD" "B532notQTMedian" "B532 Mean" "B532 SD" "% > B532+1SD" "% > B532+2SD" "F532 % Sat." "Ratio of Medians" "Ratio of Means" "Median of Ratios" "Mean of Ratios" "Ratios SD" "Rgn Ratio" "Rgn R�" "F Pixels" "B Pixels" "Sum of Medians" "Sum of Means" "Log Ratio" "F635 Median - B635" "F532 Median - [...]
+1 1 1 2100 7300 90 240 240 54 40 44 21 100 100 0 502 497 98 81 86 32 100 100 0 0.475 0.481 0.494 0.491 0.129 0.413 0.731 52 376 621 616 -1.074 200 421 200 416 0
+1 2 1 2410 7300 120 694 671 209 40 41 11 98 98 0 1406 1354 413 81 81 20 100 100 0 0.494 0.496 0.500 0.599 0.409 0.433 0.777 120 778 1979 1904 -1.019 654 1325 631 1273 0
+1 3 1 2690 7320 120 501 531 267 41 41 10 99 99 0 998 1080 498 79 82 21 99 98 0 0.501 0.490 0.493 0.641 1.280 0.460 0.837 120 810 1379 1491 -0.998 460 919 490 1001 0
+1 4 1 2980 7310 120 783 770 284 40 41 10 99 96 0 1034 1014 331 81 83 22 100 99 0 0.780 0.782 0.819 1.372 3.252 0.675 0.722 120 796 1696 1663 -0.359 743 953 730 933 0
+1 5 1 3260 7320 130 457 434 219 40 41 10 100 99 0 998 938 493 81 83 21 100 98 0 0.455 0.460 0.450 0.600 0.632 0.382 0.751 120 930 1334 1251 -1.137 417 917 394 857 0
+1 6 1 3570 7310 130 166 172 67 40 41 10 94 94 0 382 388 112 82 83 20 99 98 0 0.420 0.431 0.473 0.696 2.121 0.362 0.589 120 914 426 438 -1.252 126 300 132 306 0
+1 7 1 3860 7320 100 63 65 17 40 41 10 76 55 0 182 188 53 79 80 20 100 96 0 0.223 0.229 0.226 0.313 0.289 0.160 0.283 80 552 126 134 -2.163 23 103 25 109 0
+1 8 1 4130 7330 120 415 392 122 40 41 10 96 95 0 793 779 215 79 81 20 98 97 0 0.525 0.503 0.522 0.741 1.710 0.445 0.754 120 816 1089 1052 -0.929 375 714 352 700 0
+1 9 1 4430 7320 120 153 155 61 39 41 10 97 94 0 374 382 125 80 82 20 99 99 0 0.388 0.384 0.368 0.443 0.369 0.351 0.721 120 796 408 418 -1.367 114 294 116 302 0
+1 10 1 4730 7330 120 865 826 343 40 41 10 100 98 0 1011 941 389 79 81 21 99 98 0 0.885 0.912 0.923 1.477 3.031 0.751 0.698 120 810 1757 1648 -0.176 825 932 786 862 0
diff --git a/t/data/test.txt b/t/data/test.txt
new file mode 100644
index 0000000..27e69e8
--- /dev/null
+++ b/t/data/test.txt
@@ -0,0 +1,8 @@
+Reporter Identifier QT1(Hyb1) QT2(Hyb1) QT1(Hyb2) QT2(Hyb2)
+ID1 1 2 3 4
+
+Block Column Row X Y QT1 QT2 QT3 QT4
+1 1 1 230 230 1 2 3 4
+
+
+
diff --git a/t/data/test_calvin.CHP b/t/data/test_calvin.CHP
new file mode 100644
index 0000000..2c301b2
Binary files /dev/null and b/t/data/test_calvin.CHP differ
diff --git a/t/data/test_fgem.txt b/t/data/test_fgem.txt
new file mode 100644
index 0000000..6bcb194
--- /dev/null
+++ b/t/data/test_fgem.txt
@@ -0,0 +1,11 @@
+Reporter Identifier RMA(Sigma1278b_1)(Sigma1278b_2) RMA(W303a_1)(W303a_2)
+A102340 0.147 0.473
+A102341 0.53 0.484
+A102342 0.169 0.188
+A102343 0.742 0.684
+A102344 0.479 0.514
+A102345 0.106 0.525
+A102346 0.592 0.612
+A102347 0.000 0.106
+A102348 0.606 0.624
+A102349 0.584 0.503
diff --git a/t/data/test_xda.CEL b/t/data/test_xda.CEL
new file mode 100644
index 0000000..a3f7d36
Binary files /dev/null and b/t/data/test_xda.CEL differ
diff --git a/t/data/test_xda.CHP b/t/data/test_xda.CHP
new file mode 100644
index 0000000..802a4cf
Binary files /dev/null and b/t/data/test_xda.CHP differ
diff --git a/t/data_metrics.t b/t/data_metrics.t
new file mode 100644
index 0000000..04a9831
--- /dev/null
+++ b/t/data_metrics.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: data_metrics.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::Metrics' ); }
+
diff --git a/t/database.t b/t/database.t
new file mode 100644
index 0000000..1e7c722
--- /dev/null
+++ b/t/database.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: database.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Database' ); }
+
diff --git a/t/datafile.t b/t/datafile.t
new file mode 100644
index 0000000..d2f46cc
--- /dev/null
+++ b/t/datafile.t
@@ -0,0 +1,467 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: datafile.t 2069 2008-06-04 14:33:52Z tfrayner $
+
+use strict;
+use warnings;
+
+# Very simple mock object for testing mage_qtd mutator.
+
+use Test::More tests => 131;
+use File::Spec;
+use Cwd;
+use ArrayExpress::Datafile::QT_list qw(get_QTs);
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+BEGIN { use_ok( 'ArrayExpress::Datafile' ); }
+
+my $filename = File::Spec->catfile('t', 'data', 'test.txt'); # right
+my $filepath = '/my/path/to/test.txt'; # wrong
+my $test_columns = [qw(x y z)];
+my $binfile = File::Spec->catfile('t', 'data', 'test_xda.CEL');
+my $expfile = File::Spec->catfile('t', 'data', 'test.EXP');
+my $gprfile = File::Spec->catfile('t', 'data', 'test.gpr');
+my $fgemfile = File::Spec->catfile('t', 'data', 'test_fgem.txt');
+my $QTs = get_QTs();
+
+#######################################################################################
+{
+ # Create the instance.
+ my $file = ArrayExpress::Datafile->new({is_dummy => 1});
+ ok( defined $file, 'new() returned a value' );
+ ok( $file->isa('ArrayExpress::Datafile'),
+ 'of the correct class' );
+
+ # File name setting.
+ $file->set_name($filename);
+ is( $file->get_name, $filename, 'name');
+ is( $file->get_path, File::Spec->catfile(getcwd, $filename), 'name-set path');
+ is( $file->get_is_binary, 0, 'text file detected');
+
+ # Incrementable integer attribute checks.
+ foreach my $attr qw(row_count parse_errors not_null) {
+ my $getter = "get_$attr";
+ is($file->$getter(), 0, "$attr initial value");
+
+ my $setter = "set_$attr";
+ $file->$setter(10);
+ is($file->$getter(), 10, "$attr set value");
+
+ my $adder = "increment_$attr";
+ $file->$adder();
+ is($file->$getter, 11, "$attr incremented value");
+
+ $file->$adder(5);
+ is($file->$getter(), 16, "$attr multiply incremented value");
+ }
+
+ # Simple boolean attributes.
+ foreach my $attr qw(is_exp is_miamexpress) {
+ my $getter = "get_$attr";
+ is($file->$getter(), 0, "$attr initial value");
+
+ my $setter = "set_$attr";
+ $file->$setter(1);
+ is($file->$getter(), 1, "$attr set value 1");
+ $file->$setter(0);
+ is($file->$getter(), 0, "$attr set value 0");
+ }
+
+ # Simple string attributes (empty string init).
+ foreach my $attr qw(hyb_identifier
+ hyb_sysuid
+ array_design_id
+ ded_identifier
+ test_data_line
+ qt_type) {
+ my $getter = "get_$attr";
+ is($file->$getter(), q{}, "$attr initial value");
+
+ my $setter = "set_$attr";
+ $file->$setter('test');
+ is($file->$getter(), 'test', "$attr set value");
+ $file->$setter(q{});
+ is($file->$getter(), q{}, "$attr set value empty");
+ }
+
+ # Simple hashref attributes.
+ my %testhash = (one => 1, two => 'two', three => 'III');
+ foreach my $attr qw(data_metrics) {
+ my $getter = "get_$attr";
+ is_deeply($file->$getter(), {}, "$attr initial value");
+
+ my $setter = "set_$attr";
+ $file->$setter(\%testhash);
+ is_deeply($file->$getter(), \%testhash, "$attr set value");
+ }
+
+ # Simple arrayref attributes.
+ my @testarray = qw(one two three);
+ foreach my $attr qw(index_columns
+ column_headings
+ heading_qts
+ heading_hybs) {
+ my $getter = "get_$attr";
+ is_deeply($file->$getter(), [], "$attr initial value");
+
+ my $setter = "set_$attr";
+ $file->$setter(\@testarray);
+ is_deeply($file->$getter(), \@testarray, "$attr set value");
+ }
+
+ # Accumulative hashref attributes
+ my @testarray2 = qw(four five);
+ foreach my $attr qw(fail_columns
+ fail_hybs) {
+ my $getter = "get_$attr";
+ is_deeply($file->$getter(), [], "$attr initial value");
+
+ my $adder = "add_$attr";
+ $file->$adder(@testarray);
+ is_deeply(sort($file->$getter()), [sort @testarray], "$attr add values");
+ $file->$adder(@testarray);
+ is_deeply(sort($file->$getter()), [sort @testarray], "$attr duplicate values");
+ $file->$adder(@testarray2);
+ is_deeply(sort($file->$getter()), [sort @testarray, @testarray2], "$attr add more values");
+ }
+
+ # FactorValues.
+ $file->add_factor_value('category1', 'value1');
+ is_deeply($file->get_factor_value(), {'category1' => ['value1']}, 'add factor category and value');
+ $file->add_factor_value('category1', 'value1');
+ is_deeply($file->get_factor_value(), {'category1' => ['value1']}, 'add duplicate factor value');
+ $file->add_factor_value('category1', 'value2');
+ is_deeply($file->get_factor_value(), {'category1' => [qw(value1 value2)]}, 'add second factor value');
+ $file->add_factor_value('category2', 'value2');
+ is_deeply($file->get_factor_value(), {'category1' => [qw(value1 value2)],
+ 'category2' => ['value2']}, 'add second factor category and value');
+
+
+ # Enumerated types; some of these tests are designed to die.
+ SKIP: {
+
+ eval {
+ require Test::Exception;
+ };
+
+ skip 'Test::Exception not installed',
+ 12 if $@;
+
+ use_ok( 'Test::Exception' );
+
+ # ded_type
+ dies_ok( sub { $file->set_ded_type('notarealdedtype') }, 'exception on bad ded_type');
+ lives_ok( sub { $file->set_ded_type('Reporter') }, 'valid ded_type');
+ is($file->get_ded_type(), 'Reporter', 'ded_type correctly set');
+
+ # format_type
+ dies_ok( sub { $file->set_format_type('notarealformattype') }, 'exception on bad format_type');
+ lives_ok( sub { $file->set_format_type('GenePix') }, 'valid format_type');
+ is($file->get_format_type(), 'GenePix', 'format_type correctly set');
+
+ # data_type
+ dies_ok( sub { $file->set_data_type('notarealdatatype') }, 'exception on bad data_type');
+ lives_ok( sub { $file->set_data_type('raw') }, 'valid data_type');
+ is($file->get_data_type(), 'raw', 'data_type correctly set');
+
+ # Test mage_qtd (using mock Bio::MAGE object).
+ SKIP: {
+ eval {
+ require Bio::MAGE::BioAssayData::QuantitationTypeDimension;
+ };
+
+ skip 'Bio::MAGE::BioAssayData::QuantitationTypeDimension not installed',
+ 2 if $@;
+
+ my $qtd = Bio::MAGE::BioAssayData::QuantitationTypeDimension->new();
+
+ lives_ok( sub {$file->set_mage_qtd($qtd) }, 'set mage_qtd');
+ is_deeply($file->get_mage_qtd(), $qtd, 'get mage_qtd');
+ }
+ }
+
+ # File path setting.
+ $file->set_path($filepath);
+ is( $file->get_name, $filename, 'name');
+ is( $file->get_path, $filepath, 'name-set path');
+}
+#######################################################################################
+{
+ # Simple string attributes (undef init).
+ my $file2 = ArrayExpress::Datafile->new({is_dummy => 1});
+ foreach my $attr qw(path
+ target_filename) {
+ my $getter = "get_$attr";
+ is($file2->$getter(), undef, "$attr initial value");
+
+ my $setter = "set_$attr";
+ $file2->$setter('test');
+ is($file2->$getter(), 'test', "$attr set value");
+ $file2->$setter(q{});
+ is($file2->$getter(), q{}, "$attr set value empty");
+ }
+}
+#######################################################################################
+{
+ # EXP file accessors.
+ my $exp = ArrayExpress::Datafile->new({is_dummy => 1});
+ my $expdata = {section => {param => 'value'}};
+ $exp->set_exp_data($expdata);
+ is_deeply($exp->get_exp_data(), $expdata, 'set exp_data');
+ is($exp->get_is_exp(), 1, 'EXP flag set by set_exp_data');
+ is($exp->get_format_type(), 'Affymetrix', 'format_type set by set_exp_data');
+ is($exp->get_data_type(), 'EXP', 'data_type set by set_exp_data');
+}
+#######################################################################################
+{
+ # Create a more complex initial instance.
+ my $nfile1 = ArrayExpress::Datafile->new({
+ name => $filename,
+ hyb_identifier => 'one',
+ hyb_sysuid => 'two',
+ array_design_id => 'three',
+ is_miamexpress => 1,
+ is_exp => 1,
+ target_filename => 'four',
+ data_type => 'raw',
+ column_headings => $test_columns,
+ });
+ ok( defined $nfile1, 'new() returned a value' );
+ ok( $nfile1->isa('ArrayExpress::Datafile'),
+ 'of the correct class' );
+
+ # Test that the path is also set.
+ is( $nfile1->get_name, $filename, 'constructor-set name');
+ is( $nfile1->get_path, File::Spec->catfile(getcwd, $filename), 'name-constructor-set path');
+
+ # Other init args settings.
+ is( $nfile1->get_hyb_identifier(), 'one', 'init hyb_identifier');
+ is( $nfile1->get_hyb_sysuid(), 'two', 'init hyb_sysuid');
+ is( $nfile1->get_array_design_id(), 'three', 'init array_design_id');
+ is( $nfile1->get_is_miamexpress(), 1, 'init is_miamexpress');
+ is( $nfile1->get_is_exp(), 1, 'init is_exp');
+ is( $nfile1->get_target_filename(), 'four', 'init target_filename');
+ is( $nfile1->get_data_type(), 'raw', 'init data_type');
+ is_deeply( $nfile1->get_column_headings(), $test_columns, 'init column_headings');
+}
+#######################################################################################
+{
+ # Check precedence of path initialization
+ my $nfile2 = ArrayExpress::Datafile->new({
+ name => $filename,
+ path => $filepath,
+ is_dummy => 1,
+ });
+ is( $nfile2->get_name, $filename, 'constructor-set name');
+ is( $nfile2->get_path, $filepath, 'constructor-set path');
+
+ # Check binary file detection
+ my $file3 = ArrayExpress::Datafile->new({
+ name => $binfile,
+ is_dummy => 1,
+ });
+ is( $file3->get_is_binary(), 1, 'binary file detected');
+}
+#######################################################################################
+{
+ my $exp = ArrayExpress::Datafile->new({
+ name => $expfile,
+ is_dummy => 1,
+ });
+ $exp->parse_exp_file();
+ is( $exp->get_is_exp(), 1, 'real EXP file recognized');
+ my $expected = {
+ 'Sample Info' => {
+ 'Chip Lot' => 12345678,
+ 'Operator' => 'Gandalf',
+ 'Project' => 'Test',
+ 'Chip Type' => 'HG-U133A',
+ },
+ 'Scanner' => {
+ 'Number of Scans' => 2,
+ 'Scan Date' => 'Sep 20 2003 01:25PM',
+ 'Filter' => 570,
+ 'Scanner ID' => '87654321',
+ 'Pixel Size' => 3,
+ 'Scanner Type' => 'HP',
+ 'Scan Temperature' => 30,
+ },
+ 'Fluidics' => {
+ 'Wash A1 Recovery Mixes' => 0,
+ 'Wash A1 Temperature (C)' => 25,
+ 'Number of Wash A1 Cycles' => 10,
+ 'Mixes per Wash A1 Cycle' => 2,
+ 'Wash B Recovery Mixes' => 0,
+ 'Wash B Temperature (C)' => 50,
+ 'Number of Wash B Cycles' => 4,
+ 'Mixes per Wash B Cycle' => 15,
+ 'Stain Temperature (C)' => 25,
+ 'First Stain Time (seconds)' => 600,
+ 'Wash A2 Recovery Mixes' => 0,
+ 'Wash A2 Temperature (C)' => 25,
+ 'Number of Wash A2 Cycles' => 10,
+ 'Mixes per Wash A2 Cycle' => 4,
+ 'Second Stain Time (seconds)' => 600,
+ 'Third Stain Time (seconds)' => 600,
+ 'Wash A3 Recovery Mixes' => 0,
+ 'Wash A3 Temperature (C)' => 30,
+ 'Number of Wash A3 Cycles' => 15,
+ 'Mixes per Wash A3 Cycle' => 4,
+ 'Holding Temperature (C)' => 25,
+ 'Hybridize Date' => 'Sep 20 2003 01:10PM',
+ 'Module' => 1,
+ 'Station' => 2,
+ 'Protocol' => 'EukGE-WS2v4',
+ },
+ 'header' => {
+ 'Version' => 1,
+ },
+ };
+ is_deeply( $exp->get_exp_data, $expected, 'real EXP file fully parsed');
+}
+#######################################################################################
+my $start = [
+ 'Block',
+ 'Column',
+ 'Row',
+ 'Name',
+ 'ID',
+ 'X',
+ 'Y',
+];
+my $ignored = [
+ 'Name',
+ 'ID',
+];
+my $end = [
+ 'MetaColumn',
+ 'MetaRow',
+ 'Column',
+ 'Row',
+];
+my $expected = [
+ 'Dia.',
+ 'F635 Median',
+ 'F635 Mean',
+ 'F635 SD',
+ 'B635 Median',
+ 'B635 Mean',
+ 'B635 SD',
+ '% > B635+1SD',
+ '% > B635+2SD',
+ 'F635 % Sat.',
+ 'F532 Median',
+ 'F532 Mean',
+ 'F532 SD',
+ 'B532notQTMedian',
+ 'B532 Mean',
+ 'B532 SD',
+ '% > B532+1SD',
+ '% > B532+2SD',
+ 'F532 % Sat.',
+ 'Ratio of Medians',
+ 'Ratio of Means',
+ 'Median of Ratios',
+ 'Mean of Ratios',
+ 'Ratios SD',
+ 'Rgn Ratio',
+ 'Rgn R�',
+ 'F Pixels',
+ 'B Pixels',
+ 'Sum of Medians',
+ 'Sum of Means',
+ 'Log Ratio',
+ 'F635 Median - B635',
+ 'F532 Median - B532',
+ 'F635 Mean - B635',
+ 'F532 Mean - B532',
+ 'flags',
+];
+#######################################################################################
+{
+ my $gpr = ArrayExpress::Datafile->new({
+ name => $gprfile,
+ is_dummy => 1,
+ });
+ is($gpr->parse_header(), 'GenePix', 'parse_header return code');
+ is($gpr->get_format_type(), 'GenePix', 'real file get_format_type');
+ is_deeply($gpr->get_index_columns(), [0,1,2,5,6], 'real file get_index_columns');
+ is_deeply($gpr->get_column_headings(), [@$start, @$expected], 'real file get_column_headings');
+
+ is($gpr->check_column_headings($QTs), q{}, 'check_column_headings return code');
+ is($gpr->get_qt_type(), 'GenePix[Axon Instruments]', 'real file get_qt_type');
+ is_deeply($gpr->get_heading_qts(), [@$ignored, @$expected], 'real file get_heading_qts');
+
+ # These are alphabetically sorted.
+ is_deeply($gpr->get_fail_columns(), ['B532notQTMedian', 'flags'], 'real file get_fail_columns');
+}
+#######################################################################################
+{
+ my $gpr = ArrayExpress::Datafile->new({
+ name => $gprfile,
+ is_dummy => 1,
+ });
+ $gpr->parse_datafile($QTs, {}, {}, 0);
+ is($gpr->get_line_format(), 'Unix', 'real file get_line_format');
+ is($gpr->get_row_count(), 10, 'real file row count');
+ is($gpr->get_parse_errors(), 0, 'real file parse errors');
+ is($gpr->get_not_null(), 320, 'real file not null');
+ is($gpr->percent_null(), 5.88235, 'real file percent null');
+
+ # This is the result of hashing a number of lines together using Digest::MD5::md5_hex.
+ my $expecteddata = 'fe3055946e452021b7b2d83ac5569eaea6c1d2f25c721684a0f7c13861a997b53cb62811f2c166212ddc4ec18fa76d5ef8d0261f69960b7fe7ef41025a3ed9667d3a17bc59819151312b7c8b4a6460f8';
+ is($gpr->get_test_data_line(), $expecteddata, 'real file test_data_line');
+ is_deeply($gpr->get_column_headings(), [@$end, @$ignored, @$expected], 'parsed file get_column_headings');
+ is_deeply($gpr->get_heading_qts(), [@$ignored, @$expected], 'parsed file get_heading_qts');
+}
+#######################################################################################
+{
+ my $fgem = ArrayExpress::Datafile->new({
+ name => $fgemfile,
+ data_type => 'transformed',
+ array_design_id => 'UNKNOWN',
+ });
+ $fgem->parse_datafile($QTs, {Sigma1278b_1 => 'X', W303a_1 => 'X', W303a_2 => 'X'}, {}, 0);
+
+ is_deeply($fgem->get_heading_qts(), [qw(RMA RMA)], 'fgem heading qts');
+ is_deeply($fgem->get_heading_hybs(), [['Sigma1278b_1', 'Sigma1278b_2'],
+ ['W303a_1', 'W303a_2']], 'fgem heading hybs');
+ is_deeply($fgem->get_fail_columns(), [], 'fgem fail columns');
+ is_deeply($fgem->get_fail_hybs(), ['Sigma1278b_2'], 'fgem fail hybs');
+}
+#######################################################################################
+{
+
+ my $file = ArrayExpress::Datafile->new({
+ name => $filename,
+ data_type => 'raw',
+ array_design_id => 'UNKNOWN',
+ });
+
+ # First pass
+ $file->parse_header_with_indices( { GenePix => [qw(Block Column Row X Y)] } );
+
+ is( $file->get_format_type(), 'GenePix', 'parse_header recognises file type' );
+ is_deeply(
+ $file->get_column_headings(),
+ [qw(Block Column Row X Y QT1 QT2 QT3 QT4)],
+ 'parse_header returns correct headings'
+ );
+ is_deeply( $file->get_index_columns(), [qw(0 1 2 3 4)],
+ 'parse_header returns correct index columns' );
+
+ # Second pass
+ seek( $file->get_filehandle(), 0, 0 ) or die($!);
+ $file->set_data_type($CONFIG->get_FGEM_FILE_TYPE());
+ $file->parse_header_with_indices( { FGEM => ['Reporter Identifier'] } );
+
+ is( $file->get_format_type(), 'FGEM', 'parse_header recognises file type' );
+ is_deeply(
+ $file->get_column_headings(),
+ [ 'Reporter Identifier', qw( QT1(Hyb1) QT2(Hyb1) QT1(Hyb2) QT2(Hyb2) ) ],
+ 'parse_header returns correct headings'
+ );
+ is_deeply( $file->get_index_columns(), [0], 'parse_header returns correct index columns' );
+}
+
+# TODO: fix_known_text_format (all types), strip_and_sort
diff --git a/t/datafile_parser.t b/t/datafile_parser.t
new file mode 100644
index 0000000..7e648df
--- /dev/null
+++ b/t/datafile_parser.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: datafile_parser.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::Parser' ); }
+
diff --git a/t/db.t b/t/db.t
new file mode 100644
index 0000000..f0d6194
--- /dev/null
+++ b/t/db.t
@@ -0,0 +1,58 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: db.t 1960 2008-02-21 12:03:19Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 37;
+
+SKIP: {
+
+ eval {
+ require Class::DBI;
+ };
+
+ skip 'Class::DBI not installed',
+ 37 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::DB' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Accessionable' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::ArrayDesign' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::ArrayDesignExperiment' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::ArrayDesignOrganism' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Category' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::CategoryDesign' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::CategoryMaterial' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::CategoryTaxon' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::DataFile' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Design' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::DesignInstance' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Event' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Experiment' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::ExperimentFactor' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::ExperimentQT' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Factor' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::File' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Material' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::MaterialInstance' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Organism' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::OrganismInstance' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Permission' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::PermissionRole' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Protocol' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::QuantitationType' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Role' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::RoleUser' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Spreadsheet' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Taxon' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::User' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::LoadedData' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::ExperimentLoadedData' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::DataFormat' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::QualityMetric' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::QualityMetricInstance' );
+ require_ok( 'ArrayExpress::AutoSubmission::DB::Platform' );
+
+}
+
diff --git a/t/entrez_list.t b/t/entrez_list.t
new file mode 100644
index 0000000..e466d93
--- /dev/null
+++ b/t/entrez_list.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: entrez_list.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Entrez_list' ); }
+
diff --git a/t/experiment_checker.t b/t/experiment_checker.t
new file mode 100644
index 0000000..df04781
--- /dev/null
+++ b/t/experiment_checker.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: experiment_checker.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::ExperimentChecker' ); }
+
diff --git a/t/fgem.t b/t/fgem.t
new file mode 100644
index 0000000..7d7b3ce
--- /dev/null
+++ b/t/fgem.t
@@ -0,0 +1,107 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: common.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 33;
+
+BEGIN {
+ require_ok(
+ 'ArrayExpress::Datafile::DataMatrix'
+ );
+}
+
+my $dm = ArrayExpress::Datafile::DataMatrix->new();
+ok( defined $dm, 'new() returned a value' );
+ok( $dm->isa('ArrayExpress::Datafile::DataMatrix'), 'of the correct class' );
+
+#########################
+# get_dimension_lists() #
+#########################
+my @fgem_dimension_data = (
+ {
+
+ # DBQ/DQB (defaults to DBQ)
+ query => [
+ [ ['One'], ['Two'] ], # Hybs
+ [qw(A A)], # QTs
+ ],
+ result => [
+ [ ['One'], ['Two'] ], # Hybs
+ [qw(A)], # QTs
+ 'DBQ', # Order
+ ]
+ },
+ {
+
+ # DBQ
+ query => [ [ ['One'], ['One'], ['Two'], ['Two'] ], [qw(A B A B)], ],
+ result => [ [ ['One'], ['Two'] ], [qw(A B)], 'DBQ', ]
+ },
+ {
+
+ # DBQ
+ query => [
+ [ ['One'], ['One'], ['One'], ['Two'], ['Two'], ['Two'] ],
+ [qw(A B C A B C)],
+ ],
+ result => [ [ ['One'], ['Two'] ], [qw(A B C)], 'DBQ', ]
+ },
+ {
+
+ # DQB
+ query => [ [ ['One'], ['Two'], ['One'], ['Two'] ], [qw(A A B B)], ],
+ result => [ [ ['One'], ['Two'] ], [qw(A B)], 'DQB', ]
+ },
+ {
+
+ # DBQ, more complex
+ query => [
+ [
+ [qw(One Two)], [qw(One Two)], [qw(Three Four)], [qw(Three Four)]
+ ],
+ [qw(A B A B)],
+ ],
+ result => [ [ [qw(One Two)], [qw(Three Four)] ], [qw(A B)], 'DBQ', ]
+ },
+ {
+
+ # Broken, no order detectable
+ query => [ [ ['One'], ['Three'], ['One'], ['Two'] ], [qw(A D B C)], ],
+ result => [ [], [], undef, ]
+ },
+ {
+
+ # Broken, order at first okay, then breaks.
+ query => [ [ ['One'], ['Two'], ['One'], ['Two'] ], [qw(A A B C)], ],
+ result => [ [], [], undef, ]
+ },
+ {
+
+ # Broken, order at first okay, then breaks.
+ query => [ [ ['One'], ['Two'], ['Onxe'], ['Three'] ], [qw(A A B B)], ],
+ result => [ [], [], undef, ]
+ },
+ {
+
+ # Broken, order at first okay, then breaks.
+ query => [ [ ['One'], ['One'], ['Two'], ['Three'] ], [qw(A B A B)], ],
+ result => [ [], [], undef, ]
+ },
+ {
+
+ # Broken, order at first okay, then breaks.
+ query => [ [ ['One'], ['One'], ['Two'], ['Two'] ], [qw(A B B A)], ],
+ result => [ [], [], undef, ]
+ },
+);
+
+foreach my $test (@fgem_dimension_data) {
+ my ( $bas, $qts, $order ) = $dm->get_dimension_lists( @{ $test->{query} } );
+ is_deeply( $bas, $test->{result}[0], 'get_dimension_lists bioassays' );
+ is_deeply( $qts, $test->{result}[1], 'get_dimension_lists qts' );
+ is_deeply( $order, $test->{result}[2], 'get_dimension_lists order' );
+}
+
diff --git a/t/logger.t b/t/logger.t
new file mode 100644
index 0000000..5b1ac00
--- /dev/null
+++ b/t/logger.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id$
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::Curator::Logger' );
+
diff --git a/t/mage.t b/t/mage.t
new file mode 100644
index 0000000..86f195b
--- /dev/null
+++ b/t/mage.t
@@ -0,0 +1,19 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: mage.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval { require Bio::MAGE };
+
+ skip 'Bio::MAGE not installed', 1 if $@;
+
+ require_ok( 'ArrayExpress::Curator::MAGE' );
+
+}
+
diff --git a/t/mage_defs.t b/t/mage_defs.t
new file mode 100644
index 0000000..1647f1d
--- /dev/null
+++ b/t/mage_defs.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: mage_defs.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::MAGE::Definitions' ); }
+
diff --git a/t/magetab.t b/t/magetab.t
new file mode 100644
index 0000000..f753a8a
--- /dev/null
+++ b/t/magetab.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB' );
diff --git a/t/magetab_checker.t b/t/magetab_checker.t
new file mode 100644
index 0000000..f3356fc
--- /dev/null
+++ b/t/magetab_checker.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: validate.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+require_ok( 'ArrayExpress::MAGETAB::Checker' );
+require_ok( 'ArrayExpress::MAGETAB::Checker::IDF' );
diff --git a/t/magetab_datamatrix.t b/t/magetab_datamatrix.t
new file mode 100644
index 0000000..3ebf635
--- /dev/null
+++ b/t/magetab_datamatrix.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_datamatrix.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::DataMatrix' );
diff --git a/t/magetab_idf.t b/t/magetab_idf.t
new file mode 100644
index 0000000..ffcc2f5
--- /dev/null
+++ b/t/magetab_idf.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_idf.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::IDF' );
diff --git a/t/magetab_sdrf.t b/t/magetab_sdrf.t
new file mode 100644
index 0000000..c0dff2c
--- /dev/null
+++ b/t/magetab_sdrf.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_sdrf.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::SDRF' );
diff --git a/t/magetab_tabfile.t b/t/magetab_tabfile.t
new file mode 100644
index 0000000..6c1cefe
--- /dev/null
+++ b/t/magetab_tabfile.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: magetab_tabfile.t 1462 2007-03-15 15:45:55Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::MAGETAB::TabFile' );
diff --git a/t/miamexpress.t b/t/miamexpress.t
new file mode 100644
index 0000000..d660ba7
--- /dev/null
+++ b/t/miamexpress.t
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: miamexpress.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval {
+ require DBI;
+ require DBD::mysql;
+ };
+
+ skip 'DBI and/or DBD::mysql not installed', 1 if $@;
+
+ require_ok( 'ArrayExpress::Curator::MIAMExpress' );
+
+}
+
diff --git a/t/qt_list.t b/t/qt_list.t
new file mode 100644
index 0000000..7ddc024
--- /dev/null
+++ b/t/qt_list.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: qt_list.t 1269 2006-12-10 12:23:13Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Datafile::QT_list' ); }
+
diff --git a/t/report.t b/t/report.t
new file mode 100644
index 0000000..150254d
--- /dev/null
+++ b/t/report.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: report.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Report' ); }
+
diff --git a/t/spreadsheet.t b/t/spreadsheet.t
new file mode 100644
index 0000000..052a336
--- /dev/null
+++ b/t/spreadsheet.t
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: spreadsheet.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval {
+ require Class::DBI;
+ };
+
+ skip 'Class::DBI not installed',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::Spreadsheet' );
+
+}
diff --git a/t/standalone.t b/t/standalone.t
new file mode 100644
index 0000000..b03fd5c
--- /dev/null
+++ b/t/standalone.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: validate.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require_ok( 'ArrayExpress::Curator::Standalone' );
diff --git a/t/tab2mage.t b/t/tab2mage.t
new file mode 100644
index 0000000..b1ba412
--- /dev/null
+++ b/t/tab2mage.t
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: tab2mage.t 1298 2006-12-20 19:44:56Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+
+SKIP: {
+
+ eval { require Bio::MAGE };
+
+ skip 'Bio::MAGE not installed', 1 if $@;
+
+ require_ok( 'ArrayExpress::Curator::Tab2MAGE' );
+
+}
+
+my $t2m = ArrayExpress::Curator::Tab2MAGE->new({
+ target_directory => 't',
+ spreadsheet_filename => 'test.txt',
+});
+ok( defined $t2m, 'new() returned a value' );
+ok( $t2m->isa('ArrayExpress::Curator::Tab2MAGE'),
+ 'of the correct class' );
+
+# Test our basic attempts to sanitize protocol text for display in ArrayExpress.
+is($t2m->htmlify_protocoltext("&"), "&", 'htmlify ampersand');
+is($t2m->htmlify_protocoltext("\n"), "<br>\n", 'htmlify unix newline');
+is($t2m->htmlify_protocoltext("\r\n"), "<br>\r\n", 'htmlify dos newline');
+is($t2m->htmlify_protocoltext("°"), "°", 'htmlify named entity');
+is($t2m->htmlify_protocoltext("Δ"), "Δ", 'htmlify numbered entity');
+is($t2m->htmlify_protocoltext("�"), "°", 'htmlify degree (�) sign');
+is($t2m->htmlify_protocoltext("�X"), "°", 'htmlify degree (�X) sign');
+is($t2m->htmlify_protocoltext("�"), "μ", 'htmlify mu (�) character');
+is($t2m->htmlify_protocoltext("��"), "μ", 'htmlify mu (��) character');
+is($t2m->htmlify_protocoltext("�"), "'", 'htmlify apostrophe (�) character');
+is($t2m->htmlify_protocoltext("�"), """, 'htmlify quote (�) character');
+is($t2m->htmlify_protocoltext("�"), """, 'htmlify quote (�) character');
diff --git a/t/tracking.t b/t/tracking.t
new file mode 100644
index 0000000..a3cf186
--- /dev/null
+++ b/t/tracking.t
@@ -0,0 +1,36 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: db.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+
+SKIP: {
+
+ eval {
+ require Date::Manip;
+ };
+
+ skip 'Date::Manip not installed',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::Tracking::QueryHandler' );
+
+}
+
+require_ok( 'ArrayExpress::Tracking::Event' );
+require_ok( 'ArrayExpress::Tracking::CelQC' );
+
+SKIP: {
+
+ eval {
+ require Class::DBI;
+ };
+ skip 'Class::DBI not installed',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::Tracking::QCJobManager' );
+
+}
diff --git a/t/validate.t b/t/validate.t
new file mode 100644
index 0000000..0a63d08
--- /dev/null
+++ b/t/validate.t
@@ -0,0 +1,19 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: validate.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval { require Bio::MAGE };
+
+ skip 'Bio::MAGE not installed', 1 if $@;
+
+ require_ok( 'ArrayExpress::Curator::Validate' );
+
+}
+
diff --git a/t/visualize.t b/t/visualize.t
new file mode 100644
index 0000000..6fd17df
--- /dev/null
+++ b/t/visualize.t
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: visualize.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN { use_ok( 'ArrayExpress::Curator::Visualize' ); }
+
diff --git a/t/web_form.t b/t/web_form.t
new file mode 100644
index 0000000..ad28626
--- /dev/null
+++ b/t/web_form.t
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl -wT
+#
+# $Id: web_form.t 1242 2006-12-02 19:18:52Z tfrayner $
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+SKIP: {
+
+ eval {
+ require CGI::Application;
+ require CGI::Application::Plugin::Authentication;
+ require CGI::Application::Plugin::ValidateRM;
+ require CGI::Upload;
+ };
+
+ skip 'Some module(s) not installed: CGI::Application, CGI::Upload, CAP::Authentication or CAP::ValidateRM',
+ 1 if $@;
+
+ require_ok( 'ArrayExpress::AutoSubmission::WebForm' );
+
+}
+
diff --git a/util/AE_insert_celfiles.pl b/util/AE_insert_celfiles.pl
new file mode 100755
index 0000000..2d9aa1f
--- /dev/null
+++ b/util/AE_insert_celfiles.pl
@@ -0,0 +1,202 @@
+#!/usr/bin/env perl
+#
+# Script to take a tab-delimited list of BioAssayData identifiers
+# mapped to CEL file names, and insert them into the ArrayExpress
+# repository.
+#
+# With thanks to Margus Lukk for the actual CEL insertion code.
+#
+# Tim Rayner, 2007, EBI
+#
+# $Id: AE_insert_celfiles.pl 1978 2008-02-28 12:14:14Z tfrayner $
+#
+# From Margus: "The files go to tt_biodatacube table to netcdf
+# field. Although the name of the field indicates that the files
+# should be in netcdf format, it is not really so. Nowadays original
+# CEL is loaded to the field."
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use DBI;
+require DBD::Oracle;
+
+use Term::ReadKey;
+use Getopt::Long;
+use Readonly;
+use English qw( -no_match_vars );
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+use ArrayExpress::Datafile::Affymetrix;
+
+Readonly my $MARGIN => " " x (length(date_now()) + 2);
+
+########
+# SUBS #
+########
+
+sub get_char_locator {
+
+ # Return a new lob locator for writing.
+
+ my ( $dbh, $badata_id ) = @_;
+
+ my $sth = $dbh->prepare(
+ "select bdc.netcdf from tt_biodatacube bdc, tt_bioassaydata bad, tt_identifiable i"
+ . " where i.id=bad.id and bad.biodatavalues_id=bdc.id and i.identifier=? for update",
+ { ora_auto_lob => 0 }
+ );
+
+ $sth->execute($badata_id) or die($sth->errstr);
+ my $char_locator = $sth->fetchrow_array();
+ $sth->finish();
+
+ unless ($char_locator) {
+ print STDOUT ("$MARGIN No record for ID $badata_id in database; skipping.\n");
+ }
+
+ return $char_locator;
+}
+
+########
+# MAIN #
+########
+
+my ($listfile, $dataformat);
+GetOptions(
+ "f|file=s" => \$listfile,
+ "d|dataformat=s" => \$dataformat,
+);
+
+unless ($listfile) {
+ print <<"USAGE";
+
+Usage: $0 -f <file listing IDs and CEL files>
+
+ N.B. File should contain two columns separated by tabs:
+
+ - BioAssayData ID
+ - CEL file name
+
+ Data format will typically be CELv3 or CELv4 and should be
+ automatically detected by the script. To override, use the option:
+
+ -d <data format>
+
+USAGE
+ exit 255;
+}
+
+open (my $list_fh, '<', $listfile) or
+ die("Can't open listing file $listfile: $!\n");
+
+print STDERR ("Username: ");
+chomp( my $username = <STDIN> );
+
+ReadMode 2;
+print STDERR ("Password: ");
+chomp( my $password = <STDIN> );
+ReadMode 0;
+print STDERR ("\n");
+
+# From Margus: "It is important that LongReadLen parameter is set big
+# while connecting to the db. I used even "LongReadLen => 100000000"
+# as some of the CELs where ~50MB."
+#
+# Note that we undertake to manage our commits ourselves.
+#
+my $dbh = DBI->connect(
+# "DBI:Oracle:host=progression.ebi.ac.uk;sid=AECUR;port=1521", # AECUR
+ $CONFIG->get_AE_DSN(), # AEPUB1, usually
+ $username,
+ $password,
+ {LongReadLen => 100_000_000, RaiseError => 1, AutoCommit => 0},
+) or die($DBI::errstr);
+
+# Line-buffered output (may be unnecessary).
+$| = 1;
+
+my $affy_factory = ArrayExpress::Datafile::Affymetrix->new();
+
+my $linenum=0;
+LINE:
+while (my $line = <$list_fh>) {
+ chomp $line;
+ $linenum++;
+ my ($badata_id, $celfile) = split /\t/, $line;
+ unless ($badata_id && $celfile) {
+ warn("Can't parse values in line $linenum; skipping.\n");
+ next LINE;
+ }
+
+ printf STDOUT ("[%s] Processing file %s for BioAssayData ID %s...\n", date_now(), $celfile, $badata_id);
+
+ # Open BLOB file for update.
+ open (my $cel_fh, '<', $celfile) or die("Can't open file $celfile: $!\n"); # FIXME not to die somehow?
+
+ my $celformat;
+ if ( $dataformat ) {
+ $celformat = $dataformat;
+ }
+ else {
+ my $cel = $affy_factory->make_parser( $celfile );
+ eval { $cel->parse_header() };
+ if ($EVAL_ERROR) {
+ print STDOUT ("$MARGIN Error parsing CEL header for $celfile (skipping): $EVAL_ERROR\n");
+ next LINE;
+ }
+ $celformat = "CELv" . $cel->get_version();
+ seek($cel_fh, 0, 0) or die("Error rewinding filehandle for file $celfile: $!");
+ }
+
+ # Arbitrary chunk size; benchmarked reasonably well. N.B. the
+ # tt_biodatacube table is set up with 8K records, so an 8K chunk
+ # size might be more appropriate, but any multiple of 8K is okay
+ # (16K just beat 8K in benchmarking).
+ my $chunk_size = 16384;
+
+ # Write file to "netcdf" record.
+ my $offset = 1; # Offsets start at 1, not 0
+ my $length = 0;
+ my $buffer = q{};
+
+ # Select BLOB for modification.
+ my $char_locator = get_char_locator( $dbh, $badata_id ) or next LINE;
+
+ # This loop reads in chunks and writes them to the relevant netcdf
+ # field, doing a commit on each chunk. This is to help avoid row
+ # locking on very long transactions.
+ while( $length = read( $cel_fh, $buffer, $chunk_size ) ) {
+ $dbh->ora_lob_write( $char_locator, $offset, $buffer );
+ $offset += $length;
+ print STDERR ".";
+ }
+
+ close($cel_fh);
+ print STDERR "\n";
+
+ # If the new BLOB in DB was smaller than the original, adjust the
+ # size of the BLOB field (i.e. delete rudiments from previous blob
+ # in the end of blob).
+ if ( $offset < $dbh->ora_lob_length( $char_locator ) ) {
+ $dbh->ora_lob_trim( $char_locator, $offset - 1 );
+ }
+
+ print STDOUT "$MARGIN File length: $offset bytes. Setting dataFormat to $celformat...\n";
+ my $sth = $dbh->prepare( # FIXME this could probably be more concise
+ "update tt_biodatacube set dataformat=? where id in"
+ . " (select bdc.id from tt_biodatacube bdc, tt_bioassaydata bad, tt_identifiable i"
+ . " where i.id=bad.id and bad.biodatavalues_id=bdc.id and i.identifier=?)"
+ );
+ $sth->execute($celformat,$badata_id) or die($sth->errstr);
+ $sth->finish();
+
+ # Commit between each file.
+ $dbh->commit();
+}
+
+printf STDOUT ("[%s] Done.\n", date_now() );
diff --git a/util/fillin_null_rows.pl b/util/fillin_null_rows.pl
new file mode 100755
index 0000000..7ba86eb
--- /dev/null
+++ b/util/fillin_null_rows.pl
@@ -0,0 +1,124 @@
+#!/usr/bin/env perl
+#
+# fillin_null_rows.pl
+
+# A script taking a list of data files, and rewriting them with added
+# null rows such that subsequently generated DesignElementDimensions
+# are all the same. The original files are untouched; new files are
+# created with a ".new" extension.
+
+# $Id: fillin_null_rows.pl 1861 2007-12-24 17:26:19Z tfrayner $
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+# Added to circumvent weird Exporter bug in 5.8.3 which caused
+# crashing when requiring ArrayExpress::Datafile (which in turn uses
+# ArrayExpress::Curator::Common).
+use ArrayExpress::Curator::Common;
+
+use IO::File;
+use ArrayExpress::Datafile;
+
+# Use a null byte char to delimit the index coords.
+my $tag_delim = qq{\x0};
+
+unless (@ARGV) {
+ print STDOUT (<<"USAGE");
+ Usage: $0 <list of data files>
+
+USAGE
+
+ exit 255;
+}
+
+my %coordinate;
+foreach my $filename (@ARGV) {
+
+ print STDERR ("Recording coordinates for file $filename...\n");
+
+ my $file = ArrayExpress::Datafile->new({
+ name => $filename,
+ is_dummy => 1,
+ });
+
+ my $input_fh = $file->get_filehandle();
+
+ # Figure out what kind of file we have, get column headings and indices
+ $file->parse_header($input_fh);
+
+ while (my $line = <$input_fh>){
+ my @larry = split /\t/, $line, -1;
+
+ $coordinate{ join($tag_delim, @larry[ @{ $file->get_index_columns() } ] ) }++;
+ }
+}
+
+foreach my $filename (@ARGV) {
+
+ print STDERR ("Rewriting file $filename...\n");
+
+ my $output_fh = IO::File->new( "$filename.full", '>' )
+ or die("Error opening output file $filename.full: $!\n");
+
+ my $file = ArrayExpress::Datafile->new({
+ name => $filename,
+ is_dummy => 1,
+ });
+
+ my $input_fh = $file->get_filehandle();
+
+ # Figure out what kind of file we have, get column headings and indices
+ $file->parse_header($input_fh);
+
+ my %index = map { $_ => 1 } @{ $file->get_index_columns() };
+ my @indexlist = @{ $file->get_index_columns() };
+
+ my @headings = @{$file->get_column_headings()};
+ my @harry;
+ for (my $i = 0; $i <= $#headings; $i++) {
+ push(@harry, $index{$i} ? $headings[shift(@indexlist)] : $headings[$i]);
+ }
+ print $output_fh (join("\t", @harry), "\n");
+
+ my %data;
+ while (my $line = <$input_fh>){
+
+ # Handle both unix and dos cleanly
+ $line =~ s/[\r\n]*$//;
+ my @larry = split /\t/, $line, -1;
+
+ $data{ join($tag_delim, @larry[ @{ $file->get_index_columns() } ] ) }
+ = \@larry;
+ }
+
+ foreach my $coord ( sort keys %coordinate ) {
+
+ my @coords = split /$tag_delim/, $coord;
+
+ if (my $row = $data{$coord}) {
+
+ my @larry;
+ for (my $i = 0; $i <= $#$row; $i++) {
+ push(@larry, $index{$i} ? shift(@coords) : $row->[$i]);
+ }
+
+ print $output_fh (
+ join("\t", @larry), "\n",
+ );
+ }
+ else { # empty row
+
+ my @larry;
+ for (my $i = 0; $i <= $#{ $file->get_column_headings() }; $i++) {
+ push(@larry, $index{$i} ? shift(@coords) : q{null});
+ }
+ print $output_fh (
+ join("\t", @larry), "\n",
+ );
+ }
+ }
+}
diff --git a/util/join_by_first_column.pl b/util/join_by_first_column.pl
new file mode 100755
index 0000000..88209b6
--- /dev/null
+++ b/util/join_by_first_column.pl
@@ -0,0 +1,122 @@
+#!/sw/arch/bin/perl
+#
+# Joins a series of data files using the first column as index.
+#
+# $Id: join_by_first_column.pl 1861 2007-12-24 17:26:19Z tfrayner $
+
+use strict;
+use warnings;
+
+unless (@ARGV) {
+ print STDOUT (<<"USAGE");
+ Usage: $0 list of data files > new_FGEM_file.txt
+
+USAGE
+
+ exit 255;
+}
+
+# %HoH stores the data:
+#
+# {$file1 => {$index1 => \@row, $index2 => \@row}, $file2 => {}...]
+#
+# where $index is the first-column index.
+my %HoH;
+
+# %common_indices simply aggregates all the first-column indices in
+# the files.
+my %common_indices;
+
+# $uniq is a random unique tag, can be anything; this is used to keep
+# track of duplicate first-column indices. Set to a null byte since
+# it's unlikely we'll encounter that in a data file.
+my $uniq = qq{\x0};
+
+foreach my $file (@ARGV) {
+
+ open( my $in_fh, "<$file" ) or die("$!\n");
+
+ my %store;
+ while ( my $line = <$in_fh> ) {
+
+ # DOS and unix line endings
+ $line =~ s/[\r\n]*$//;
+
+ my @larry = split /\t/, $line, -1;
+
+ foreach my $value (@larry) {
+
+ # Allow zero values.
+ if ( ! defined($value) || $value eq q{} ) {
+ $value = 'null';
+ }
+ }
+
+ my $index = shift(@larry);
+
+ while ( exists( $store{$index} ) ) {
+ $index .= $uniq;
+ }
+
+ $store{$index} = \@larry;
+
+ $common_indices{$index}++;
+ }
+
+ $HoH{$file} = \%store;
+
+}
+
+# Figure out the row array sizes
+my %max_row_sizes;
+foreach my $file (@ARGV) {
+
+ my $dataset = $HoH{$file};
+ my $max_row_size = [];
+ my @datarows = values %$dataset;
+ foreach my $newmax_row_size (@datarows) {
+ if ( $#$max_row_size < $#$newmax_row_size ) {
+ $max_row_size = $newmax_row_size;
+ }
+ }
+ $max_row_sizes{$file} = $max_row_size;
+}
+
+# Print out a header
+my @header = q{};
+foreach my $file ( sort keys %HoH ) {
+ push(@header, map { $file } @{ $max_row_sizes{$file} });
+}
+print STDOUT (join("\t", @header),"\n");
+
+# Print out the data.
+foreach my $index ( sort keys %common_indices ) {
+
+ my $orig_index = $index;
+
+ $orig_index =~ s/$uniq//g;
+
+ print STDOUT "$orig_index";
+
+ foreach my $file (@ARGV) {
+
+ my $dataset = $HoH{$file};
+
+ my $max_row_size = $max_row_sizes{$file};
+
+ my $datastr;
+
+ if ( $dataset->{$index} ) {
+ $datastr = join( "\t", @{ $dataset->{$index} } );
+ }
+
+ else {
+ $datastr = join( "\t", map {'null'} @$max_row_size );
+ }
+
+ print STDOUT ("\t$datastr");
+ }
+
+ print STDOUT "\n";
+
+}
diff --git a/util/mx_legacy_check.pl b/util/mx_legacy_check.pl
new file mode 100755
index 0000000..884eb03
--- /dev/null
+++ b/util/mx_legacy_check.pl
@@ -0,0 +1,155 @@
+#!/usr/bin/env perl
+#
+# Script to re-check legacy MX submissions, and enter the relevant
+# info into the autosubs database.
+#
+# $Id: mx_legacy_check.pl 497 2006-10-10 21:04:09Z tfrayner $
+
+use strict;
+use warnings;
+
+require ArrayExpress::Curator::MIAMExpress;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use DBI;
+use Getopt::Long;
+
+# Set up a global DB handle for MX; N.B. another handle will be
+# created for the checker instances (see MIAMExpress.pm).
+my $MX_DBH = DBI->connect(
+ $CONFIG->get_MX_DSN(), $CONFIG->get_MX_USERNAME(),
+ $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS(),
+ )
+ or die("Database connection error: $DBI::errstr\n");
+
+########
+# SUBS #
+########
+
+sub check_sub {
+
+ my $sub = shift;
+
+ # Quick sanity check.
+ unless ( $sub->name() && $sub->miamexpress_login() ) {
+ printf("Submission %s has incomplete mx title and/or login. Skipping.\n",
+ $sub->miamexpress_subid());
+ return;
+ }
+
+ printf(
+ "%s Checking experiment %s: %s | %s\n",
+ date_now(),
+ $sub->miamexpress_subid(),
+ $sub->miamexpress_login(),
+ $sub->name()
+ );
+
+ my $checker = ArrayExpress::Curator::MIAMExpress->new(
+ { mx_login => $sub->miamexpress_login(),
+ mx_title => $sub->name(),
+ skip_data_checks => 1,
+ log_to_current_dir => '',
+ clobber => 1,
+ }
+ );
+
+ eval { $checker->check() };
+
+ if ($@) {
+ $sub->set(
+ comment => $sub->comment() . "\n\nLegacy checks crashed: $@",
+ );
+ print("Checker CRASH!!\n");
+ }
+ else {
+ $sub->set(
+ miame_score => $checker->get_miame(),
+ data_warehouse_ready => $checker->get_aedw_score(),
+ );
+ }
+
+ my $sth = $MX_DBH->prepare(<<'QUERY');
+select TSUBMIS_LAST_CHANGE from TSUBMIS
+where TSUBMIS_SYSUID=? and TSUBMIS_DEL_STATUS='U'
+QUERY
+
+ $sth->execute( $sub->miamexpress_subid() ) or die( $sth->errstr );
+
+ my $results = $sth->fetchrow_arrayref();
+
+ if ($results) {
+
+ my $mx_date = $results->[0];
+
+ my @dateparts;
+ unless (
+ @dateparts = (
+ $mx_date
+ =~ m/\A (\d+)-(\d+)-(\d+) [ ]+ (\d+):(\d+):(\d+) \z/xms
+ )
+ ) {
+ die( "Error: Unable to parse date for submission "
+ . $sub->miamexpress_subid() );
+ }
+
+ my $date = join( '-', @dateparts[ 0 .. 2 ] ) . 'T'
+ . join( ':', @dateparts[ 3 .. 5 ] ) . 'Z';
+
+ $sub->set( date_submitted => $date, );
+ }
+
+ $sub->update();
+
+ return;
+}
+
+########
+# MAIN #
+########
+
+my $check_all;
+GetOptions( "a|all" => \$check_all );
+
+unless ( $check_all || scalar @ARGV ) {
+ print(<<"USAGE");
+ Usage: $0 -a (check all experiments in submissions database)
+ $0 <MX subids> (just check a handful)
+USAGE
+
+ exit 255;
+}
+
+if ($check_all) {
+
+ # Check all undeleted MX experiments.
+ print("Querying autosubs DB for all MX experiments...\n");
+
+ my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ is_deleted => 0,
+ experiment_type => 'MIAMExpress',
+ );
+ while ( my $sub = $expt_iterator->next() ) {
+ check_sub($sub);
+ }
+}
+else {
+
+ # Check only those experiments listed on the command line.
+ foreach my $subid (@ARGV) {
+ print("Querying autosubs DB for MX submission $subid...\n");
+ my $sub = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+ miamexpress_subid => $subid,
+ experiment_type => 'MIAMExpress',
+ is_deleted => 0,
+ );
+
+ if ( $sub ) {
+ check_sub($sub);
+ }
+ else {
+ print ("Error: cannot find subid $subid in database. Skipping.\n");
+ }
+ }
+}
diff --git a/util/mx_qt_update.pl b/util/mx_qt_update.pl
new file mode 100755
index 0000000..5f6212e
--- /dev/null
+++ b/util/mx_qt_update.pl
@@ -0,0 +1,94 @@
+#!/usr/bin/env perl
+#
+# mx_qt_update.pl
+#
+# Maintenance script to update a QT_list.txt file with QT information taken from a local MIAMExpress installation.
+#
+# Tim Rayner 2005 ArrayExpress Team, EBI
+#
+# $Id: mx_qt_update.pl 1269 2006-12-10 12:23:13Z tfrayner $
+#
+
+use strict;
+use warnings;
+
+# Next line is a placeholder. Uncomment and edit as necessary, or set PERL5LIB environmental variable
+# use lib /path/to/directory/containing/Curator/modules;
+
+use Getopt::Long;
+use DBI;
+use DBD::mysql;
+
+use ArrayExpress::Curator::Config qw($CONFIG);
+
+use ArrayExpress::Datafile::QT_list qw(
+ get_QTs
+ write_QTs
+);
+
+my $read_defaults;
+
+GetOptions( "d|defaults" => \$read_defaults, );
+
+my $new_QTs = {};
+
+if ($read_defaults) {
+ $new_QTs = get_QTs;
+}
+elsif (@ARGV) {
+ print STDOUT ("Parsing old QT file(s)... \n");
+ $new_QTs = get_QTs( \@ARGV );
+}
+
+print STDOUT ("Querying MIAMExpress for QT information... \n");
+
+my $dbh = DBI->connect(
+ $CONFIG->get_MX_DSN(), $CONFIG->get_MX_USERNAME(),
+ $CONFIG->get_MX_PASSWORD(), $CONFIG->get_MX_DBPARAMS()
+ )
+ or die("$DBI::errstr\n");
+
+# Pull in all undeleted QTs
+my $sth = $dbh->prepare(<<'QUERY');
+select distinct TQUANTIT_TYPE, TQUANTIT_NAME, TQUANTIT_SUBCLASS,
+TQUANTIT_ISBACKGROUND, TQUANTIT_CHANNEL, TQUANTIT_SCALE,
+TQUANTIT_DATATYPE, TQUANTIT_TARGETQT, TQUANTIT_NAMESPACE,
+TQUANTIT_DESCR
+from TQUANTIT
+where TQUANTIT_DEL_STATUS='U'
+QUERY
+
+$sth->execute() or die("$sth->errstr\n");
+
+# Get the relevant stuff out of $sth
+while ( my $rowref = $sth->fetchrow_hashref ) {
+
+ my $software = $rowref->{'TQUANTIT_TYPE'} . "["
+ . $rowref->{'TQUANTIT_NAMESPACE'} . "]";
+
+ $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{datatype}
+ = $rowref->{'TQUANTIT_DATATYPE'} || q{};
+
+ $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{scale}
+ = $rowref->{'TQUANTIT_SCALE'} || q{};
+
+ $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{subclass}
+ = $rowref->{'TQUANTIT_SUBCLASS'} || q{};
+
+ $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{is_background}
+ = $rowref->{'TQUANTIT_ISBACKGROUND'} || q{};
+
+ $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{confmap}
+ = $rowref->{'TQUANTIT_TARGETQT'} || q{};
+
+ $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{channel}
+ = $rowref->{'TQUANTIT_CHANNEL'} || q{};
+
+ $new_QTs->{$software}{ $rowref->{'TQUANTIT_NAME'} }{description}
+ = $rowref->{'TQUANTIT_DESCR'} || q{};
+}
+
+$sth->finish(); # dump the statement handler now that we are finished with it
+
+write_QTs( $new_QTs, \*STDOUT );
+
diff --git a/util/t2m_legacy_check.pl b/util/t2m_legacy_check.pl
new file mode 100755
index 0000000..b806e70
--- /dev/null
+++ b/util/t2m_legacy_check.pl
@@ -0,0 +1,138 @@
+#!/usr/bin/env perl
+#
+# Script to re-check legacy spreadsheet submissions, and enter the
+# relevant info into the autosubs database.
+#
+# $Id$
+
+use strict;
+use warnings;
+
+use ArrayExpress::Curator::Validate;
+use ArrayExpress::Curator::Config qw($CONFIG);
+use ArrayExpress::Curator::Common qw(date_now);
+require ArrayExpress::AutoSubmission::DB::Experiment;
+use Getopt::Long;
+
+########
+# SUBS #
+########
+
+sub check_sub {
+
+ my $sub = shift;
+
+ my $spreadsheet = $sub->spreadsheets(is_deleted => 0)->next();
+
+ # Quick sanity check.
+ unless ( $spreadsheet ) {
+ printf("Submission %s has no spreadsheet. Skipping.\n",
+ $sub->id());
+ return;
+ }
+
+ printf(
+ "%s Checking experiment %s: %s | %s\n",
+ date_now(),
+ $sub->id(),
+ $sub->user_id()->login(),
+ $sub->name()
+ );
+
+ my $checker = ArrayExpress::Curator::Validate->new(
+ { spreadsheet_filename => $spreadsheet->filesystem_path(),
+ source_directory => $sub->unpack_directory(),
+ skip_data_checks => 1,
+ log_to_current_dir => q{},
+ clobber => 1,
+ }
+ );
+
+ eval { $checker->check() };
+
+ if ($@) {
+ $sub->set(
+ comment => $sub->comment() . "\n\nLegacy checks crashed: $@",
+ );
+ print("Checker CRASH!!\n");
+ }
+ else {
+ $sub->set(
+ miame_score => $checker->get_miame(),
+ data_warehouse_ready => $checker->get_aedw_score(),
+ );
+ }
+
+ $sub->update();
+
+ return;
+}
+
+########
+# MAIN #
+########
+
+my ($check_all, $experiment_type);
+GetOptions(
+ "a|all" => \$check_all,
+ "t|type=s" => \$experiment_type,
+);
+
+unless ( $check_all || scalar @ARGV ) {
+ print(<<"USAGE");
+ Usage: $0 -a (check all experiments in submissions database)
+ $0 <sub ids> (just check a handful)
+
+ Optional: -t (specify experiment type, e.g. "Tab2MAGE", "GEO")
+
+USAGE
+
+ exit 255;
+}
+
+$experiment_type ||= 'Tab2MAGE';
+
+if ($check_all) {
+
+ # Check all undeleted spreadsheet experiments.
+ print("Querying autosubs DB for all $experiment_type spreadsheet experiments...\n");
+
+ my $expt_iterator = ArrayExpress::AutoSubmission::DB::Experiment->search(
+ is_deleted => 0,
+ experiment_type => $experiment_type,
+ );
+
+ EVERYEXPT:
+ while ( my $sub = $expt_iterator->next() ) {
+ if ( $sub->user_id()
+ && scalar( $sub->spreadsheets(is_deleted => 0) ) ) {
+ check_sub($sub);
+ }
+ }
+}
+else {
+
+ # Check only those experiments listed on the command line.
+ EACHEXPT:
+ foreach my $id (@ARGV) {
+ print("Querying autosubs DB for $experiment_type spreadsheet submission $id...\n");
+ my $sub = ArrayExpress::AutoSubmission::DB::Experiment->retrieve(
+ id => $id,
+ experiment_type => $experiment_type,
+ is_deleted => 0,
+ in_curation => 1,
+ );
+ if ( $sub ) {
+ unless ( $sub->user_id() ) {
+ print ("Submission $id has no associated user. Skipping.\n");
+ next EACHEXPT;
+ }
+
+ # Run the tests here.
+ check_sub($sub);
+ }
+ else {
+ print ("Error: cannot find submission with id $id in database. Skipping.\n");
+ }
+ }
+}
diff --git a/util/t2m_update_docs.pl b/util/t2m_update_docs.pl
new file mode 100755
index 0000000..239c9de
--- /dev/null
+++ b/util/t2m_update_docs.pl
@@ -0,0 +1,93 @@
+#!/usr/bin/env perl
+#
+# t2m_update_docs.pl
+#
+# Tim Rayner 2005 ArrayExpress Team, EBI
+#
+# $Id: t2m_update_docs.pl 2069 2008-06-04 14:33:52Z tfrayner $
+#
+
+use 5.8.0; # Need a recent Pod::HTML, which only comes with newer Perls
+
+use strict;
+use warnings;
+
+use Pod::Html;
+use File::Spec;
+
+foreach my $doc qw(
+ bin/expt_check.pl
+ bin/tab2mage.pl
+ bin/magetab.pl
+ bin/t2m_visualize.pl
+ bin/parse_affy.pl
+ lib/ArrayExpress/ArrayMAGE.pm
+ lib/ArrayExpress/AutoSubmission/DB.pm
+ lib/ArrayExpress/AutoSubmission/Creator.pm
+ lib/ArrayExpress/AutoSubmission/Spreadsheet.pm
+ lib/ArrayExpress/AutoSubmission/WebForm.pm
+ lib/ArrayExpress/Curator/Config.pm
+ lib/ArrayExpress/Curator/Common.pm
+ lib/ArrayExpress/Curator/Database.pm
+ lib/ArrayExpress/Curator/Report.pm
+ lib/ArrayExpress/Curator/Entrez_list.pm
+ lib/ArrayExpress/Curator/Visualize.pm
+ lib/ArrayExpress/Curator/ExperimentChecker.pm
+ lib/ArrayExpress/Curator/Logger.pm
+ lib/ArrayExpress/Curator/MAGE/Definitions.pm
+ lib/ArrayExpress/Curator/MIAMExpress.pm
+ lib/ArrayExpress/Curator/Standalone.pm
+ lib/ArrayExpress/Curator/Validate.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CDF/GDAC_CDF.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CDF/XDA_CDF.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CDF.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CDFFactory.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv3.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CEL/CELv4.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CEL.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CELFactory.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv12.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv13.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CHP/CHPv8.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CHP/GDAC_CHP.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CHP/XDA_CHP.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CHP.pm
+ lib/ArrayExpress/Datafile/Affymetrix/CHPFactory.pm
+ lib/ArrayExpress/Datafile/Affymetrix/EXP.pm
+ lib/ArrayExpress/Datafile/Affymetrix/EXPFactory.pm
+ lib/ArrayExpress/Datafile/Affymetrix/Parser.pm
+ lib/ArrayExpress/Datafile/Affymetrix.pm
+ lib/ArrayExpress/Datafile/Parser.pm
+ lib/ArrayExpress/Datafile/QT_list.pm
+ lib/ArrayExpress/Datafile/ArrayDesign.pm
+ lib/ArrayExpress/Datafile.pm
+ lib/ArrayExpress/MAGETAB.pm
+ lib/ArrayExpress/MAGETAB/Checker.pm
+ ) {
+
+ unless ( -f $doc ) {
+ die("Error: Input file not found: $doc\n");
+ }
+
+ my ( $directory, $file ) = ( File::Spec->splitpath($doc) )[ 1, 2 ];
+
+ my @dirs = File::Spec->splitdir($directory);
+
+ shift @dirs; # top-level dir is always stripped
+
+ my $htmldoc
+ = File::Spec->catfile( 'docs', @dirs, $file ); # docs subdir
+
+ $htmldoc =~ s!\.p[mlh]$!\.html!i; # replace extension
+
+ print "Creating $htmldoc\n";
+
+ pod2html(
+ "--infile=$doc", "--outfile=$htmldoc",
+ "--css=/docs/style.css", "--noindex",
+ "--htmldir=docs", "--podroot=lib",
+ );
+
+ system("tidy -m $htmldoc");
+
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/tab2mage.git
More information about the debian-med-commit
mailing list