From e863fcdd60b0a0766e1af270ef8bdd350212dc7d Mon Sep 17 00:00:00 2001 From: Chaoran Chen Date: Thu, 18 Jun 2026 18:13:43 +0200 Subject: [PATCH] Experiment --- .github/workflows/build-arm-images.yaml | 12 + .github/workflows/config-adapter-image.yml | 93 + .github/workflows/config-loader-image.yml | 93 + .github/workflows/integration-tests.yml | 11 + .github/workflows/update-argocd-metadata.yml | 14 + .github/workflows/website-image.yml | 11 +- .github/workflows/website-tests.yml | 9 +- AGENTS.md | 5 +- backend/README.md | 21 +- backend/docs/db/schema.sql | 322 ++ backend/import_local_test_config.py | 299 ++ .../org/loculus/backend/api/DataUseTerms.kt | 7 - .../org/loculus/backend/api/Organism.kt | 6 +- .../loculus/backend/auth/AuthenticatedUser.kt | 1 + .../backend/config/BackendSpringConfig.kt | 221 +- .../org/loculus/backend/config/Config.kt | 236 +- .../loculus/backend/config/InstanceConfig.kt | 112 + .../org/loculus/backend/config/OpenApi.kt | 50 +- .../backend/config/QueryOpenApiCustomizer.kt | 537 +++ .../loculus/backend/config/SecurityConfig.kt | 13 +- .../org/loculus/backend/config/WebConfig.kt | 1 + .../controller/AdminConfigController.kt | 314 ++ .../controller/PublicConfigController.kt | 147 + .../backend/config/dbtables/ConfigTables.kt | 106 + .../backend/config/operations/Operation.kt | 59 + .../config/operations/OperationDispatcher.kt | 49 + .../handlers/AddOptionalMetadataField.kt | 60 + .../operations/handlers/LinkOutHandlers.kt | 142 + .../handlers/ReorderMetadataFields.kt | 52 + .../handlers/SetInstanceBranding.kt | 63 + .../handlers/SetMetadataFieldDisplay.kt | 68 + .../operations/handlers/SetOrganismDisplay.kt | 61 + .../backend/config/service/AuditLogService.kt | 132 + .../backend/config/service/ConfigService.kt | 180 + .../backend/config/service/DraftService.kt | 484 +++ .../config/service/OrganismAdminService.kt | 96 + .../service/PreprocessingConfigService.kt | 94 + .../backend/controller/FilesController.kt | 5 +- .../backend/controller/InfoController.kt | 246 +- .../backend/controller/LapisAccessFilter.kt | 67 + .../controller/LapisProxyController.kt | 52 + .../backend/controller/LapisProxyService.kt | 97 + .../backend/controller/LapisUrlResolver.kt | 22 + .../controller/OpenApiSplitController.kt | 76 + .../backend/controller/QueryController.kt | 360 ++ .../controller/SubmissionController.kt | 14 +- .../backend/model/ReleasedDataModel.kt | 15 +- .../org/loculus/backend/model/SubmitModel.kt | 10 +- .../GenerateAccessionFromNumberService.kt | 13 +- .../DataUseTermsPreconditionValidator.kt | 9 +- .../debug/DeleteSequenceDataService.kt | 6 +- .../backend/service/files/S3Service.kt | 29 +- .../SeqSetCitationsDatabaseService.kt | 7 +- .../submission/CompressionDictService.kt | 189 +- .../submission/EmptyProcessedDataProvider.kt | 16 +- .../FileMappingPreconditionValidator.kt | 10 +- .../ProcessedMetadataPostprocessor.kt | 25 +- .../ProcessedSequenceEntryValidator.kt | 29 +- .../ProcessedSequencesPostprocessor.kt | 58 +- .../backend/utils/LocalDateJacksonModule.kt | 13 + .../resources/application-docker.properties | 6 +- .../src/main/resources/application.properties | 3 +- .../db/migration/V2.0__add_config_tables.sql | 121 + .../org/loculus/backend/SwaggerUiTest.kt | 69 + .../backend/config/AdminConfigEndpointTest.kt | 419 ++ .../backend/config/BackendSpringConfigTest.kt | 76 - .../config/InstanceConfigSerializationTest.kt | 92 + .../config/PreprocessingConfigEndpointTest.kt | 139 + .../config/PublicConfigEndpointTest.kt | 66 + .../config/QueryOpenApiCustomizerTest.kt | 100 + .../backend/config/fixtures/ConfigFixtures.kt | 140 + .../config/fixtures/ConfigFixturesTest.kt | 80 + .../config/operations/OperationHandlerTest.kt | 294 ++ .../controller/EndpointTestExtension.kt | 44 +- .../controller/ExceptionHandlerTest.kt | 4 + .../controller/OpenApiSplitControllerTest.kt | 59 + .../backend/controller/QueryControllerTest.kt | 270 ++ .../controller/RequestAuthorization.kt | 2 + .../admin/PipelineStatisticsEndpointTest.kt | 11 +- .../CompleteMultipartUploadEndpointTest.kt | 14 +- .../controller/files/GetFilesEndpointTest.kt | 16 +- .../RequestMultipartUploadEndpointTest.kt | 15 +- .../files/RequestUploadEndpointTest.kt | 15 +- .../ApproveProcessedDataEndpointTest.kt | 15 +- .../ExtractUnprocessedDataEndpointTest.kt | 11 +- ...sedDataDataUseTermsDisabledEndpointTest.kt | 20 +- .../submission/GetReleasedDataEndpointTest.kt | 40 +- .../GetReleasedDataFileSharingEndpointTest.kt | 15 +- .../submission/ReviseEndpointTest.kt | 16 +- .../submission/SubmissionConvenienceClient.kt | 10 +- .../SubmissionJourneyWithFilesTest.kt | 16 +- ...tEditedSequenceEntryVersionEndpointTest.kt | 15 +- .../SubmitEndpointDataUseTermsDisabledTest.kt | 24 +- .../SubmitEndpointFileSharingTest.kt | 22 +- .../SubmitEndpointMultipartFileSharingTest.kt | 22 +- .../submission/SubmitEndpointTest.kt | 28 +- .../SubmitProcessedDataEndpointTest.kt | 15 +- .../GenerateAccessionFromNumberServiceTest.kt | 33 +- .../ProcessedMetadataPostprocessorTest.kt | 49 +- .../ProcessedSequencesPostprocessorTest.kt | 60 +- .../service/files/EnabledS3ServiceTest.kt | 26 + .../submission/CompressionDictServiceTest.kt | 41 +- .../EmptyProcessedDataProviderTest.kt | 36 +- .../submission/ValidateFileNameTest.kt | 6 +- .../src/test/resources/application.properties | 3 +- .../src/test/resources/backend_config.json | 238 -- ...ackend_config_data_use_terms_disabled.json | 112 - .../src/test/resources/backend_config_s3.json | 272 -- .../backend_config_single_segment.json | 87 - .../data-use-terms-disabled/instance.yaml | 7 + .../organisms/dummyOrganism.yaml | 28 + .../resources/fixtures/default/instance.yaml | 7 + .../default/organisms/dummyOrganism.yaml | 29 + ...ummyOrganismWithoutConsensusSequences.yaml | 14 + .../default/organisms/otherOrganism.yaml | 25 + .../test/resources/fixtures/s3/instance.yaml | 7 + .../fixtures/s3/organisms/dummyOrganism.yaml | 39 + ...ummyOrganismWithoutConsensusSequences.yaml | 13 + .../fixtures/s3/organisms/otherOrganism.yaml | 25 + .../fixtures/single-segment/instance.yaml | 7 + .../organisms/dummyOrganism.yaml | 22 + backend/start_dev.sh | 3 +- build-local-images.sh | 210 + check_preview.sh | 126 + config-tools/Dockerfile.adapter | 37 + config-tools/Dockerfile.loader | 40 + config-tools/README.md | 176 + config-tools/package-lock.json | 2200 ++++++++++ config-tools/package.json | 29 + config-tools/src/adapter/cli.ts | 303 ++ config-tools/src/adapter/commonMetadata.ts | 211 + config-tools/src/adapter/lineageSystems.ts | 31 + .../src/adapter/overviewConfig.spec.ts | 168 + config-tools/src/adapter/overviewConfig.ts | 204 + config-tools/src/adapter/referenceGenomes.ts | 71 + config-tools/src/adapter/renderers.spec.ts | 253 ++ .../src/adapter/siloDatabaseConfig.ts | 92 + .../src/adapter/siloPreprocessingConfig.ts | 26 + config-tools/src/index.ts | 3 + config-tools/src/loader/adminClient.ts | 188 + config-tools/src/loader/cli.ts | 151 + config-tools/src/loader/compare.ts | 34 + config-tools/src/loader/fixtures.ts | 137 + config-tools/src/loader/publish.spec.ts | 234 ++ config-tools/src/loader/publish.ts | 247 ++ config-tools/src/schema/adminApi.ts | 110 + config-tools/src/schema/canonicalConfig.ts | 340 ++ config-tools/tsconfig.json | 27 + config-tools/vitest.config.ts | 8 + deploy.py | 42 +- docs/astro.config.mjs | 10 + .../build-new-preprocessing-pipeline.md | 23 +- .../configuration-system.md | 53 + .../configure-pipeline-admin-panel.md | 49 + .../configuring-extra-files.md | 4 + .../for-administrators/data-use-terms.mdx | 7 + .../existing-preprocessing-pipelines.md | 10 +- .../for-administrators/getting-started.md | 2 +- .../for-administrators/local-dev-instance.md | 308 ++ .../managing-configuration.md | 284 ++ .../for-administrators/my-first-loculus.md | 120 +- .../for-administrators/pipeline-concept.md | 6 +- .../rolling-out-organism-config.md | 96 + .../for-administrators/sequence-reporting.md | 6 +- .../setup-with-k3d-and-nginx.mdx | 50 +- .../setup-with-kubernetes.md | 10 +- .../for-administrators/user-administration.md | 10 + docs/src/content/docs/reference/glossary.md | 20 +- .../docs/reference/helm-chart-config.mdx | 21 +- documentation-experimental-features/README.md | 10 + .../api-structure/01_introductionAndGoals.md | 28 + .../api-structure/02_constraints.md | 15 + .../api-structure/03_contextAndScope.md | 27 + .../api-structure/04_solutionStrategy.md | 10 + .../api-structure/05_buildingBlockView.md | 30 + .../api-structure/06_runtimeView.md | 18 + .../api-structure/07_deploymentView.md | 20 + .../api-structure/08_crosscuttingConcepts.md | 22 + .../09_architecturalDecisions.md | 38 + .../api-structure/10_qualityRequirements.md | 11 + .../api-structure/11_risksAndTechnicalDebt.md | 8 + .../api-structure/12_glossary.md | 10 + .../api-structure/13_databaseSchema.md | 12 + .../api-structure/14_configSchema.md | 25 + .../01_introductionAndGoals.md | 39 + .../02_constraints.md | 33 + .../03_contextAndScope.md | 93 + .../04_solutionStrategy.md | 14 + .../05_buildingBlockView.md | 114 + .../06_runtimeView.md | 80 + .../07_deploymentView.md | 101 + .../08_crosscuttingConcepts.md | 68 + .../09_architecturalDecisions.md | 140 + .../10_qualityRequirements.md | 11 + .../11_risksAndTechnicalDebt.md | 30 + .../configuration-management/12_glossary.md | 26 + .../13_databaseSchema.md | 190 + .../14_configSchema.md | 201 + .../views/01_introductionAndGoals.md | 24 + .../views/02_constraints.md | 10 + .../views/03_contextAndScope.md | 28 + .../views/04_solutionStrategy.md | 10 + .../views/05_buildingBlockView.md | 36 + .../views/06_runtimeView.md | 16 + .../views/07_deploymentView.md | 17 + .../views/08_crosscuttingConcepts.md | 25 + .../views/09_architecturalDecisions.md | 43 + .../views/10_qualityRequirements.md | 11 + .../views/11_risksAndTechnicalDebt.md | 10 + .../views/12_glossary.md | 12 + .../views/13_databaseSchema.md | 18 + .../views/14_configSchema.md | 48 + .../features/annotations.dependent.spec.ts | 73 +- .../specs/features/api-documentation.spec.ts | 140 + .../tests/specs/features/views.spec.ts | 120 + kubernetes/config-processor/README.md | 13 +- .../config-processor/config-processor.py | 66 +- kubernetes/config-processor/requirements.txt | 2 +- kubernetes/loculus/fixtures/README.md | 54 + kubernetes/loculus/fixtures/instance.yaml | 1972 +++++++++ .../fixtures/organisms/cchf-multi-ref.yaml | 1692 ++++++++ .../loculus/fixtures/organisms/cchf.yaml | 1584 ++++++++ .../organisms/dummy-organism-with-files.yaml | 117 + .../fixtures/organisms/dummy-organism.yaml | 169 + .../fixtures/organisms/ebola-sudan.yaml | 1561 ++++++++ .../fixtures/organisms/enteroviruses.yaml | 1793 +++++++++ .../organisms/not-aligned-organism.yaml | 102 + .../loculus/fixtures/organisms/west-nile.yaml | 1620 ++++++++ .../loculus/fixtures/preprocessing/README.md | 72 + .../preprocessing/cchf-multi-ref/1.yaml | 1191 ++++++ .../fixtures/preprocessing/cchf/1.yaml | 1140 ++++++ .../fixtures/preprocessing/ebola-sudan/1.yaml | 916 +++++ .../fixtures/preprocessing/ebola-sudan/2.yaml | 916 +++++ .../preprocessing/enteroviruses/1.yaml | 1002 +++++ .../preprocessing/not-aligned-organism/4.yaml | 65 + .../fixtures/preprocessing/west-nile/1.yaml | 945 +++++ .../loculus/templates/_common-metadata.tpl | 407 +- .../loculus/templates/_config-adapter.tpl | 93 + .../loculus/templates/_config-processor.tpl | 5 + .../_lineage-system-for-organism.tpl | 11 - .../templates/_merged-reference-genomes.tpl | 51 - .../templates/_preprocessingFromValues.tpl | 88 - .../loculus/templates/_siloDatabaseConfig.tpl | 46 - .../templates/config-loader-fixtures.yaml | 32 + .../loculus/templates/config-loader-job.yaml | 145 + kubernetes/loculus/templates/ingest.yaml | 19 +- .../templates/keycloak-config-map.yaml | 56 + .../loculus/templates/lapis-deployment.yaml | 15 +- .../templates/lapis-silo-database-config.yaml | 41 - .../loculus/templates/loculus-backend.yaml | 4 +- .../loculus-preprocessing-config.yaml | 31 - .../loculus-preprocessing-deployment.yaml | 11 +- .../templates/loculus-website-config.yaml | 7 +- .../templates/overview-lapis-deployment.yaml | 102 + .../templates/overview-silo-deployment.yaml | 164 + .../loculus/templates/seed-preview-data.yaml | 269 ++ .../loculus/templates/silo-deployment.yaml | 38 +- kubernetes/loculus/test-data | 1 + kubernetes/loculus/values.schema.json | 480 +-- kubernetes/loculus/values.yaml | 451 +-- kubernetes/loculus/values_e2e_and_dev.yaml | 3 + kubernetes/loculus/values_local_images.yaml | 25 + kubernetes/loculus/values_preview_server.yaml | 3 + loculus-silo/README.md | 2 +- loculus-silo/pyproject.toml | 2 + loculus-silo/src/silo_import/__main__.py | 23 +- loculus-silo/src/silo_import/config.py | 51 +- loculus-silo/src/silo_import/overview.py | 603 +++ loculus-silo/tests/test_config.py | 36 +- loculus-silo/tests/test_overview.py | 418 ++ preprocessing/dummy/main.py | 10 + .../src/loculus_preprocessing/backend.py | 6 +- .../src/loculus_preprocessing/config.py | 90 +- .../nextclade/tests/test_config_fetch.py | 130 + preview_config_experiment.sh | 194 + test-data/ebola-sudan-metadata.tsv | 16 + test-data/ebola-sudan-sequences.fasta | 3555 +++++++++++++++++ test-data/west-nile-metadata.tsv | 16 + test-data/west-nile-sequences.fasta | 1950 +++++++++ website/Dockerfile | 25 +- website/eslint.config.js | 2 +- website/src/components/Edit/EditPage.spec.tsx | 3 +- .../components/Navigation/Navigation.astro | 14 +- .../components/Navigation/SandwichMenu.tsx | 13 +- .../src/components/SearchPage/SearchForm.tsx | 2 +- .../components/SearchPage/SearchFullUI.tsx | 37 +- website/src/components/SearchPage/Table.tsx | 14 +- .../SearchPage/fields/FieldProps.tsx | 2 +- .../SearchPage/fields/NormalTextField.tsx | 2 +- .../components/SeqSetCitations/SeqSetItem.tsx | 3 + .../SeqSetRecordsTableWithMetadata.tsx | 20 +- .../SequenceDetailsPage/getTableData.spec.ts | 4 +- .../FileUpload/FolderUploadComponent.spec.tsx | 2 +- website/src/components/User/GroupPage.tsx | 14 +- .../components/admin/AuditLogTable.spec.tsx | 81 + .../src/components/admin/AuditLogTable.tsx | 134 + .../src/components/admin/ConfirmDialog.tsx | 52 + .../components/admin/CreateOrganismDialog.tsx | 156 + .../components/admin/DraftStatusBanner.tsx | 60 + .../admin/EditOrganismSection.spec.tsx | 212 + .../components/admin/EditOrganismSection.tsx | 187 + .../admin/InstanceConfigEditor.spec.tsx | 53 + .../components/admin/InstanceConfigEditor.tsx | 432 ++ website/src/components/admin/JsonViewer.tsx | 44 + .../admin/MarkOrganismDeployedButton.tsx | 62 + .../admin/OrganismDocumentEditor.tsx | 189 + .../src/components/admin/PendingOpsList.tsx | 28 + .../admin/PreprocessingConfigEditor.spec.tsx | 125 + .../admin/PreprocessingConfigEditor.tsx | 215 + .../components/admin/PublishModal.spec.tsx | 31 + website/src/components/admin/PublishModal.tsx | 146 + .../forms/AddOptionalMetadataFieldForm.tsx | 83 + .../components/admin/forms/LinkOutsForm.tsx | 150 + .../admin/forms/MetadataFieldsManager.tsx | 235 ++ .../admin/forms/SetOrganismDisplayForm.tsx | 134 + website/src/components/admin/forms/types.ts | 7 + website/src/config.spec.ts | 31 +- website/src/config.ts | 126 +- website/src/env.d.ts | 2 + website/src/hooks/useUnsavedGuard.ts | 23 + website/src/layouts/AdminConfigLayout.astro | 110 + website/src/middleware.ts | 19 +- .../middleware/adminRoleMiddleware.spec.ts | 49 + website/src/middleware/adminRoleMiddleware.ts | 22 + website/src/middleware/authMiddleware.ts | 6 +- website/src/middleware/configMiddleware.ts | 66 + .../submissionPagesDisablingMiddleware.ts | 17 +- website/src/pages/admin/config/audit.astro | 29 + website/src/pages/admin/config/index.astro | 3 + website/src/pages/admin/config/instance.astro | 40 + .../pages/admin/config/instance/history.astro | 77 + .../admin/config/organisms/[key]/draft.astro | 70 + .../admin/config/organisms/[key]/edit.astro | 113 + .../organisms/[key]/edit/add-field.astro | 26 + .../config/organisms/[key]/edit/display.astro | 27 + .../organisms/[key]/edit/linkouts.astro | 26 + .../organisms/[key]/edit/metadata.astro | 27 + .../organisms/[key]/edit/preprocessing.astro | 46 + .../config/organisms/[key]/history.astro | 94 + .../admin/config/organisms/[key]/json.astro | 106 + .../pages/admin/config/organisms/index.astro | 199 + website/src/pages/admin/dashboard.astro | 2 +- .../src/pages/api-documentation/index.astro | 160 +- website/src/pages/index.astro | 47 +- website/src/pages/loculus-info/index.ts | 4 +- website/src/pages/overview/index.astro | 60 + .../pages/seqsets/[seqSetId].[version].astro | 1 + website/src/pages/views/[view]/index.astro | 57 + website/src/routes/navigationItems.ts | 29 +- website/src/routes/routes.ts | 2 + website/src/serverWebsiteConfigStore.ts | 28 + .../src/services/adminConfigClient.spec.ts | 188 + website/src/services/adminConfigClient.ts | 304 ++ website/src/services/configClient.ts | 59 + website/src/services/configTransform.spec.ts | 289 ++ website/src/services/configTransform.ts | 272 ++ website/src/types/config.ts | 14 + website/src/types/loculusConfig.ts | 3 + website/src/types/runtimeConfig.ts | 1 - website/src/utils/extractRealmRoles.spec.ts | 36 + website/src/utils/extractRealmRoles.ts | 17 + website/src/utils/loadOrganismEditDraft.ts | 69 + .../shouldMiddlewareEnforceLogin.spec.ts | 3 + .../src/utils/shouldMiddlewareEnforceLogin.ts | 6 +- website/tsconfig.json | 2 +- website/vitest.setup.ts | 64 +- 366 files changed, 47475 insertions(+), 3175 deletions(-) create mode 100644 .github/workflows/config-adapter-image.yml create mode 100644 .github/workflows/config-loader-image.yml create mode 100755 backend/import_local_test_config.py create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/InstanceConfig.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/QueryOpenApiCustomizer.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/controller/AdminConfigController.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/controller/PublicConfigController.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/dbtables/ConfigTables.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/Operation.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/OperationDispatcher.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/AddOptionalMetadataField.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/LinkOutHandlers.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/ReorderMetadataFields.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetInstanceBranding.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetMetadataFieldDisplay.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetOrganismDisplay.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/service/AuditLogService.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/service/ConfigService.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/service/DraftService.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/service/OrganismAdminService.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/config/service/PreprocessingConfigService.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/controller/LapisAccessFilter.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyController.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyService.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/controller/LapisUrlResolver.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/controller/OpenApiSplitController.kt create mode 100644 backend/src/main/kotlin/org/loculus/backend/controller/QueryController.kt create mode 100644 backend/src/main/resources/db/migration/V2.0__add_config_tables.sql create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/AdminConfigEndpointTest.kt delete mode 100644 backend/src/test/kotlin/org/loculus/backend/config/BackendSpringConfigTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/InstanceConfigSerializationTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/PreprocessingConfigEndpointTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/PublicConfigEndpointTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/QueryOpenApiCustomizerTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixtures.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixturesTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/config/operations/OperationHandlerTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/controller/OpenApiSplitControllerTest.kt create mode 100644 backend/src/test/kotlin/org/loculus/backend/controller/QueryControllerTest.kt delete mode 100644 backend/src/test/resources/backend_config.json delete mode 100644 backend/src/test/resources/backend_config_data_use_terms_disabled.json delete mode 100644 backend/src/test/resources/backend_config_s3.json delete mode 100644 backend/src/test/resources/backend_config_single_segment.json create mode 100644 backend/src/test/resources/fixtures/data-use-terms-disabled/instance.yaml create mode 100644 backend/src/test/resources/fixtures/data-use-terms-disabled/organisms/dummyOrganism.yaml create mode 100644 backend/src/test/resources/fixtures/default/instance.yaml create mode 100644 backend/src/test/resources/fixtures/default/organisms/dummyOrganism.yaml create mode 100644 backend/src/test/resources/fixtures/default/organisms/dummyOrganismWithoutConsensusSequences.yaml create mode 100644 backend/src/test/resources/fixtures/default/organisms/otherOrganism.yaml create mode 100644 backend/src/test/resources/fixtures/s3/instance.yaml create mode 100644 backend/src/test/resources/fixtures/s3/organisms/dummyOrganism.yaml create mode 100644 backend/src/test/resources/fixtures/s3/organisms/dummyOrganismWithoutConsensusSequences.yaml create mode 100644 backend/src/test/resources/fixtures/s3/organisms/otherOrganism.yaml create mode 100644 backend/src/test/resources/fixtures/single-segment/instance.yaml create mode 100644 backend/src/test/resources/fixtures/single-segment/organisms/dummyOrganism.yaml create mode 100755 build-local-images.sh create mode 100755 check_preview.sh create mode 100644 config-tools/Dockerfile.adapter create mode 100644 config-tools/Dockerfile.loader create mode 100644 config-tools/README.md create mode 100644 config-tools/package-lock.json create mode 100644 config-tools/package.json create mode 100644 config-tools/src/adapter/cli.ts create mode 100644 config-tools/src/adapter/commonMetadata.ts create mode 100644 config-tools/src/adapter/lineageSystems.ts create mode 100644 config-tools/src/adapter/overviewConfig.spec.ts create mode 100644 config-tools/src/adapter/overviewConfig.ts create mode 100644 config-tools/src/adapter/referenceGenomes.ts create mode 100644 config-tools/src/adapter/renderers.spec.ts create mode 100644 config-tools/src/adapter/siloDatabaseConfig.ts create mode 100644 config-tools/src/adapter/siloPreprocessingConfig.ts create mode 100644 config-tools/src/index.ts create mode 100644 config-tools/src/loader/adminClient.ts create mode 100644 config-tools/src/loader/cli.ts create mode 100644 config-tools/src/loader/compare.ts create mode 100644 config-tools/src/loader/fixtures.ts create mode 100644 config-tools/src/loader/publish.spec.ts create mode 100644 config-tools/src/loader/publish.ts create mode 100644 config-tools/src/schema/adminApi.ts create mode 100644 config-tools/src/schema/canonicalConfig.ts create mode 100644 config-tools/tsconfig.json create mode 100644 config-tools/vitest.config.ts create mode 100644 docs/src/content/docs/for-administrators/configuration-system.md create mode 100644 docs/src/content/docs/for-administrators/configure-pipeline-admin-panel.md create mode 100644 docs/src/content/docs/for-administrators/local-dev-instance.md create mode 100644 docs/src/content/docs/for-administrators/managing-configuration.md create mode 100644 docs/src/content/docs/for-administrators/rolling-out-organism-config.md create mode 100644 documentation-experimental-features/README.md create mode 100644 documentation-experimental-features/api-structure/01_introductionAndGoals.md create mode 100644 documentation-experimental-features/api-structure/02_constraints.md create mode 100644 documentation-experimental-features/api-structure/03_contextAndScope.md create mode 100644 documentation-experimental-features/api-structure/04_solutionStrategy.md create mode 100644 documentation-experimental-features/api-structure/05_buildingBlockView.md create mode 100644 documentation-experimental-features/api-structure/06_runtimeView.md create mode 100644 documentation-experimental-features/api-structure/07_deploymentView.md create mode 100644 documentation-experimental-features/api-structure/08_crosscuttingConcepts.md create mode 100644 documentation-experimental-features/api-structure/09_architecturalDecisions.md create mode 100644 documentation-experimental-features/api-structure/10_qualityRequirements.md create mode 100644 documentation-experimental-features/api-structure/11_risksAndTechnicalDebt.md create mode 100644 documentation-experimental-features/api-structure/12_glossary.md create mode 100644 documentation-experimental-features/api-structure/13_databaseSchema.md create mode 100644 documentation-experimental-features/api-structure/14_configSchema.md create mode 100644 documentation-experimental-features/configuration-management/01_introductionAndGoals.md create mode 100644 documentation-experimental-features/configuration-management/02_constraints.md create mode 100644 documentation-experimental-features/configuration-management/03_contextAndScope.md create mode 100644 documentation-experimental-features/configuration-management/04_solutionStrategy.md create mode 100644 documentation-experimental-features/configuration-management/05_buildingBlockView.md create mode 100644 documentation-experimental-features/configuration-management/06_runtimeView.md create mode 100644 documentation-experimental-features/configuration-management/07_deploymentView.md create mode 100644 documentation-experimental-features/configuration-management/08_crosscuttingConcepts.md create mode 100644 documentation-experimental-features/configuration-management/09_architecturalDecisions.md create mode 100644 documentation-experimental-features/configuration-management/10_qualityRequirements.md create mode 100644 documentation-experimental-features/configuration-management/11_risksAndTechnicalDebt.md create mode 100644 documentation-experimental-features/configuration-management/12_glossary.md create mode 100644 documentation-experimental-features/configuration-management/13_databaseSchema.md create mode 100644 documentation-experimental-features/configuration-management/14_configSchema.md create mode 100644 documentation-experimental-features/views/01_introductionAndGoals.md create mode 100644 documentation-experimental-features/views/02_constraints.md create mode 100644 documentation-experimental-features/views/03_contextAndScope.md create mode 100644 documentation-experimental-features/views/04_solutionStrategy.md create mode 100644 documentation-experimental-features/views/05_buildingBlockView.md create mode 100644 documentation-experimental-features/views/06_runtimeView.md create mode 100644 documentation-experimental-features/views/07_deploymentView.md create mode 100644 documentation-experimental-features/views/08_crosscuttingConcepts.md create mode 100644 documentation-experimental-features/views/09_architecturalDecisions.md create mode 100644 documentation-experimental-features/views/10_qualityRequirements.md create mode 100644 documentation-experimental-features/views/11_risksAndTechnicalDebt.md create mode 100644 documentation-experimental-features/views/12_glossary.md create mode 100644 documentation-experimental-features/views/13_databaseSchema.md create mode 100644 documentation-experimental-features/views/14_configSchema.md create mode 100644 integration-tests/tests/specs/features/api-documentation.spec.ts create mode 100644 integration-tests/tests/specs/features/views.spec.ts create mode 100644 kubernetes/loculus/fixtures/README.md create mode 100644 kubernetes/loculus/fixtures/instance.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/cchf-multi-ref.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/cchf.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/dummy-organism-with-files.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/dummy-organism.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/ebola-sudan.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/enteroviruses.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/not-aligned-organism.yaml create mode 100644 kubernetes/loculus/fixtures/organisms/west-nile.yaml create mode 100644 kubernetes/loculus/fixtures/preprocessing/README.md create mode 100644 kubernetes/loculus/fixtures/preprocessing/cchf-multi-ref/1.yaml create mode 100644 kubernetes/loculus/fixtures/preprocessing/cchf/1.yaml create mode 100644 kubernetes/loculus/fixtures/preprocessing/ebola-sudan/1.yaml create mode 100644 kubernetes/loculus/fixtures/preprocessing/ebola-sudan/2.yaml create mode 100644 kubernetes/loculus/fixtures/preprocessing/enteroviruses/1.yaml create mode 100644 kubernetes/loculus/fixtures/preprocessing/not-aligned-organism/4.yaml create mode 100644 kubernetes/loculus/fixtures/preprocessing/west-nile/1.yaml create mode 100644 kubernetes/loculus/templates/_config-adapter.tpl delete mode 100644 kubernetes/loculus/templates/_lineage-system-for-organism.tpl delete mode 100644 kubernetes/loculus/templates/_preprocessingFromValues.tpl delete mode 100644 kubernetes/loculus/templates/_siloDatabaseConfig.tpl create mode 100644 kubernetes/loculus/templates/config-loader-fixtures.yaml create mode 100644 kubernetes/loculus/templates/config-loader-job.yaml delete mode 100644 kubernetes/loculus/templates/lapis-silo-database-config.yaml delete mode 100644 kubernetes/loculus/templates/loculus-preprocessing-config.yaml create mode 100644 kubernetes/loculus/templates/overview-lapis-deployment.yaml create mode 100644 kubernetes/loculus/templates/overview-silo-deployment.yaml create mode 100644 kubernetes/loculus/templates/seed-preview-data.yaml create mode 120000 kubernetes/loculus/test-data create mode 100644 kubernetes/loculus/values_local_images.yaml create mode 100644 loculus-silo/src/silo_import/overview.py create mode 100644 loculus-silo/tests/test_overview.py create mode 100644 preprocessing/nextclade/tests/test_config_fetch.py create mode 100755 preview_config_experiment.sh create mode 100644 test-data/ebola-sudan-metadata.tsv create mode 100644 test-data/ebola-sudan-sequences.fasta create mode 100644 test-data/west-nile-metadata.tsv create mode 100644 test-data/west-nile-sequences.fasta create mode 100644 website/src/components/admin/AuditLogTable.spec.tsx create mode 100644 website/src/components/admin/AuditLogTable.tsx create mode 100644 website/src/components/admin/ConfirmDialog.tsx create mode 100644 website/src/components/admin/CreateOrganismDialog.tsx create mode 100644 website/src/components/admin/DraftStatusBanner.tsx create mode 100644 website/src/components/admin/EditOrganismSection.spec.tsx create mode 100644 website/src/components/admin/EditOrganismSection.tsx create mode 100644 website/src/components/admin/InstanceConfigEditor.spec.tsx create mode 100644 website/src/components/admin/InstanceConfigEditor.tsx create mode 100644 website/src/components/admin/JsonViewer.tsx create mode 100644 website/src/components/admin/MarkOrganismDeployedButton.tsx create mode 100644 website/src/components/admin/OrganismDocumentEditor.tsx create mode 100644 website/src/components/admin/PendingOpsList.tsx create mode 100644 website/src/components/admin/PreprocessingConfigEditor.spec.tsx create mode 100644 website/src/components/admin/PreprocessingConfigEditor.tsx create mode 100644 website/src/components/admin/PublishModal.spec.tsx create mode 100644 website/src/components/admin/PublishModal.tsx create mode 100644 website/src/components/admin/forms/AddOptionalMetadataFieldForm.tsx create mode 100644 website/src/components/admin/forms/LinkOutsForm.tsx create mode 100644 website/src/components/admin/forms/MetadataFieldsManager.tsx create mode 100644 website/src/components/admin/forms/SetOrganismDisplayForm.tsx create mode 100644 website/src/components/admin/forms/types.ts create mode 100644 website/src/hooks/useUnsavedGuard.ts create mode 100644 website/src/layouts/AdminConfigLayout.astro create mode 100644 website/src/middleware/adminRoleMiddleware.spec.ts create mode 100644 website/src/middleware/adminRoleMiddleware.ts create mode 100644 website/src/middleware/configMiddleware.ts create mode 100644 website/src/pages/admin/config/audit.astro create mode 100644 website/src/pages/admin/config/index.astro create mode 100644 website/src/pages/admin/config/instance.astro create mode 100644 website/src/pages/admin/config/instance/history.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/draft.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/edit.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/edit/add-field.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/edit/display.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/edit/linkouts.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/edit/metadata.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/edit/preprocessing.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/history.astro create mode 100644 website/src/pages/admin/config/organisms/[key]/json.astro create mode 100644 website/src/pages/admin/config/organisms/index.astro create mode 100644 website/src/pages/overview/index.astro create mode 100644 website/src/pages/views/[view]/index.astro create mode 100644 website/src/serverWebsiteConfigStore.ts create mode 100644 website/src/services/adminConfigClient.spec.ts create mode 100644 website/src/services/adminConfigClient.ts create mode 100644 website/src/services/configClient.ts create mode 100644 website/src/services/configTransform.spec.ts create mode 100644 website/src/services/configTransform.ts create mode 100644 website/src/types/loculusConfig.ts create mode 100644 website/src/utils/extractRealmRoles.spec.ts create mode 100644 website/src/utils/extractRealmRoles.ts create mode 100644 website/src/utils/loadOrganismEditDraft.ts diff --git a/.github/workflows/build-arm-images.yaml b/.github/workflows/build-arm-images.yaml index 7bf61f20c4..20cd457fcd 100644 --- a/.github/workflows/build-arm-images.yaml +++ b/.github/workflows/build-arm-images.yaml @@ -75,3 +75,15 @@ jobs: uses: ./.github/workflows/preprocessing-nextclade-image.yml with: build_arm: true + trigger-config-loader: + needs: should-build + if: needs.should-build.outputs.should_run == 'true' + uses: ./.github/workflows/config-loader-image.yml + with: + build_arm: true + trigger-config-adapter: + needs: should-build + if: needs.should-build.outputs.should_run == 'true' + uses: ./.github/workflows/config-adapter-image.yml + with: + build_arm: true diff --git a/.github/workflows/config-adapter-image.yml b/.github/workflows/config-adapter-image.yml new file mode 100644 index 0000000000..ed67ce41c9 --- /dev/null +++ b/.github/workflows/config-adapter-image.yml @@ -0,0 +1,93 @@ +name: config-adapter-image +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + inputs: + build_arm: + type: boolean + description: "Build for ARM as well" + default: false + required: false + workflow_call: + inputs: + build_arm: + type: string + description: "Build for ARM as well" + default: "false" + required: true +env: + DOCKER_IMAGE_NAME: ghcr.io/loculus-project/config-adapter + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + BUILD_ARM: ${{ github.event.inputs.build_arm || inputs.build_arm || github.ref == 'refs/heads/main' }} + sha: ${{ github.event.pull_request.head.sha || github.sha }} +concurrency: + group: ci-${{ github.ref == 'refs/heads/main' && github.run_id || github.ref }}-config-adapter + cancel-in-progress: true +jobs: + config-adapter-image: + name: Build Config Adapter Docker Image + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + packages: write + contents: read + checks: read + steps: + - name: Shorten sha + run: echo "sha=${sha::7}" >> $GITHUB_ENV + - uses: actions/checkout@v6 + - name: Add filename hash to environment + run: | + find config-tools -type f -not -path 'config-tools/node_modules/*' -print | sort | sha256sum > config-tools/filename_hash + cat config-tools/filename_hash + - name: Generate files hash + id: files-hash + run: | + DIR_HASH=$(echo -n ${{ hashFiles('config-tools/**', '.github/workflows/config-adapter-image.yml') }}) + echo "DIR_HASH=$DIR_HASH${{ env.BUILD_ARM == 'true' && '-arm' || '' }}" >> $GITHUB_ENV + rm config-tools/filename_hash + - name: Setup Docker metadata + id: dockerMetadata + uses: docker/metadata-action@v6 + with: + images: ${{ env.DOCKER_IMAGE_NAME }} + tags: | + type=raw,value=${{ env.DIR_HASH }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=commit-${{ env.sha }} + type=raw,value=${{ env.BRANCH_NAME }} + type=raw,value=${{ env.BRANCH_NAME }}-arm,enable=${{ env.BUILD_ARM }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Check if image exists + id: check-image + run: | + EXISTS=$(docker manifest inspect ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DIR_HASH }} > /dev/null 2>&1 && echo "true" || echo "false") + echo "CACHE_HIT=$EXISTS" >> $GITHUB_ENV + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Build and push image if input files changed + if: env.CACHE_HIT == 'false' + uses: docker/build-push-action@v7 + with: + context: ./config-tools + file: ./config-tools/Dockerfile.adapter + push: true + tags: ${{ steps.dockerMetadata.outputs.tags }} + cache-from: type=gha,scope=config-adapter-${{ github.ref }} + cache-to: type=gha,mode=max,scope=config-adapter-${{ github.ref }} + platforms: ${{ env.BUILD_ARM == 'true' && 'linux/amd64,linux/arm64' || 'linux/amd64' }} + - name: Retag and push existing image if cache hit + if: env.CACHE_HIT == 'true' + run: | + TAGS=(${{ steps.dockerMetadata.outputs.tags }}) + for TAG in "${TAGS[@]}"; do + docker buildx imagetools create --tag $TAG ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DIR_HASH }} + done diff --git a/.github/workflows/config-loader-image.yml b/.github/workflows/config-loader-image.yml new file mode 100644 index 0000000000..1147d342ff --- /dev/null +++ b/.github/workflows/config-loader-image.yml @@ -0,0 +1,93 @@ +name: config-loader-image +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + inputs: + build_arm: + type: boolean + description: "Build for ARM as well" + default: false + required: false + workflow_call: + inputs: + build_arm: + type: string + description: "Build for ARM as well" + default: "false" + required: true +env: + DOCKER_IMAGE_NAME: ghcr.io/loculus-project/config-loader + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + BUILD_ARM: ${{ github.event.inputs.build_arm || inputs.build_arm || github.ref == 'refs/heads/main' }} + sha: ${{ github.event.pull_request.head.sha || github.sha }} +concurrency: + group: ci-${{ github.ref == 'refs/heads/main' && github.run_id || github.ref }}-config-loader + cancel-in-progress: true +jobs: + config-loader-image: + name: Build Config Loader Docker Image + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + packages: write + contents: read + checks: read + steps: + - name: Shorten sha + run: echo "sha=${sha::7}" >> $GITHUB_ENV + - uses: actions/checkout@v6 + - name: Add filename hash to environment + run: | + find config-tools -type f -not -path 'config-tools/node_modules/*' -print | sort | sha256sum > config-tools/filename_hash + cat config-tools/filename_hash + - name: Generate files hash + id: files-hash + run: | + DIR_HASH=$(echo -n ${{ hashFiles('config-tools/**', '.github/workflows/config-loader-image.yml') }}) + echo "DIR_HASH=$DIR_HASH${{ env.BUILD_ARM == 'true' && '-arm' || '' }}" >> $GITHUB_ENV + rm config-tools/filename_hash + - name: Setup Docker metadata + id: dockerMetadata + uses: docker/metadata-action@v6 + with: + images: ${{ env.DOCKER_IMAGE_NAME }} + tags: | + type=raw,value=${{ env.DIR_HASH }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=commit-${{ env.sha }} + type=raw,value=${{ env.BRANCH_NAME }} + type=raw,value=${{ env.BRANCH_NAME }}-arm,enable=${{ env.BUILD_ARM }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Check if image exists + id: check-image + run: | + EXISTS=$(docker manifest inspect ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DIR_HASH }} > /dev/null 2>&1 && echo "true" || echo "false") + echo "CACHE_HIT=$EXISTS" >> $GITHUB_ENV + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Build and push image if input files changed + if: env.CACHE_HIT == 'false' + uses: docker/build-push-action@v7 + with: + context: ./config-tools + file: ./config-tools/Dockerfile.loader + push: true + tags: ${{ steps.dockerMetadata.outputs.tags }} + cache-from: type=gha,scope=config-loader-${{ github.ref }} + cache-to: type=gha,mode=max,scope=config-loader-${{ github.ref }} + platforms: ${{ env.BUILD_ARM == 'true' && 'linux/amd64,linux/arm64' || 'linux/amd64' }} + - name: Retag and push existing image if cache hit + if: env.CACHE_HIT == 'true' + run: | + TAGS=(${{ steps.dockerMetadata.outputs.tags }}) + for TAG in "${TAGS[@]}"; do + docker buildx imagetools create --tag $TAG ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DIR_HASH }} + done diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index cef3b62093..125e018e4d 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -110,6 +110,17 @@ jobs: run: cd integration-tests && npx playwright install --with-deps --only-shell chromium ${{ matrix.browser == 'firefox' && 'firefox' || '' }} - name: Wait for the pods to be ready run: ./.github/scripts/wait_for_pods_to_be_ready.py --timeout ${{ env.wait_timeout }} + # Phase 3.6: the config-loader Job posts fixture YAMLs to the backend admin API + # as a Helm post-install hook. SILO + LAPIS adapter init containers depend on + # those organisms existing in the backend, so wait for the Job to complete + # before continuing. + - name: Wait for config-loader Job to complete + run: | + kubectl wait --for=condition=complete --timeout=300s job/loculus-config-loader || { + echo '--- config-loader Job logs ---' + kubectl logs job/loculus-config-loader --all-containers --tail=200 || true + exit 1 + } - name: Sleep for 10 secs run: sleep 10 - name: Run Integration test diff --git a/.github/workflows/update-argocd-metadata.yml b/.github/workflows/update-argocd-metadata.yml index 8dcf28c7e3..9b3e17fa00 100644 --- a/.github/workflows/update-argocd-metadata.yml +++ b/.github/workflows/update-argocd-metadata.yml @@ -99,6 +99,20 @@ jobs: max_attempts: 200 retry_wait_seconds: 5 command: docker manifest inspect ghcr.io/loculus-project/website:commit-${{ steps.get_sha.outputs.sha }} + - name: Wait for Config Loader Docker Image + uses: lewagon/wait-on-check-action@v1.7.0 + with: + ref: ${{ github.sha }} + check-name: Build Config Loader Docker Image + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 2 + - name: Wait for Config Adapter Docker Image + uses: lewagon/wait-on-check-action@v1.7.0 + with: + ref: ${{ github.sha }} + check-name: Build Config Adapter Docker Image + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 2 # End of wait block - name: Checkout External Repository uses: actions/checkout@v6 diff --git a/.github/workflows/website-image.yml b/.github/workflows/website-image.yml index 6e5b6e4429..94290553dd 100644 --- a/.github/workflows/website-image.yml +++ b/.github/workflows/website-image.yml @@ -30,7 +30,10 @@ jobs: - name: Generate files hash id: prepare run: | - DIR_HASH=$(echo -n ${{ hashFiles('website/**', '.github/workflows/website-image.yml') }}) + # config-tools/src/schema/ is the source of truth for the canonical Zod + # schemas; website re-exports them. Changes there must invalidate the + # website image cache too. + DIR_HASH=$(echo -n ${{ hashFiles('website/**', 'config-tools/src/schema/**', '.github/workflows/website-image.yml') }}) echo "dir-hash=$DIR_HASH" >> $GITHUB_OUTPUT echo "sha-short=${sha::7}" >> $GITHUB_OUTPUT - name: Login to GitHub Container Registry @@ -89,6 +92,12 @@ jobs: uses: docker/build-push-action@v7 with: context: ./website + # The website re-exports canonical Zod schemas from `config-tools/`, + # which lives outside its build context. Bring it in as a named build + # context so `../../../config-tools/...` relative imports resolve at + # build time (the Dockerfile does `COPY --from=config-tools .`). + build-contexts: | + config-tools=./config-tools platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha,scope=website-${{ github.ref }}-${{ env.PLATFORM_PAIR }} diff --git a/.github/workflows/website-tests.yml b/.github/workflows/website-tests.yml index e5912a9bf0..30064a368c 100644 --- a/.github/workflows/website-tests.yml +++ b/.github/workflows/website-tests.yml @@ -28,7 +28,9 @@ jobs: - uses: actions/cache@v5 with: path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('website/**/package-lock.json') }} + key: ${{ runner.os }}-node-${{ hashFiles('website/**/package-lock.json', 'config-tools/**/package-lock.json') }} + - run: npm ci + working-directory: ./config-tools - run: npm ci - run: npm run check-format - run: npm run check-types @@ -49,7 +51,10 @@ jobs: uses: actions/cache@v5 with: path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('website/**/package-lock.json') }} + key: ${{ runner.os }}-node-${{ hashFiles('website/**/package-lock.json', 'config-tools/**/package-lock.json') }} + - name: Install config-tools dependencies + run: npm ci + working-directory: ./config-tools - name: Install dependencies run: npm ci - name: Run tests diff --git a/AGENTS.md b/AGENTS.md index 8fd73de9cf..905392dce6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,10 @@ Components include: website, backend, deployment, preprocessing, ingest, deposit Write detailed PR summaries, not just short bullet points. When creating PRs, you should generally create them as a draft PR. If you have access to https://github.com/loculus-project/agent_store/ you can upload screenshots (which you might take using `playwright-cli` there to be able to use in PR descriptions, and maybe issue descriptions). +## Markdown formatting + +When writing or editing Markdown files, keep each paragraph on a single line — do not insert manual line breaks (hard wraps) within a paragraph. Readers use editors with soft line wrapping. Hard-wrapping is only acceptable where the syntax requires a new line (list items, table rows, headings, code blocks, etc.). + ## Preventing flaky Playwright tests (website) When adding or modifying interactive components in the website, ensure they are disabled until React hydration completes to prevent race conditions in Playwright tests: @@ -19,4 +23,3 @@ These wrappers automatically disable components until client-side hydration is c Conda dependencies in `environment.yml` files are not automatically updated by dependabot. The `maintenance-scripts/` folder contains utilities to help update conda environment versions. - diff --git a/backend/README.md b/backend/README.md index 401b81b7c6..1fa4c5400e 100644 --- a/backend/README.md +++ b/backend/README.md @@ -24,16 +24,26 @@ All commands mentioned in this section are run from the `backend` directory unle postgres:latest ``` -2. Start the backend (including test config): +2. Start the backend once to run Flyway migrations: + + ```sh + ./start_dev.sh + ``` + + Stop it again after startup has completed. + +3. Import the preview organisms and restart the backend: ```sh ../generate_local_test_config.sh + ./import_local_test_config.py ./start_dev.sh ``` -The service listens, by default, to **port 8079**: . The test config will be written to `loculus/website/tests/config`. +The service listens, by default, to **port 8079**: . The test config will be written to `loculus/website/tests/config`; `import_local_test_config.py` imports the generated preview organisms into the DB-backed config tables and merges website-facing schema fields from the generated website config. +The importer uses a local `psql` binary when available, then falls back to the k3d/Helm database via `kubectl`, the `loculus_postgres` container created above, or a temporary Docker PostgreSQL client against the exposed host port. -3. Clean up the database when done: +4. Clean up the database when done: ```sh docker stop loculus_postgres @@ -54,10 +64,11 @@ You need to set: --spring.datasource.password=unsecure ``` -- the path to the config file (use `../generate_local_test_config.sh` to generate this file): +- the technical backend URLs: ```sh ---loculus.config.path=../website/tests/config/backend_config.json +--loculus.backend.website-url=http://localhost:3000 +--loculus.backend.backend-url=http://localhost:8079 ``` - the url to fetch the public key for JWT verification diff --git a/backend/docs/db/schema.sql b/backend/docs/db/schema.sql index f2b4eb3909..8b43736cf2 100644 --- a/backend/docs/db/schema.sql +++ b/backend/docs/db/schema.sql @@ -256,6 +256,180 @@ ALTER TABLE public.compression_dictionaries ALTER COLUMN id ADD GENERATED ALWAYS ); +-- +-- Name: config_audit_log; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_audit_log ( + id bigint NOT NULL, + occurred_at timestamp without time zone DEFAULT now() NOT NULL, + actor text NOT NULL, + scope text NOT NULL, + organism_key text, + action text NOT NULL, + details jsonb, + result_version bigint +); + + +ALTER TABLE public.config_audit_log OWNER TO postgres; + +-- +-- Name: config_audit_log_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.config_audit_log_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.config_audit_log_id_seq OWNER TO postgres; + +-- +-- Name: config_audit_log_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.config_audit_log_id_seq OWNED BY public.config_audit_log.id; + + +-- +-- Name: config_instance_draft; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_instance_draft ( + singleton boolean DEFAULT true NOT NULL, + config jsonb NOT NULL, + base_version bigint, + revision bigint DEFAULT 0 NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + created_by text NOT NULL, + updated_by text NOT NULL, + CONSTRAINT config_instance_draft_singleton_check CHECK (singleton) +); + + +ALTER TABLE public.config_instance_draft OWNER TO postgres; + +-- +-- Name: config_instance_state; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_instance_state ( + singleton boolean DEFAULT true NOT NULL, + current_version bigint, + CONSTRAINT config_instance_state_singleton_check CHECK (singleton) +); + + +ALTER TABLE public.config_instance_state OWNER TO postgres; + +-- +-- Name: config_instance_versions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_instance_versions ( + version bigint NOT NULL, + config jsonb NOT NULL, + published_at timestamp without time zone DEFAULT now() NOT NULL, + published_by text NOT NULL +); + + +ALTER TABLE public.config_instance_versions OWNER TO postgres; + +-- +-- Name: config_instance_versions_version_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.config_instance_versions_version_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.config_instance_versions_version_seq OWNER TO postgres; + +-- +-- Name: config_instance_versions_version_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.config_instance_versions_version_seq OWNED BY public.config_instance_versions.version; + + +-- +-- Name: config_organism_drafts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_organism_drafts ( + organism_key text NOT NULL, + config jsonb NOT NULL, + base_version bigint, + revision bigint DEFAULT 0 NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + created_by text NOT NULL, + updated_by text NOT NULL +); + + +ALTER TABLE public.config_organism_drafts OWNER TO postgres; + +-- +-- Name: config_organism_versions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_organism_versions ( + organism_key text NOT NULL, + version bigint NOT NULL, + config jsonb NOT NULL, + published_at timestamp without time zone DEFAULT now() NOT NULL, + published_by text NOT NULL +); + + +ALTER TABLE public.config_organism_versions OWNER TO postgres; + +-- +-- Name: config_organisms; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_organisms ( + key text NOT NULL, + status text NOT NULL, + current_version bigint, + created_at timestamp without time zone DEFAULT now() NOT NULL, + created_by text NOT NULL, + first_published_at timestamp without time zone, + last_published_at timestamp without time zone, + deployed boolean DEFAULT true NOT NULL, + CONSTRAINT config_organisms_check CHECK (((status = 'unreleased'::text) = (current_version IS NULL))), + CONSTRAINT config_organisms_status_check CHECK ((status = ANY (ARRAY['unreleased'::text, 'released'::text]))) +); + + +ALTER TABLE public.config_organisms OWNER TO postgres; + +-- +-- Name: config_preprocessing_files; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.config_preprocessing_files ( + organism_key text NOT NULL, + pipeline_version bigint NOT NULL, + config_file text NOT NULL, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + updated_by text NOT NULL +); + + +ALTER TABLE public.config_preprocessing_files OWNER TO postgres; + -- -- Name: current_processing_pipeline; Type: TABLE; Schema: public; Owner: postgres -- @@ -717,6 +891,20 @@ ALTER SEQUENCE public.user_groups_table_id_seq OWNED BY public.user_groups_table ALTER TABLE ONLY public.audit_log ALTER COLUMN id SET DEFAULT nextval('public.audit_log_id_seq'::regclass); +-- +-- Name: config_audit_log id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_audit_log ALTER COLUMN id SET DEFAULT nextval('public.config_audit_log_id_seq'::regclass); + + +-- +-- Name: config_instance_versions version; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_instance_versions ALTER COLUMN version SET DEFAULT nextval('public.config_instance_versions_version_seq'::regclass); + + -- -- Name: groups_table group_id; Type: DEFAULT; Schema: public; Owner: postgres -- @@ -776,6 +964,70 @@ ALTER TABLE ONLY public.compression_dictionaries ADD CONSTRAINT compression_dictionaries_pkey PRIMARY KEY (id); +-- +-- Name: config_audit_log config_audit_log_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_audit_log + ADD CONSTRAINT config_audit_log_pkey PRIMARY KEY (id); + + +-- +-- Name: config_instance_draft config_instance_draft_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_instance_draft + ADD CONSTRAINT config_instance_draft_pkey PRIMARY KEY (singleton); + + +-- +-- Name: config_instance_state config_instance_state_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_instance_state + ADD CONSTRAINT config_instance_state_pkey PRIMARY KEY (singleton); + + +-- +-- Name: config_instance_versions config_instance_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_instance_versions + ADD CONSTRAINT config_instance_versions_pkey PRIMARY KEY (version); + + +-- +-- Name: config_organism_drafts config_organism_drafts_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_organism_drafts + ADD CONSTRAINT config_organism_drafts_pkey PRIMARY KEY (organism_key); + + +-- +-- Name: config_organism_versions config_organism_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_organism_versions + ADD CONSTRAINT config_organism_versions_pkey PRIMARY KEY (organism_key, version); + + +-- +-- Name: config_organisms config_organisms_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_organisms + ADD CONSTRAINT config_organisms_pkey PRIMARY KEY (key); + + +-- +-- Name: config_preprocessing_files config_preprocessing_files_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_preprocessing_files + ADD CONSTRAINT config_preprocessing_files_pkey PRIMARY KEY (organism_key, pipeline_version); + + -- -- Name: current_processing_pipeline current_processing_pipeline_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres -- @@ -942,6 +1194,20 @@ CREATE INDEX data_use_terms_table_accession_idx ON public.data_use_terms_table U CREATE INDEX flyway_schema_history_s_idx ON public.flyway_schema_history USING btree (success); +-- +-- Name: ix_config_audit_log_actor_time; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX ix_config_audit_log_actor_time ON public.config_audit_log USING btree (actor, occurred_at DESC); + + +-- +-- Name: ix_config_audit_log_organism_time; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX ix_config_audit_log_organism_time ON public.config_audit_log USING btree (organism_key, occurred_at DESC); + + -- -- Name: sequence_entries_organism_covering_idx; Type: INDEX; Schema: public; Owner: postgres -- @@ -1103,6 +1369,62 @@ CREATE TRIGGER update_tracker_trigger_upd AFTER UPDATE ON public.sequence_entrie CREATE TRIGGER update_tracker_trigger_upd AFTER UPDATE ON public.sequence_entries_preprocessed_data REFERENCING NEW TABLE AS changed_rows FOR EACH STATEMENT EXECUTE FUNCTION public.update_preprocessed_data_tracker(); +-- +-- Name: config_instance_draft config_instance_draft_base_version_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_instance_draft + ADD CONSTRAINT config_instance_draft_base_version_fkey FOREIGN KEY (base_version) REFERENCES public.config_instance_versions(version); + + +-- +-- Name: config_instance_state config_instance_state_current_version_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_instance_state + ADD CONSTRAINT config_instance_state_current_version_fkey FOREIGN KEY (current_version) REFERENCES public.config_instance_versions(version); + + +-- +-- Name: config_organism_drafts config_organism_drafts_organism_key_base_version_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_organism_drafts + ADD CONSTRAINT config_organism_drafts_organism_key_base_version_fkey FOREIGN KEY (organism_key, base_version) REFERENCES public.config_organism_versions(organism_key, version); + + +-- +-- Name: config_organism_drafts config_organism_drafts_organism_key_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_organism_drafts + ADD CONSTRAINT config_organism_drafts_organism_key_fkey FOREIGN KEY (organism_key) REFERENCES public.config_organisms(key) ON DELETE CASCADE; + + +-- +-- Name: config_organism_versions config_organism_versions_organism_key_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_organism_versions + ADD CONSTRAINT config_organism_versions_organism_key_fkey FOREIGN KEY (organism_key) REFERENCES public.config_organisms(key); + + +-- +-- Name: config_organisms config_organisms_current_version_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_organisms + ADD CONSTRAINT config_organisms_current_version_fk FOREIGN KEY (key, current_version) REFERENCES public.config_organism_versions(organism_key, version) DEFERRABLE INITIALLY DEFERRED; + + +-- +-- Name: config_preprocessing_files config_preprocessing_files_organism_key_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.config_preprocessing_files + ADD CONSTRAINT config_preprocessing_files_organism_key_fkey FOREIGN KEY (organism_key) REFERENCES public.config_organisms(key) ON DELETE CASCADE; + + -- -- Name: files files_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres -- diff --git a/backend/import_local_test_config.py b/backend/import_local_test_config.py new file mode 100755 index 0000000000..016f0fc4ab --- /dev/null +++ b/backend/import_local_test_config.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 + +"""Seed a local Loculus database with the canonical config fixtures. + +Under the DB-backed config architecture (see ``config-architecture/``) the +domain config — instance branding, organisms, and the opaque per-pipeline +preprocessing config files — lives in the ``config_*`` tables and is seeded for +previews and CI by the ``loculus-config-loader`` running ``kubernetes/loculus/ +fixtures/`` against the admin API. + +This script is the lightweight, backend-free equivalent for local development: +it reads the same ``kubernetes/loculus/fixtures/`` directory and writes the rows +directly into Postgres via ``psql``/``kubectl``/``docker``. The result matches +what the loader produces, so a locally-seeded preview behaves exactly like a +CI/preview deployment — all organisms, instance branding (logo, banners, …), +and preprocessing config files visible in the admin panel. + +The fixtures are already in the canonical schema, so no per-field merging or +display-name copying is needed (unlike the legacy ``backend_config.json`` path +this replaced). +""" + +from __future__ import annotations + +import argparse +import json +import os +import shutil +import subprocess +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parent.parent +DEFAULT_FIXTURES_DIR = REPO_ROOT / "kubernetes" / "loculus" / "fixtures" +LOCALHOST_NAMES = {"localhost", "127.0.0.1", "::1"} + +# values.yaml organism-override constructs that linger in the migrated fixtures +# but are NOT part of the canonical/backend `Schema`. The config-loader posts +# fixtures through the canonical Zod schema, which silently strips unknown keys; +# this SQL-direct importer must drop them too, otherwise the backend's +# (fail-on-unknown) Jackson deserializer 500s when it reads the organism config. +NON_CANONICAL_SCHEMA_KEYS = {"extraInputFields", "metadataAdd"} + + +def clean_organism_config(organism_config: dict) -> dict: + schema = organism_config.get("schema") + if isinstance(schema, dict): + for key in NON_CANONICAL_SCHEMA_KEYS: + schema.pop(key, None) + return organism_config + + +def sql_literal(value: str) -> str: + return "'" + value.replace("'", "''") + "'" + + +def load_yaml(path: Path) -> dict: + with path.open() as file: + return yaml.safe_load(file) + + +def load_fixtures(fixtures_dir: Path) -> tuple[dict, dict[str, dict], dict[str, dict[int, str]]]: + """Read instance.yaml, organisms/*.yaml, and preprocessing//.. + + Mirrors ``config-tools/src/loader/fixtures.ts``: the organism key is the + file stem, the organism config is the whole file, and preprocessing config + files are stored verbatim keyed by (organism, pipeline version). + """ + instance_path = fixtures_dir / "instance.yaml" + if not instance_path.is_file(): + raise SystemExit(f"Instance fixture not found: {instance_path}") + instance_config = load_yaml(instance_path) + + organisms: dict[str, dict] = {} + organisms_dir = fixtures_dir / "organisms" + if organisms_dir.is_dir(): + for path in sorted(organisms_dir.glob("*.y*ml")): + organisms[path.stem] = clean_organism_config(load_yaml(path)) + if not organisms: + raise SystemExit(f"No organism fixtures found under {organisms_dir}") + + preprocessing: dict[str, dict[int, str]] = {} + preprocessing_dir = fixtures_dir / "preprocessing" + if preprocessing_dir.is_dir(): + for organism_dir in sorted(p for p in preprocessing_dir.iterdir() if p.is_dir()): + by_version: dict[int, str] = {} + for path in sorted(organism_dir.iterdir()): + if not path.is_file(): + continue + try: + version = int(path.stem) + except ValueError as error: + raise SystemExit( + f"Invalid preprocessing config filename '{path.name}' in " + f"{organism_dir.name}: the stem must be a positive integer " + "pipeline version (e.g. '1.yaml')." + ) from error + by_version[version] = path.read_text() + if by_version: + preprocessing[organism_dir.name] = by_version + + return instance_config, organisms, preprocessing + + +def build_sql( + instance_config: dict, + organisms: dict[str, dict], + preprocessing: dict[str, dict[int, str]], +) -> str: + statements = [ + "BEGIN;", + "SET CONSTRAINTS ALL DEFERRED;", + "DELETE FROM config_preprocessing_files;", + "DELETE FROM config_audit_log;", + "DELETE FROM config_instance_draft;", + "DELETE FROM config_organism_drafts;", + "DELETE FROM config_organism_versions;", + "DELETE FROM config_organisms;", + "DELETE FROM config_instance_state;", + "DELETE FROM config_instance_versions;", + "DELETE FROM current_processing_pipeline;", + "INSERT INTO config_instance_versions (version, config, published_at, published_by) " + f"VALUES (1, {sql_literal(json.dumps(instance_config, separators=(',', ':')))}::jsonb, now(), 'local-import');", + "INSERT INTO config_instance_state (singleton, current_version) VALUES (TRUE, 1);", + ] + + for key, organism_config in sorted(organisms.items()): + key_literal = sql_literal(key) + config_literal = sql_literal(json.dumps(organism_config, separators=(",", ":"))) + statements.extend( + [ + "INSERT INTO config_organisms " + "(key, status, current_version, created_at, created_by, first_published_at, last_published_at) " + f"VALUES ({key_literal}, 'released', 1, now(), 'local-import', now(), now());", + "INSERT INTO config_organism_versions " + "(organism_key, version, config, published_at, published_by) " + f"VALUES ({key_literal}, 1, {config_literal}::jsonb, now(), 'local-import');", + # The backend always starts an organism at pipeline version 1 and + # auto-upgrades once a newer pipeline has processed everything + # (CurrentProcessingPipelineTable.setV1ForOrganismsIfNotExist). + "INSERT INTO current_processing_pipeline (organism, version, started_using_at) " + f"VALUES ({key_literal}, 1, now());", + ], + ) + + for organism_key, by_version in sorted(preprocessing.items()): + if organism_key not in organisms: + # A preprocessing fixture without a matching organism would violate + # the foreign key; skip it rather than fail the whole import. + continue + for version, content in sorted(by_version.items()): + statements.append( + "INSERT INTO config_preprocessing_files " + "(organism_key, pipeline_version, config_file, updated_at, updated_by) " + f"VALUES ({sql_literal(organism_key)}, {version}, {sql_literal(content)}, now(), 'local-import');", + ) + + statements.append("COMMIT;") + return "\n".join(statements) + "\n" + + +def psql_args(args: argparse.Namespace) -> list[str]: + return [ + "psql", + "-v", + "ON_ERROR_STOP=1", + "-U", + args.user, + "-d", + args.database, + ] + + +def candidate_commands(args: argparse.Namespace) -> list[list[str]]: + commands = [] + if shutil.which("psql") is not None: + commands.append( + psql_args(args) + + [ + "-h", + args.host, + "-p", + args.port, + ], + ) + + if shutil.which("kubectl") is not None: + commands.append( + [ + "kubectl", + "exec", + "-i", + "-n", + args.kube_namespace, + "deployment/loculus-database", + "--", + "env", + f"PGPASSWORD={args.password}", + ] + + psql_args(args), + ) + + if shutil.which("docker") is not None: + commands.append( + [ + "docker", + "exec", + "-i", + "-e", + f"PGPASSWORD={args.password}", + args.docker_container, + ] + + psql_args(args), + ) + host = "host.docker.internal" if args.host in LOCALHOST_NAMES else args.host + commands.append( + [ + "docker", + "run", + "--rm", + "-i", + "-e", + f"PGPASSWORD={args.password}", + "--add-host=host.docker.internal:host-gateway", + args.postgres_image, + ] + + psql_args(args) + + [ + "-h", + host, + "-p", + args.port, + ], + ) + + return commands + + +def run_sql(args: argparse.Namespace, sql: str) -> None: + env = os.environ.copy() + env["PGPASSWORD"] = args.password + errors = [] + + for command in candidate_commands(args): + result = subprocess.run(command, input=sql, text=True, capture_output=True, env=env) + if result.returncode == 0: + return + errors.append(f"$ {' '.join(command[:8])} ...\n{result.stderr.strip()}") + + if not errors: + raise SystemExit("Neither psql, kubectl nor docker was found. Install psql or run the dev cluster/container.") + + raise SystemExit( + "Could not import the config into Postgres. Tried local psql, Kubernetes, and Docker fallbacks.\n\n" + + "\n\n".join(errors), + ) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Seed the DB-backed config tables from kubernetes/loculus/fixtures/ for local previews.", + ) + parser.add_argument( + "--fixtures", + default=str(DEFAULT_FIXTURES_DIR), + help="Path to the canonical config fixtures directory (instance.yaml + organisms/ + preprocessing/).", + ) + parser.add_argument("--host", default="localhost") + parser.add_argument("--port", default="5432") + parser.add_argument("--database", default="loculus") + parser.add_argument("--user", default="postgres") + parser.add_argument("--password", default=os.environ.get("PGPASSWORD", "unsecure")) + parser.add_argument("--docker-container", default="loculus_postgres") + parser.add_argument("--postgres-image", default="postgres:15.12") + parser.add_argument("--kube-namespace", default="default") + parser.add_argument("--dry-run", action="store_true", help="Print SQL instead of executing it.") + args = parser.parse_args() + + fixtures_dir = Path(args.fixtures) + if not fixtures_dir.is_dir(): + raise SystemExit(f"Fixtures directory not found: {fixtures_dir}") + + instance_config, organisms, preprocessing = load_fixtures(fixtures_dir) + sql = build_sql(instance_config, organisms, preprocessing) + if args.dry_run: + print(sql) + return + + run_sql(args, sql) + preprocessing_count = sum(len(v) for v in preprocessing.values()) + print( + f"Imported {len(organisms)} organisms and {preprocessing_count} preprocessing " + f"config files from {fixtures_dir}. Restart the backend if it was running." + ) + + +if __name__ == "__main__": + main() diff --git a/backend/src/main/kotlin/org/loculus/backend/api/DataUseTerms.kt b/backend/src/main/kotlin/org/loculus/backend/api/DataUseTerms.kt index 5bba9e10d6..eb195b9b85 100644 --- a/backend/src/main/kotlin/org/loculus/backend/api/DataUseTerms.kt +++ b/backend/src/main/kotlin/org/loculus/backend/api/DataUseTerms.kt @@ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.fasterxml.jackson.databind.ser.std.StdSerializer import io.swagger.v3.oas.annotations.media.Schema import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime import org.loculus.backend.controller.BadRequestException import org.loculus.backend.utils.Accession @@ -121,9 +120,3 @@ private class LocalDateSerializer : StdSerializer(LocalDate::class.ja gen.writeString(value.toString()) } } - -private class LocalDateTimeSerializer : StdSerializer(LocalDateTime::class.java) { - override fun serialize(value: LocalDateTime, gen: JsonGenerator, provider: SerializerProvider) { - gen.writeString(value.toString()) - } -} diff --git a/backend/src/main/kotlin/org/loculus/backend/api/Organism.kt b/backend/src/main/kotlin/org/loculus/backend/api/Organism.kt index 4cbb60875e..b63d327fbf 100644 --- a/backend/src/main/kotlin/org/loculus/backend/api/Organism.kt +++ b/backend/src/main/kotlin/org/loculus/backend/api/Organism.kt @@ -5,8 +5,8 @@ import jakarta.validation.Constraint import jakarta.validation.ConstraintValidator import jakarta.validation.ConstraintValidatorContext import jakarta.validation.Payload -import org.loculus.backend.config.BackendConfig import org.loculus.backend.config.ORGANISM_SCHEMA_NAME +import org.loculus.backend.config.service.ConfigService import org.springframework.core.convert.converter.Converter import org.springframework.stereotype.Component import kotlin.reflect.KClass @@ -24,9 +24,9 @@ annotation class ValidOrganism( val payload: Array> = [], ) -class OrganismValidator(private val backendConfig: BackendConfig) : ConstraintValidator { +class OrganismValidator(private val configService: ConfigService) : ConstraintValidator { override fun isValid(value: Organism, context: ConstraintValidatorContext): Boolean { - val keys = backendConfig.organisms.keys + val keys = configService.listOrganismKeys() if (keys.contains(value.name)) { return true } diff --git a/backend/src/main/kotlin/org/loculus/backend/auth/AuthenticatedUser.kt b/backend/src/main/kotlin/org/loculus/backend/auth/AuthenticatedUser.kt index b3bb9ec2ad..cf83667535 100644 --- a/backend/src/main/kotlin/org/loculus/backend/auth/AuthenticatedUser.kt +++ b/backend/src/main/kotlin/org/loculus/backend/auth/AuthenticatedUser.kt @@ -15,6 +15,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver import org.springframework.web.method.support.ModelAndViewContainer object Roles { + const val LOCULUS_ADMINISTRATOR = "loculus_administrator" const val SUPER_USER = "super_user" const val PREPROCESSING_PIPELINE = "preprocessing_pipeline" const val EXTERNAL_METADATA_UPDATER = "external_metadata_updater" diff --git a/backend/src/main/kotlin/org/loculus/backend/config/BackendSpringConfig.kt b/backend/src/main/kotlin/org/loculus/backend/config/BackendSpringConfig.kt index e9ab9640a1..db77c72a39 100644 --- a/backend/src/main/kotlin/org/loculus/backend/config/BackendSpringConfig.kt +++ b/backend/src/main/kotlin/org/loculus/backend/config/BackendSpringConfig.kt @@ -1,22 +1,20 @@ package org.loculus.backend.config -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue +import io.swagger.v3.oas.models.OpenAPI import io.swagger.v3.oas.models.headers.Header import io.swagger.v3.oas.models.media.StringSchema import io.swagger.v3.oas.models.parameters.HeaderParameter +import io.swagger.v3.oas.models.tags.Tag import org.flywaydb.core.Flyway import org.jetbrains.exposed.spring.autoconfigure.ExposedAutoConfiguration import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.DatabaseConfig import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger -import org.jetbrains.exposed.sql.transactions.TransactionManager -import org.jetbrains.exposed.sql.transactions.transaction +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.LoculusCustomHeaders +import org.loculus.backend.controller.QueryController import org.loculus.backend.log.REQUEST_ID_HEADER_DESCRIPTION -import org.loculus.backend.service.submission.dbtables.CurrentProcessingPipelineTable -import org.loculus.backend.utils.DateProvider +import org.springdoc.core.customizers.OpenApiCustomizer import org.springdoc.core.customizers.OperationCustomizer import org.springframework.beans.factory.InitializingBean import org.springframework.beans.factory.annotation.Value @@ -26,14 +24,14 @@ import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Profile +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.stereotype.Component import org.springframework.web.filter.CommonsRequestLoggingFilter -import java.io.File import javax.sql.DataSource object BackendSpringProperty { - const val BACKEND_CONFIG_PATH = "loculus.config.path" const val STALE_AFTER_SECONDS = "loculus.cleanup.task.reset-stale-in-processing-after-seconds" const val CLEAN_UP_RUN_EVERY_SECONDS = "loculus.cleanup.task.run-every-seconds" const val PIPELINE_VERSION_UPGRADE_CHECK_INTERVAL_SECONDS = @@ -53,6 +51,30 @@ object BackendSpringProperty { const val DEBUG_MODE_ON_VALUE = "true" const val ENABLE_SEQSETS_TRUE_VALUE = "true" +const val LAPIS_PROXY_CONTROLLER_TAG = "lapis-proxy-controller" + +private const val LAPIS_PROXY_CONTROLLER_DESCRIPTION = + "This is temporary and used for calls that have not yet switched to using the new query API." + +fun updateLapisProxyOpenApiTags(openApi: OpenAPI) { + val operationTagNames = openApi.paths.orEmpty().values + .flatMap { it.readOperations() } + .flatMap { it.tags.orEmpty() } + .distinct() + val tagsByName = linkedMapOf() + openApi.tags.orEmpty() + .filter { it.name in operationTagNames } + .forEach { tag -> tagsByName[tag.name] = tag } + operationTagNames.forEach { tagName -> tagsByName.putIfAbsent(tagName, Tag().name(tagName)) } + tagsByName[LAPIS_PROXY_CONTROLLER_TAG] = tagsByName[LAPIS_PROXY_CONTROLLER_TAG] + ?: Tag().name(LAPIS_PROXY_CONTROLLER_TAG) + tagsByName[LAPIS_PROXY_CONTROLLER_TAG]?.description(LAPIS_PROXY_CONTROLLER_DESCRIPTION) + openApi.tags = orderOpenApiTags(tagsByName.values) +} + +private fun orderOpenApiTags(tags: Collection) = tags + .filter { it.name.isNotBlank() } + .sortedWith(compareBy { it.name == LAPIS_PROXY_CONTROLLER_TAG }.thenBy { it.name }) private val logger = mu.KotlinLogging.logger {} @@ -83,13 +105,7 @@ class BackendSpringConfig { } @Bean - fun backendConfig( - objectMapper: ObjectMapper, - @Value("\${${BackendSpringProperty.BACKEND_CONFIG_PATH}}") configPath: String, - ): BackendConfig = readBackendConfig(objectMapper, configPath) - - @Bean - fun openApi(backendConfig: BackendConfig) = buildOpenApiSchema(backendConfig) + fun openApi() = buildOpenApiSchema() @Bean fun s3Config( @@ -139,6 +155,121 @@ class BackendSpringConfig { } operation } + + @Bean + @Order(Ordered.LOWEST_PRECEDENCE) + fun lapisProxyTagCustomizer() = OpenApiCustomizer { openApi -> updateLapisProxyOpenApiTags(openApi) } + + @Bean + fun queryControllerOpenApiCustomizer(configService: ConfigService) = + OperationCustomizer { operation, handlerMethod -> + if (handlerMethod.beanType != QueryController::class.java) { + return@OperationCustomizer operation + } + + val organismKeys = runCatching { configService.listReleasedOrganisms().map { it.key } } + .getOrDefault(emptyList()) + val endpointDocs = queryEndpointDocs(handlerMethod.method.name) + operation.tags = listOf("Query") + operation.summary = endpointDocs?.summary ?: operation.summary + operation.description = endpointDocs?.description ?: operation.description + operation.parameters?.forEach { parameter -> + when (parameter.name) { + "organism" -> { + parameter.description = "Organism key configured for this instance." + parameter.example = organismKeys.firstOrNull() + parameter.schema = StringSchema()._enum(organismKeys) + } + + "versionGroup" -> { + parameter.description = "Use current for latest versions or allVersions for version history." + parameter.example = "current" + parameter.schema = StringSchema()._enum(listOf("current", "allVersions")) + } + + "segment" -> parameter.description = "Sequence segment name as configured in LAPIS." + + "referenceName" -> parameter.description = "Nucleotide reference name as configured in LAPIS." + + "geneName" -> parameter.description = "Gene name as configured in LAPIS." + } + } + operation + } + + private companion object { + data class QueryEndpointDocs(val summary: String, val description: String) + + fun queryEndpointDocs(methodName: String) = + QUERY_ENDPOINT_DOCS[methodName] ?: methodName.removeSuffix("Get").let { QUERY_ENDPOINT_DOCS[it] } + + val QUERY_ENDPOINT_DOCS = mapOf( + "metadata" to QueryEndpointDocs( + "Query metadata", + "Return metadata rows for released sequence entries.", + ), + "aggregated" to QueryEndpointDocs( + "Aggregate metadata", + "Return aggregated metadata counts for released sequence entries.", + ), + "sequences" to QueryEndpointDocs( + "Query unaligned nucleotide sequences", + "Return unaligned nucleotide sequences for released sequence entries.", + ), + "sequencesForSegment" to QueryEndpointDocs( + "Query unaligned nucleotide sequences by segment", + "Return unaligned nucleotide sequences for one segment of released sequence entries.", + ), + "sequencesAligned" to QueryEndpointDocs( + "Query aligned nucleotide sequences", + "Return aligned nucleotide sequences for released sequence entries.", + ), + "sequencesAlignedMutations" to QueryEndpointDocs( + "Query nucleotide mutations", + "Return nucleotide mutation records for released sequence entries.", + ), + "sequencesAlignedInsertions" to QueryEndpointDocs( + "Query nucleotide insertions", + "Return nucleotide insertion records for released sequence entries.", + ), + "sequencesAlignedAggregatedMutations" to QueryEndpointDocs( + "Aggregate nucleotide mutations", + "Return aggregated nucleotide mutations for released sequence entries.", + ), + "sequencesAlignedForSegment" to QueryEndpointDocs( + "Query aligned nucleotide sequences by reference", + "Return aligned nucleotide sequences for one reference of released sequence entries.", + ), + "sequencesAlignedForSegmentMutations" to QueryEndpointDocs( + "Query nucleotide mutations by reference", + "Return nucleotide mutation records for one reference.", + ), + "sequencesAlignedForSegmentAggregatedMutations" to QueryEndpointDocs( + "Aggregate nucleotide mutations by reference", + "Return aggregated nucleotide mutations for one reference.", + ), + "translations" to QueryEndpointDocs( + "Query aligned amino acid sequences", + "Return aligned amino acid sequences for one gene.", + ), + "translationsMutations" to QueryEndpointDocs( + "Query amino acid mutations", + "Return amino acid mutation records for released sequence entries.", + ), + "translationsInsertions" to QueryEndpointDocs( + "Query amino acid insertions", + "Return amino acid insertion records for released sequence entries.", + ), + "translationsForGeneMutations" to QueryEndpointDocs( + "Query amino acid mutations by gene", + "Return amino acid mutation records for one gene.", + ), + "translationsForGeneAggregatedMutations" to QueryEndpointDocs( + "Aggregate amino acid mutations by gene", + "Return aggregated amino acid mutations for one gene.", + ), + ) + } } @Component @@ -146,8 +277,6 @@ class BackendSpringConfig { class FlywayInit( // get Flyway from the Spring autoconfiguration so that Java based migrations can use Spring beans private val flyway: Flyway, - private val backendConfig: BackendConfig, - private val dateProvider: DateProvider, private val dataSource: DataSource, ) : InitializingBean { override fun afterPropertiesSet() { @@ -155,60 +284,6 @@ class FlywayInit( flyway.migrate() - // Since migration V1.10 we need to initialize the CurrentProcessingPipelineTable - // in code, because the configured organisms are not known in the SQL table definitions. - logger.info("Initializing CurrentProcessingPipelineTable") - transaction { - val insertedRows = CurrentProcessingPipelineTable.setV1ForOrganismsIfNotExist( - backendConfig.organisms.keys, - dateProvider.getCurrentDateTime(), - ) - logger.info("$insertedRows inserted.") - } - } -} - -/** - * Check whether configured metadata fields for earliestReleaseDate are actually fields and are of type date. - * Returns a non-empty list of errors if validation errors were found. - */ -internal fun validateEarliestReleaseDateFields(config: BackendConfig): List { - val errors = mutableListOf() - config.organisms.values.forEach { - val organism = it.schema.organismName - val allFields = it.schema.metadata.map { it.name }.toSet() - val dateFields = it.schema.metadata.filter { it.type == MetadataType.DATE }.map { it.name }.toSet() - it.schema.earliestReleaseDate.externalFields.forEach { - if (!allFields.contains(it)) { - errors.add( - "Error on organism $organism in earliestReleaseDate.externalFields: " + - "Field $it does not exist.", - ) - } else { - if (!dateFields.contains(it)) { - errors.add( - "Error on organism $organism in earliestReleaseDate.externalFields: " + - "Field $it is not of type ${MetadataType.DATE}.", - ) - } - } - } - } - return errors -} - -fun readBackendConfig(objectMapper: ObjectMapper, configPath: String): BackendConfig { - val config = objectMapper - .enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES) - .readValue(File(configPath)) - logger.info { "Loaded backend config from $configPath" } - logger.info { "Config: $config" } - val validationErrors = validateEarliestReleaseDateFields(config) - if (validationErrors.isNotEmpty()) { - throw IllegalArgumentException( - "The configuration file at $configPath is invalid: " + - validationErrors.joinToString(" "), - ) + logger.info("Flyway migration complete") } - return config } diff --git a/backend/src/main/kotlin/org/loculus/backend/config/Config.kt b/backend/src/main/kotlin/org/loculus/backend/config/Config.kt index 31e0dd9531..1c47d859e5 100644 --- a/backend/src/main/kotlin/org/loculus/backend/config/Config.kt +++ b/backend/src/main/kotlin/org/loculus/backend/config/Config.kt @@ -2,23 +2,20 @@ package org.loculus.backend.config import com.fasterxml.jackson.annotation.JsonProperty import org.apache.commons.lang3.StringUtils.lowerCase -import org.loculus.backend.api.Organism +import org.springframework.boot.context.properties.ConfigurationProperties +/** + * Backend technical configuration sourced from Spring/Helm. Domain config (organisms, + * accessionPrefix, dataUseTerms, fileSharing) now lives in the database and is accessed + * via [org.loculus.backend.config.service.ConfigService]. + */ +@ConfigurationProperties(prefix = "loculus.backend") data class BackendConfig( val websiteUrl: String, val backendUrl: String, - val organisms: Map, - val accessionPrefix: String, - val dataUseTerms: DataUseTerms, - val fileSharing: FileSharing = FileSharing(), val zstdCompressionLevel: Int = 10, - val pipelineVersionUpgradeCheckIntervalSeconds: Long = 10, val readOnlyMode: Boolean = false, -) { - fun getInstanceConfig(organism: Organism) = organisms[organism.name] ?: throw IllegalArgumentException( - "Organism: ${organism.name} not found in backend config. Available organisms: ${organisms.keys}", - ) -} +) data class DataUseTerms(val enabled: Boolean, val urls: DataUseTermsUrls?) @@ -46,19 +43,125 @@ enum class FileUrlType { override fun toString(): String = lowerCase(name) } -data class InstanceConfig(val schema: Schema, val referenceGenome: ReferenceGenome) +data class OrganismConfig( + val schema: Schema, + val referenceGenome: ReferenceGenome, + /** + * Human-facing display name for this organism (e.g. "Lassa virus"). The + * canonical name field for new code — read by `PublicConfigController` + * and (preferred) by the website's `configTransform`. Nullable so a + * freshly-created unreleased organism can exist before an admin assigns + * a name; consumers fall back to the organism key. + * + * Note: there is a historical second name field, `schema.organismName`, + * inherited from the pre-DB Helm values schema. It is preserved for + * backward compatibility with the website's internal schema type and the + * SILO database config (`instanceName`); new code should not read it, + * and a follow-up cleanup will drop it once those consumers migrate. + */ + val displayName: String? = null, + val description: String? = null, + val image: OrganismImage? = null, + val referenceGenomes: List? = null, + /** + * Upstream LAPIS base URL for this organism, used by the backend's LAPIS + * proxy / query API to forward requests (e.g. the in-cluster service URL + * `http://loculus-lapis-{organismKey}:8080`). Set by the deployment + * tooling when an organism is created/published. Nullable so a freshly + * created organism can exist before it is assigned; the proxy returns 404 + * when it is absent. + */ + val lapisUrl: String? = null, +) + +data class OrganismImage(val url: String) + +data class LinkOut( + val name: String, + val url: String, + val maxNumberOfRecommendedEntries: Int? = null, + val onlyForReferences: Map? = null, + val category: String? = null, +) + +data class MultiFieldSearch( + val name: String, + val displayName: String, + val fields: List, + val orderInSearchDisplay: Int? = null, +) + +data class InputFieldOption(val name: String) + +data class InputField( + val name: String, + val displayName: String? = null, + val noEdit: Boolean? = null, + val required: Boolean? = null, + val definition: String? = null, + val example: Any? = null, + val guidance: String? = null, + val desired: Boolean? = null, + val options: List? = null, +) + +data class ReferenceGenomeSegment(val name: String, val displayName: String? = null, val references: List) + +data class Reference( + val name: String, + val displayName: String? = null, + val sequence: String, + val insdcAccessionFull: String? = null, + val genes: List? = null, +) + +data class ReferenceGene(val name: String, val sequence: String) + +enum class OrderDirection { + @JsonProperty("ascending") + ASCENDING, + + @JsonProperty("descending") + DESCENDING, + + ; + + override fun toString(): String = lowerCase(name) +} data class Schema( + /** + * **Legacy.** The original required name field from the pre-DB Helm + * config schema. Still read by the website's `configTransform` (which + * maps it onto the website-internal `Schema.organismName`) and by the + * SILO database config's `instanceName`. New code should prefer + * `OrganismConfig.displayName` and treat this as a backward-compat + * fallback; this field will be removed once all consumers migrate. + */ val organismName: String, + val image: String? = null, val metadata: List, val externalMetadata: List = emptyList(), + val metadataTemplate: List? = null, + val inputFields: List = emptyList(), + val tableColumns: List = emptyList(), + val primaryKey: String? = null, + val defaultOrderBy: String? = null, + val defaultOrder: OrderDirection? = null, val earliestReleaseDate: EarliestReleaseDate = EarliestReleaseDate(false, emptyList()), val submissionDataTypes: SubmissionDataTypes = SubmissionDataTypes(), val files: List = emptyList(), // Allowed file categories for output files + val loadSequencesAutomatically: Boolean? = null, + val richFastaHeaderFields: List? = null, + val linkOuts: List = emptyList(), + val referenceIdentifierField: String? = null, + val multiFieldSearches: List? = null, ) data class SubmissionDataTypes( val consensusSequences: Boolean = true, + // Whether aligned nucleotide sequences / mutations / insertions are available (LAPIS query API support) + val alignedNucleotideSequences: Boolean = true, val maxSequencesPerEntry: Int? = null, // null means unlimited sequences per entry // Allowed file categories for submission files val files: FilesSubmissionDataType = FilesSubmissionDataType(false, emptyList()), @@ -66,7 +169,7 @@ data class SubmissionDataTypes( data class FilesSubmissionDataType(val enabled: Boolean = false, val categories: List) -data class FileCategory(val name: String) +data class FileCategory(val name: String, val displayName: String? = null) // The Json property names need to be kept in sync with website config enum `metadataPossibleTypes` in `config.ts` // They also need to be in sync with SILO database config, as the Loculus config is a sort of superset of it @@ -87,6 +190,9 @@ enum class MetadataType { @JsonProperty("date") DATE, + @JsonProperty("timestamp") + TIMESTAMP, + @JsonProperty("boolean") BOOLEAN, @@ -98,6 +204,20 @@ enum class MetadataType { override fun toString(): String = lowerCase(name) } +data class RangeOverlapSearch(val rangeName: String, val rangeDisplayName: String, val bound: RangeBound) + +enum class RangeBound { + @JsonProperty("lower") + LOWER, + + @JsonProperty("upper") + UPPER, + + ; + + override fun toString(): String = lowerCase(name) +} + // common abstraction sealed class BaseMetadata { abstract val name: String @@ -109,8 +229,98 @@ data class Metadata( override val name: String, override val type: MetadataType, override val required: Boolean = false, + val displayName: String? = null, + val description: String? = null, + val definition: String? = null, + val header: String? = null, + val hidden: Boolean? = null, + val customDisplay: Map? = null, + val autocomplete: Boolean? = null, + val notSearchable: Boolean? = null, + val noInput: Boolean? = null, + val hideInSearchResultsTable: Boolean? = null, + val initiallyVisible: Boolean? = null, + val hideOnSequenceDetailsPage: Boolean? = null, + val rangeSearch: Boolean? = null, + val rangeOverlapSearch: RangeOverlapSearch? = null, + val substringSearch: Boolean? = null, + val lineageSearch: Boolean? = null, + val columnWidth: Int? = null, + val order: Int? = null, + val orderOnDetailsPage: Int? = null, + val orderInSearchDisplay: Int? = null, + val includeInDownloadsByDefault: Boolean? = null, + val onlyForReference: String? = null, + val isSequenceFilter: Boolean? = null, + val relatesToSegment: String? = null, + val percentage: Boolean? = null, + // Adapter-side fields used to render SILO config (see config-tools/src/adapter). + val perSegment: Boolean? = null, + val lineageSystem: String? = null, + val generateIndex: Boolean? = null, + val oneHeader: Boolean? = null, + val options: List? = null, + val ingest: String? = null, + @get:JsonProperty("ontology_id") + @field:JsonProperty("ontology_id") + val ontologyId: String? = null, ) : BaseMetadata() +data class MetadataOption(val name: String) + +/** + * Expands `perSegment` metadata fields into one entry per nucleotide segment for + * multi-segment organisms (e.g. `completeness` -> `completeness_L`, + * `completeness_M`, `completeness_S`), mirroring the per-segment field names the + * preprocessing pipeline emits and the SILO/LAPIS adapter config expects. + * Single-segment organisms are returned unchanged. + * + * The backend must use this *effective* metadata field set everywhere it + * reasons about processed-metadata fields — submit validation + * ([ProcessedSequenceEntryValidator]) and the released-data projection + * ([EmptyProcessedDataProvider]) — so the data it accepts, stores and serves + * stays consistent with SILO's schema. (On the legacy Helm path this expansion + * was baked into `generateBackendMetadata`.) + */ +fun expandPerSegmentMetadata(metadata: List, segments: List): List { + if (segments.size <= 1) { + return metadata + } + return metadata.flatMap { field -> + if (field.perSegment == true) { + val baseDisplayName = field.displayName ?: field.name + segments.map { segment -> + field.copy( + name = "${field.name}_$segment", + displayName = "$baseDisplayName $segment", + customDisplay = field.customDisplay?.let { customDisplay -> + val updated = customDisplay.toMutableMap() + customDisplay["displayGroup"]?.let { updated["displayGroup"] = "${it}_$segment" } + customDisplay["label"]?.let { updated["label"] = "$it $segment" } + updated + }, + perSegment = false, + ) + } + } else { + listOf(field) + } + } +} + +/** + * The nucleotide *segment* names used for perSegment field expansion. The + * preprocessing pipeline and the SILO/LAPIS adapter derive these from the rich + * [OrganismConfig.referenceGenomes] (segment names such as `L`/`M`/`S`, or a + * single `main`) — NOT from the simple [OrganismConfig.referenceGenome] + * `nucleotideSequences`, which for multi-*reference* organisms holds per-reference + * entries (e.g. `M-MH396653`, or enterovirus per-type references) that are not + * segments. Falls back to the simple genome only when `referenceGenomes` is absent. + */ +fun perSegmentExpansionSegments(organismConfig: OrganismConfig): List = + organismConfig.referenceGenomes?.map { it.name }?.distinct() + ?: organismConfig.referenceGenome.nucleotideSequences.map { it.name } + data class ExternalMetadata( val externalMetadataUpdater: String, override val name: String, diff --git a/backend/src/main/kotlin/org/loculus/backend/config/InstanceConfig.kt b/backend/src/main/kotlin/org/loculus/backend/config/InstanceConfig.kt new file mode 100644 index 0000000000..b79acdecfc --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/InstanceConfig.kt @@ -0,0 +1,112 @@ +package org.loculus.backend.config + +import com.fasterxml.jackson.annotation.JsonProperty +import org.apache.commons.lang3.StringUtils.lowerCase + +data class InstanceConfig( + val name: String, + val accessionPrefix: String, + val dataUseTerms: DataUseTerms = DataUseTerms(enabled = false, urls = null), + val fileSharing: FileSharing = FileSharing(), + val description: String? = null, + val logo: Logo? = null, + val supportContact: SupportContact? = null, + val bannerMessage: String? = null, + val bannerMessageURL: String? = null, + val submissionBannerMessage: String? = null, + val submissionBannerMessageURL: String? = null, + val welcomeMessageHTML: String? = null, + val additionalHeadHTML: String? = null, + val gitHubEditLink: String? = null, + val gitHubMainUrl: String? = null, + val gitHubIssuesUrl: String? = null, + val issuesEmail: String? = null, + val enableSeqSets: Boolean = false, + val seqSetsFieldsToDisplay: List? = null, + val seqSetsGraphs: List? = null, + val enableLoginNavigationItem: Boolean = true, + val enableSubmissionNavigationItem: Boolean = true, + val enableSubmissionPages: Boolean = true, + val dataUseTermsAgreementHTML: String? = null, + val sequenceFlagging: SequenceFlaggingConfig? = null, + val dateFieldForGroupGraph: String? = null, + // Map: lineage system key (e.g. `pangoLineage`) → pipeline version (string) → definition-file URL. + val lineageSystemDefinitions: Map>? = null, + // SQL-backed views, keyed by their public route/proxy key. + val views: Map = emptyMap(), + // Legacy single overview table / LAPIS instance configuration. + val overview: OverviewConfig? = null, +) + +/** + * Instance-level configuration for SQL-backed view tables / LAPIS instances. + * `query` defines the view over per-organism transformed release files; `schema` + * is the manual SILO database_config.yaml for the query output; `tableColumns` + * are visible by default. Views are metadata-only unless `sequenceData` opts in. + */ +data class ViewConfig( + val displayName: String = "Overview", + val query: String, + val schema: String, + val tableColumns: List = emptyList(), + val sequenceData: ViewSequenceData? = null, + // Upstream LAPIS base URL for this view instance, used by the backend proxy. + val lapisUrl: String? = null, +) + +typealias OverviewConfig = ViewConfig + +data class ViewSequenceData(val unalignedNucleotideSequences: ViewUnalignedNucleotideSequences? = null) + +data class ViewUnalignedNucleotideSequences( + val enabled: Boolean = false, + val segments: List = emptyList(), + val sourceSegments: Map> = emptyMap(), +) + +fun InstanceConfig.configuredViews(): Map = + if (overview == null) views else mapOf("overview" to overview) + views + +data class Logo(val url: String, val alt: String? = null, val height: Int? = null, val width: Int? = null) + +data class SupportContact(val email: String? = null, val url: String? = null) + +data class FieldToDisplay(val field: String, val displayName: String) + +data class SeqSetGraph(val name: String, val displayName: String, val type: SeqSetGraphType, val fields: List) + +enum class SeqSetGraphType { + @JsonProperty("date") + DATE, + + @JsonProperty("category") + CATEGORY, + + ; + + override fun toString(): String = lowerCase(name) +} + +data class SequenceFlaggingConfig(val github: GithubSequenceFlagging) + +data class GithubSequenceFlagging(val organization: String, val repository: String, val issueTemplate: String? = null) + +val DEFAULT_INSTANCE_CONFIG = InstanceConfig( + name = "Loculus", + accessionPrefix = "LOC_", + dataUseTerms = DataUseTerms(enabled = false, urls = null), + fileSharing = FileSharing(), +) + +const val DEFAULT_INSTANCE_CONFIG_JSON = """{"name":"Loculus","accessionPrefix":"LOC_",""" + + """"dataUseTerms":{"enabled":false,"urls":null},""" + + """"fileSharing":{"outputFileUrlType":"website"},""" + + """"description":null,"logo":null,"supportContact":null,""" + + """"bannerMessage":null,"bannerMessageURL":null,""" + + """"submissionBannerMessage":null,"submissionBannerMessageURL":null,""" + + """"welcomeMessageHTML":null,"additionalHeadHTML":null,""" + + """"gitHubEditLink":null,"gitHubMainUrl":null,"gitHubIssuesUrl":null,"issuesEmail":null,""" + + """"enableSeqSets":false,"seqSetsFieldsToDisplay":null,"seqSetsGraphs":null,""" + + """"enableLoginNavigationItem":true,"enableSubmissionNavigationItem":true,"enableSubmissionPages":true,""" + + """"dataUseTermsAgreementHTML":null,"sequenceFlagging":null,"dateFieldForGroupGraph":null,""" + + """"lineageSystemDefinitions":null,"views":{},"overview":null}""" diff --git a/backend/src/main/kotlin/org/loculus/backend/config/OpenApi.kt b/backend/src/main/kotlin/org/loculus/backend/config/OpenApi.kt index 685be45b12..7a9296d037 100644 --- a/backend/src/main/kotlin/org/loculus/backend/config/OpenApi.kt +++ b/backend/src/main/kotlin/org/loculus/backend/config/OpenApi.kt @@ -3,27 +3,43 @@ package org.loculus.backend.config import io.swagger.v3.oas.models.Components import io.swagger.v3.oas.models.OpenAPI import io.swagger.v3.oas.models.media.Schema +import io.swagger.v3.oas.models.media.StringSchema import io.swagger.v3.oas.models.security.SecurityRequirement import io.swagger.v3.oas.models.security.SecurityScheme +import kotlinx.datetime.LocalDateTime import org.loculus.backend.controller.PROJECT_NAME +import org.springdoc.core.utils.SpringDocUtils const val ORGANISM_SCHEMA_NAME = "Organism" -fun buildOpenApiSchema(backendConfig: BackendConfig): OpenAPI = OpenAPI() - .addSecurityItem(SecurityRequirement().addList("bearerAuth")) - .components( - Components() - .addSchemas( - ORGANISM_SCHEMA_NAME, - Schema() - .type("string") - .description("valid names of organisms that this $PROJECT_NAME instance supports") - ._enum(backendConfig.organisms.keys.toList()), - ) - .addSecuritySchemes( - "bearerAuth", - SecurityScheme().name("bearerAuth").type(SecurityScheme.Type.HTTP).`in`(SecurityScheme.In.HEADER) - .scheme("bearer") - .bearerFormat("JWT"), - ), +// Organism keys are DB-backed; the schema is a plain string and clients should +// consult `GET /api/config/organisms` for the live list of valid keys. +fun buildOpenApiSchema(): OpenAPI { + SpringDocUtils.getConfig().replaceWithSchema( + LocalDateTime::class.java, + StringSchema() + .format("date-time") + .example("2026-05-24T12:15:55.221007"), ) + + return OpenAPI() + .addSecurityItem(SecurityRequirement().addList("bearerAuth")) + .components( + Components() + .addSchemas( + ORGANISM_SCHEMA_NAME, + Schema() + .type("string") + .description( + "Key of an organism configured in this $PROJECT_NAME instance. " + + "Use GET /api/config/organisms for the live list of valid values.", + ), + ) + .addSecuritySchemes( + "bearerAuth", + SecurityScheme().name("bearerAuth").type(SecurityScheme.Type.HTTP).`in`(SecurityScheme.In.HEADER) + .scheme("bearer") + .bearerFormat("JWT"), + ), + ) +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/QueryOpenApiCustomizer.kt b/backend/src/main/kotlin/org/loculus/backend/config/QueryOpenApiCustomizer.kt new file mode 100644 index 0000000000..d97092439c --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/QueryOpenApiCustomizer.kt @@ -0,0 +1,537 @@ +package org.loculus.backend.config + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import io.swagger.v3.core.util.Json +import io.swagger.v3.oas.models.Operation +import io.swagger.v3.oas.models.PathItem +import io.swagger.v3.oas.models.media.ArraySchema +import io.swagger.v3.oas.models.media.BooleanSchema +import io.swagger.v3.oas.models.media.Content +import io.swagger.v3.oas.models.media.IntegerSchema +import io.swagger.v3.oas.models.media.MediaType +import io.swagger.v3.oas.models.media.NumberSchema +import io.swagger.v3.oas.models.media.ObjectSchema +import io.swagger.v3.oas.models.media.StringSchema +import io.swagger.v3.oas.models.parameters.Parameter +import io.swagger.v3.oas.models.parameters.QueryParameter +import io.swagger.v3.oas.models.parameters.RequestBody +import org.loculus.backend.config.service.ConfigService +import org.springdoc.core.customizers.OpenApiCustomizer +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order +import io.swagger.v3.oas.models.media.Schema as OpenApiSchema + +@Configuration +class QueryOpenApiCustomizer { + private val yamlMapper = ObjectMapper(YAMLFactory()).registerKotlinModule() + + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + fun organismSpecificQueryOpenApiCustomizer(configService: ConfigService) = OpenApiCustomizer { openApi -> + val genericQueryPaths = openApi.paths + .filterKeys { it.startsWith("/query/{organism}/") } + .toMap() + + genericQueryPaths.keys.forEach { openApi.paths.remove(it) } + + // Reading organisms hits the config DB. If that is unavailable (e.g. during + // OpenAPI generation in tests without a database) degrade gracefully to no + // organism-specific paths rather than failing doc generation entirely. + val organisms = runCatching { + configService.listReleasedOrganisms().mapNotNull { listing -> + runCatching { listing.key to configService.getOrganismConfig(listing.key).config }.getOrNull() + } + overviewOpenApiConfig(configService) + }.getOrDefault(emptyList()) + + genericQueryPaths.forEach { (genericPath, genericPathItem) -> + organisms.forEach { (organism, organismConfig) -> + val pathItem = clonePathItem(genericPathItem) + customizePathItem(pathItem, genericPath, organism, organismConfig) + if (pathItem.readOperations().isNotEmpty()) { + openApi.paths.addPathItem(genericPath.replace("{organism}", organism), pathItem) + } + } + } + updateLapisProxyOpenApiTags(openApi) + } + + private fun customizePathItem( + pathItem: PathItem, + genericPath: String, + organism: String, + organismConfig: OrganismConfig, + ) { + pathItem.readOperationsMap().forEach { (method, operation) -> + val kind = classifyEndpoint(genericPath) ?: return@forEach + if (!supportsEndpoint(kind, organismConfig)) { + removeOperation(pathItem, method) + return@forEach + } + operation.tags = listOf("Query: $organism") + operation.parameters = pathParameters(operation, kind, organism, organismConfig) + + getQueryParameters(kind, organismConfig) + + if (operation.requestBody != null) { + operation.requestBody = RequestBody().content( + Content().addMediaType( + "application/json", + MediaType().schema(postBodySchema(kind, organismConfig)), + ), + ) + } + } + } + + private fun classifyEndpoint(genericPath: String): QueryEndpointKind? = when { + genericPath.endsWith("/metadata") -> QueryEndpointKind.METADATA + + genericPath.endsWith("/aggregated") -> QueryEndpointKind.AGGREGATED + + genericPath.endsWith("/sequences/{segment}") -> QueryEndpointKind.UNALIGNED_SEQUENCES_SEGMENT + + genericPath.endsWith("/sequences") -> QueryEndpointKind.UNALIGNED_SEQUENCES + + genericPath.endsWith("/sequencesAligned/insertions") -> QueryEndpointKind.NUCLEOTIDE_INSERTIONS + + genericPath.endsWith("/sequencesAligned/mutations") || + genericPath.endsWith("/sequencesAligned/aggregatedMutations") || + genericPath.endsWith("/sequencesAligned/{referenceName}/mutations") || + genericPath.endsWith("/sequencesAligned/{referenceName}/aggregatedMutations") -> + QueryEndpointKind.NUCLEOTIDE_MUTATIONS + + genericPath.endsWith( + "/sequencesAligned/{referenceName}", + ) -> QueryEndpointKind.ALIGNED_NUCLEOTIDE_SEQUENCES_REFERENCE + + genericPath.endsWith("/sequencesAligned") -> QueryEndpointKind.ALIGNED_NUCLEOTIDE_SEQUENCES + + genericPath.endsWith("/translations/insertions") -> QueryEndpointKind.AMINO_ACID_INSERTIONS + + genericPath.endsWith("/translations/mutations") || + genericPath.endsWith("/translations/{geneName}/mutations") || + genericPath.endsWith("/translations/{geneName}/aggregatedMutations") -> + QueryEndpointKind.AMINO_ACID_MUTATIONS + + genericPath.endsWith("/translations/{geneName}") -> QueryEndpointKind.TRANSLATIONS + + else -> null + } + + private fun supportsEndpoint(kind: QueryEndpointKind, organismConfig: OrganismConfig): Boolean { + val supportsConsensusSequences = organismConfig.schema.submissionDataTypes.consensusSequences + val hasNucleotideSequences = organismConfig.referenceGenome.nucleotideSequences.isNotEmpty() + val hasAlignedNucleotideSequences = organismConfig.schema.submissionDataTypes.alignedNucleotideSequences + val hasGenes = organismConfig.referenceGenome.genes.isNotEmpty() + + return when (kind) { + QueryEndpointKind.METADATA, + QueryEndpointKind.AGGREGATED, + -> true + + QueryEndpointKind.UNALIGNED_SEQUENCES, + QueryEndpointKind.UNALIGNED_SEQUENCES_SEGMENT, + -> supportsConsensusSequences && hasNucleotideSequences + + QueryEndpointKind.ALIGNED_NUCLEOTIDE_SEQUENCES, + QueryEndpointKind.ALIGNED_NUCLEOTIDE_SEQUENCES_REFERENCE, + QueryEndpointKind.NUCLEOTIDE_MUTATIONS, + QueryEndpointKind.NUCLEOTIDE_INSERTIONS, + -> supportsConsensusSequences && hasNucleotideSequences && hasAlignedNucleotideSequences + + QueryEndpointKind.TRANSLATIONS, + QueryEndpointKind.AMINO_ACID_MUTATIONS, + QueryEndpointKind.AMINO_ACID_INSERTIONS, + -> supportsConsensusSequences && hasGenes + } + } + + private fun postBodySchema(kind: QueryEndpointKind, organismConfig: OrganismConfig): OpenApiSchema { + val fieldNames = metadataFieldNames(organismConfig) + val schema = ObjectSchema() + .description("LAPIS-compatible query body for ${organismConfig.schema.organismName}.") + .addProperty("limit", IntegerSchema().description("Maximum number of rows to return.")) + .addProperty("offset", IntegerSchema().description("Number of rows to skip.")) + .addProperty( + "orderBy", + ArraySchema() + .items(orderBySchema(fieldNames)) + .description("Metadata fields to order by."), + ) + .addProperty("advancedQuery", StringSchema().description("LAPIS advanced query expression.")) + .addProperty( + "nucleotideMutations", + ArraySchema() + .items(StringSchema().example("main:A123T")) + .description("Nucleotide mutation filters."), + ) + .addProperty( + "aminoAcidMutations", + ArraySchema() + .items(StringSchema().example("S:123T")) + .description("Amino acid mutation filters."), + ) + .addProperty( + "nucleotideInsertions", + ArraySchema() + .items(StringSchema().example("ins_123:ATT")) + .description("Nucleotide insertion filters."), + ) + .addProperty( + "aminoAcidInsertions", + ArraySchema() + .items(StringSchema().example("ins_ORF1a:123:ATT")) + .description("Amino acid insertion filters."), + ) + .addProperty( + "downloadAsFile", + BooleanSchema().description("Set to true to download the response as a file."), + ) + .addProperty("downloadFileBasename", StringSchema().description("Base name for the downloaded file.")) + .addProperty( + "compression", + StringSchema()._enum(compressionFormats()) + .description("Optional response compression. Omit for an uncompressed response."), + ) + schema.additionalProperties = true + + metadataFields(organismConfig).forEach { metadata -> + addMetadataFilterProperties(schema, metadata) + } + + if (kind.supportsFields()) { + schema.addProperty( + "fields", + ArraySchema() + .items(StringSchema()._enum(fieldNames)) + .description("Metadata fields to return or aggregate."), + ) + } + + schema.addProperty( + "dataFormat", + StringSchema() + ._enum(kind.dataFormats()) + .description("Response format for this endpoint."), + ) + + if (kind.supportsFastaHeaderTemplate()) { + schema.addProperty( + "fastaHeaderTemplate", + StringSchema().description("Template for FASTA headers, e.g. {accessionVersion}."), + ) + } + + if (kind.supportsMinProportion()) { + schema.addProperty("minProportion", NumberSchema().description("Minimum mutation proportion to return.")) + } + + schema.example = kind.postExample(fieldNames) + return schema + } + + private fun getQueryParameters(kind: QueryEndpointKind, organismConfig: OrganismConfig): List { + val fieldNames = metadataFieldNames(organismConfig) + val parameters = mutableListOf( + queryParameter("downloadAsFile", BooleanSchema(), "Set to true to download the response as a file."), + queryParameter("downloadFileBasename", StringSchema(), "Base name for the downloaded file."), + queryParameter( + "compression", + StringSchema()._enum(compressionFormats()), + "Optional response compression. Omit for an uncompressed response.", + ), + queryParameter("limit", IntegerSchema(), "Maximum number of rows to return."), + queryParameter("offset", IntegerSchema(), "Number of rows to skip."), + queryParameter("orderBy", StringSchema(), "Fields to order by, for example date or date:descending."), + queryParameter("advancedQuery", StringSchema(), "LAPIS advanced query expression."), + queryParameter("nucleotideMutations", arrayOfStrings(), "Nucleotide mutation filters."), + queryParameter("aminoAcidMutations", arrayOfStrings(), "Amino acid mutation filters."), + queryParameter("nucleotideInsertions", arrayOfStrings(), "Nucleotide insertion filters."), + queryParameter("aminoAcidInsertions", arrayOfStrings(), "Amino acid insertion filters."), + ) + parameters.addAll(metadataFilterQueryParameters(organismConfig)) + + if (kind.supportsFields()) { + parameters.add( + queryParameter( + "fields", + arrayOfStrings(fieldNames), + "Metadata fields to return. Repeat the parameter or pass comma-separated values.", + ), + ) + } + parameters.add( + queryParameter( + "dataFormat", + StringSchema()._enum(kind.dataFormats()), + "Response format for this endpoint.", + ), + ) + if (kind.supportsFastaHeaderTemplate()) { + parameters.add( + queryParameter( + "fastaHeaderTemplate", + StringSchema(), + "Template for FASTA headers, e.g. {accessionVersion}.", + ), + ) + } + if (kind.supportsMinProportion()) { + parameters.add(queryParameter("minProportion", NumberSchema(), "Minimum mutation proportion to return.")) + } + + return parameters + } + + private fun pathParameters( + operation: Operation, + kind: QueryEndpointKind, + organism: String, + organismConfig: OrganismConfig, + ) = operation.parameters + ?.filterNot { it.name == "organism" || it.name == "Accept" } + ?.onEach { parameter -> + when (parameter.name) { + "versionGroup" -> { + parameter.description = "Use current for latest versions or allVersions for version history." + parameter.example = "current" + parameter.schema = StringSchema()._enum(listOf("current", "allVersions")) + } + + "segment" -> { + parameter.description = "Sequence segment name for $organism." + parameter.schema = StringSchema()._enum(nucleotideSequenceNames(organismConfig)) + } + + "referenceName" -> { + parameter.description = "Nucleotide reference name for $organism." + parameter.schema = StringSchema()._enum(nucleotideSequenceNames(organismConfig)) + } + + "geneName" -> { + parameter.description = "Gene name for $organism." + parameter.schema = StringSchema()._enum(geneNames(organismConfig)) + } + } + } + ?.filter { parameter -> + parameter.name != "segment" || kind == QueryEndpointKind.UNALIGNED_SEQUENCES_SEGMENT + } + ?: emptyList() + + private fun removeOperation(pathItem: PathItem, method: PathItem.HttpMethod) { + when (method) { + PathItem.HttpMethod.GET -> pathItem.get = null + PathItem.HttpMethod.PUT -> pathItem.put = null + PathItem.HttpMethod.POST -> pathItem.post = null + PathItem.HttpMethod.DELETE -> pathItem.delete = null + PathItem.HttpMethod.OPTIONS -> pathItem.options = null + PathItem.HttpMethod.HEAD -> pathItem.head = null + PathItem.HttpMethod.PATCH -> pathItem.patch = null + PathItem.HttpMethod.TRACE -> pathItem.trace = null + } + } + + private fun queryParameter(name: String, schema: OpenApiSchema<*>, description: String) = QueryParameter().apply { + this.name = name + this.required = false + this.description = description + this.schema = schema + } + + private fun metadataFilterQueryParameters(organismConfig: OrganismConfig) = + metadataFields(organismConfig).flatMap { metadata -> + val parameters = mutableListOf( + queryParameter(metadata.name, metadataFilterSchema(metadata), "Filter by ${metadata.name}."), + queryParameter( + "${metadata.name}.isNull", + BooleanSchema(), + "Filter by nullness of ${metadata.name}.", + ), + ) + when (metadata.type) { + MetadataType.STRING, MetadataType.AUTHORS -> parameters.add( + queryParameter( + "${metadata.name}.regex", + StringSchema(), + "Regex filter for ${metadata.name}.", + ), + ) + + MetadataType.INTEGER, + MetadataType.FLOAT, + MetadataType.NUMBER, + MetadataType.DATE, + MetadataType.TIMESTAMP, + -> { + parameters.add( + queryParameter( + "${metadata.name}From", + metadataFilterSchema(metadata), + "Lower bound filter for ${metadata.name}.", + ), + ) + parameters.add( + queryParameter( + "${metadata.name}To", + metadataFilterSchema(metadata), + "Upper bound filter for ${metadata.name}.", + ), + ) + } + + MetadataType.BOOLEAN -> Unit + } + parameters + } + + private fun arrayOfStrings(values: List) = ArraySchema() + .items(StringSchema()._enum(values)) + + private fun arrayOfStrings() = ArraySchema() + .items(StringSchema()) + + private fun orderBySchema(fieldNames: List) = ObjectSchema() + .addProperty("field", StringSchema()._enum(fieldNames)) + .addProperty("type", StringSchema()._enum(listOf("ascending", "descending"))) + + private fun addMetadataFilterProperties(schema: OpenApiSchema, metadata: BaseMetadata) { + schema.addProperty(metadata.name, metadataFilterSchema(metadata)) + schema.addProperty( + "${metadata.name}.isNull", + BooleanSchema().description("Filter by nullness of ${metadata.name}."), + ) + when (metadata.type) { + MetadataType.STRING, MetadataType.AUTHORS -> schema.addProperty( + "${metadata.name}.regex", + StringSchema().description("Regex filter for ${metadata.name}."), + ) + + MetadataType.INTEGER, + MetadataType.FLOAT, + MetadataType.NUMBER, + MetadataType.DATE, + MetadataType.TIMESTAMP, + -> { + schema.addProperty("${metadata.name}From", metadataFilterSchema(metadata)) + schema.addProperty("${metadata.name}To", metadataFilterSchema(metadata)) + } + + MetadataType.BOOLEAN -> Unit + } + } + + private fun metadataFilterSchema(metadata: BaseMetadata): OpenApiSchema<*> { + val schema = when (metadata.type) { + MetadataType.STRING, MetadataType.AUTHORS -> StringSchema() + MetadataType.INTEGER, MetadataType.TIMESTAMP -> IntegerSchema() + MetadataType.FLOAT, MetadataType.NUMBER -> NumberSchema() + MetadataType.DATE -> StringSchema().format("date") + MetadataType.BOOLEAN -> BooleanSchema() + } + schema.description = "Filter by ${metadata.name}." + schema.nullable = true + return schema + } + + private fun metadataFields(organismConfig: OrganismConfig) = + (organismConfig.schema.metadata + organismConfig.schema.externalMetadata) + .distinctBy { it.name } + + private fun metadataFieldNames(organismConfig: OrganismConfig) = metadataFields(organismConfig).map { it.name } + + private fun nucleotideSequenceNames(organismConfig: OrganismConfig) = + organismConfig.referenceGenome.nucleotideSequences.map { it.name } + + private fun geneNames(organismConfig: OrganismConfig) = organismConfig.referenceGenome.genes.map { it.name } + + private fun compressionFormats() = listOf("gzip", "zstd") + + private fun clonePathItem(pathItem: PathItem): PathItem = Json.mapper().convertValue(pathItem, PathItem::class.java) + + private fun overviewOpenApiConfig(configService: ConfigService): List> = runCatching { + configService.getInstanceConfig().config.configuredViews().map { (key, view) -> + key to view.toOrganismConfig() + } + }.getOrDefault(emptyList()) + + private fun ViewConfig.toOrganismConfig(): OrganismConfig { + val root = yamlMapper.readTree(schema) + val schemaNode = root.required("schema") + val metadata = schemaNode.required("metadata").map { yamlMapper.treeToValue(it, Metadata::class.java) } + val sequenceSegments = sequenceData + ?.unalignedNucleotideSequences + ?.takeIf { it.enabled } + ?.segments + .orEmpty() + return OrganismConfig( + schema = Schema( + organismName = displayName, + metadata = metadata, + inputFields = emptyList(), + tableColumns = tableColumns, + primaryKey = schemaNode.path("primaryKey").asText("accessionVersion"), + defaultOrderBy = schemaNode.path( + "defaultOrderBy", + ).asText(schemaNode.path("primaryKey").asText("accessionVersion")), + defaultOrder = when (schemaNode.path("defaultOrder").asText("ascending")) { + "descending" -> OrderDirection.DESCENDING + else -> OrderDirection.ASCENDING + }, + submissionDataTypes = SubmissionDataTypes( + consensusSequences = sequenceSegments.isNotEmpty(), + alignedNucleotideSequences = false, + ), + ), + referenceGenome = ReferenceGenome(sequenceSegments.map { ReferenceSequence(it, "N") }, emptyList()), + displayName = displayName, + lapisUrl = lapisUrl, + ) + } +} + +private enum class QueryEndpointKind { + METADATA, + AGGREGATED, + UNALIGNED_SEQUENCES, + UNALIGNED_SEQUENCES_SEGMENT, + ALIGNED_NUCLEOTIDE_SEQUENCES, + ALIGNED_NUCLEOTIDE_SEQUENCES_REFERENCE, + NUCLEOTIDE_MUTATIONS, + NUCLEOTIDE_INSERTIONS, + TRANSLATIONS, + AMINO_ACID_MUTATIONS, + AMINO_ACID_INSERTIONS, + ; + + fun supportsFields() = this == METADATA || + this == AGGREGATED || + this == NUCLEOTIDE_MUTATIONS || + this == AMINO_ACID_MUTATIONS + + fun supportsFastaHeaderTemplate() = isSequenceEndpoint() + + fun supportsMinProportion() = this == NUCLEOTIDE_MUTATIONS || this == AMINO_ACID_MUTATIONS + + fun dataFormats() = when { + isSequenceEndpoint() -> listOf("FASTA", "JSON", "NDJSON") + else -> listOf("JSON", "CSV", "CSV-WITHOUT-HEADERS", "TSV", "TSV-ESCAPED") + } + + fun postExample(fieldNames: List) = when { + this == METADATA -> mapOf("fields" to fieldNames.take(1), "limit" to 10) + this == AGGREGATED -> mapOf("fields" to fieldNames.take(1)) + isSequenceEndpoint() -> mapOf("dataFormat" to "FASTA", "limit" to 10) + supportsMinProportion() -> mapOf("minProportion" to 0.01) + else -> mapOf("limit" to 10) + } + + private fun isSequenceEndpoint() = this == UNALIGNED_SEQUENCES || + this == UNALIGNED_SEQUENCES_SEGMENT || + this == ALIGNED_NUCLEOTIDE_SEQUENCES || + this == ALIGNED_NUCLEOTIDE_SEQUENCES_REFERENCE || + this == TRANSLATIONS +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/SecurityConfig.kt b/backend/src/main/kotlin/org/loculus/backend/config/SecurityConfig.kt index a66ef75461..6aa5af6c68 100644 --- a/backend/src/main/kotlin/org/loculus/backend/config/SecurityConfig.kt +++ b/backend/src/main/kotlin/org/loculus/backend/config/SecurityConfig.kt @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import mu.KotlinLogging import org.loculus.backend.auth.Roles.EXTERNAL_METADATA_UPDATER +import org.loculus.backend.auth.Roles.LOCULUS_ADMINISTRATOR import org.loculus.backend.auth.Roles.PREPROCESSING_PIPELINE import org.loculus.backend.auth.Roles.SUPER_USER import org.springframework.beans.factory.InitializingBean @@ -60,6 +61,7 @@ class SecurityConfig { "/*/get-released-data", "/files/get/**", "/groups/*", + "/api/config/**", ) private val headEndpointsThatArePublic = arrayOf( @@ -72,6 +74,7 @@ class SecurityConfig { private val adminEndpoints = arrayOf( "/admin/*", + "/api/admin/config/**", ) @Bean @@ -87,8 +90,13 @@ class SecurityConfig { "/actuator/**", "/api-docs**", "/api-docs/**", + "/api-docs.json/**", "/swagger-ui/**", + "/scalar-api-reference", ).permitAll() + // LAPIS proxy / query API are open (no authentication) for now. + auth.requestMatchers(HttpMethod.GET, "/*/lapis/**", "/query/**").permitAll() + auth.requestMatchers(HttpMethod.POST, "/*/lapis/**", "/query/**").permitAll() auth.requestMatchers(HttpMethod.GET, *getEndpointsThatArePublic).permitAll() auth.requestMatchers(HttpMethod.HEAD, *headEndpointsThatArePublic).permitAll() auth.requestMatchers(HttpMethod.OPTIONS).permitAll() @@ -96,10 +104,13 @@ class SecurityConfig { auth.requestMatchers( *endpointsForExternalMetadataUpdater, ).hasAuthority(EXTERNAL_METADATA_UPDATER) - auth.requestMatchers(*adminEndpoints).hasAuthority(SUPER_USER) + auth.requestMatchers(*adminEndpoints).hasAuthority(LOCULUS_ADMINISTRATOR) auth.requestMatchers(*debugEndpoints).hasAuthority(SUPER_USER) auth.anyRequest().authenticated() } + .csrf { csrf -> + csrf.ignoringRequestMatchers("/*/lapis/**", "/query/**") + } .oauth2ResourceServer { oauth2 -> oauth2.jwt { jwt -> jwt.jwtAuthenticationConverter(keycloakAuthoritiesConverter) diff --git a/backend/src/main/kotlin/org/loculus/backend/config/WebConfig.kt b/backend/src/main/kotlin/org/loculus/backend/config/WebConfig.kt index e30d5e9fa9..d1b3aef8e8 100644 --- a/backend/src/main/kotlin/org/loculus/backend/config/WebConfig.kt +++ b/backend/src/main/kotlin/org/loculus/backend/config/WebConfig.kt @@ -20,6 +20,7 @@ class WebConfig(private val backendConfig: BackendConfig) : WebMvcConfigurer { override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(ReadOnlyModeInterceptor(backendConfig)) + .excludePathPatterns("/*/lapis/**", "/query/**") registry.addInterceptor(OrganismMdcInterceptor()) } diff --git a/backend/src/main/kotlin/org/loculus/backend/config/controller/AdminConfigController.kt b/backend/src/main/kotlin/org/loculus/backend/config/controller/AdminConfigController.kt new file mode 100644 index 0000000000..242a4c0436 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/controller/AdminConfigController.kt @@ -0,0 +1,314 @@ +package org.loculus.backend.config.controller + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import kotlinx.datetime.LocalDateTime +import org.loculus.backend.auth.AuthenticatedUser +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.operations.OperationRequest +import org.loculus.backend.config.operations.OperationValidationException +import org.loculus.backend.config.operations.UnknownOperationException +import org.loculus.backend.config.operations.ValidationError +import org.loculus.backend.config.service.AuditLogService +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.config.service.DraftScopeMismatchException +import org.loculus.backend.config.service.DraftService +import org.loculus.backend.config.service.NoDraftToPublishException +import org.loculus.backend.config.service.OptimisticConcurrencyException +import org.loculus.backend.config.service.OrganismAdminService +import org.loculus.backend.config.service.OrganismAlreadyExistsException +import org.loculus.backend.config.service.OrganismDeploymentException +import org.loculus.backend.config.service.OrganismNotFoundException +import org.loculus.backend.config.service.PreprocessingConfigService +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestHeader +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.ResponseStatus +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/api/admin/config") +@SecurityRequirement(name = "bearerAuth") +class AdminConfigController( + private val configService: ConfigService, + private val organismAdminService: OrganismAdminService, + private val draftService: DraftService, + private val auditLogService: AuditLogService, + private val preprocessingConfigService: PreprocessingConfigService, +) { + @Operation(description = "Create a new unreleased organism.") + @PostMapping("/organisms") + @ResponseStatus(HttpStatus.CREATED) + fun createOrganism( + @RequestBody body: CreateOrganismRequest, + authenticatedUser: AuthenticatedUser, + ): ConfigService.OrganismListing = organismAdminService.createOrganism(body.key, authenticatedUser.username) + + @Operation(description = "List all organisms including unreleased ones.") + @GetMapping("/organisms") + fun listOrganisms(): AdminOrganismsListResponse = + AdminOrganismsListResponse(organisms = configService.listAllOrganisms()) + + @Operation(description = "Get the current draft for an organism (200) or no-content (204) if no draft.") + @GetMapping("/organisms/{key}/draft") + fun getOrganismDraft(@PathVariable key: String): ResponseEntity { + val draft = draftService.getOrganismDraft(key) + ?: return ResponseEntity.noContent().build() + return ResponseEntity.ok(OrganismDraftResponse.from(draft)) + } + + @Operation(description = "Replace the entire draft for an UNRELEASED organism. 403 if released.") + @PutMapping("/organisms/{key}/draft") + fun putOrganismDraft( + @PathVariable key: String, + @RequestBody body: PutDraftRequest, + @RequestHeader(value = "If-Match", required = false) ifMatch: Long?, + authenticatedUser: AuthenticatedUser, + ): DraftMutationResponse { + val newRevision = draftService.putOrganismDraft(key, body.config, ifMatch, authenticatedUser.username) + return DraftMutationResponse(revision = newRevision) + } + + @Operation(description = "Append operation(s) to a RELEASED organism's draft. 403 if unreleased.") + @PostMapping("/organisms/{key}/draft/operations") + fun appendOrganismOperations( + @PathVariable key: String, + @RequestBody body: AppendOperationsRequest, + @RequestHeader(value = "If-Match", required = false) ifMatch: Long?, + authenticatedUser: AuthenticatedUser, + ): DraftMutationResponse { + val newRevision = draftService.appendOrganismOperations( + key = key, + ops = body.operations, + ifMatch = ifMatch, + actor = authenticatedUser.username, + ) + return DraftMutationResponse(revision = newRevision) + } + + @Operation(description = "Discard the entire draft for an organism.") + @DeleteMapping("/organisms/{key}/draft") + @ResponseStatus(HttpStatus.NO_CONTENT) + fun discardOrganismDraft(@PathVariable key: String, authenticatedUser: AuthenticatedUser) { + draftService.discardOrganismDraft(key, authenticatedUser.username) + } + + @Operation(description = "Publish the current draft as a new immutable organism version.") + @PostMapping("/organisms/{key}/publish") + fun publishOrganism(@PathVariable key: String, authenticatedUser: AuthenticatedUser): PublishResponse { + val result = draftService.publishOrganism(key, authenticatedUser.username) + return PublishResponse( + version = result.version, + previousVersion = result.previousVersion, + publishedAt = result.publishedAt, + publishedBy = result.publishedBy, + ) + } + + @Operation( + description = "Mark a released organism as deployed after SILO/LAPIS have been rolled out and checked.", + ) + @PostMapping("/organisms/{key}/mark-deployed") + fun markOrganismDeployed( + @PathVariable key: String, + authenticatedUser: AuthenticatedUser, + ): ConfigService.OrganismListing = organismAdminService.markDeployed(key, authenticatedUser.username) + + @Operation(description = "List known versions of an organism config.") + @GetMapping("/organisms/{key}/versions") + fun listOrganismVersions(@PathVariable key: String) = VersionsResponse( + versions = configService.listOrganismVersions(key), + ) + + @Operation(description = "Get the current instance config draft, if any.") + @GetMapping("/instance/draft") + fun getInstanceDraft(): ResponseEntity { + val draft = draftService.getInstanceDraft() ?: return ResponseEntity.noContent().build() + return ResponseEntity.ok( + InstanceDraftResponse(config = draft.config, baseVersion = draft.baseVersion, revision = draft.revision), + ) + } + + @Operation(description = "Replace the entire instance config draft (full-document PUT).") + @PutMapping("/instance/draft") + fun putInstanceDraft( + @RequestBody body: PutDraftRequest, + @RequestHeader(value = "If-Match", required = false) ifMatch: Long?, + authenticatedUser: AuthenticatedUser, + ): DraftMutationResponse { + val newRevision = draftService.putInstanceDraft(body.config, ifMatch, authenticatedUser.username) + return DraftMutationResponse(revision = newRevision) + } + + @Operation(description = "Append operation(s) to the instance config draft.") + @PostMapping("/instance/draft/operations") + fun appendInstanceOperations( + @RequestBody body: AppendOperationsRequest, + @RequestHeader(value = "If-Match", required = false) ifMatch: Long?, + authenticatedUser: AuthenticatedUser, + ): DraftMutationResponse { + val newRevision = draftService.appendInstanceOperations( + ops = body.operations, + ifMatch = ifMatch, + actor = authenticatedUser.username, + ) + return DraftMutationResponse(revision = newRevision) + } + + @Operation(description = "Discard the instance config draft.") + @DeleteMapping("/instance/draft") + @ResponseStatus(HttpStatus.NO_CONTENT) + fun discardInstanceDraft(authenticatedUser: AuthenticatedUser) { + draftService.discardInstanceDraft(authenticatedUser.username) + } + + @Operation(description = "Publish the instance config draft as a new version.") + @PostMapping("/instance/publish") + fun publishInstance(authenticatedUser: AuthenticatedUser): PublishResponse { + val result = draftService.publishInstance(authenticatedUser.username) + return PublishResponse( + version = result.version, + previousVersion = result.previousVersion, + publishedAt = result.publishedAt, + publishedBy = result.publishedBy, + ) + } + + @Operation(description = "List known versions of the instance config.") + @GetMapping("/instance/versions") + fun listInstanceVersions() = VersionsResponse(versions = configService.listInstanceVersions()) + + @Operation( + description = "Create or replace the opaque preprocessing config file for an organism + " + + "pipeline version. The body is stored verbatim; it is not versioned and not interpreted.", + ) + @PutMapping( + "/organisms/{key}/preprocessing/{pipelineVersion}", + consumes = [MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE], + ) + @ResponseStatus(HttpStatus.NO_CONTENT) + fun putPreprocessingConfig( + @PathVariable key: String, + @PathVariable pipelineVersion: Long, + @RequestBody content: String, + authenticatedUser: AuthenticatedUser, + ) { + preprocessingConfigService.setConfigFile(key, pipelineVersion, content, authenticatedUser.username) + } + + @Operation(description = "Delete the preprocessing config file for an organism + pipeline version.") + @DeleteMapping("/organisms/{key}/preprocessing/{pipelineVersion}") + @ResponseStatus(HttpStatus.NO_CONTENT) + fun deletePreprocessingConfig( + @PathVariable key: String, + @PathVariable pipelineVersion: Long, + authenticatedUser: AuthenticatedUser, + ) { + preprocessingConfigService.deleteConfigFile(key, pipelineVersion) + } + + @Operation(description = "Recent audit-log entries scoped to one organism, or to instance config.") + @GetMapping("/audit") + fun audit( + @org.springframework.web.bind.annotation.RequestParam(required = false) organism: String?, + ): AuditResponse = if (organism != null) { + AuditResponse(entries = auditLogService.listForOrganism(organism)) + } else { + AuditResponse(entries = auditLogService.listInstance()) + } + + @ExceptionHandler(OrganismAlreadyExistsException::class) + fun handleAlreadyExists(e: OrganismAlreadyExistsException) = + errorResponse("organism_already_exists", e.message, HttpStatus.CONFLICT) + + @ExceptionHandler(OrganismNotFoundException::class) + fun handleOrganismNotFound(e: OrganismNotFoundException) = + errorResponse("organism_not_found", e.message, HttpStatus.NOT_FOUND) + + @ExceptionHandler(DraftScopeMismatchException::class) + fun handleScopeMismatch(e: DraftScopeMismatchException) = + errorResponse("draft_scope_mismatch", e.message, HttpStatus.FORBIDDEN) + + @ExceptionHandler(OptimisticConcurrencyException::class) + fun handleConcurrency(e: OptimisticConcurrencyException) = + errorResponse("revision_conflict", e.message, HttpStatus.CONFLICT) + + @ExceptionHandler(NoDraftToPublishException::class) + fun handleNoDraftToPublish(e: NoDraftToPublishException) = + errorResponse("no_draft_to_publish", e.message, HttpStatus.CONFLICT) + + @ExceptionHandler(OrganismDeploymentException::class) + fun handleDeploymentException(e: OrganismDeploymentException) = + errorResponse("invalid_deployment_state", e.message, HttpStatus.CONFLICT) + + @ExceptionHandler(UnknownOperationException::class) + fun handleUnknownOp(e: UnknownOperationException): ResponseEntity> = ResponseEntity( + mapOf("error" to "unknown_operation", "opType" to e.opType, "message" to e.message), + HttpStatus.BAD_REQUEST, + ) + + @ExceptionHandler(OperationValidationException::class) + fun handleOpValidation(e: OperationValidationException): ResponseEntity> = ResponseEntity( + mapOf( + "error" to "operation_validation_failed", + "errors" to e.errors.map { mapOf("path" to it.path, "message" to it.message) }, + ), + HttpStatus.BAD_REQUEST, + ) + + @ExceptionHandler(IllegalArgumentException::class) + fun handleBadRequest(e: IllegalArgumentException) = + errorResponse("bad_request", e.message ?: "Invalid input", HttpStatus.BAD_REQUEST) + + private fun errorResponse(code: String, message: String?, status: HttpStatus) = + ResponseEntity(mapOf("error" to code, "message" to (message ?: "")), status) +} + +data class CreateOrganismRequest(val key: String) + +data class AdminOrganismsListResponse(val organisms: List) + +data class PutDraftRequest(val config: T) + +data class AppendOperationsRequest(val operations: List) + +data class DraftMutationResponse(val revision: Long) + +data class PublishResponse( + val version: Long, + val previousVersion: Long?, + val publishedAt: LocalDateTime, + val publishedBy: String, +) + +data class VersionsResponse(val versions: List) + +data class AuditResponse(val entries: List) + +data class OrganismDraftResponse( + val config: OrganismConfig, + val baseVersion: Long?, + val revision: Long, + val operations: List, +) { + companion object { + fun from(v: DraftService.OrganismDraftView) = OrganismDraftResponse( + config = v.config, + baseVersion = v.baseVersion, + revision = v.revision, + operations = v.operations, + ) + } +} + +data class InstanceDraftResponse(val config: InstanceConfig, val baseVersion: Long?, val revision: Long) diff --git a/backend/src/main/kotlin/org/loculus/backend/config/controller/PublicConfigController.kt b/backend/src/main/kotlin/org/loculus/backend/config/controller/PublicConfigController.kt new file mode 100644 index 0000000000..4a63d03455 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/controller/PublicConfigController.kt @@ -0,0 +1,147 @@ +package org.loculus.backend.config.controller + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import kotlinx.datetime.LocalDateTime +import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.expandPerSegmentMetadata +import org.loculus.backend.config.perSegmentExpansionSegments +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.config.service.OrganismNotFoundException +import org.loculus.backend.config.service.PreprocessingConfigService +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/api/config") +class PublicConfigController( + private val configService: ConfigService, + private val backendConfig: BackendConfig, + private val preprocessingConfigService: PreprocessingConfigService, +) { + + @Operation(description = "Returns the latest published instance config, or the given version if ?version=N.") + @GetMapping("/instance") + fun getInstance( + @Parameter(description = "Optional version pin") @RequestParam(required = false) version: Long?, + ): InstanceResponse { + val versioned = if (version != null) { + configService.getInstanceVersion(version) + ?: throw VersionNotFoundException("Instance config version $version is not kept.") + } else { + configService.getInstanceConfig() + } + return InstanceResponse( + version = versioned.version, + publishedAt = versioned.publishedAt, + config = versioned.config, + readOnlyMode = backendConfig.readOnlyMode, + ) + } + + @Operation(description = "Lists released and deployed organisms with their current versions.") + @GetMapping("/organisms") + fun listOrganisms(): OrganismsListResponse = OrganismsListResponse( + organisms = configService.listReleasedOrganisms().map { listing -> + val organism = configService.getOrganismConfig(listing.key) + OrganismsListResponse.OrganismSummary( + key = listing.key, + // Prefer the canonical OrganismConfig.displayName; fall back to + // the legacy schema.organismName for organisms whose first + // PUT'd version pre-dates the displayName field. The unused + // top-level `organismName` (scientific name) was removed in + // the 2026-05-28 cleanup. + displayName = organism.config.displayName ?: organism.config.schema.organismName, + currentVersion = listing.currentVersion + ?: error("released organism with no current_version; invariant violated"), + ) + }, + ) + + @Operation( + description = "Returns the latest published config for one released organism, " + + "or the given version if ?version=N.", + ) + @GetMapping("/organisms/{key}") + fun getOrganism( + @PathVariable key: String, + @Parameter(description = "Optional version pin") @RequestParam(required = false) version: Long?, + ): OrganismResponse { + val versioned = if (version != null) { + configService.getOrganismVersion(key, version) + ?: throw VersionNotFoundException("Organism $key has no version $version.") + } else { + configService.getOrganismConfig(key) + } + return OrganismResponse( + key = versioned.key, + version = versioned.version, + publishedAt = versioned.publishedAt, + config = versioned.config.withEffectiveWebsiteMetadata(), + ) + } + + @Operation( + description = "Returns the raw, opaque preprocessing config file for an organism + pipeline " + + "version, if one is configured (404 otherwise). The backend does not interpret the content.", + ) + @GetMapping("/organisms/{key}/preprocessing/{pipelineVersion}", produces = [MediaType.TEXT_PLAIN_VALUE]) + fun getPreprocessingConfig( + @PathVariable key: String, + @PathVariable pipelineVersion: Long, + ): ResponseEntity { + val content = preprocessingConfigService.getConfigFile(key, pipelineVersion) + ?: return ResponseEntity.notFound().build() + return ResponseEntity.ok(content) + } + + @Operation(description = "Lists the pipeline versions that have a preprocessing config file for an organism.") + @GetMapping("/organisms/{key}/preprocessing") + fun listPreprocessingConfigs(@PathVariable key: String): PreprocessingConfigListResponse = + PreprocessingConfigListResponse(versions = preprocessingConfigService.listVersions(key)) + + @ExceptionHandler(OrganismNotFoundException::class) + fun handleOrganismNotFound(e: OrganismNotFoundException): ResponseEntity> = + ResponseEntity(mapOf("error" to "organism_not_found", "message" to e.message!!), HttpStatus.NOT_FOUND) + + @ExceptionHandler(VersionNotFoundException::class) + fun handleVersionNotFound(e: VersionNotFoundException): ResponseEntity> = + ResponseEntity(mapOf("error" to "version_not_found", "message" to e.message!!), HttpStatus.NOT_FOUND) +} + +private fun OrganismConfig.withEffectiveWebsiteMetadata(): OrganismConfig = copy( + schema = schema.copy( + metadata = expandPerSegmentMetadata(schema.metadata, perSegmentExpansionSegments(this)), + ), +) + +class VersionNotFoundException(message: String) : RuntimeException(message) + +data class InstanceResponse( + val version: Long, + val publishedAt: LocalDateTime, + val config: InstanceConfig, + val readOnlyMode: Boolean = false, +) + +data class OrganismResponse( + val key: String, + val version: Long, + val publishedAt: LocalDateTime, + val config: OrganismConfig, +) + +data class OrganismsListResponse(val organisms: List) { + data class OrganismSummary(val key: String, val displayName: String, val currentVersion: Long) +} + +data class PreprocessingConfigListResponse(val versions: List) diff --git a/backend/src/main/kotlin/org/loculus/backend/config/dbtables/ConfigTables.kt b/backend/src/main/kotlin/org/loculus/backend/config/dbtables/ConfigTables.kt new file mode 100644 index 0000000000..87f804f150 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/dbtables/ConfigTables.kt @@ -0,0 +1,106 @@ +package org.loculus.backend.config.dbtables + +import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.sql.kotlin.datetime.datetime +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.service.jacksonSerializableJsonb + +const val CONFIG_ORGANISMS_TABLE_NAME = "config_organisms" +const val CONFIG_INSTANCE_VERSIONS_TABLE_NAME = "config_instance_versions" +const val CONFIG_INSTANCE_STATE_TABLE_NAME = "config_instance_state" +const val CONFIG_ORGANISM_VERSIONS_TABLE_NAME = "config_organism_versions" +const val CONFIG_INSTANCE_DRAFT_TABLE_NAME = "config_instance_draft" +const val CONFIG_ORGANISM_DRAFTS_TABLE_NAME = "config_organism_drafts" +const val CONFIG_AUDIT_LOG_TABLE_NAME = "config_audit_log" +const val CONFIG_PREPROCESSING_FILES_TABLE_NAME = "config_preprocessing_files" + +object ConfigOrganismsTable : Table(CONFIG_ORGANISMS_TABLE_NAME) { + val keyColumn = text("key") + val statusColumn = text("status") // 'unreleased' | 'released' + val currentVersionColumn = long("current_version").nullable() + val deployedColumn = bool("deployed") + val createdAtColumn = datetime("created_at") + val createdByColumn = text("created_by") + val firstPublishedAtColumn = datetime("first_published_at").nullable() + val lastPublishedAtColumn = datetime("last_published_at").nullable() + + override val primaryKey = PrimaryKey(keyColumn) +} + +object ConfigInstanceVersionsTable : Table(CONFIG_INSTANCE_VERSIONS_TABLE_NAME) { + val versionColumn = long("version").autoIncrement() + val configColumn = jacksonSerializableJsonb("config") + val publishedAtColumn = datetime("published_at") + val publishedByColumn = text("published_by") + + override val primaryKey = PrimaryKey(versionColumn) +} + +object ConfigInstanceStateTable : Table(CONFIG_INSTANCE_STATE_TABLE_NAME) { + val singletonColumn = bool("singleton") + val currentVersionColumn = long("current_version").nullable() + + override val primaryKey = PrimaryKey(singletonColumn) +} + +object ConfigOrganismVersionsTable : Table(CONFIG_ORGANISM_VERSIONS_TABLE_NAME) { + val organismKeyColumn = text("organism_key") + val versionColumn = long("version") + val configColumn = jacksonSerializableJsonb("config") + val publishedAtColumn = datetime("published_at") + val publishedByColumn = text("published_by") + + override val primaryKey = PrimaryKey(organismKeyColumn, versionColumn) +} + +object ConfigInstanceDraftTable : Table(CONFIG_INSTANCE_DRAFT_TABLE_NAME) { + val singletonColumn = bool("singleton") + val configColumn = jacksonSerializableJsonb("config") + val baseVersionColumn = long("base_version").nullable() + val revisionColumn = long("revision") + val createdAtColumn = datetime("created_at") + val updatedAtColumn = datetime("updated_at") + val createdByColumn = text("created_by") + val updatedByColumn = text("updated_by") + + override val primaryKey = PrimaryKey(singletonColumn) +} + +object ConfigOrganismDraftsTable : Table(CONFIG_ORGANISM_DRAFTS_TABLE_NAME) { + val organismKeyColumn = text("organism_key") + val configColumn = jacksonSerializableJsonb("config") + val baseVersionColumn = long("base_version").nullable() + val revisionColumn = long("revision") + val createdAtColumn = datetime("created_at") + val updatedAtColumn = datetime("updated_at") + val createdByColumn = text("created_by") + val updatedByColumn = text("updated_by") + + override val primaryKey = PrimaryKey(organismKeyColumn) +} + +// Opaque, unversioned preprocessing config files, keyed by (organism, pipeline version). +// The backend stores/serves the text verbatim; it never parses or interprets it. +object ConfigPreprocessingFilesTable : Table(CONFIG_PREPROCESSING_FILES_TABLE_NAME) { + val organismKeyColumn = text("organism_key") + val pipelineVersionColumn = long("pipeline_version") + val configFileColumn = text("config_file") + val updatedAtColumn = datetime("updated_at") + val updatedByColumn = text("updated_by") + + override val primaryKey = PrimaryKey(organismKeyColumn, pipelineVersionColumn) +} + +object ConfigAuditLogTable : Table(CONFIG_AUDIT_LOG_TABLE_NAME) { + val idColumn = long("id").autoIncrement() + val occurredAtColumn = datetime("occurred_at") + val actorColumn = text("actor") + val scopeColumn = text("scope") // 'instance' | 'organism' + val organismKeyColumn = text("organism_key").nullable() + val actionColumn = text("action") + val detailsColumn = jacksonSerializableJsonb>("details").nullable() + val resultVersionColumn = long("result_version").nullable() + + override val primaryKey = PrimaryKey(idColumn) +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/Operation.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/Operation.kt new file mode 100644 index 0000000000..b48b1fdb82 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/Operation.kt @@ -0,0 +1,59 @@ +package org.loculus.backend.config.operations + +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.OrganismConfig +import org.springframework.stereotype.Component +import kotlin.reflect.KClass + +enum class OperationScope { INSTANCE, ORGANISM } + +sealed interface ConfigDocument { + data class Instance(val config: InstanceConfig) : ConfigDocument + data class Organism(val config: OrganismConfig) : ConfigDocument +} + +sealed interface ValidationResult { + data object Valid : ValidationResult + data class Invalid(val errors: List) : ValidationResult +} + +data class ValidationError(val path: String, val message: String) + +fun ValidationResult.throwIfInvalid() { + if (this is ValidationResult.Invalid) { + throw OperationValidationException(errors) + } +} + +class OperationValidationException(val errors: List) : + RuntimeException("Operation validation failed: ${errors.joinToString { "${it.path}: ${it.message}" }}") + +class UnknownOperationException(val opType: String) : RuntimeException("Unknown operation type: $opType") + +interface OperationHandler

{ + val opType: String + val payloadClass: KClass

+ val scope: OperationScope + + fun validate(payload: P, draft: ConfigDocument): ValidationResult + fun apply(payload: P, draft: ConfigDocument): ConfigDocument + fun summary(payload: P, draft: ConfigDocument): String +} + +@Component +class OperationRegistry(handlers: List>) { + private val byType: Map> + + init { + val duplicates = handlers.groupBy { it.opType }.filterValues { it.size > 1 }.keys + require(duplicates.isEmpty()) { "Duplicate operation handler opType(s): $duplicates" } + byType = handlers.associateBy { it.opType } + } + + fun get(opType: String): OperationHandler<*> = byType[opType] ?: throw UnknownOperationException(opType) + + fun types(): Set = byType.keys + + fun typesByScope(scope: OperationScope): List = + byType.values.filter { it.scope == scope }.map { it.opType }.sorted() +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/OperationDispatcher.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/OperationDispatcher.kt new file mode 100644 index 0000000000..59d039aa01 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/OperationDispatcher.kt @@ -0,0 +1,49 @@ +package org.loculus.backend.config.operations + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.stereotype.Component + +data class OperationRequest(val type: String, val payload: JsonNode) + +@Component +class OperationDispatcher(private val registry: OperationRegistry, private val objectMapper: ObjectMapper) { + + fun applyOne(op: OperationRequest, draft: ConfigDocument): AppliedOperation { + val handler = registry.get(op.type) + val payload = objectMapper.treeToValue(op.payload, handler.payloadClass.java) + ?: throw OperationValidationException( + listOf(ValidationError("payload", "could not parse payload for ${op.type}")), + ) + return applyTyped(handler, payload, draft, op.type) + } + + fun applyMany(ops: List, draft: ConfigDocument): AppliedBatch { + var current = draft + val applied = mutableListOf() + for (op in ops) { + val step = applyOne(op, current) + current = step.newDraft + applied += step + } + return AppliedBatch(newDraft = current, applied = applied) + } + + @Suppress("UNCHECKED_CAST") + private fun

applyTyped( + handler: OperationHandler

, + payload: Any, + draft: ConfigDocument, + opType: String, + ): AppliedOperation { + val typedPayload = payload as P + handler.validate(typedPayload, draft).throwIfInvalid() + val newDraft = handler.apply(typedPayload, draft) + val summary = handler.summary(typedPayload, draft) + return AppliedOperation(opType = opType, payload = payload, summary = summary, newDraft = newDraft) + } +} + +data class AppliedOperation(val opType: String, val payload: Any, val summary: String, val newDraft: ConfigDocument) + +data class AppliedBatch(val newDraft: ConfigDocument, val applied: List) diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/AddOptionalMetadataField.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/AddOptionalMetadataField.kt new file mode 100644 index 0000000000..ebdd86d98f --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/AddOptionalMetadataField.kt @@ -0,0 +1,60 @@ +package org.loculus.backend.config.operations.handlers + +import org.loculus.backend.config.Metadata +import org.loculus.backend.config.MetadataType +import org.loculus.backend.config.operations.ConfigDocument +import org.loculus.backend.config.operations.OperationHandler +import org.loculus.backend.config.operations.OperationScope +import org.loculus.backend.config.operations.ValidationError +import org.loculus.backend.config.operations.ValidationResult +import org.springframework.stereotype.Component +import kotlin.reflect.KClass + +data class AddOptionalMetadataFieldPayload( + val name: String, + val type: MetadataType, + val displayName: String? = null, + val description: String? = null, + val header: String? = null, + val hidden: Boolean? = null, + val customDisplay: Map? = null, +) + +@Component +class AddOptionalMetadataFieldHandler : OperationHandler { + override val opType = "addOptionalMetadataField" + override val payloadClass: KClass = AddOptionalMetadataFieldPayload::class + override val scope = OperationScope.ORGANISM + + override fun validate(payload: AddOptionalMetadataFieldPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Organism) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected organism scope"))) + } + val errors = mutableListOf() + if (payload.name.isBlank()) errors.add(ValidationError("name", "must not be blank")) + if (draft.config.schema.metadata.any { it.name == payload.name }) { + errors.add(ValidationError("name", "field '${payload.name}' already exists")) + } + return if (errors.isEmpty()) ValidationResult.Valid else ValidationResult.Invalid(errors) + } + + override fun apply(payload: AddOptionalMetadataFieldPayload, draft: ConfigDocument): ConfigDocument { + val organism = (draft as ConfigDocument.Organism).config + val field = Metadata( + name = payload.name, + type = payload.type, + required = false, + displayName = payload.displayName, + description = payload.description, + header = payload.header, + hidden = payload.hidden, + customDisplay = payload.customDisplay, + ) + return ConfigDocument.Organism( + organism.copy(schema = organism.schema.copy(metadata = organism.schema.metadata + field)), + ) + } + + override fun summary(payload: AddOptionalMetadataFieldPayload, draft: ConfigDocument): String = + "Add optional metadata field '${payload.name}' (${payload.type})" +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/LinkOutHandlers.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/LinkOutHandlers.kt new file mode 100644 index 0000000000..5929b00f0d --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/LinkOutHandlers.kt @@ -0,0 +1,142 @@ +package org.loculus.backend.config.operations.handlers + +import org.loculus.backend.config.LinkOut +import org.loculus.backend.config.operations.ConfigDocument +import org.loculus.backend.config.operations.OperationHandler +import org.loculus.backend.config.operations.OperationScope +import org.loculus.backend.config.operations.ValidationError +import org.loculus.backend.config.operations.ValidationResult +import org.springframework.stereotype.Component +import kotlin.reflect.KClass + +data class AddLinkOutPayload(val linkOut: LinkOut) + +@Component +class AddLinkOutHandler : OperationHandler { + override val opType = "addLinkOut" + override val payloadClass: KClass = AddLinkOutPayload::class + override val scope = OperationScope.ORGANISM + + override fun validate(payload: AddLinkOutPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Organism) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected organism scope"))) + } + val errors = mutableListOf() + val link = payload.linkOut + if (link.name.isBlank()) errors.add(ValidationError("linkOut.name", "must not be blank")) + if (link.url.isBlank()) errors.add(ValidationError("linkOut.url", "must not be blank")) + if (link.maxNumberOfRecommendedEntries != null && link.maxNumberOfRecommendedEntries < 1) { + errors.add(ValidationError("linkOut.maxNumberOfRecommendedEntries", "must be at least 1 if provided")) + } + if (draft.config.schema.linkOuts.any { it.name == link.name }) { + errors.add(ValidationError("linkOut.name", "name '${link.name}' already exists")) + } + return if (errors.isEmpty()) ValidationResult.Valid else ValidationResult.Invalid(errors) + } + + override fun apply(payload: AddLinkOutPayload, draft: ConfigDocument): ConfigDocument { + val organism = (draft as ConfigDocument.Organism).config + return ConfigDocument.Organism( + organism.copy( + schema = organism.schema.copy(linkOuts = organism.schema.linkOuts + payload.linkOut), + ), + ) + } + + override fun summary(payload: AddLinkOutPayload, draft: ConfigDocument): String = + "Add link-out '${payload.linkOut.name}'" +} + +data class UpdateLinkOutPayload( + val name: String, + val url: String? = null, + val maxNumberOfRecommendedEntries: Int? = null, + val onlyForReferences: Map? = null, + val category: String? = null, +) + +@Component +class UpdateLinkOutHandler : OperationHandler { + override val opType = "updateLinkOut" + override val payloadClass: KClass = UpdateLinkOutPayload::class + override val scope = OperationScope.ORGANISM + + override fun validate(payload: UpdateLinkOutPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Organism) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected organism scope"))) + } + val errors = mutableListOf() + if (draft.config.schema.linkOuts.none { it.name == payload.name }) { + errors.add(ValidationError("name", "no link-out with name '${payload.name}'")) + } + if (payload.url != null && payload.url.isBlank()) { + errors.add(ValidationError("url", "must not be blank if provided")) + } + if (payload.maxNumberOfRecommendedEntries != null && payload.maxNumberOfRecommendedEntries < 1) { + errors.add(ValidationError("maxNumberOfRecommendedEntries", "must be at least 1 if provided")) + } + return if (errors.isEmpty()) ValidationResult.Valid else ValidationResult.Invalid(errors) + } + + override fun apply(payload: UpdateLinkOutPayload, draft: ConfigDocument): ConfigDocument { + val organism = (draft as ConfigDocument.Organism).config + val updated = organism.schema.linkOuts.map { link -> + if (link.name != payload.name) { + link + } else { + link.copy( + url = payload.url ?: link.url, + maxNumberOfRecommendedEntries = + payload.maxNumberOfRecommendedEntries ?: link.maxNumberOfRecommendedEntries, + onlyForReferences = payload.onlyForReferences ?: link.onlyForReferences, + category = payload.category ?: link.category, + ) + } + } + return ConfigDocument.Organism(organism.copy(schema = organism.schema.copy(linkOuts = updated))) + } + + override fun summary(payload: UpdateLinkOutPayload, draft: ConfigDocument): String { + val changes = buildList { + payload.url?.let { add("url set") } + payload.maxNumberOfRecommendedEntries?.let { add("maxNumberOfRecommendedEntries=$it") } + payload.onlyForReferences?.let { add("onlyForReferences set") } + payload.category?.let { add("category='$it'") } + } + val verb = if (changes.isEmpty()) "no-op" else changes.joinToString(", ") + return "Update link-out '${payload.name}': $verb" + } +} + +data class RemoveLinkOutPayload(val name: String) + +@Component +class RemoveLinkOutHandler : OperationHandler { + override val opType = "removeLinkOut" + override val payloadClass: KClass = RemoveLinkOutPayload::class + override val scope = OperationScope.ORGANISM + + override fun validate(payload: RemoveLinkOutPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Organism) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected organism scope"))) + } + if (draft.config.schema.linkOuts.none { it.name == payload.name }) { + return ValidationResult.Invalid(listOf(ValidationError("name", "no link-out with name '${payload.name}'"))) + } + return ValidationResult.Valid + } + + override fun apply(payload: RemoveLinkOutPayload, draft: ConfigDocument): ConfigDocument { + val organism = (draft as ConfigDocument.Organism).config + return ConfigDocument.Organism( + organism.copy( + schema = organism.schema.copy( + linkOuts = organism.schema.linkOuts.filterNot { it.name == payload.name }, + ), + ), + ) + } + + override fun summary(payload: RemoveLinkOutPayload, draft: ConfigDocument): String = + "Remove link-out '${payload.name}'" +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/ReorderMetadataFields.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/ReorderMetadataFields.kt new file mode 100644 index 0000000000..44d0a2dc03 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/ReorderMetadataFields.kt @@ -0,0 +1,52 @@ +package org.loculus.backend.config.operations.handlers + +import org.loculus.backend.config.operations.ConfigDocument +import org.loculus.backend.config.operations.OperationHandler +import org.loculus.backend.config.operations.OperationScope +import org.loculus.backend.config.operations.ValidationError +import org.loculus.backend.config.operations.ValidationResult +import org.springframework.stereotype.Component +import kotlin.reflect.KClass + +data class ReorderMetadataFieldsPayload(val order: List) + +@Component +class ReorderMetadataFieldsHandler : OperationHandler { + override val opType = "reorderMetadataFields" + override val payloadClass: KClass = ReorderMetadataFieldsPayload::class + override val scope = OperationScope.ORGANISM + + override fun validate(payload: ReorderMetadataFieldsPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Organism) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected organism scope"))) + } + val errors = mutableListOf() + val current = draft.config.schema.metadata.map { it.name } + val supplied = payload.order + + if (supplied.size != supplied.toSet().size) { + errors.add(ValidationError("order", "must not contain duplicates")) + } + val currentSet = current.toSet() + val suppliedSet = supplied.toSet() + val missing = currentSet - suppliedSet + val extra = suppliedSet - currentSet + if (missing.isNotEmpty()) { + errors.add(ValidationError("order", "missing fields: ${missing.sorted().joinToString(", ")}")) + } + if (extra.isNotEmpty()) { + errors.add(ValidationError("order", "unknown fields: ${extra.sorted().joinToString(", ")}")) + } + return if (errors.isEmpty()) ValidationResult.Valid else ValidationResult.Invalid(errors) + } + + override fun apply(payload: ReorderMetadataFieldsPayload, draft: ConfigDocument): ConfigDocument { + val organism = (draft as ConfigDocument.Organism).config + val byName = organism.schema.metadata.associateBy { it.name } + val reordered = payload.order.map { byName.getValue(it) } + return ConfigDocument.Organism(organism.copy(schema = organism.schema.copy(metadata = reordered))) + } + + override fun summary(payload: ReorderMetadataFieldsPayload, draft: ConfigDocument): String = + "Reorder metadata fields (${payload.order.size} fields)" +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetInstanceBranding.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetInstanceBranding.kt new file mode 100644 index 0000000000..72adffe4fe --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetInstanceBranding.kt @@ -0,0 +1,63 @@ +package org.loculus.backend.config.operations.handlers + +import org.loculus.backend.config.Logo +import org.loculus.backend.config.SupportContact +import org.loculus.backend.config.operations.ConfigDocument +import org.loculus.backend.config.operations.OperationHandler +import org.loculus.backend.config.operations.OperationScope +import org.loculus.backend.config.operations.ValidationError +import org.loculus.backend.config.operations.ValidationResult +import org.springframework.stereotype.Component +import kotlin.reflect.KClass + +data class SetInstanceBrandingPayload( + val name: String? = null, + val description: String? = null, + val logo: Logo? = null, + val supportContact: SupportContact? = null, +) + +@Component +class SetInstanceBrandingHandler : OperationHandler { + override val opType = "setInstanceBranding" + override val payloadClass: KClass = SetInstanceBrandingPayload::class + override val scope = OperationScope.INSTANCE + + override fun validate(payload: SetInstanceBrandingPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Instance) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected instance scope"))) + } + if (payload.name != null && payload.name.isBlank()) { + return ValidationResult.Invalid(listOf(ValidationError("name", "must not be blank if provided"))) + } + return ValidationResult.Valid + } + + override fun apply(payload: SetInstanceBrandingPayload, draft: ConfigDocument): ConfigDocument { + val instance = (draft as ConfigDocument.Instance).config + return ConfigDocument.Instance( + instance.copy( + name = payload.name ?: instance.name, + description = payload.description ?: instance.description, + logo = payload.logo ?: instance.logo, + supportContact = payload.supportContact ?: instance.supportContact, + ), + ) + } + + override fun summary(payload: SetInstanceBrandingPayload, draft: ConfigDocument): String { + val changes = buildList { + payload.name?.let { add("name='$it'") } + payload.description?.let { add("description='$it'") } + if (payload.logo != null) add("logo set") + if (payload.supportContact != null) add("supportContact set") + } + return if (changes.isEmpty()) { + "No-op set instance branding" + } else { + "Set instance branding: ${changes.joinToString( + ", ", + )}" + } + } +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetMetadataFieldDisplay.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetMetadataFieldDisplay.kt new file mode 100644 index 0000000000..d28e3a1a17 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetMetadataFieldDisplay.kt @@ -0,0 +1,68 @@ +package org.loculus.backend.config.operations.handlers + +import org.loculus.backend.config.operations.ConfigDocument +import org.loculus.backend.config.operations.OperationHandler +import org.loculus.backend.config.operations.OperationScope +import org.loculus.backend.config.operations.ValidationError +import org.loculus.backend.config.operations.ValidationResult +import org.springframework.stereotype.Component +import kotlin.reflect.KClass + +data class SetMetadataFieldDisplayPayload( + val field: String, + val displayName: String? = null, + val description: String? = null, + val header: String? = null, + val hidden: Boolean? = null, + val customDisplay: Map? = null, +) + +@Component +class SetMetadataFieldDisplayHandler : OperationHandler { + override val opType = "setMetadataFieldDisplay" + override val payloadClass: KClass = SetMetadataFieldDisplayPayload::class + override val scope = OperationScope.ORGANISM + + override fun validate(payload: SetMetadataFieldDisplayPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Organism) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected organism scope"))) + } + val exists = draft.config.schema.metadata.any { it.name == payload.field } + if (!exists) { + return ValidationResult.Invalid( + listOf(ValidationError("field", "no metadata field named '${payload.field}'")), + ) + } + return ValidationResult.Valid + } + + override fun apply(payload: SetMetadataFieldDisplayPayload, draft: ConfigDocument): ConfigDocument { + val organism = (draft as ConfigDocument.Organism).config + val updatedMetadata = organism.schema.metadata.map { field -> + if (field.name != payload.field) { + field + } else { + field.copy( + displayName = payload.displayName ?: field.displayName, + description = payload.description ?: field.description, + header = payload.header ?: field.header, + hidden = payload.hidden ?: field.hidden, + customDisplay = payload.customDisplay ?: field.customDisplay, + ) + } + } + return ConfigDocument.Organism(organism.copy(schema = organism.schema.copy(metadata = updatedMetadata))) + } + + override fun summary(payload: SetMetadataFieldDisplayPayload, draft: ConfigDocument): String { + val changes = buildList { + payload.displayName?.let { add("displayName='$it'") } + payload.description?.let { add("description set") } + payload.header?.let { add("header='$it'") } + payload.hidden?.let { add("hidden=$it") } + if (payload.customDisplay != null) add("customDisplay set") + } + val verb = if (changes.isEmpty()) "no-op" else changes.joinToString(", ") + return "Set display on field '${payload.field}': $verb" + } +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetOrganismDisplay.kt b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetOrganismDisplay.kt new file mode 100644 index 0000000000..cfc0b7c749 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/operations/handlers/SetOrganismDisplay.kt @@ -0,0 +1,61 @@ +package org.loculus.backend.config.operations.handlers + +import org.loculus.backend.config.OrganismImage +import org.loculus.backend.config.operations.ConfigDocument +import org.loculus.backend.config.operations.OperationHandler +import org.loculus.backend.config.operations.OperationScope +import org.loculus.backend.config.operations.ValidationError +import org.loculus.backend.config.operations.ValidationResult +import org.springframework.stereotype.Component +import kotlin.reflect.KClass + +data class SetOrganismDisplayPayload( + val displayName: String? = null, + val description: String? = null, + val image: OrganismImage? = null, +) + +@Component +class SetOrganismDisplayHandler : OperationHandler { + override val opType = "setOrganismDisplay" + override val payloadClass: KClass = SetOrganismDisplayPayload::class + override val scope = OperationScope.ORGANISM + + override fun validate(payload: SetOrganismDisplayPayload, draft: ConfigDocument): ValidationResult { + if (draft !is ConfigDocument.Organism) { + return ValidationResult.Invalid(listOf(ValidationError("scope", "expected organism scope"))) + } + val errors = mutableListOf() + if (payload.displayName?.isBlank() == true) { + errors.add(ValidationError("displayName", "must not be blank if provided")) + } + if (payload.image?.url?.isBlank() == true) { + errors.add(ValidationError("image.url", "must not be blank if provided")) + } + return if (errors.isEmpty()) ValidationResult.Valid else ValidationResult.Invalid(errors) + } + + override fun apply(payload: SetOrganismDisplayPayload, draft: ConfigDocument): ConfigDocument { + val organism = (draft as ConfigDocument.Organism).config + return ConfigDocument.Organism( + organism.copy( + displayName = payload.displayName ?: organism.displayName, + description = payload.description ?: organism.description, + image = payload.image ?: organism.image, + ), + ) + } + + override fun summary(payload: SetOrganismDisplayPayload, draft: ConfigDocument): String { + val changes = buildList { + payload.displayName?.let { add("displayName='$it'") } + payload.description?.let { add("description set") } + if (payload.image != null) add("image set") + } + return if (changes.isEmpty()) { + "No-op set organism display" + } else { + "Set organism display: ${changes.joinToString(", ")}" + } + } +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/service/AuditLogService.kt b/backend/src/main/kotlin/org/loculus/backend/config/service/AuditLogService.kt new file mode 100644 index 0000000000..01fce21d7a --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/service/AuditLogService.kt @@ -0,0 +1,132 @@ +package org.loculus.backend.config.service + +import com.fasterxml.jackson.databind.ObjectMapper +import kotlinx.datetime.LocalDateTime +import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.andWhere +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.loculus.backend.config.dbtables.ConfigAuditLogTable +import org.loculus.backend.utils.DateProvider +import org.springframework.stereotype.Service + +enum class AuditAction(val value: String) { + ORGANISM_CREATE("organism_create"), + DOCUMENT_REPLACE("document_replace"), + OP_APPEND("op_append"), + PUBLISH("publish"), + MARK_DEPLOYED("mark_deployed"), + DISCARD_DRAFT("discard_draft"), +} + +enum class AuditScope(val value: String) { + INSTANCE("instance"), + ORGANISM("organism"), +} + +@Service +class AuditLogService(private val dateProvider: DateProvider, private val objectMapper: ObjectMapper) { + + fun append( + actor: String, + scope: AuditScope, + organismKey: String?, + action: AuditAction, + details: Map? = null, + resultVersion: Long? = null, + ) { + val now = dateProvider.getCurrentDateTime() + transaction { + ConfigAuditLogTable.insert { + it[ConfigAuditLogTable.occurredAtColumn] = now + it[ConfigAuditLogTable.actorColumn] = actor + it[ConfigAuditLogTable.scopeColumn] = scope.value + it[ConfigAuditLogTable.organismKeyColumn] = organismKey + it[ConfigAuditLogTable.actionColumn] = action.value + it[ConfigAuditLogTable.detailsColumn] = details + it[ConfigAuditLogTable.resultVersionColumn] = resultVersion + } + } + } + + fun listForOrganism(organismKey: String, limit: Int = 200): List = transaction { + ConfigAuditLogTable.selectAll() + .where { ConfigAuditLogTable.organismKeyColumn eq organismKey } + .orderBy( + ConfigAuditLogTable.occurredAtColumn to SortOrder.DESC, + ConfigAuditLogTable.idColumn to SortOrder.DESC, + ) + .limit(limit) + .map { it.toAuditEntry() } + } + + fun listInstance(limit: Int = 200): List = transaction { + ConfigAuditLogTable.selectAll() + .where { ConfigAuditLogTable.scopeColumn eq AuditScope.INSTANCE.value } + .orderBy( + ConfigAuditLogTable.occurredAtColumn to SortOrder.DESC, + ConfigAuditLogTable.idColumn to SortOrder.DESC, + ) + .limit(limit) + .map { it.toAuditEntry() } + } + + fun pendingOrganismOps(organismKey: String): List = transaction { + val resetMarker = ConfigAuditLogTable.selectAll() + .where { ConfigAuditLogTable.organismKeyColumn eq organismKey } + .andWhere { + ConfigAuditLogTable.actionColumn inList + listOf( + AuditAction.PUBLISH.value, + AuditAction.DISCARD_DRAFT.value, + AuditAction.ORGANISM_CREATE.value, + ) + } + .orderBy( + ConfigAuditLogTable.occurredAtColumn to SortOrder.DESC, + ConfigAuditLogTable.idColumn to SortOrder.DESC, + ) + .limit(1) + .map { it[ConfigAuditLogTable.occurredAtColumn] } + .singleOrNull() + + val baseQuery = ConfigAuditLogTable.selectAll() + .where { ConfigAuditLogTable.organismKeyColumn eq organismKey } + .andWhere { ConfigAuditLogTable.actionColumn eq AuditAction.OP_APPEND.value } + + val filteredQuery = if (resetMarker != null) { + baseQuery.andWhere { ConfigAuditLogTable.occurredAtColumn greater resetMarker } + } else { + baseQuery + } + + filteredQuery.orderBy( + ConfigAuditLogTable.occurredAtColumn to SortOrder.ASC, + ConfigAuditLogTable.idColumn to SortOrder.ASC, + ) + .map { it.toAuditEntry() } + } + + private fun org.jetbrains.exposed.sql.ResultRow.toAuditEntry() = AuditEntry( + id = this[ConfigAuditLogTable.idColumn], + occurredAt = this[ConfigAuditLogTable.occurredAtColumn], + actor = this[ConfigAuditLogTable.actorColumn], + scope = this[ConfigAuditLogTable.scopeColumn], + organismKey = this[ConfigAuditLogTable.organismKeyColumn], + action = this[ConfigAuditLogTable.actionColumn], + details = this[ConfigAuditLogTable.detailsColumn], + resultVersion = this[ConfigAuditLogTable.resultVersionColumn], + ) + + data class AuditEntry( + val id: Long, + val occurredAt: LocalDateTime, + val actor: String, + val scope: String, + val organismKey: String?, + val action: String, + val details: Map?, + val resultVersion: Long?, + ) +} diff --git a/backend/src/main/kotlin/org/loculus/backend/config/service/ConfigService.kt b/backend/src/main/kotlin/org/loculus/backend/config/service/ConfigService.kt new file mode 100644 index 0000000000..cff2766e65 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/service/ConfigService.kt @@ -0,0 +1,180 @@ +package org.loculus.backend.config.service + +import kotlinx.datetime.LocalDateTime +import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.andWhere +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.loculus.backend.api.Organism +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.dbtables.ConfigInstanceStateTable +import org.loculus.backend.config.dbtables.ConfigInstanceVersionsTable +import org.loculus.backend.config.dbtables.ConfigOrganismVersionsTable +import org.loculus.backend.config.dbtables.ConfigOrganismsTable +import org.springframework.stereotype.Service +import java.util.concurrent.ConcurrentHashMap + +@Service +class ConfigService { + + @Volatile + private var instanceCache: VersionedInstance? = null + private val organismCache: MutableMap = ConcurrentHashMap() + + fun getInstanceConfig(): VersionedInstance { + instanceCache?.let { return it } + return transaction { + val state = ConfigInstanceStateTable.selectAll().single() + val currentVersion = state[ConfigInstanceStateTable.currentVersionColumn] + ?: error("config_instance_state has no current_version; migration is broken") + val row = ConfigInstanceVersionsTable.selectAll() + .where { ConfigInstanceVersionsTable.versionColumn eq currentVersion } + .single() + val loaded = VersionedInstance( + version = currentVersion, + publishedAt = row[ConfigInstanceVersionsTable.publishedAtColumn], + publishedBy = row[ConfigInstanceVersionsTable.publishedByColumn], + config = row[ConfigInstanceVersionsTable.configColumn], + ) + instanceCache = loaded + loaded + } + } + + fun getInstanceVersion(version: Long): VersionedInstance? = transaction { + ConfigInstanceVersionsTable.selectAll() + .where { ConfigInstanceVersionsTable.versionColumn eq version } + .map { + VersionedInstance( + version = it[ConfigInstanceVersionsTable.versionColumn], + publishedAt = it[ConfigInstanceVersionsTable.publishedAtColumn], + publishedBy = it[ConfigInstanceVersionsTable.publishedByColumn], + config = it[ConfigInstanceVersionsTable.configColumn], + ) + } + .singleOrNull() + } + + fun getOrganismConfig(key: String): VersionedOrganism { + organismCache[key]?.let { return it } + val loaded = transaction { + val orgRow = ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.keyColumn eq key } + .singleOrNull() ?: throw OrganismNotFoundException(key) + val currentVersion = orgRow[ConfigOrganismsTable.currentVersionColumn] + ?: throw OrganismNotFoundException(key) + val versionRow = ConfigOrganismVersionsTable.selectAll() + .where { ConfigOrganismVersionsTable.organismKeyColumn eq key } + .andWhere { ConfigOrganismVersionsTable.versionColumn eq currentVersion } + .single() + VersionedOrganism( + key = key, + version = currentVersion, + publishedAt = versionRow[ConfigOrganismVersionsTable.publishedAtColumn], + publishedBy = versionRow[ConfigOrganismVersionsTable.publishedByColumn], + config = versionRow[ConfigOrganismVersionsTable.configColumn], + ) + } + organismCache[key] = loaded + return loaded + } + + fun getOrganismConfig(organism: Organism): VersionedOrganism = getOrganismConfig(organism.name) + + fun getOrganismVersion(key: String, version: Long): VersionedOrganism? = transaction { + ConfigOrganismVersionsTable.selectAll() + .where { ConfigOrganismVersionsTable.organismKeyColumn eq key } + .andWhere { ConfigOrganismVersionsTable.versionColumn eq version } + .map { + VersionedOrganism( + key = key, + version = version, + publishedAt = it[ConfigOrganismVersionsTable.publishedAtColumn], + publishedBy = it[ConfigOrganismVersionsTable.publishedByColumn], + config = it[ConfigOrganismVersionsTable.configColumn], + ) + } + .singleOrNull() + } + + fun listOrganismKeys(): Set = transaction { + ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.statusColumn eq "released" } + .andWhere { ConfigOrganismsTable.deployedColumn eq true } + .map { it[ConfigOrganismsTable.keyColumn] } + .toSet() + } + + fun listReleasedOrganisms(): List = transaction { + ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.statusColumn eq "released" } + .andWhere { ConfigOrganismsTable.deployedColumn eq true } + .orderBy(ConfigOrganismsTable.keyColumn to SortOrder.ASC) + .map { it.toOrganismListing() } + } + + fun listAllOrganisms(): List = transaction { + ConfigOrganismsTable.selectAll() + .orderBy(ConfigOrganismsTable.keyColumn to SortOrder.ASC) + .map { it.toOrganismListing() } + } + + fun listOrganismVersions(key: String): List = transaction { + ConfigOrganismVersionsTable.selectAll() + .where { ConfigOrganismVersionsTable.organismKeyColumn eq key } + .orderBy(ConfigOrganismVersionsTable.versionColumn to SortOrder.DESC) + .map { + VersionListing( + version = it[ConfigOrganismVersionsTable.versionColumn], + publishedAt = it[ConfigOrganismVersionsTable.publishedAtColumn], + publishedBy = it[ConfigOrganismVersionsTable.publishedByColumn], + ) + } + } + + fun listInstanceVersions(): List = transaction { + ConfigInstanceVersionsTable.selectAll() + .orderBy(ConfigInstanceVersionsTable.versionColumn to SortOrder.DESC) + .map { + VersionListing( + version = it[ConfigInstanceVersionsTable.versionColumn], + publishedAt = it[ConfigInstanceVersionsTable.publishedAtColumn], + publishedBy = it[ConfigInstanceVersionsTable.publishedByColumn], + ) + } + } + + fun invalidateCache() { + instanceCache = null + organismCache.clear() + } + + data class VersionedInstance( + val version: Long, + val publishedAt: LocalDateTime, + val publishedBy: String, + val config: InstanceConfig, + ) + + data class VersionedOrganism( + val key: String, + val version: Long, + val publishedAt: LocalDateTime, + val publishedBy: String, + val config: OrganismConfig, + ) + + data class OrganismListing(val key: String, val status: String, val currentVersion: Long?, val deployed: Boolean) + + data class VersionListing(val version: Long, val publishedAt: LocalDateTime, val publishedBy: String) + + private fun org.jetbrains.exposed.sql.ResultRow.toOrganismListing() = OrganismListing( + key = this[ConfigOrganismsTable.keyColumn], + status = this[ConfigOrganismsTable.statusColumn], + currentVersion = this[ConfigOrganismsTable.currentVersionColumn], + deployed = this[ConfigOrganismsTable.deployedColumn], + ) +} + +class OrganismNotFoundException(val key: String) : RuntimeException("Organism not found or unreleased: $key") diff --git a/backend/src/main/kotlin/org/loculus/backend/config/service/DraftService.kt b/backend/src/main/kotlin/org/loculus/backend/config/service/DraftService.kt new file mode 100644 index 0000000000..544eb842ec --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/service/DraftService.kt @@ -0,0 +1,484 @@ +package org.loculus.backend.config.service + +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.andWhere +import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.dbtables.ConfigInstanceDraftTable +import org.loculus.backend.config.dbtables.ConfigInstanceStateTable +import org.loculus.backend.config.dbtables.ConfigInstanceVersionsTable +import org.loculus.backend.config.dbtables.ConfigOrganismDraftsTable +import org.loculus.backend.config.dbtables.ConfigOrganismVersionsTable +import org.loculus.backend.config.dbtables.ConfigOrganismsTable +import org.loculus.backend.config.operations.AppliedBatch +import org.loculus.backend.config.operations.ConfigDocument +import org.loculus.backend.config.operations.OperationDispatcher +import org.loculus.backend.config.operations.OperationRequest +import org.loculus.backend.utils.DateProvider +import org.springframework.stereotype.Service + +@Service +class DraftService( + private val configService: ConfigService, + private val auditLogService: AuditLogService, + private val operationDispatcher: OperationDispatcher, + private val dateProvider: DateProvider, +) { + fun getOrganismDraft(key: String): OrganismDraftView? = transaction { + readOrganismDraft(key) + } + + fun putOrganismDraft(key: String, newConfig: OrganismConfig, ifMatch: Long?, actor: String): Long { + val now = dateProvider.getCurrentDateTime() + return transaction { + val orgRow = ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.keyColumn eq key } + .singleOrNull() ?: throw OrganismNotFoundException(key) + val status = orgRow[ConfigOrganismsTable.statusColumn] + if (status != "unreleased") { + throw DraftScopeMismatchException( + "PUT /draft is only allowed for unreleased organisms; organism '$key' is '$status'.", + ) + } + + val existing = ConfigOrganismDraftsTable.selectAll() + .where { ConfigOrganismDraftsTable.organismKeyColumn eq key } + .forUpdate() + .singleOrNull() + + val newRevision: Long + if (existing == null) { + if (ifMatch != null && ifMatch != 0L) { + throw OptimisticConcurrencyException( + "If-Match=$ifMatch but no draft exists yet (effective revision 0).", + ) + } + ConfigOrganismDraftsTable.insert { + it[ConfigOrganismDraftsTable.organismKeyColumn] = key + it[ConfigOrganismDraftsTable.configColumn] = newConfig + it[ConfigOrganismDraftsTable.baseVersionColumn] = null + it[ConfigOrganismDraftsTable.revisionColumn] = 1L + it[ConfigOrganismDraftsTable.createdAtColumn] = now + it[ConfigOrganismDraftsTable.updatedAtColumn] = now + it[ConfigOrganismDraftsTable.createdByColumn] = actor + it[ConfigOrganismDraftsTable.updatedByColumn] = actor + } + newRevision = 1L + } else { + val currentRevision = existing[ConfigOrganismDraftsTable.revisionColumn] + if (ifMatch != null && ifMatch != currentRevision) { + throw OptimisticConcurrencyException( + "If-Match=$ifMatch but current draft revision is $currentRevision.", + ) + } + newRevision = currentRevision + 1L + ConfigOrganismDraftsTable.update({ ConfigOrganismDraftsTable.organismKeyColumn eq key }) { + it[ConfigOrganismDraftsTable.configColumn] = newConfig + it[ConfigOrganismDraftsTable.revisionColumn] = newRevision + it[ConfigOrganismDraftsTable.updatedAtColumn] = now + it[ConfigOrganismDraftsTable.updatedByColumn] = actor + } + } + + auditLogService.append( + actor = actor, + scope = AuditScope.ORGANISM, + organismKey = key, + action = AuditAction.DOCUMENT_REPLACE, + details = mapOf("revision" to newRevision), + ) + newRevision + } + } + + fun appendOrganismOperations(key: String, ops: List, ifMatch: Long?, actor: String): Long { + require(ops.isNotEmpty()) { "At least one operation is required." } + val now = dateProvider.getCurrentDateTime() + return transaction { + val orgRow = ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.keyColumn eq key } + .singleOrNull() ?: throw OrganismNotFoundException(key) + val status = orgRow[ConfigOrganismsTable.statusColumn] + if (status != "released") { + throw DraftScopeMismatchException( + "POST /draft/operations is only allowed for released organisms; organism '$key' is '$status'.", + ) + } + val currentVersion = orgRow[ConfigOrganismsTable.currentVersionColumn]!! + + val (currentConfig, currentRevision) = loadOrCreateOrganismDraft(key, currentVersion, actor, now) + if (ifMatch != null && ifMatch != currentRevision) { + throw OptimisticConcurrencyException( + "If-Match=$ifMatch but current draft revision is $currentRevision.", + ) + } + + val batch: AppliedBatch = operationDispatcher.applyMany(ops, ConfigDocument.Organism(currentConfig)) + val newConfig = (batch.newDraft as ConfigDocument.Organism).config + val newRevision = currentRevision + 1L + + ConfigOrganismDraftsTable.update({ ConfigOrganismDraftsTable.organismKeyColumn eq key }) { + it[ConfigOrganismDraftsTable.configColumn] = newConfig + it[ConfigOrganismDraftsTable.revisionColumn] = newRevision + it[ConfigOrganismDraftsTable.updatedAtColumn] = now + it[ConfigOrganismDraftsTable.updatedByColumn] = actor + } + + for (applied in batch.applied) { + auditLogService.append( + actor = actor, + scope = AuditScope.ORGANISM, + organismKey = key, + action = AuditAction.OP_APPEND, + details = mapOf( + "opType" to applied.opType, + "summary" to applied.summary, + "revision" to newRevision, + ), + ) + } + newRevision + } + } + + fun discardOrganismDraft(key: String, actor: String) { + transaction { + val deleted = ConfigOrganismDraftsTable.deleteWhere { organismKeyColumn eq key } + if (deleted > 0) { + auditLogService.append( + actor = actor, + scope = AuditScope.ORGANISM, + organismKey = key, + action = AuditAction.DISCARD_DRAFT, + ) + } + } + } + + fun publishOrganism(key: String, actor: String): PublishResult { + val now = dateProvider.getCurrentDateTime() + val result = transaction { + val draft = ConfigOrganismDraftsTable.selectAll() + .where { ConfigOrganismDraftsTable.organismKeyColumn eq key } + .singleOrNull() ?: throw NoDraftToPublishException(key) + val orgRow = ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.keyColumn eq key } + .single() + + val draftConfig = draft[ConfigOrganismDraftsTable.configColumn] + val previousVersion = orgRow[ConfigOrganismsTable.currentVersionColumn] + val nextVersion = (previousVersion ?: 0L) + 1L + + ConfigOrganismVersionsTable.insert { + it[ConfigOrganismVersionsTable.organismKeyColumn] = key + it[ConfigOrganismVersionsTable.versionColumn] = nextVersion + it[ConfigOrganismVersionsTable.configColumn] = draftConfig + it[ConfigOrganismVersionsTable.publishedAtColumn] = now + it[ConfigOrganismVersionsTable.publishedByColumn] = actor + } + + ConfigOrganismsTable.update({ ConfigOrganismsTable.keyColumn eq key }) { + it[ConfigOrganismsTable.statusColumn] = "released" + it[ConfigOrganismsTable.currentVersionColumn] = nextVersion + if (previousVersion == null) { + it[ConfigOrganismsTable.firstPublishedAtColumn] = now + } + it[ConfigOrganismsTable.lastPublishedAtColumn] = now + } + + ConfigOrganismDraftsTable.deleteWhere { organismKeyColumn eq key } + + auditLogService.append( + actor = actor, + scope = AuditScope.ORGANISM, + organismKey = key, + action = AuditAction.PUBLISH, + resultVersion = nextVersion, + ) + + PublishResult( + version = nextVersion, + previousVersion = previousVersion, + publishedAt = now, + publishedBy = actor, + ) + } + configService.invalidateCache() + return result + } + + fun getInstanceDraft(): InstanceDraftView? = transaction { + ConfigInstanceDraftTable.selectAll() + .singleOrNull() + ?.let { + InstanceDraftView( + config = it[ConfigInstanceDraftTable.configColumn], + baseVersion = it[ConfigInstanceDraftTable.baseVersionColumn], + revision = it[ConfigInstanceDraftTable.revisionColumn], + ) + } + } + + fun putInstanceDraft(newConfig: InstanceConfig, ifMatch: Long?, actor: String): Long { + val now = dateProvider.getCurrentDateTime() + return transaction { + val baseVersion = ConfigInstanceStateTable.selectAll() + .single()[ConfigInstanceStateTable.currentVersionColumn] + + val existing = ConfigInstanceDraftTable.selectAll().forUpdate().singleOrNull() + val newRevision: Long + if (existing == null) { + if (ifMatch != null && ifMatch != 0L) { + throw OptimisticConcurrencyException( + "If-Match=$ifMatch but no instance draft exists yet (effective revision 0).", + ) + } + ConfigInstanceDraftTable.insert { + it[ConfigInstanceDraftTable.singletonColumn] = true + it[ConfigInstanceDraftTable.configColumn] = newConfig + it[ConfigInstanceDraftTable.baseVersionColumn] = baseVersion + it[ConfigInstanceDraftTable.revisionColumn] = 1L + it[ConfigInstanceDraftTable.createdAtColumn] = now + it[ConfigInstanceDraftTable.updatedAtColumn] = now + it[ConfigInstanceDraftTable.createdByColumn] = actor + it[ConfigInstanceDraftTable.updatedByColumn] = actor + } + newRevision = 1L + } else { + val currentRevision = existing[ConfigInstanceDraftTable.revisionColumn] + if (ifMatch != null && ifMatch != currentRevision) { + throw OptimisticConcurrencyException( + "If-Match=$ifMatch but current instance draft revision is $currentRevision.", + ) + } + newRevision = currentRevision + 1L + ConfigInstanceDraftTable.update({ ConfigInstanceDraftTable.singletonColumn eq true }) { + it[ConfigInstanceDraftTable.configColumn] = newConfig + it[ConfigInstanceDraftTable.revisionColumn] = newRevision + it[ConfigInstanceDraftTable.updatedAtColumn] = now + it[ConfigInstanceDraftTable.updatedByColumn] = actor + } + } + + auditLogService.append( + actor = actor, + scope = AuditScope.INSTANCE, + organismKey = null, + action = AuditAction.DOCUMENT_REPLACE, + details = mapOf("revision" to newRevision), + ) + newRevision + } + } + + fun appendInstanceOperations(ops: List, ifMatch: Long?, actor: String): Long { + require(ops.isNotEmpty()) { "At least one operation is required." } + val now = dateProvider.getCurrentDateTime() + return transaction { + val currentVersion = ConfigInstanceStateTable.selectAll() + .single()[ConfigInstanceStateTable.currentVersionColumn] + ?: error("instance has no current_version; migration is broken") + + val (currentConfig, currentRevision) = loadOrCreateInstanceDraft(currentVersion, actor, now) + if (ifMatch != null && ifMatch != currentRevision) { + throw OptimisticConcurrencyException( + "If-Match=$ifMatch but current instance draft revision is $currentRevision.", + ) + } + val batch = operationDispatcher.applyMany(ops, ConfigDocument.Instance(currentConfig)) + val newConfig = (batch.newDraft as ConfigDocument.Instance).config + val newRevision = currentRevision + 1L + + ConfigInstanceDraftTable.update({ ConfigInstanceDraftTable.singletonColumn eq true }) { + it[ConfigInstanceDraftTable.configColumn] = newConfig + it[ConfigInstanceDraftTable.revisionColumn] = newRevision + it[ConfigInstanceDraftTable.updatedAtColumn] = now + it[ConfigInstanceDraftTable.updatedByColumn] = actor + } + + for (applied in batch.applied) { + auditLogService.append( + actor = actor, + scope = AuditScope.INSTANCE, + organismKey = null, + action = AuditAction.OP_APPEND, + details = mapOf( + "opType" to applied.opType, + "summary" to applied.summary, + "revision" to newRevision, + ), + ) + } + newRevision + } + } + + fun discardInstanceDraft(actor: String) { + transaction { + val deleted = ConfigInstanceDraftTable.deleteWhere { singletonColumn eq true } + if (deleted > 0) { + auditLogService.append( + actor = actor, + scope = AuditScope.INSTANCE, + organismKey = null, + action = AuditAction.DISCARD_DRAFT, + ) + } + } + } + + fun publishInstance(actor: String): PublishResult { + val now = dateProvider.getCurrentDateTime() + val result = transaction { + val draft = ConfigInstanceDraftTable.selectAll().singleOrNull() + ?: throw NoDraftToPublishException("instance") + val draftConfig = draft[ConfigInstanceDraftTable.configColumn] + + val previousVersion = ConfigInstanceStateTable.selectAll() + .single()[ConfigInstanceStateTable.currentVersionColumn] + val nextVersion = (previousVersion ?: 0L) + 1L + + ConfigInstanceVersionsTable.insert { + it[ConfigInstanceVersionsTable.versionColumn] = nextVersion + it[ConfigInstanceVersionsTable.configColumn] = draftConfig + it[ConfigInstanceVersionsTable.publishedAtColumn] = now + it[ConfigInstanceVersionsTable.publishedByColumn] = actor + } + + ConfigInstanceStateTable.update({ ConfigInstanceStateTable.singletonColumn eq true }) { + it[ConfigInstanceStateTable.currentVersionColumn] = nextVersion + } + + ConfigInstanceDraftTable.deleteWhere { singletonColumn eq true } + + auditLogService.append( + actor = actor, + scope = AuditScope.INSTANCE, + organismKey = null, + action = AuditAction.PUBLISH, + resultVersion = nextVersion, + ) + + PublishResult( + version = nextVersion, + previousVersion = previousVersion, + publishedAt = now, + publishedBy = actor, + ) + } + configService.invalidateCache() + return result + } + + private fun readOrganismDraft(key: String): OrganismDraftView? { + val row = ConfigOrganismDraftsTable.selectAll() + .where { ConfigOrganismDraftsTable.organismKeyColumn eq key } + .singleOrNull() ?: return null + return OrganismDraftView( + config = row[ConfigOrganismDraftsTable.configColumn], + baseVersion = row[ConfigOrganismDraftsTable.baseVersionColumn], + revision = row[ConfigOrganismDraftsTable.revisionColumn], + operations = auditLogService.pendingOrganismOps(key).map { entry -> + PendingOp( + opType = entry.details?.get("opType")?.toString() ?: "?", + summary = entry.details?.get("summary")?.toString() ?: "?", + appliedAt = entry.occurredAt, + appliedBy = entry.actor, + ) + }, + ) + } + + private fun loadOrCreateOrganismDraft( + key: String, + currentVersion: Long, + actor: String, + now: kotlinx.datetime.LocalDateTime, + ): Pair { + val existing = ConfigOrganismDraftsTable.selectAll() + .where { ConfigOrganismDraftsTable.organismKeyColumn eq key } + .forUpdate() + .singleOrNull() + if (existing != null) { + return Pair( + existing[ConfigOrganismDraftsTable.configColumn], + existing[ConfigOrganismDraftsTable.revisionColumn], + ) + } + val currentConfig = ConfigOrganismVersionsTable.selectAll() + .where { ConfigOrganismVersionsTable.organismKeyColumn eq key } + .andWhere { ConfigOrganismVersionsTable.versionColumn eq currentVersion } + .single()[ConfigOrganismVersionsTable.configColumn] + ConfigOrganismDraftsTable.insert { + it[ConfigOrganismDraftsTable.organismKeyColumn] = key + it[ConfigOrganismDraftsTable.configColumn] = currentConfig + it[ConfigOrganismDraftsTable.baseVersionColumn] = currentVersion + it[ConfigOrganismDraftsTable.revisionColumn] = 0L + it[ConfigOrganismDraftsTable.createdAtColumn] = now + it[ConfigOrganismDraftsTable.updatedAtColumn] = now + it[ConfigOrganismDraftsTable.createdByColumn] = actor + it[ConfigOrganismDraftsTable.updatedByColumn] = actor + } + return Pair(currentConfig, 0L) + } + + private fun loadOrCreateInstanceDraft( + currentVersion: Long, + actor: String, + now: kotlinx.datetime.LocalDateTime, + ): Pair { + val existing = ConfigInstanceDraftTable.selectAll().forUpdate().singleOrNull() + if (existing != null) { + return Pair( + existing[ConfigInstanceDraftTable.configColumn], + existing[ConfigInstanceDraftTable.revisionColumn], + ) + } + val currentConfig = ConfigInstanceVersionsTable.selectAll() + .where { ConfigInstanceVersionsTable.versionColumn eq currentVersion } + .single()[ConfigInstanceVersionsTable.configColumn] + ConfigInstanceDraftTable.insert { + it[ConfigInstanceDraftTable.singletonColumn] = true + it[ConfigInstanceDraftTable.configColumn] = currentConfig + it[ConfigInstanceDraftTable.baseVersionColumn] = currentVersion + it[ConfigInstanceDraftTable.revisionColumn] = 0L + it[ConfigInstanceDraftTable.createdAtColumn] = now + it[ConfigInstanceDraftTable.updatedAtColumn] = now + it[ConfigInstanceDraftTable.createdByColumn] = actor + it[ConfigInstanceDraftTable.updatedByColumn] = actor + } + return Pair(currentConfig, 0L) + } + + data class OrganismDraftView( + val config: OrganismConfig, + val baseVersion: Long?, + val revision: Long, + val operations: List, + ) + + data class InstanceDraftView(val config: InstanceConfig, val baseVersion: Long?, val revision: Long) + + data class PendingOp( + val opType: String, + val summary: String, + val appliedAt: kotlinx.datetime.LocalDateTime, + val appliedBy: String, + ) + + data class PublishResult( + val version: Long, + val previousVersion: Long?, + val publishedAt: kotlinx.datetime.LocalDateTime, + val publishedBy: String, + ) +} + +class DraftScopeMismatchException(message: String) : RuntimeException(message) +class OptimisticConcurrencyException(message: String) : RuntimeException(message) +class NoDraftToPublishException(scope: String) : RuntimeException("No draft to publish for $scope.") diff --git a/backend/src/main/kotlin/org/loculus/backend/config/service/OrganismAdminService.kt b/backend/src/main/kotlin/org/loculus/backend/config/service/OrganismAdminService.kt new file mode 100644 index 0000000000..ffed450b22 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/service/OrganismAdminService.kt @@ -0,0 +1,96 @@ +package org.loculus.backend.config.service + +import org.jetbrains.exposed.exceptions.ExposedSQLException +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update +import org.loculus.backend.config.dbtables.ConfigOrganismsTable +import org.loculus.backend.service.submission.dbtables.CurrentProcessingPipelineTable +import org.loculus.backend.utils.DateProvider +import org.springframework.stereotype.Service + +@Service +class OrganismAdminService( + private val configService: ConfigService, + private val auditLogService: AuditLogService, + private val dateProvider: DateProvider, +) { + + fun createOrganism(key: String, createdBy: String): ConfigService.OrganismListing { + require(key.isNotBlank()) { "organism key must not be blank" } + val now = dateProvider.getCurrentDateTime() + return transaction { + val existing = ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.keyColumn eq key } + .limit(1) + .toList() + if (existing.isNotEmpty()) { + throw OrganismAlreadyExistsException(key) + } + try { + ConfigOrganismsTable.insert { + it[ConfigOrganismsTable.keyColumn] = key + it[ConfigOrganismsTable.statusColumn] = "unreleased" + it[ConfigOrganismsTable.currentVersionColumn] = null + it[ConfigOrganismsTable.deployedColumn] = false + it[ConfigOrganismsTable.createdAtColumn] = now + it[ConfigOrganismsTable.createdByColumn] = createdBy + it[ConfigOrganismsTable.firstPublishedAtColumn] = null + it[ConfigOrganismsTable.lastPublishedAtColumn] = null + } + } catch (e: ExposedSQLException) { + throw OrganismAlreadyExistsException(key) + } + + CurrentProcessingPipelineTable.setV1ForOrganismsIfNotExist(listOf(key), now) + auditLogService.append( + actor = createdBy, + scope = AuditScope.ORGANISM, + organismKey = key, + action = AuditAction.ORGANISM_CREATE, + ) + + configService.invalidateCache() + + ConfigService.OrganismListing(key = key, status = "unreleased", currentVersion = null, deployed = false) + } + } + + fun markDeployed(key: String, actor: String): ConfigService.OrganismListing = transaction { + val row = ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.keyColumn eq key } + .singleOrNull() ?: throw OrganismNotFoundException(key) + + val status = row[ConfigOrganismsTable.statusColumn] + val currentVersion = row[ConfigOrganismsTable.currentVersionColumn] + if (status != "released" || currentVersion == null) { + throw OrganismDeploymentException("Only released organisms can be marked deployed.") + } + + val wasDeployed = row[ConfigOrganismsTable.deployedColumn] + if (!wasDeployed) { + ConfigOrganismsTable.update({ ConfigOrganismsTable.keyColumn eq key }) { + it[ConfigOrganismsTable.deployedColumn] = true + } + auditLogService.append( + actor = actor, + scope = AuditScope.ORGANISM, + organismKey = key, + action = AuditAction.MARK_DEPLOYED, + details = mapOf("version" to currentVersion), + ) + configService.invalidateCache() + } + + ConfigService.OrganismListing( + key = key, + status = status, + currentVersion = currentVersion, + deployed = true, + ) + } +} + +class OrganismAlreadyExistsException(val key: String) : RuntimeException("Organism '$key' already exists.") +class OrganismDeploymentException(message: String) : RuntimeException(message) diff --git a/backend/src/main/kotlin/org/loculus/backend/config/service/PreprocessingConfigService.kt b/backend/src/main/kotlin/org/loculus/backend/config/service/PreprocessingConfigService.kt new file mode 100644 index 0000000000..a2f4dd0ad0 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/config/service/PreprocessingConfigService.kt @@ -0,0 +1,94 @@ +package org.loculus.backend.config.service + +import kotlinx.datetime.LocalDateTime +import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.andWhere +import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update +import org.loculus.backend.config.dbtables.ConfigOrganismsTable +import org.loculus.backend.config.dbtables.ConfigPreprocessingFilesTable +import org.loculus.backend.utils.DateProvider +import org.springframework.stereotype.Service + +/** + * Stores and serves opaque, unversioned preprocessing config files, keyed by + * (organism, pipeline version). The backend never parses or interprets the + * content — it is a generic text channel for external preprocessing pipelines. + * Editing is a direct save (no draft/publish/version flow); the current value + * is what gets served. + */ +@Service +class PreprocessingConfigService(private val dateProvider: DateProvider) { + + fun getConfigFile(organismKey: String, pipelineVersion: Long): String? = transaction { + ConfigPreprocessingFilesTable.selectAll() + .where { ConfigPreprocessingFilesTable.organismKeyColumn eq organismKey } + .andWhere { ConfigPreprocessingFilesTable.pipelineVersionColumn eq pipelineVersion } + .map { it[ConfigPreprocessingFilesTable.configFileColumn] } + .singleOrNull() + } + + fun listVersions(organismKey: String): List = transaction { + requireOrganismExists(organismKey) + ConfigPreprocessingFilesTable.selectAll() + .where { ConfigPreprocessingFilesTable.organismKeyColumn eq organismKey } + .orderBy(ConfigPreprocessingFilesTable.pipelineVersionColumn to SortOrder.ASC) + .map { + PreprocessingConfigVersion( + pipelineVersion = it[ConfigPreprocessingFilesTable.pipelineVersionColumn], + updatedAt = it[ConfigPreprocessingFilesTable.updatedAtColumn], + updatedBy = it[ConfigPreprocessingFilesTable.updatedByColumn], + ) + } + } + + fun setConfigFile(organismKey: String, pipelineVersion: Long, content: String, updatedBy: String) { + val now = dateProvider.getCurrentDateTime() + transaction { + requireOrganismExists(organismKey) + val updated = ConfigPreprocessingFilesTable.update({ + (ConfigPreprocessingFilesTable.organismKeyColumn eq organismKey) and + (ConfigPreprocessingFilesTable.pipelineVersionColumn eq pipelineVersion) + }) { + it[configFileColumn] = content + it[updatedAtColumn] = now + it[updatedByColumn] = updatedBy + } + if (updated == 0) { + ConfigPreprocessingFilesTable.insert { + it[organismKeyColumn] = organismKey + it[pipelineVersionColumn] = pipelineVersion + it[configFileColumn] = content + it[updatedAtColumn] = now + it[updatedByColumn] = updatedBy + } + } + } + } + + fun deleteConfigFile(organismKey: String, pipelineVersion: Long): Boolean = transaction { + ConfigPreprocessingFilesTable.deleteWhere { + (ConfigPreprocessingFilesTable.organismKeyColumn eq organismKey) and + (ConfigPreprocessingFilesTable.pipelineVersionColumn eq pipelineVersion) + } > 0 + } + + private fun requireOrganismExists(organismKey: String) { + val exists = ConfigOrganismsTable.selectAll() + .where { ConfigOrganismsTable.keyColumn eq organismKey } + .limit(1) + .any() + if (!exists) throw OrganismNotFoundException(organismKey) + } + + data class PreprocessingConfigVersion( + val pipelineVersion: Long, + val updatedAt: LocalDateTime, + val updatedBy: String, + ) +} diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/FilesController.kt b/backend/src/main/kotlin/org/loculus/backend/controller/FilesController.kt index cc829b28d0..39a2374373 100644 --- a/backend/src/main/kotlin/org/loculus/backend/controller/FilesController.kt +++ b/backend/src/main/kotlin/org/loculus/backend/controller/FilesController.kt @@ -128,6 +128,9 @@ class FilesController( @Parameter(description = "Number of files, default is 1.") @RequestParam numberFiles: Int = 1, + @Parameter(description = "Whether to return upload URLs signed for the internal S3 endpoint.") + @RequestParam + useInternalEndpoint: Boolean = false, ): List { filesPreconditionValidator.validateUserIsAllowedToUploadFileForGroup(groupId, authenticatedUser) val response = mutableListOf() @@ -136,7 +139,7 @@ class FilesController( } repeat(numberFiles) { val fileId = generateFileId() - val presignedUploadUrl = s3Service.createUrlToUploadPrivateFile(fileId) + val presignedUploadUrl = s3Service.createUrlToUploadPrivateFile(fileId, useInternalEndpoint) filesDatabaseService.createFileEntry(fileId, authenticatedUser.username, groupId) response.add(FileIdAndWriteUrl(fileId, presignedUploadUrl)) } diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/InfoController.kt b/backend/src/main/kotlin/org/loculus/backend/controller/InfoController.kt index ca5111ea6c..be75297068 100644 --- a/backend/src/main/kotlin/org/loculus/backend/controller/InfoController.kt +++ b/backend/src/main/kotlin/org/loculus/backend/controller/InfoController.kt @@ -1,19 +1,30 @@ package org.loculus.backend.controller +import com.fasterxml.jackson.databind.ObjectMapper import io.swagger.v3.oas.annotations.Hidden import jakarta.servlet.http.HttpServletRequest +import org.loculus.backend.config.BackendConfig import org.loculus.backend.config.BackendSpringProperty import org.loculus.backend.config.DEBUG_MODE_ON_VALUE +import org.loculus.backend.config.configuredViews +import org.loculus.backend.config.service.ConfigService import org.springframework.beans.factory.annotation.Value import org.springframework.http.MediaType import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController const val PROJECT_NAME = "Loculus" +private const val DEFAULT_API_DOCS_URL = "/api-docs.json" +private const val DATA_USE_TERMS_PATH = "/about/terms-of-use/data-use-terms" @Hidden @RestController -class InfoController(@Value("\${${BackendSpringProperty.DEBUG_MODE}}") private val debugMode: String) { +class InfoController( + @Value("\${${BackendSpringProperty.DEBUG_MODE}}") private val debugMode: String, + private val configService: ConfigService, + private val backendConfig: BackendConfig, +) { @RequestMapping("/", produces = [MediaType.TEXT_HTML_VALUE]) fun htmlInfo(request: HttpServletRequest) = """ @@ -25,11 +36,226 @@ class InfoController(@Value("\${${BackendSpringProperty.DEBUG_MODE}}") private v

Welcome to the $PROJECT_NAME Backend

- Visit our swagger-ui + Visit our Swagger UI +
+ Visit our Scalar API reference """.trimIndent() + @RequestMapping("/swagger-ui/loculus", produces = [MediaType.TEXT_HTML_VALUE]) + fun swaggerUiWithLoculusBar(@RequestParam(defaultValue = DEFAULT_API_DOCS_URL) url: String): String { + val resolvedUrl = resolveSpecUrl(url) + val specUrl = ObjectMapper().writeValueAsString(resolvedUrl) + return docsShell( + url = resolvedUrl, + mode = ApiDocsMode.SWAGGER, + extraHead = """ + + + """.trimIndent(), + content = """ +
+ + + + """.trimIndent(), + ) + } + + @RequestMapping("/scalar-api-reference", produces = [MediaType.TEXT_HTML_VALUE]) + fun scalarApiReference(@RequestParam(defaultValue = DEFAULT_API_DOCS_URL) url: String): String { + val resolvedUrl = resolveSpecUrl(url) + val specUrl = ObjectMapper().writeValueAsString(resolvedUrl) + return docsShell( + url = resolvedUrl, + mode = ApiDocsMode.SCALAR, + content = """ +
+ + + """.trimIndent(), + ) + } + + private fun docsShell(url: String, mode: ApiDocsMode, content: String, extraHead: String = ""): String { + val bar = docsBar(url, mode) + return """ + + + + + + $PROJECT_NAME API Reference + $extraHead + + + + $bar + $content + + + + """.trimIndent() + } + + private fun docsBar(url: String, mode: ApiDocsMode): String { + val options = apiDocOptions() + .joinToString("\n") { option -> + val selected = if (option.url == url) " selected" else "" + """""" + } + val terms = dataUseTermsMessage() + return """ + + """.trimIndent() + } + + /** + * Resolve the requested spec URL against the allowlist of server-generated options. Anything else + * (including attacker-crafted values that would otherwise be reflected into the docs page) falls back + * to the default spec, so only known-safe URLs ever reach the rendered HTML. + */ + private fun resolveSpecUrl(url: String): String = + if (apiDocOptions().any { it.url == url }) url else DEFAULT_API_DOCS_URL + + private fun apiDocOptions(): List { + val configuredOptions = runCatching { + val organismOptions = configService.listReleasedOrganisms().map { listing -> + val organism = runCatching { configService.getOrganismConfig(listing.key).config }.getOrNull() + val displayName = organism?.displayName ?: organism?.schema?.organismName ?: listing.key + ApiDocOption("Organism: $displayName", "/api-docs/query/${listing.key}.json") + } + val viewOptions = configService.getInstanceConfig().config.configuredViews().map { (key, view) -> + ApiDocOption("View: ${view.displayName}", "/api-docs/query/$key.json") + } + organismOptions + viewOptions + }.getOrDefault(emptyList()) + + return listOf( + ApiDocOption("Complete API", DEFAULT_API_DOCS_URL), + ApiDocOption("General backend API", "/api-docs/general.json"), + ) + configuredOptions.sortedBy { it.label } + } + + private fun dataUseTermsMessage(): String = runCatching { + if (!configService.getInstanceConfig().config.dataUseTerms.enabled) { + return@runCatching "" + } + """ + + By using the API, you agree to the + Data Use Terms + + """.trimIndent() + }.getOrDefault("") + @RequestMapping("/", produces = [MediaType.APPLICATION_JSON_VALUE]) fun jsonInfo() = Info( isInDebugMode = debugMode == DEBUG_MODE_ON_VALUE, @@ -39,6 +265,20 @@ class InfoController(@Value("\${${BackendSpringProperty.DEBUG_MODE}}") private v data class Info( val name: String = "$PROJECT_NAME backend", val status: String = "Healthy", - val documentation: String = "visit /swagger-ui/index.html", + val documentation: String = "visit /swagger-ui/loculus or /scalar-api-reference", val isInDebugMode: Boolean, ) + +private data class ApiDocOption(val label: String, val url: String) + +private enum class ApiDocsMode(val value: String) { + SWAGGER("swagger"), + SCALAR("scalar"), +} + +private fun htmlEscape(value: String): String = value + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'") diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/LapisAccessFilter.kt b/backend/src/main/kotlin/org/loculus/backend/controller/LapisAccessFilter.kt new file mode 100644 index 0000000000..b04a6c51c2 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/controller/LapisAccessFilter.kt @@ -0,0 +1,67 @@ +package org.loculus.backend.controller + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.node.ObjectNode +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Component +import org.springframework.web.server.ResponseStatusException +import java.net.URLDecoder +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +/** + * Prepares request bodies / query strings before they are forwarded to the + * upstream LAPIS instance. It injects the LAPIS `versionStatus` filter that + * distinguishes the `current` (latest) and `allVersions` query groups. + * + * Note: query endpoints are currently open (no authentication), so no + * group-based visibility filtering is applied here. Should per-group + * visibility be reintroduced, this is the single place to add it (combine a + * visibility clause into `advancedQuery` for both the body and query-string + * paths). + */ +@Component +class LapisAccessFilter(private val objectMapper: ObjectMapper) { + fun prepareBody(body: JsonNode?, versionStatus: String? = null): ByteArray { + val node: ObjectNode = when { + body == null || body.isNull -> objectMapper.createObjectNode() + body.isObject -> body.deepCopy() + else -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Request body must be a JSON object") + } + if (versionStatus != null) { + node.put("versionStatus", versionStatus) + } + return objectMapper.writeValueAsBytes(node) + } + + fun prepareBody(rawBody: ByteArray, versionStatus: String? = null): ByteArray { + val body = rawBody.takeIf { it.isNotEmpty() }?.let { objectMapper.readTree(it) } + return prepareBody(body, versionStatus) + } + + fun prepareQuery(queryString: String?, versionStatus: String? = null): String { + val queryParameters = parseQueryString(queryString).toMutableList() + if (versionStatus != null) { + queryParameters.add("versionStatus" to versionStatus) + } + if (queryParameters.isEmpty()) return "" + return "?" + queryParameters.joinToString("&") { (key, value) -> + "${urlEncode(key)}=${urlEncode(value)}" + } + } + + private fun parseQueryString(queryString: String?): List> { + if (queryString.isNullOrBlank()) return emptyList() + return queryString.split("&").mapNotNull { part -> + val (key, value) = part.split("=", limit = 2).let { + it[0] to it.getOrElse(1) { "" } + } + if (key.isBlank()) null else urlDecode(key) to urlDecode(value) + } + } + + private fun urlEncode(value: String) = URLEncoder.encode(value, StandardCharsets.UTF_8) + + private fun urlDecode(value: String) = URLDecoder.decode(value, StandardCharsets.UTF_8) +} diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyController.kt b/backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyController.kt new file mode 100644 index 0000000000..739969152b --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyController.kt @@ -0,0 +1,52 @@ +package org.loculus.backend.controller + +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.servlet.http.HttpServletRequest +import org.loculus.backend.config.LAPIS_PROXY_CONTROLLER_TAG +import org.loculus.backend.config.service.ConfigService +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestMethod +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody + +@RestController +@Tag( + name = LAPIS_PROXY_CONTROLLER_TAG, + description = "This is temporary and used for calls that have not yet switched to using the new query API.", +) +class LapisProxyController( + private val configService: ConfigService, + private val lapisProxyService: LapisProxyService, + private val lapisAccessFilter: LapisAccessFilter, +) { + + @RequestMapping( + value = ["/{organism}/lapis/**"], + method = [RequestMethod.GET, RequestMethod.POST], + ) + fun proxy(@PathVariable organism: String, request: HttpServletRequest): ResponseEntity { + val lapisUrl = configService.lapisUrlFor(organism) + + val lapisPath = request.requestURI.removePrefix("/$organism/lapis") + val query = request.queryString?.let { "?$it" } ?: "" + + return if (request.method == "POST") { + lapisProxyService.proxyPost( + lapisUrl, + "$lapisPath$query", + lapisAccessFilter.prepareBody(request.inputStream.readBytes()), + request.getHeader("Accept"), + request.contentType ?: "application/json", + ) + } else { + lapisProxyService.proxyGet( + lapisUrl, + lapisPath, + lapisAccessFilter.prepareQuery(request.queryString), + request.getHeader("Accept"), + ) + } + } +} diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyService.kt b/backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyService.kt new file mode 100644 index 0000000000..34427af2a9 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/controller/LapisProxyService.kt @@ -0,0 +1,97 @@ +package org.loculus.backend.controller + +import mu.KotlinLogging +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Component +import org.springframework.web.server.ResponseStatusException +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody +import java.io.IOException +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse +import java.time.Duration + +private val log = KotlinLogging.logger {} + +@Component +class LapisProxyService { + + private val httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .connectTimeout(Duration.ofSeconds(10)) + .build() + + fun proxyPost( + lapisBaseUrl: String, + lapisPath: String, + bodyBytes: ByteArray, + acceptHeader: String?, + contentType: String = "application/json", + ): ResponseEntity { + val url = lapisBaseUrl.trimEnd('/') + lapisPath + log.debug { "Proxying POST $url" } + val request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", contentType) + .apply { acceptHeader?.let { header("Accept", it) } } + .POST(HttpRequest.BodyPublishers.ofByteArray(bodyBytes)) + .build() + return dispatch(request) + } + + fun proxyGet( + lapisBaseUrl: String, + lapisPath: String, + query: String, + acceptHeader: String?, + ): ResponseEntity { + val url = lapisBaseUrl.trimEnd('/') + lapisPath + query + log.debug { "Proxying GET $url" } + val request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .apply { acceptHeader?.let { header("Accept", it) } } + .GET() + .build() + return dispatch(request) + } + + private fun dispatch(request: HttpRequest): ResponseEntity { + val upstreamResponse = try { + httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()) + } catch (e: IOException) { + throw ResponseStatusException(HttpStatus.BAD_GATEWAY, "LAPIS service unavailable: ${e.message}") + } + + val responseHeaders = HttpHeaders() + upstreamResponse.headers().map().forEach { (name, values) -> + if (name.lowercase() !in HOP_BY_HOP_HEADERS) { + responseHeaders[name] = values + } + } + responseHeaders.remove(HttpHeaders.CONTENT_LENGTH) + + val body = StreamingResponseBody { outputStream -> + upstreamResponse.body().use { inputStream -> + inputStream.copyTo(outputStream, bufferSize = 8192) + } + } + + return ResponseEntity(body, responseHeaders, HttpStatus.valueOf(upstreamResponse.statusCode())) + } + + companion object { + val HOP_BY_HOP_HEADERS = setOf( + "connection", + "keep-alive", + "transfer-encoding", + "te", + "trailer", + "upgrade", + "proxy-authorization", + "proxy-authenticate", + ) + } +} diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/LapisUrlResolver.kt b/backend/src/main/kotlin/org/loculus/backend/controller/LapisUrlResolver.kt new file mode 100644 index 0000000000..cf9365b605 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/controller/LapisUrlResolver.kt @@ -0,0 +1,22 @@ +package org.loculus.backend.controller + +import org.loculus.backend.config.configuredViews +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.config.service.OrganismNotFoundException +import org.springframework.http.HttpStatus +import org.springframework.web.server.ResponseStatusException + +/** Resolve the LAPIS base URL backing a view key or an organism key. */ +fun ConfigService.lapisUrlFor(organism: String): String { + getInstanceConfig().config.configuredViews()[organism]?.let { view -> + return view.lapisUrl + ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "No LAPIS URL configured for view: $organism") + } + val config = try { + getOrganismConfig(organism).config + } catch (e: OrganismNotFoundException) { + throw ResponseStatusException(HttpStatus.NOT_FOUND, "Unknown organism: $organism", e) + } + return config.lapisUrl + ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "No LAPIS URL configured for organism: $organism") +} diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/OpenApiSplitController.kt b/backend/src/main/kotlin/org/loculus/backend/controller/OpenApiSplitController.kt new file mode 100644 index 0000000000..98c46ed4bd --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/controller/OpenApiSplitController.kt @@ -0,0 +1,76 @@ +package org.loculus.backend.controller + +import io.swagger.v3.core.util.Json +import io.swagger.v3.oas.annotations.Hidden +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.Paths +import io.swagger.v3.oas.models.tags.Tag +import jakarta.servlet.http.HttpServletRequest +import org.springdoc.webmvc.api.OpenApiWebMvcResource +import org.springframework.context.i18n.LocaleContextHolder +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.server.ResponseStatusException + +private const val SPRINGDOC_API_DOCS_PATH = "/api-docs.json" + +@Hidden +@RestController +class OpenApiSplitController(private val openApiResource: OpenApiWebMvcResource) { + + @GetMapping("/api-docs/general.json", produces = [MediaType.APPLICATION_JSON_VALUE]) + fun generalJson(request: HttpServletRequest): ResponseEntity { + val openApi = generatedOpenApi(request) + openApi.paths = filteredPaths(openApi) { path -> !path.startsWith("/query/") } + openApi.keepOnlyUsedTags() + return jsonResponse(Json.mapper().writeValueAsBytes(openApi)) + } + + @GetMapping("/api-docs.yaml", produces = ["application/vnd.oai.openapi"]) + fun completeYaml(request: HttpServletRequest): ResponseEntity = ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/vnd.oai.openapi")) + .body(openApiResource.openapiYaml(request, SPRINGDOC_API_DOCS_PATH, LocaleContextHolder.getLocale())) + + @GetMapping("/api-docs/query/{organism}.json", produces = [MediaType.APPLICATION_JSON_VALUE]) + fun queryJson(request: HttpServletRequest, @PathVariable organism: String): ResponseEntity { + val openApi = generatedOpenApi(request) + openApi.paths = filteredPaths(openApi) { path -> path.startsWith("/query/$organism/") } + if (openApi.paths.isEmpty()) { + throw ResponseStatusException(HttpStatus.NOT_FOUND, "No query OpenAPI specification exists for $organism") + } + openApi.keepOnlyUsedTags() + return jsonResponse(Json.mapper().writeValueAsBytes(openApi)) + } + + private fun generatedOpenApi(request: HttpServletRequest): OpenAPI { + val json = openApiResource.openapiJson(request, SPRINGDOC_API_DOCS_PATH, LocaleContextHolder.getLocale()) + return Json.mapper().readValue(json, OpenAPI::class.java) + } + + private fun filteredPaths(openApi: OpenAPI, includePath: (String) -> Boolean): Paths { + val paths = Paths() + openApi.paths.orEmpty() + .filterKeys(includePath) + .forEach { (path, pathItem) -> paths.addPathItem(path, pathItem) } + return paths + } + + private fun OpenAPI.keepOnlyUsedTags() { + val usedTags = paths.orEmpty() + .values + .flatMap { pathItem -> pathItem.readOperations().flatMap { operation -> operation.tags.orEmpty() } } + .toSet() + + tags = tags + ?.filter { tag -> tag.name in usedTags } + ?.map { tag -> Tag().name(tag.name).description(tag.description).externalDocs(tag.externalDocs) } + } + + private fun jsonResponse(body: ByteArray): ResponseEntity = ResponseEntity.ok() + .contentType(MediaType.APPLICATION_JSON) + .body(body) +} diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/QueryController.kt b/backend/src/main/kotlin/org/loculus/backend/controller/QueryController.kt new file mode 100644 index 0000000000..49039d8af1 --- /dev/null +++ b/backend/src/main/kotlin/org/loculus/backend/controller/QueryController.kt @@ -0,0 +1,360 @@ +package org.loculus.backend.controller + +import com.fasterxml.jackson.databind.JsonNode +import jakarta.servlet.http.HttpServletRequest +import org.loculus.backend.config.service.ConfigService +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestHeader +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.server.ResponseStatusException +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody + +@RestController +@RequestMapping("/query/{organism}") +class QueryController( + private val configService: ConfigService, + private val lapisProxyService: LapisProxyService, + private val lapisAccessFilter: LapisAccessFilter, +) { + private enum class VersionGroup(val lapisFilter: String?) { + CURRENT("LATEST_VERSION"), + ALL_VERSIONS(null), + ; + + companion object { + fun fromPath(value: String): VersionGroup = when (value) { + "current" -> CURRENT + "allVersions" -> ALL_VERSIONS + else -> throw ResponseStatusException(HttpStatus.NOT_FOUND, "Unknown versionGroup: $value") + } + } + } + + private object LapisSamplePath { + const val DETAILS = "/sample/details" + const val AGGREGATED = "/sample/aggregated" + const val UNALIGNED_NUCLEOTIDE_SEQUENCES = "/sample/unalignedNucleotideSequences" + const val ALIGNED_NUCLEOTIDE_SEQUENCES = "/sample/alignedNucleotideSequences" + const val NUCLEOTIDE_MUTATIONS = "/sample/nucleotideMutations" + const val NUCLEOTIDE_INSERTIONS = "/sample/nucleotideInsertions" + const val AMINO_ACID_MUTATIONS = "/sample/aminoAcidMutations" + const val AMINO_ACID_INSERTIONS = "/sample/aminoAcidInsertions" + + fun unalignedNucleotideSequences(segment: String) = "$UNALIGNED_NUCLEOTIDE_SEQUENCES/$segment" + + fun alignedNucleotideSequences(referenceName: String) = "$ALIGNED_NUCLEOTIDE_SEQUENCES/$referenceName" + + fun alignedAminoAcidSequences(geneName: String) = "/sample/alignedAminoAcidSequences/$geneName" + } + + private fun post( + organism: String, + versionGroup: String, + lapisPath: String, + body: JsonNode?, + accept: String?, + ): ResponseEntity { + val lapisUrl = configService.lapisUrlFor(organism) + val vg = VersionGroup.fromPath(versionGroup) + return lapisProxyService.proxyPost( + lapisUrl, + lapisPath, + lapisAccessFilter.prepareBody(body, vg.lapisFilter), + accept, + ) + } + + private fun get( + organism: String, + versionGroup: String, + lapisPath: String, + request: HttpServletRequest, + accept: String?, + ): ResponseEntity { + val lapisUrl = configService.lapisUrlFor(organism) + val vg = VersionGroup.fromPath(versionGroup) + return lapisProxyService.proxyGet( + lapisUrl, + lapisPath, + lapisAccessFilter.prepareQuery(request.queryString, vg.lapisFilter), + accept, + ) + } + + @PostMapping("/{versionGroup}/metadata") + fun metadata( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.DETAILS, body, accept) + + @PostMapping("/{versionGroup}/aggregated") + fun aggregated( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.AGGREGATED, body, accept) + + @PostMapping("/{versionGroup}/sequences") + fun sequences( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.UNALIGNED_NUCLEOTIDE_SEQUENCES, body, accept) + + @PostMapping("/{versionGroup}/sequences/{segment}") + fun sequencesForSegment( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable segment: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.unalignedNucleotideSequences(segment), body, accept) + + @PostMapping("/{versionGroup}/sequencesAligned") + fun sequencesAligned( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.ALIGNED_NUCLEOTIDE_SEQUENCES, body, accept) + + @PostMapping("/{versionGroup}/sequencesAligned/mutations") + fun sequencesAlignedMutations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, body, accept) + + @PostMapping("/{versionGroup}/sequencesAligned/insertions") + fun sequencesAlignedInsertions( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_INSERTIONS, body, accept) + + @PostMapping("/{versionGroup}/sequencesAligned/aggregatedMutations") + fun sequencesAlignedAggregatedMutations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, body, accept) + + @PostMapping("/{versionGroup}/sequencesAligned/{referenceName}") + fun sequencesAlignedForSegment( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable referenceName: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.alignedNucleotideSequences(referenceName), body, accept) + + @PostMapping("/{versionGroup}/sequencesAligned/{referenceName}/mutations") + fun sequencesAlignedForSegmentMutations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable referenceName: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, body, accept) + + @PostMapping("/{versionGroup}/sequencesAligned/{referenceName}/aggregatedMutations") + fun sequencesAlignedForSegmentAggregatedMutations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable referenceName: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, body, accept) + + @PostMapping("/{versionGroup}/translations/{geneName}") + fun translations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable geneName: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.alignedAminoAcidSequences(geneName), body, accept) + + @PostMapping("/{versionGroup}/translations/mutations") + fun translationsMutations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.AMINO_ACID_MUTATIONS, body, accept) + + @PostMapping("/{versionGroup}/translations/insertions") + fun translationsInsertions( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.AMINO_ACID_INSERTIONS, body, accept) + + @PostMapping("/{versionGroup}/translations/{geneName}/mutations") + fun translationsForGeneMutations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable geneName: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.AMINO_ACID_MUTATIONS, body, accept) + + @PostMapping("/{versionGroup}/translations/{geneName}/aggregatedMutations") + fun translationsForGeneAggregatedMutations( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable geneName: String, + @RequestBody(required = false) body: JsonNode?, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = post(organism, versionGroup, LapisSamplePath.AMINO_ACID_MUTATIONS, body, accept) + + @GetMapping("/{versionGroup}/metadata") + fun metadataGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.DETAILS, request, accept) + + @GetMapping("/{versionGroup}/aggregated") + fun aggregatedGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.AGGREGATED, request, accept) + + @GetMapping("/{versionGroup}/sequences") + fun sequencesGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.UNALIGNED_NUCLEOTIDE_SEQUENCES, request, accept) + + @GetMapping("/{versionGroup}/sequences/{segment}") + fun sequencesForSegmentGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable segment: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.unalignedNucleotideSequences(segment), request, accept) + + @GetMapping("/{versionGroup}/sequencesAligned") + fun sequencesAlignedGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.ALIGNED_NUCLEOTIDE_SEQUENCES, request, accept) + + @GetMapping("/{versionGroup}/sequencesAligned/mutations") + fun sequencesAlignedMutationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, request, accept) + + @GetMapping("/{versionGroup}/sequencesAligned/insertions") + fun sequencesAlignedInsertionsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_INSERTIONS, request, accept) + + @GetMapping("/{versionGroup}/sequencesAligned/aggregatedMutations") + fun sequencesAlignedAggregatedMutationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, request, accept) + + @GetMapping("/{versionGroup}/sequencesAligned/{referenceName}") + fun sequencesAlignedForSegmentGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable referenceName: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.alignedNucleotideSequences(referenceName), request, accept) + + @GetMapping("/{versionGroup}/sequencesAligned/{referenceName}/mutations") + fun sequencesAlignedForSegmentMutationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable referenceName: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, request, accept) + + @GetMapping("/{versionGroup}/sequencesAligned/{referenceName}/aggregatedMutations") + fun sequencesAlignedForSegmentAggregatedMutationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable referenceName: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.NUCLEOTIDE_MUTATIONS, request, accept) + + @GetMapping("/{versionGroup}/translations/{geneName}") + fun translationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable geneName: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.alignedAminoAcidSequences(geneName), request, accept) + + @GetMapping("/{versionGroup}/translations/mutations") + fun translationsMutationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.AMINO_ACID_MUTATIONS, request, accept) + + @GetMapping("/{versionGroup}/translations/insertions") + fun translationsInsertionsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.AMINO_ACID_INSERTIONS, request, accept) + + @GetMapping("/{versionGroup}/translations/{geneName}/mutations") + fun translationsForGeneMutationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable geneName: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.AMINO_ACID_MUTATIONS, request, accept) + + @GetMapping("/{versionGroup}/translations/{geneName}/aggregatedMutations") + fun translationsForGeneAggregatedMutationsGet( + @PathVariable organism: String, + @PathVariable versionGroup: String, + @PathVariable geneName: String, + request: HttpServletRequest, + @RequestHeader(HttpHeaders.ACCEPT, required = false) accept: String?, + ) = get(organism, versionGroup, LapisSamplePath.AMINO_ACID_MUTATIONS, request, accept) +} diff --git a/backend/src/main/kotlin/org/loculus/backend/controller/SubmissionController.kt b/backend/src/main/kotlin/org/loculus/backend/controller/SubmissionController.kt index fc7a906fc7..a7b59166ab 100644 --- a/backend/src/main/kotlin/org/loculus/backend/controller/SubmissionController.kt +++ b/backend/src/main/kotlin/org/loculus/backend/controller/SubmissionController.kt @@ -37,7 +37,7 @@ import org.loculus.backend.api.SubmittedProcessedData import org.loculus.backend.api.UnprocessedData import org.loculus.backend.auth.AuthenticatedUser import org.loculus.backend.auth.HiddenParam -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.LoculusCustomHeaders.X_TOTAL_RECORDS import org.loculus.backend.log.ORGANISM_MDC_KEY import org.loculus.backend.log.REQUEST_ID_MDC_KEY @@ -100,7 +100,7 @@ open class SubmissionController( private val submissionDatabaseService: SubmissionDatabaseService, private val iteratorStreamer: IteratorStreamer, private val requestIdContext: RequestIdContext, - private val backendConfig: BackendConfig, + private val configService: ConfigService, private val objectMapper: ObjectMapper, private val groupManagementPreconditionValidator: GroupManagementPreconditionValidator, private val dataUseTermsPreconditionValidator: DataUseTermsPreconditionValidator, @@ -280,7 +280,7 @@ open class SubmissionController( @Parameter( description = ( "Name of the pipeline submitting the external metadata update. This should match the " + - "externalMetadataUpdater value of the externalMetadata fields (in the backend_config.json) that are being updated." + "externalMetadataUpdater value of the externalMetadata fields (configured per organism) that are being updated." ), ) @RequestParam externalMetadataUpdater: String, request: HttpServletRequest, @@ -524,9 +524,9 @@ open class SubmissionController( "attachment; filename=\"${submittedDataDownloadFilename(organism)}\"", ) - val instanceConfig = backendConfig.getInstanceConfig(organism) - val hasConsensusSequences = instanceConfig.schema.submissionDataTypes.consensusSequences - val isMultiSegmented = instanceConfig.referenceGenome.nucleotideSequences.size > 1 + val organismConfig = configService.getOrganismConfig(organism).config + val hasConsensusSequences = organismConfig.schema.submissionDataTypes.consensusSequences + val isMultiSegmented = organismConfig.referenceGenome.nucleotideSequences.size > 1 val streamBody = StreamingResponseBody { responseBodyStream -> val startTime = System.currentTimeMillis() @@ -675,7 +675,7 @@ open class SubmissionController( fun parseFileMapping(fileMapping: String?, organism: Organism): SubmissionIdFilesMap? { val fileMappingParsed = fileMapping?.let { - if (!backendConfig.getInstanceConfig(organism).schema.submissionDataTypes.files.enabled) { + if (!configService.getOrganismConfig(organism).config.schema.submissionDataTypes.files.enabled) { throw BadRequestException("the ${organism.name} organism does not support file submission.") } try { diff --git a/backend/src/main/kotlin/org/loculus/backend/model/ReleasedDataModel.kt b/backend/src/main/kotlin/org/loculus/backend/model/ReleasedDataModel.kt index 9a8a9cd232..b64e5fdc62 100644 --- a/backend/src/main/kotlin/org/loculus/backend/model/ReleasedDataModel.kt +++ b/backend/src/main/kotlin/org/loculus/backend/model/ReleasedDataModel.kt @@ -20,6 +20,7 @@ import org.loculus.backend.api.ReleasedData import org.loculus.backend.api.VersionStatus import org.loculus.backend.config.BackendConfig import org.loculus.backend.config.FileUrlType +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.service.datauseterms.DATA_USE_TERMS_TABLE_NAME import org.loculus.backend.service.files.S3Service import org.loculus.backend.service.groupmanagement.GROUPS_TABLE_NAME @@ -59,6 +60,7 @@ val RELEASED_DATA_RELATED_TABLES: List = open class ReleasedDataModel( private val submissionDatabaseService: SubmissionDatabaseService, private val backendConfig: BackendConfig, + private val configService: ConfigService, private val dateProvider: DateProvider, private val s3Service: S3Service, private val objectMapper: ObjectMapper, @@ -70,7 +72,8 @@ open class ReleasedDataModel( val latestVersions = submissionDatabaseService.getLatestVersions(organism) val latestRevocationVersions = submissionDatabaseService.getLatestRevocationVersions(organism) - val earliestReleaseDateConfig = backendConfig.getInstanceConfig(organism).schema.earliestReleaseDate + val earliestReleaseDateConfig = + configService.getOrganismConfig(organism).config.schema.earliestReleaseDate val finder = if (earliestReleaseDateConfig.enabled) { EarliestReleaseDateFinder(earliestReleaseDateConfig.externalFields) } else { @@ -150,14 +153,16 @@ open class ReleasedDataModel( val earliestReleaseDate = earliestReleaseDateFinder?.calculateEarliestReleaseDate(rawProcessedData) - val dataUseTermsUrl: String? = backendConfig.dataUseTerms.urls?.let { urls -> + val instanceConfig = configService.getInstanceConfig().config + val dataUseTermsUrl: String? = instanceConfig.dataUseTerms.urls?.let { urls -> when (currentDataUseTerms) { DataUseTerms.Open -> urls.open is DataUseTerms.Restricted -> urls.restricted } } - val filesFieldNames = backendConfig.getInstanceConfig(organism).schema.files.map { it.name } + val filesFieldNames = + configService.getOrganismConfig(organism).config.schema.files.map { it.name } val metadata = rawProcessedData.processedData.metadata + mapOf( @@ -177,7 +182,7 @@ open class ReleasedDataModel( ("pipelineVersion" to LongNode(rawProcessedData.pipelineVersion)), ) + conditionalMetadata( - backendConfig.dataUseTerms.enabled, + instanceConfig.dataUseTerms.enabled, { mapOf( "dataUseTerms" to TextNode(currentDataUseTerms.type.name), @@ -233,7 +238,7 @@ open class ReleasedDataModel( ): Map> = filesMap.mapValues { (category, fileIdandName) -> fileIdandName.map { (fileId, name) -> val encoded = URLEncoder.encode(name, StandardCharsets.UTF_8) - val url = when (backendConfig.fileSharing.outputFileUrlType) { + val url = when (configService.getInstanceConfig().config.fileSharing.outputFileUrlType) { FileUrlType.WEBSITE -> "${backendConfig.websiteUrl}/seq/$accession.$version/$category/$encoded" FileUrlType.BACKEND -> "${backendConfig.backendUrl}/files/get/$accession/$version/$category/$encoded" FileUrlType.S3 -> s3Service.getPublicUrl(fileId) diff --git a/backend/src/main/kotlin/org/loculus/backend/model/SubmitModel.kt b/backend/src/main/kotlin/org/loculus/backend/model/SubmitModel.kt index 8fec3462e4..814de38de6 100644 --- a/backend/src/main/kotlin/org/loculus/backend/model/SubmitModel.kt +++ b/backend/src/main/kotlin/org/loculus/backend/model/SubmitModel.kt @@ -10,7 +10,7 @@ import org.loculus.backend.api.SubmissionIdFilesMap import org.loculus.backend.api.SubmissionIdMapping import org.loculus.backend.api.getAllFileIds import org.loculus.backend.auth.AuthenticatedUser -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.BadRequestException import org.loculus.backend.controller.DuplicateKeyException import org.loculus.backend.controller.UnprocessableEntityException @@ -84,7 +84,7 @@ class SubmitModel( private val filesDatabaseService: FilesDatabaseService, private val submissionIdFilesMappingPreconditionValidator: SubmissionIdFilesMappingPreconditionValidator, private val dateProvider: DateProvider, - private val backendConfig: BackendConfig, + private val configService: ConfigService, ) { companion object AcceptedFileTypes { @@ -414,8 +414,6 @@ class SubmitModel( } } - private fun requiresConsensusSequenceFile(organism: Organism): Boolean = backendConfig.getInstanceConfig(organism) - .schema - .submissionDataTypes - .consensusSequences + private fun requiresConsensusSequenceFile(organism: Organism): Boolean = + configService.getOrganismConfig(organism).config.schema.submissionDataTypes.consensusSequences } diff --git a/backend/src/main/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberService.kt b/backend/src/main/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberService.kt index 95a25a873c..69e40e1ab4 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberService.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberService.kt @@ -1,12 +1,15 @@ package org.loculus.backend.service -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.utils.Accession import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service @Service -class GenerateAccessionFromNumberService(@Autowired val backendConfig: BackendConfig) { +class GenerateAccessionFromNumberService(@Autowired private val configService: ConfigService) { + + private val accessionPrefix: String + get() = configService.getInstanceConfig().config.accessionPrefix fun generateCustomId(sequenceNumber: Long): String { val base34Digits: MutableList = mutableListOf() @@ -21,14 +24,14 @@ class GenerateAccessionFromNumberService(@Autowired val backendConfig: BackendCo val serialAccessionPart = base34Digits .joinToString("") .padStart(6, '0') - return backendConfig.accessionPrefix + serialAccessionPart + generateCheckCharacter(serialAccessionPart) + return accessionPrefix + serialAccessionPart + generateCheckCharacter(serialAccessionPart) } fun validateAccession(accession: Accession): Boolean { - if (!accession.startsWith(backendConfig.accessionPrefix)) { + if (!accession.startsWith(accessionPrefix)) { return false } - return validateCheckCharacter(accession.removePrefix(backendConfig.accessionPrefix)) + return validateCheckCharacter(accession.removePrefix(accessionPrefix)) } // See https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm for details diff --git a/backend/src/main/kotlin/org/loculus/backend/service/datauseterms/DataUseTermsPreconditionValidator.kt b/backend/src/main/kotlin/org/loculus/backend/service/datauseterms/DataUseTermsPreconditionValidator.kt index cc40a5ad7a..4fbc43e5af 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/datauseterms/DataUseTermsPreconditionValidator.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/datauseterms/DataUseTermsPreconditionValidator.kt @@ -7,7 +7,7 @@ import mu.KotlinLogging import org.jetbrains.exposed.sql.and import org.loculus.backend.api.DataUseTerms import org.loculus.backend.api.DataUseTermsType -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.BadRequestException import org.loculus.backend.controller.UnprocessableEntityException import org.loculus.backend.utils.Accession @@ -17,10 +17,13 @@ import org.springframework.stereotype.Component private val logger = KotlinLogging.logger { } @Component -class DataUseTermsPreconditionValidator(private val dateProvider: DateProvider, val backendConfig: BackendConfig) { +class DataUseTermsPreconditionValidator( + private val dateProvider: DateProvider, + private val configService: ConfigService, +) { fun constructDataUseTermsAndValidate(dataUseTermsType: DataUseTermsType?, restrictedUntil: String?): DataUseTerms = - when (backendConfig.dataUseTerms.enabled) { + when (configService.getInstanceConfig().config.dataUseTerms.enabled) { false -> DataUseTerms.Open true -> when (dataUseTermsType) { diff --git a/backend/src/main/kotlin/org/loculus/backend/service/debug/DeleteSequenceDataService.kt b/backend/src/main/kotlin/org/loculus/backend/service/debug/DeleteSequenceDataService.kt index c41e4d2f2e..b94248ba65 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/debug/DeleteSequenceDataService.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/debug/DeleteSequenceDataService.kt @@ -1,7 +1,7 @@ package org.loculus.backend.service.debug import org.jetbrains.exposed.sql.deleteAll -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.service.datauseterms.DataUseTermsTable import org.loculus.backend.service.submission.MetadataUploadAuxTable import org.loculus.backend.service.submission.SequenceEntriesPreprocessedDataTable @@ -13,7 +13,7 @@ import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Transactional @Component -class DeleteSequenceDataService(private val dateProvider: DateProvider, private val config: BackendConfig) { +class DeleteSequenceDataService(private val dateProvider: DateProvider, private val configService: ConfigService) { @Transactional fun deleteAllSequenceData() { SequenceEntriesTable.deleteAll() @@ -23,7 +23,7 @@ class DeleteSequenceDataService(private val dateProvider: DateProvider, private DataUseTermsTable.deleteAll() CurrentProcessingPipelineTable.deleteAll() CurrentProcessingPipelineTable.setV1ForOrganismsIfNotExist( - config.organisms.keys, + configService.listOrganismKeys(), dateProvider.getCurrentDateTime(), ) } diff --git a/backend/src/main/kotlin/org/loculus/backend/service/files/S3Service.kt b/backend/src/main/kotlin/org/loculus/backend/service/files/S3Service.kt index a03e5f6f85..b8d8f012ef 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/files/S3Service.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/files/S3Service.kt @@ -39,8 +39,11 @@ class S3Service(private val s3Config: S3Config) { private val s3Client: S3Client by lazy { createClient(getS3BucketConfig()) } private val presigner: S3Presigner by lazy { createPresigner(getS3BucketConfig()) } + private val internalPresigner: S3Presigner by lazy { + createPresigner(getS3BucketConfig(), useInternalEndpoint = true) + } - fun createUrlToUploadPrivateFile(fileId: FileId): String = s3ErrorMapping { + fun createUrlToUploadPrivateFile(fileId: FileId, useInternalEndpoint: Boolean = false): String = s3ErrorMapping { val config = getS3BucketConfig() val putObjectRequest = PutObjectRequest.builder() .bucket(config.bucket) @@ -50,7 +53,8 @@ class S3Service(private val s3Config: S3Config) { .putObjectRequest(putObjectRequest) .signatureDuration(Duration.ofSeconds(PRESIGNED_URL_EXPIRY_SECONDS.toLong())) .build() - presigner.presignPutObject(presignRequest).url().toString() + val selectedPresigner = if (useInternalEndpoint) internalPresigner else presigner + selectedPresigner.presignPutObject(presignRequest).url().toString() } fun initiateMultipartUploadAndCreateUrlsToUpload(fileId: FileId, numberParts: Int): MultipartUploadHandler = @@ -197,12 +201,21 @@ class S3Service(private val s3Config: S3Config) { .serviceConfiguration(createServiceConfiguration()) .build() - private fun createPresigner(bucketConfig: S3BucketConfig): S3Presigner = S3Presigner.builder() - .endpointOverride(URI.create(bucketConfig.endpoint)) - .region(Region.of(bucketConfig.region)) - .credentialsProvider(createCredentialProvider(bucketConfig)) - .serviceConfiguration(createServiceConfiguration()) - .build() + private fun createPresigner(bucketConfig: S3BucketConfig, useInternalEndpoint: Boolean = false): S3Presigner = + S3Presigner.builder() + .endpointOverride( + URI.create( + if (useInternalEndpoint) { + bucketConfig.internalEndpoint ?: bucketConfig.endpoint + } else { + bucketConfig.endpoint + }, + ), + ) + .region(Region.of(bucketConfig.region)) + .credentialsProvider(createCredentialProvider(bucketConfig)) + .serviceConfiguration(createServiceConfiguration()) + .build() private fun createCredentialProvider(bucketConfig: S3BucketConfig) = StaticCredentialsProvider.create( AwsBasicCredentials.create(bucketConfig.accessKey, bucketConfig.secretKey), diff --git a/backend/src/main/kotlin/org/loculus/backend/service/seqsetcitations/SeqSetCitationsDatabaseService.kt b/backend/src/main/kotlin/org/loculus/backend/service/seqsetcitations/SeqSetCitationsDatabaseService.kt index 93a23233fb..ca26c5399b 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/seqsetcitations/SeqSetCitationsDatabaseService.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/seqsetcitations/SeqSetCitationsDatabaseService.kt @@ -34,7 +34,7 @@ import org.loculus.backend.api.SeqSetRecord import org.loculus.backend.api.Status.APPROVED_FOR_RELEASE import org.loculus.backend.api.SubmittedSeqSetRecord import org.loculus.backend.auth.AuthenticatedUser -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.ForbiddenException import org.loculus.backend.controller.NotFoundException import org.loculus.backend.controller.UnprocessableEntityException @@ -59,7 +59,7 @@ data class SeqSetToCitationSourceEntry(val citationSourceId: Long, val seqSetId: @Transactional class SeqSetCitationsDatabaseService( private val accessionPreconditionValidator: AccessionPreconditionValidator, - private val backendConfig: BackendConfig, + private val configService: ConfigService, private val crossRefService: CrossRefService, private val dateProvider: DateProvider, pool: DataSource, @@ -68,7 +68,8 @@ class SeqSetCitationsDatabaseService( Database.connect(pool) } - fun constructSeqsetId(seqsetIdNumber: Long): String = "${backendConfig.accessionPrefix}SS_$seqsetIdNumber" + fun constructSeqsetId(seqsetIdNumber: Long): String = + "${configService.getInstanceConfig().config.accessionPrefix}SS_$seqsetIdNumber" fun createSeqSet( authenticatedUser: AuthenticatedUser, diff --git a/backend/src/main/kotlin/org/loculus/backend/service/submission/CompressionDictService.kt b/backend/src/main/kotlin/org/loculus/backend/service/submission/CompressionDictService.kt index b49d974f19..b9447ca236 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/submission/CompressionDictService.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/submission/CompressionDictService.kt @@ -3,7 +3,9 @@ package org.loculus.backend.service.submission import mu.KotlinLogging import org.jetbrains.exposed.sql.transactions.transaction import org.loculus.backend.api.Organism -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.ReferenceSequence +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.service.submission.dbtables.CompressionDictionariesTable import org.loculus.backend.service.submission.dbtables.CompressionDictionaryEntity import org.loculus.backend.utils.DateProvider @@ -20,97 +22,94 @@ class DictEntry(val id: Int, val dict: ByteArray) * Caches the contents in memory to avoid repeated DB lookups. */ @Service -class CompressionDictService(private val backendConfig: BackendConfig, private val dateProvider: DateProvider) { - private data class DictKey(val organism: Organism, val segmentOrGene: String) - - private data class DictCaches( - val byOrganismAndName: Map, - val unalignedByOrganism: Map, - val dictsById: ConcurrentHashMap, +class CompressionDictService(private val configService: ConfigService, private val dateProvider: DateProvider) { + private data class DictKey( + val organism: Organism, + val organismVersion: Long, + val segmentOrGene: String, + val referenceHash: String, ) + private data class UnalignedDictKey(val organism: Organism, val organismVersion: Long, val referenceHash: String) - private val caches: DictCaches by lazy { - // Must be lazy to make sure the caches are only populated after the Flyway migration has run - // The Spring bean is created before Flyway runs, i.e. we must not read from the DB when creating this class - populateCaches() - } + private val byOrganismAndName = ConcurrentHashMap() + private val unalignedByOrganism = ConcurrentHashMap() + private val dictsById = ConcurrentHashMap() + + @Volatile + private var cachesPopulated = false /** * Main responsibility: Make sure that all dictionaries that might be used for new data exist in the database. * * Also, already populates the caches. */ - private fun populateCaches(): DictCaches { - log.info { "Populating compression dictionary caches" } + private fun populateCachesIfNeeded() { + if (cachesPopulated) { + return + } + synchronized(this) { + if (cachesPopulated) { + return + } + populateCaches() + cachesPopulated = true + } + } - val dictCache = mutableMapOf() - val unalignedDictCache = mutableMapOf() - val cacheById = ConcurrentHashMap() + private fun populateCaches() { + log.info { "Populating compression dictionary caches" } transaction { - backendConfig.organisms.forEach { (organismString, instanceConfig) -> - val organism = Organism(organismString) - val nucleotideSequences = instanceConfig.referenceGenome.nucleotideSequences - val genes = instanceConfig.referenceGenome.genes - for (referenceSequence in nucleotideSequences + genes) { - val reference = referenceSequence.sequence - val description = "${organism.name} - ${referenceSequence.name}" - val dictId = getDictIdOrInsertNewEntry(reference, description) - - val dict = reference.toByteArray() - - val dictKey = DictKey(organism = organism, segmentOrGene = referenceSequence.name) - dictCache[dictKey] = DictEntry(dictId, dict) - cacheById[dictId] = dict - } - - // No need to concatenate nothing, it'll never be used - if (nucleotideSequences.isNotEmpty()) { - val concatenatedNucleotideSequences = nucleotideSequences - .map { it.sequence } - .sortedBy { it } - .joinToString("") - val dictId = getDictIdOrInsertNewEntry( - concatenatedNucleotideSequences, - "${organism.name} - concatenated nucleotide sequences", - ) - val unalignedDict = concatenatedNucleotideSequences.toByteArray() - val dictEntry = DictEntry(dictId, unalignedDict) - - unalignedDictCache[organism] = dictEntry - cacheById[dictId] = unalignedDict - } + configService.listReleasedOrganisms().forEach { listing -> + val organism = Organism(listing.key) + val versionedOrganism = configService.getOrganismConfig(listing.key) + cacheDictionaries(organism, versionedOrganism.version, versionedOrganism.config) } } log.info { - "Populated compression dictionary caches: ${dictCache.size} byOrganismAndName, " + - "${unalignedDictCache.size} unalignedByOrganism, ${cacheById.size} dictsById" + "Populated compression dictionary caches: ${byOrganismAndName.size} byOrganismAndName, " + + "${unalignedByOrganism.size} unalignedByOrganism, ${dictsById.size} dictsById" } + } - return DictCaches( - byOrganismAndName = dictCache.toMap(), - unalignedByOrganism = unalignedDictCache.toMap(), - dictsById = cacheById, - ) + fun getDictForSegmentOrGene(organism: Organism, segmentOrGene: String): DictEntry? { + val versionedOrganism = configService.getOrganismConfig(organism) + val reference = versionedOrganism.config.referenceGenome.nucleotideSequences + .plus(versionedOrganism.config.referenceGenome.genes) + .find { it.name == segmentOrGene } + ?: return null + val key = DictKey(organism, versionedOrganism.version, segmentOrGene, computeHash(reference.sequence)) + byOrganismAndName[key]?.let { return it } + + populateCachesIfNeeded() + byOrganismAndName[key]?.let { return it } + + return transaction { cacheReference(organism, versionedOrganism.version, reference) } } - /** - * Get dictionary for a specific segment or gene (used when compressing processed sequences) - */ - fun getDictForSegmentOrGene(organism: Organism, segmentOrGene: String): DictEntry? = - caches.byOrganismAndName[DictKey(organism = organism, segmentOrGene = segmentOrGene)] + fun getDictForUnalignedSequence(organism: Organism): DictEntry? { + val versionedOrganism = configService.getOrganismConfig(organism) + if (versionedOrganism.config.referenceGenome.nucleotideSequences.isEmpty()) { + return null + } + val concatenatedNucleotideSequences = concatenateNucleotideSequences(versionedOrganism.config) + val key = UnalignedDictKey(organism, versionedOrganism.version, computeHash(concatenatedNucleotideSequences)) + unalignedByOrganism[key]?.let { return it } - /** - * Get dictionary for unaligned sequences (used when compressing submitted sequences) - */ - fun getDictForUnalignedSequence(organism: Organism): DictEntry? = caches.unalignedByOrganism[organism] + populateCachesIfNeeded() + unalignedByOrganism[key]?.let { return it } + + return transaction { + cacheUnalignedDictionary(organism, versionedOrganism.version, concatenatedNucleotideSequences) + } + } /** * Get dictionary by ID (used when decompressing sequences) */ fun getDictById(id: Int): ByteArray { - val cachedDict = caches.dictsById[id] + val cachedDict = dictsById[id] if (cachedDict != null) { return cachedDict } @@ -119,11 +118,63 @@ class CompressionDictService(private val backendConfig: BackendConfig, private v val dict = CompressionDictionaryEntity.findById(id) ?.dictContents ?: throw RuntimeException("Did not find compression dictionary with id $id") - caches.dictsById[id] = dict + dictsById[id] = dict dict } } + private fun cacheDictionaries(organism: Organism, organismVersion: Long, organismConfig: OrganismConfig) { + val nucleotideSequences = organismConfig.referenceGenome.nucleotideSequences + val genes = organismConfig.referenceGenome.genes + for (referenceSequence in nucleotideSequences + genes) { + cacheReference(organism, organismVersion, referenceSequence) + } + + if (nucleotideSequences.isNotEmpty()) { + cacheUnalignedDictionary(organism, organismVersion, concatenateNucleotideSequences(organismConfig)) + } + } + + private fun cacheReference( + organism: Organism, + organismVersion: Long, + referenceSequence: ReferenceSequence, + ): DictEntry { + val reference = referenceSequence.sequence + val dictId = getDictIdOrInsertNewEntry(reference, "${organism.name} - ${referenceSequence.name}") + val dict = reference.toByteArray() + val dictEntry = DictEntry(dictId, dict) + byOrganismAndName[ + DictKey(organism, organismVersion, referenceSequence.name, computeHash(reference)), + ] = dictEntry + dictsById[dictId] = dict + return dictEntry + } + + private fun cacheUnalignedDictionary( + organism: Organism, + organismVersion: Long, + concatenatedNucleotideSequences: String, + ): DictEntry { + val dictId = getDictIdOrInsertNewEntry( + concatenatedNucleotideSequences, + "${organism.name} - concatenated nucleotide sequences", + ) + val dict = concatenatedNucleotideSequences.toByteArray() + val dictEntry = DictEntry(dictId, dict) + unalignedByOrganism[ + UnalignedDictKey(organism, organismVersion, computeHash(concatenatedNucleotideSequences)), + ] = dictEntry + dictsById[dictId] = dict + return dictEntry + } + + private fun concatenateNucleotideSequences(organismConfig: OrganismConfig): String = + organismConfig.referenceGenome.nucleotideSequences + .map { it.sequence } + .sortedBy { it } + .joinToString("") + private fun getDictIdOrInsertNewEntry(dict: String, description: String): Int { val hash = computeHash(dict) @@ -145,7 +196,7 @@ class CompressionDictService(private val backendConfig: BackendConfig, private v } .also { log.debug { - "Inserted new dict entry: id ${it.id.value}, $description, for dict ${dict.substring(0..10)}..." + "Inserted new dict entry: id ${it.id.value}, $description, for dict ${dict.take(11)}..." } } .id diff --git a/backend/src/main/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProvider.kt b/backend/src/main/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProvider.kt index 8de2774f41..f5601ae6ee 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProvider.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProvider.kt @@ -4,17 +4,25 @@ import com.fasterxml.jackson.databind.node.NullNode import org.loculus.backend.api.GeneticSequence import org.loculus.backend.api.Organism import org.loculus.backend.api.ProcessedData -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.expandPerSegmentMetadata +import org.loculus.backend.config.perSegmentExpansionSegments +import org.loculus.backend.config.service.ConfigService import org.springframework.stereotype.Component @Component -class EmptyProcessedDataProvider(private val backendConfig: BackendConfig) { +class EmptyProcessedDataProvider(private val configService: ConfigService) { fun provide(organism: Organism): ProcessedData { - val (schema, referenceGenome) = backendConfig.getInstanceConfig(organism) + val organismConfig = configService.getOrganismConfig(organism).config + val schema = organismConfig.schema + val referenceGenome = organismConfig.referenceGenome + // Expand perSegment fields (completeness -> completeness_L/_M/_S, ...) so the + // released-data projection serves the same field set the pipeline produced + // and SILO's adapter-rendered schema expects. + val metadataFields = expandPerSegmentMetadata(schema.metadata, perSegmentExpansionSegments(organismConfig)) val nucleotideSequences = referenceGenome.nucleotideSequences.map { it.name }.associateWith { null } return ProcessedData( - metadata = schema.metadata.map { it.name }.associateWith { NullNode.instance }, + metadata = metadataFields.map { it.name }.associateWith { NullNode.instance }, unalignedNucleotideSequences = nucleotideSequences, alignedNucleotideSequences = nucleotideSequences, alignedAminoAcidSequences = referenceGenome.genes.map { it.name }.associateWith { null }, diff --git a/backend/src/main/kotlin/org/loculus/backend/service/submission/FileMappingPreconditionValidator.kt b/backend/src/main/kotlin/org/loculus/backend/service/submission/FileMappingPreconditionValidator.kt index 3bf50fb4d7..fb8c1b78b3 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/submission/FileMappingPreconditionValidator.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/submission/FileMappingPreconditionValidator.kt @@ -7,7 +7,7 @@ import org.loculus.backend.api.SubmissionIdFilesMap import org.loculus.backend.api.categories import org.loculus.backend.api.fileIds import org.loculus.backend.api.getDuplicateFileNames -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.UnprocessableEntityException import org.loculus.backend.service.files.FileId import org.loculus.backend.service.files.FilesDatabaseService @@ -16,7 +16,7 @@ import org.springframework.stereotype.Component @Component class FileMappingPreconditionValidator( - private val backendConfig: BackendConfig, + private val configService: ConfigService, private val s3Service: S3Service, private val filesDatabaseService: FilesDatabaseService, ) { @@ -55,8 +55,8 @@ class FileMappingPreconditionValidator( organism: Organism, ): FileMappingPreconditionValidator { if (fileCategoriesFilesMap == null) return this - val allowedCategories = backendConfig - .getInstanceConfig(organism).schema.submissionDataTypes.files.categories + val allowedCategories = configService + .getOrganismConfig(organism).config.schema.submissionDataTypes.files.categories return validateCategoriesMatchSchema(fileCategoriesFilesMap, allowedCategories, organism, "submission") } @@ -69,7 +69,7 @@ class FileMappingPreconditionValidator( organism: Organism, ): FileMappingPreconditionValidator { if (fileCategoriesFilesMap == null) return this - val allowedCategories = backendConfig.getInstanceConfig(organism).schema.files + val allowedCategories = configService.getOrganismConfig(organism).config.schema.files return validateCategoriesMatchSchema(fileCategoriesFilesMap, allowedCategories, organism, "output") } diff --git a/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedMetadataPostprocessor.kt b/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedMetadataPostprocessor.kt index 8e37e632a2..9aa12e20c5 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedMetadataPostprocessor.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedMetadataPostprocessor.kt @@ -3,24 +3,29 @@ package org.loculus.backend.service.submission import com.fasterxml.jackson.databind.node.NullNode import org.loculus.backend.api.Organism import org.loculus.backend.api.ProcessedData -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.expandPerSegmentMetadata +import org.loculus.backend.config.perSegmentExpansionSegments +import org.loculus.backend.config.service.ConfigService import org.springframework.stereotype.Service @Service -class ProcessedMetadataPostprocessor(private val backendConfig: BackendConfig) { +class ProcessedMetadataPostprocessor(private val configService: ConfigService) { fun stripNullValuesFromMetadata(processedData: ProcessedData) = processedData.copy(metadata = processedData.metadata.filterNot { (_, value) -> value.isNull }) /** Filter out any extra fields that are not in the current schema and add nulls for any missing fields. */ fun filterOutExtraFieldsAndAddNulls(processedData: ProcessedData, organism: Organism) = processedData.copy( - metadata = backendConfig - .getInstanceConfig(organism) - .schema - .metadata - .map { it.name } - .associateWith { fieldName -> - processedData.metadata[fieldName] ?: NullNode.instance - }, + metadata = run { + val config = configService.getOrganismConfig(organism).config + // Expand perSegment fields (completeness -> completeness_L/_M/_S, ...) for + // multi-segment organisms so stored per-segment values are kept (not dropped + // as "extra"), matching SILO's adapter-rendered schema. + expandPerSegmentMetadata(config.schema.metadata, perSegmentExpansionSegments(config)) + .map { it.name } + .associateWith { fieldName -> + processedData.metadata[fieldName] ?: NullNode.instance + } + }, ) } diff --git a/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequenceEntryValidator.kt b/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequenceEntryValidator.kt index e803ddbb95..abf942ffb4 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequenceEntryValidator.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequenceEntryValidator.kt @@ -7,12 +7,14 @@ import org.loculus.backend.api.Insertion import org.loculus.backend.api.MetadataMap import org.loculus.backend.api.Organism import org.loculus.backend.api.ProcessedData -import org.loculus.backend.config.BackendConfig import org.loculus.backend.config.BaseMetadata import org.loculus.backend.config.MetadataType import org.loculus.backend.config.ReferenceGenome import org.loculus.backend.config.ReferenceSequence import org.loculus.backend.config.Schema +import org.loculus.backend.config.expandPerSegmentMetadata +import org.loculus.backend.config.perSegmentExpansionSegments +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.ProcessingValidationException import org.springframework.stereotype.Component import java.time.LocalDate @@ -167,10 +169,10 @@ private fun isValidDate(dateStringCandidate: String): Boolean { } @Component -class ExternalMetadataValidatorFactory(private val backendConfig: BackendConfig) { +class ExternalMetadataValidatorFactory(private val configService: ConfigService) { fun create(organism: Organism): ExternalMetadataValidator { - val instanceConfig = backendConfig.organisms[organism.name]!! - return ExternalMetadataValidator(instanceConfig.schema) + val organismConfig = configService.getOrganismConfig(organism).config + return ExternalMetadataValidator(organismConfig.schema) } } @@ -188,17 +190,22 @@ class ExternalMetadataValidator(private val schema: Schema) { } @Component -class ProcessedSequenceEntryValidatorFactory(private val backendConfig: BackendConfig) { +class ProcessedSequenceEntryValidatorFactory(private val configService: ConfigService) { fun create(organism: Organism): ProcessedSequenceEntryValidator { - val instanceConfig = backendConfig.organisms[organism.name]!! + val organismConfig = configService.getOrganismConfig(organism).config return ProcessedSequenceEntryValidator( - schema = instanceConfig.schema, - referenceGenome = instanceConfig.referenceGenome, + schema = organismConfig.schema, + referenceGenome = organismConfig.referenceGenome, + perSegmentNames = perSegmentExpansionSegments(organismConfig), ) } } -class ProcessedSequenceEntryValidator(private val schema: Schema, private val referenceGenome: ReferenceGenome) { +class ProcessedSequenceEntryValidator( + private val schema: Schema, + private val referenceGenome: ReferenceGenome, + private val perSegmentNames: List, +) { fun validate(processedData: ProcessedData): ProcessedData { val processedDataWithAllMetadataFields = validateMetadata(processedData) validateNucleotideSequences(processedDataWithAllMetadataFields) @@ -208,7 +215,9 @@ class ProcessedSequenceEntryValidator(private val schema: Schema, private val re } private fun validateMetadata(processedData: ProcessedData): ProcessedData { - val metadataFields = schema.metadata + // Expand perSegment fields (e.g. completeness -> completeness_L/_M/_S) for + // multi-segment organisms so the field set matches what the pipeline emits. + val metadataFields = expandPerSegmentMetadata(schema.metadata, perSegmentNames) var processedMetadataMap = processedData.metadata validateNoUnknownInMetaData(processedMetadataMap, metadataFields.map { it.name }) diff --git a/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequencesPostprocessor.kt b/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequencesPostprocessor.kt index e73312ca0d..ed6b88a16c 100644 --- a/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequencesPostprocessor.kt +++ b/backend/src/main/kotlin/org/loculus/backend/service/submission/ProcessedSequencesPostprocessor.kt @@ -3,11 +3,11 @@ package org.loculus.backend.service.submission import com.fasterxml.jackson.databind.node.NullNode import org.loculus.backend.api.Organism import org.loculus.backend.api.ProcessedData -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.springframework.stereotype.Service @Service -class ProcessedSequencesPostprocessor(private val backendConfig: BackendConfig) { +class ProcessedSequencesPostprocessor(private val configService: ConfigService) { fun stripNullValuesFromSequences(processedData: ProcessedData) = processedData.copy( unalignedNucleotideSequences = processedData.unalignedNucleotideSequences .filterValues { it != null }, @@ -25,46 +25,26 @@ class ProcessedSequencesPostprocessor(private val backendConfig: BackendConfig) fun filterOutExtraSequencesAndAddNulls( processedData: ProcessedData, organism: Organism, - ) = processedData.copy( - unalignedNucleotideSequences = backendConfig - .getInstanceConfig(organism) - .referenceGenome - .nucleotideSequences - .map { it.name } - .associateWith { seqName -> - processedData.unalignedNucleotideSequences[seqName] + ): ProcessedData { + val referenceGenome = configService.getOrganismConfig(organism).config.referenceGenome + val nucleotideSequenceNames = referenceGenome.nucleotideSequences.map { it.name } + val geneNames = referenceGenome.genes.map { it.name } + return processedData.copy( + unalignedNucleotideSequences = nucleotideSequenceNames.associateWith { + processedData.unalignedNucleotideSequences[it] }, - alignedNucleotideSequences = backendConfig - .getInstanceConfig(organism) - .referenceGenome - .nucleotideSequences - .map { it.name } - .associateWith { seqName -> - processedData.alignedNucleotideSequences[seqName] + alignedNucleotideSequences = nucleotideSequenceNames.associateWith { + processedData.alignedNucleotideSequences[it] }, - alignedAminoAcidSequences = backendConfig - .getInstanceConfig(organism) - .referenceGenome - .genes - .map { it.name } - .associateWith { geneName -> - processedData.alignedAminoAcidSequences[geneName] + alignedAminoAcidSequences = geneNames.associateWith { + processedData.alignedAminoAcidSequences[it] }, - aminoAcidInsertions = backendConfig - .getInstanceConfig(organism) - .referenceGenome - .genes - .map { it.name } - .associateWith { geneName -> - processedData.aminoAcidInsertions[geneName] ?: emptyList() + aminoAcidInsertions = geneNames.associateWith { + processedData.aminoAcidInsertions[it] ?: emptyList() }, - nucleotideInsertions = backendConfig - .getInstanceConfig(organism) - .referenceGenome - .nucleotideSequences - .map { it.name } - .associateWith { seqName -> - processedData.nucleotideInsertions[seqName] ?: emptyList() + nucleotideInsertions = nucleotideSequenceNames.associateWith { + processedData.nucleotideInsertions[it] ?: emptyList() }, - ) + ) + } } diff --git a/backend/src/main/kotlin/org/loculus/backend/utils/LocalDateJacksonModule.kt b/backend/src/main/kotlin/org/loculus/backend/utils/LocalDateJacksonModule.kt index be687bfc7c..4044090beb 100644 --- a/backend/src/main/kotlin/org/loculus/backend/utils/LocalDateJacksonModule.kt +++ b/backend/src/main/kotlin/org/loculus/backend/utils/LocalDateJacksonModule.kt @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.JsonSerializer import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.module.SimpleModule import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime import org.springframework.context.annotation.Configuration @Configuration @@ -15,6 +16,8 @@ class LocalDateJacksonModule : SimpleModule() { init { addSerializer(LocalDate::class.java, LocalDateSerializer()) addDeserializer(LocalDate::class.java, LocalDateDeserializer()) + addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer()) + addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer()) } } @@ -27,3 +30,13 @@ class LocalDateSerializer : JsonSerializer() { class LocalDateDeserializer : JsonDeserializer() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDate = LocalDate.parse(p.text) } + +class LocalDateTimeSerializer : JsonSerializer() { + override fun serialize(value: LocalDateTime, gen: JsonGenerator, serializers: SerializerProvider) { + gen.writeString(value.toString()) + } +} + +class LocalDateTimeDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime = LocalDateTime.parse(p.text) +} diff --git a/backend/src/main/resources/application-docker.properties b/backend/src/main/resources/application-docker.properties index 1e53ba05d6..3797785d38 100644 --- a/backend/src/main/resources/application-docker.properties +++ b/backend/src/main/resources/application-docker.properties @@ -1 +1,5 @@ -loculus.config.path=/config/backend_config.json +# Domain config (organisms, accessionPrefix, dataUseTerms, fileSharing) is now sourced from +# the database (config_* tables) via ConfigService. Only technical fields stay in Spring config; +# they are overridden per-deployment by Helm values. +loculus.backend.website-url=https://example.com +loculus.backend.backend-url=http://backend:8079 diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 1ce6999fef..95d86cc625 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -3,10 +3,11 @@ server.max-http-request-header-size=20KB springdoc.default-produces-media-type=application/json springdoc.default-consumes-media-type=application/json -springdoc.api-docs.path=/api-docs +springdoc.api-docs.path=/api-docs.json springdoc.api-docs.enabled=true springdoc.swagger-ui.enabled=true springdoc.swagger-ui.operationsSorter=alpha +springdoc.swagger-ui.queryConfigEnabled=true server.forward-headers-strategy=framework spring.servlet.multipart.max-file-size=5000MB spring.servlet.multipart.max-request-size=5000MB diff --git a/backend/src/main/resources/db/migration/V2.0__add_config_tables.sql b/backend/src/main/resources/db/migration/V2.0__add_config_tables.sql new file mode 100644 index 0000000000..2247dfb2ba --- /dev/null +++ b/backend/src/main/resources/db/migration/V2.0__add_config_tables.sql @@ -0,0 +1,121 @@ +-- DB-backed domain config system; see config-architecture/13_databaseSchema.md. +-- +-- This single migration introduces the whole config subsystem (organism + +-- instance versions, drafts, audit log, opaque preprocessing config files, and +-- the per-organism deployed flag). It is kept as one V2.0 migration on purpose: +-- the feature was developed across several files but is squashed here so the +-- config schema lands atomically rather than as an incremental V2.x history. + +CREATE TABLE config_organisms ( + key TEXT PRIMARY KEY, + status TEXT NOT NULL, + current_version BIGINT, + created_at TIMESTAMP NOT NULL DEFAULT now(), + created_by TEXT NOT NULL, + first_published_at TIMESTAMP, + last_published_at TIMESTAMP, + CHECK (status IN ('unreleased', 'released')), + CHECK ((status = 'unreleased') = (current_version IS NULL)) +); + +CREATE TABLE config_instance_versions ( + version BIGSERIAL PRIMARY KEY, + config JSONB NOT NULL, + published_at TIMESTAMP NOT NULL DEFAULT now(), + published_by TEXT NOT NULL +); + +CREATE TABLE config_instance_state ( + singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton), + current_version BIGINT REFERENCES config_instance_versions(version) +); + +CREATE TABLE config_organism_versions ( + organism_key TEXT NOT NULL REFERENCES config_organisms(key), + version BIGINT NOT NULL, + config JSONB NOT NULL, + published_at TIMESTAMP NOT NULL DEFAULT now(), + published_by TEXT NOT NULL, + PRIMARY KEY (organism_key, version) +); + +ALTER TABLE config_organisms + ADD CONSTRAINT config_organisms_current_version_fk + FOREIGN KEY (key, current_version) + REFERENCES config_organism_versions(organism_key, version) + DEFERRABLE INITIALLY DEFERRED; + +CREATE TABLE config_instance_draft ( + singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton), + config JSONB NOT NULL, + base_version BIGINT REFERENCES config_instance_versions(version), + revision BIGINT NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT now(), + updated_at TIMESTAMP NOT NULL DEFAULT now(), + created_by TEXT NOT NULL, + updated_by TEXT NOT NULL +); + +CREATE TABLE config_organism_drafts ( + organism_key TEXT PRIMARY KEY REFERENCES config_organisms(key) ON DELETE CASCADE, + config JSONB NOT NULL, + base_version BIGINT, + revision BIGINT NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT now(), + updated_at TIMESTAMP NOT NULL DEFAULT now(), + created_by TEXT NOT NULL, + updated_by TEXT NOT NULL, + FOREIGN KEY (organism_key, base_version) + REFERENCES config_organism_versions(organism_key, version) +); + +CREATE TABLE config_audit_log ( + id BIGSERIAL PRIMARY KEY, + occurred_at TIMESTAMP NOT NULL DEFAULT now(), + actor TEXT NOT NULL, + scope TEXT NOT NULL, + organism_key TEXT, + action TEXT NOT NULL, + details JSONB, + result_version BIGINT +); + +CREATE INDEX ix_config_audit_log_organism_time ON config_audit_log (organism_key, occurred_at DESC); +CREATE INDEX ix_config_audit_log_actor_time ON config_audit_log (actor, occurred_at DESC); + +-- Optional, unversioned per-(organism, pipeline version) preprocessing config files. +-- The backend stores and serves these verbatim and never interprets them; they are +-- a generic, opaque text channel for external preprocessing pipelines. +-- See config-architecture/outdated-implementation-support-files/61_preprocessingPipeline.md. +CREATE TABLE config_preprocessing_files ( + organism_key TEXT NOT NULL REFERENCES config_organisms(key) ON DELETE CASCADE, + pipeline_version BIGINT NOT NULL, + config_file TEXT NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT now(), + updated_by TEXT NOT NULL, + PRIMARY KEY (organism_key, pipeline_version) +); + +-- Per-organism deployment readiness flag. Newly created organisms set this to +-- FALSE until an administrator confirms the SILO/LAPIS deployment is ready; +-- existing released organisms are assumed already deployed. +ALTER TABLE config_organisms + ADD COLUMN deployed BOOLEAN; + +UPDATE config_organisms + SET deployed = (status = 'released'); + +ALTER TABLE config_organisms + ALTER COLUMN deployed SET NOT NULL, + ALTER COLUMN deployed SET DEFAULT TRUE; + +-- Mirror of DEFAULT_INSTANCE_CONFIG_JSON in InstanceConfig.kt; keep in sync. +INSERT INTO config_instance_versions (version, config, published_at, published_by) +VALUES ( + 1, + '{"name":"Loculus","accessionPrefix":"LOC_","dataUseTerms":{"enabled":false,"urls":null},"fileSharing":{"outputFileUrlType":"website"},"description":null,"logo":null,"supportContact":null,"bannerMessage":null,"bannerMessageURL":null,"submissionBannerMessage":null,"submissionBannerMessageURL":null,"welcomeMessageHTML":null,"additionalHeadHTML":null,"gitHubEditLink":null,"gitHubMainUrl":null,"gitHubIssuesUrl":null,"issuesEmail":null,"enableSeqSets":false,"seqSetsFieldsToDisplay":null,"seqSetsGraphs":null,"enableLoginNavigationItem":true,"enableSubmissionNavigationItem":true,"enableSubmissionPages":true,"dataUseTermsAgreementHTML":null,"sequenceFlagging":null,"dateFieldForGroupGraph":null,"lineageSystemDefinitions":null,"overview":null}'::jsonb, + now(), + 'system' +); + +INSERT INTO config_instance_state (singleton, current_version) VALUES (TRUE, 1); diff --git a/backend/src/test/kotlin/org/loculus/backend/SwaggerUiTest.kt b/backend/src/test/kotlin/org/loculus/backend/SwaggerUiTest.kt index 155269a2c1..ba1f263cf0 100644 --- a/backend/src/test/kotlin/org/loculus/backend/SwaggerUiTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/SwaggerUiTest.kt @@ -26,13 +26,82 @@ class SwaggerUiTest(@Autowired val mockMvc: MockMvc) { .andExpect(content().string(containsString("Swagger UI"))) } + @Test + fun `Swagger UI with Loculus bar endpoint is reachable`() { + mockMvc.perform(get("/swagger-ui/loculus").param("url", "/api-docs/general.json")) + .andExpect(status().isOk) + .andExpect(content().contentTypeCompatibleWith("text/html")) + .andExpect(content().string(containsString("Loculus home"))) + .andExpect(content().string(containsString("/api-docs/general.json"))) + .andExpect(content().string(containsString("SwaggerUIBundle"))) + } + + @Test + fun `Scalar API reference endpoint is reachable`() { + mockMvc.perform(get("/scalar-api-reference")) + .andExpect(status().isOk) + .andExpect(content().contentTypeCompatibleWith("text/html")) + .andExpect(content().string(containsString("@scalar/api-reference"))) + .andExpect(content().string(containsString("Loculus home"))) + .andExpect(content().string(containsString("/api-docs.json"))) + } + + @Test + fun `Scalar API reference can target a split specification`() { + mockMvc.perform(get("/scalar-api-reference").param("url", "/api-docs/general.json")) + .andExpect(status().isOk) + .andExpect(content().contentTypeCompatibleWith("text/html")) + .andExpect(content().string(containsString("@scalar/api-reference"))) + .andExpect(content().string(containsString("/api-docs/general.json"))) + } + + @Test + fun `docs endpoints do not reflect an unknown url parameter`() { + val injection = "" + mockMvc.perform(get("/swagger-ui/loculus").param("url", injection)) + .andExpect(status().isOk) + .andExpect(content().string(org.hamcrest.core.IsNot.not(containsString(injection)))) + .andExpect(content().string(containsString("/api-docs.json"))) + mockMvc.perform(get("/scalar-api-reference").param("url", injection)) + .andExpect(status().isOk) + .andExpect(content().string(org.hamcrest.core.IsNot.not(containsString(injection)))) + .andExpect(content().string(containsString("/api-docs.json"))) + } + @Test fun `JSON API docs are available`() { + mockMvc.perform(get("/api-docs.json")) + .andExpect(status().isOk) + .andExpect(content().contentType("application/json")) + .andExpect(jsonPath("\$.openapi").exists()) + .andExpect(jsonPath("\$.paths./{organism}/submit").exists()) + .andExpect(jsonPath("\$.components.schemas.OrganismResponse.properties.publishedAt.type").value("string")) + .andExpect( + jsonPath("\$.components.schemas.OrganismResponse.properties.publishedAt.format").value("date-time"), + ) + .andExpect( + jsonPath("\$.components.schemas.OrganismResponse.properties.publishedAt.example") + .value("2026-05-24T12:15:55.221007"), + ) + } + + @Test + fun `legacy JSON API docs path is not exposed`() { mockMvc.perform(get("/api-docs")) + .andExpect(status().isNotFound) + } + + @Test + fun `general JSON API docs exclude query endpoints`() { + val result = mockMvc.perform(get("/api-docs/general.json")) .andExpect(status().isOk) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("\$.openapi").exists()) .andExpect(jsonPath("\$.paths./{organism}/submit").exists()) + .andReturn() + + val json = ObjectMapper().readTree(result.response.contentAsString) + assertTrue(json.get("paths").fieldNames().asSequence().none { it.startsWith("/query/") }) } @Test diff --git a/backend/src/test/kotlin/org/loculus/backend/config/AdminConfigEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/AdminConfigEndpointTest.kt new file mode 100644 index 0000000000..2ff3ae9b8e --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/AdminConfigEndpointTest.kt @@ -0,0 +1,419 @@ +package org.loculus.backend.config + +import com.fasterxml.jackson.databind.ObjectMapper +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.containsString +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.Test +import org.loculus.backend.controller.EndpointTest +import org.loculus.backend.controller.jwtForDefaultUser +import org.loculus.backend.controller.jwtForLoculusAdministrator +import org.loculus.backend.controller.jwtForSuperUser +import org.loculus.backend.controller.withAuth +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.MediaType +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +@EndpointTest +class AdminConfigEndpointTest(@Autowired val mockMvc: MockMvc, @Autowired val objectMapper: ObjectMapper) { + + private val sampleOrganismConfig = OrganismConfig( + schema = Schema( + organismName = "Test organism", + metadata = listOf( + Metadata(name = "country", type = MetadataType.STRING), + Metadata(name = "date", type = MetadataType.DATE, required = true), + ), + ), + referenceGenome = ReferenceGenome( + nucleotideSequences = emptyList(), + genes = emptyList(), + ), + ) + + @Test + fun `POST organisms requires admin auth`() { + mockMvc.perform(get("/api/admin/config/organisms")) + .andExpect(status().isUnauthorized) + + mockMvc.perform(get("/api/admin/config/organisms").withAuth(jwtForDefaultUser)) + .andExpect(status().isForbidden) + + mockMvc.perform(get("/api/admin/config/organisms").withAuth(jwtForSuperUser)) + .andExpect(status().isForbidden) + + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForDefaultUser) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"x"}"""), + ).andExpect(status().isForbidden) + } + + @Test + fun `Create organism then list returns it as unreleased`() { + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"lassa"}"""), + ) + .andExpect(status().isCreated) + .andExpect(jsonPath("$.key", equalTo("lassa"))) + .andExpect(jsonPath("$.status", equalTo("unreleased"))) + .andExpect(jsonPath("$.currentVersion").doesNotExist()) + .andExpect(jsonPath("$.deployed", equalTo(false))) + + mockMvc.perform(get("/api/admin/config/organisms").withAuth(jwtForLoculusAdministrator)) + .andExpect(status().isOk) + .andExpect(jsonPath("$.organisms[?(@.key == 'lassa')].status", equalTo(listOf("unreleased")))) + .andExpect(jsonPath("$.organisms[?(@.key == 'lassa')].deployed", equalTo(listOf(false)))) + } + + @Test + fun `Duplicate create returns 409`() { + val body = """{"key":"duplicate-key"}""" + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(body), + ).andExpect(status().isCreated) + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(body), + ) + .andExpect(status().isConflict) + .andExpect(jsonPath("$.error", equalTo("organism_already_exists"))) + } + + @Test + fun `Full flow - create then PUT draft then publish then public read returns v1`() { + // Step 1: create organism + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"ebola-test"}"""), + ).andExpect(status().isCreated) + + // Step 2: PUT draft with full config + val putBody = mapOf("config" to sampleOrganismConfig) + mockMvc.perform( + put("/api/admin/config/organisms/ebola-test/draft") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(putBody)), + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.revision", equalTo(1))) + + // Step 3: GET draft returns it + mockMvc.perform(get("/api/admin/config/organisms/ebola-test/draft").withAuth(jwtForLoculusAdministrator)) + .andExpect(status().isOk) + .andExpect(jsonPath("$.revision", equalTo(1))) + .andExpect(jsonPath("$.config.schema.organismName", equalTo("Test organism"))) + + // Step 4: Publish + mockMvc.perform( + post("/api/admin/config/organisms/ebola-test/publish").withAuth(jwtForLoculusAdministrator), + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(1))) + .andExpect(jsonPath("$.publishedAt", containsString("T"))) + + // Step 5: Public read endpoint returns v1 + mockMvc.perform(get("/api/config/organisms/ebola-test")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(1))) + .andExpect(jsonPath("$.publishedAt", containsString("T"))) + .andExpect(jsonPath("$.config.schema.metadata[0].name", equalTo("country"))) + + // Step 6: Public listing does not include it until the runtime deployment is confirmed + mockMvc.perform(get("/api/config/organisms")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.organisms[?(@.key == 'ebola-test')]", equalTo(emptyList()))) + + // Step 7: Mark deployed after SILO/LAPIS are ready + mockMvc.perform( + post("/api/admin/config/organisms/ebola-test/mark-deployed").withAuth(jwtForLoculusAdministrator), + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.key", equalTo("ebola-test"))) + .andExpect(jsonPath("$.deployed", equalTo(true))) + + // Step 8: Public listing now includes it + mockMvc.perform(get("/api/config/organisms")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.organisms[?(@.key == 'ebola-test')].displayName", equalTo(listOf("Test organism")))) + .andExpect(jsonPath("$.organisms[?(@.key == 'ebola-test')].currentVersion", equalTo(listOf(1)))) + } + + @Test + fun `Mark deployed requires a released organism`() { + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"undeployed-draft"}"""), + ).andExpect(status().isCreated) + + mockMvc.perform( + post("/api/admin/config/organisms/undeployed-draft/mark-deployed").withAuth(jwtForLoculusAdministrator), + ) + .andExpect(status().isConflict) + .andExpect(jsonPath("$.error", equalTo("invalid_deployment_state"))) + } + + @Test + fun `PUT draft on a released organism returns 403`() { + // Create + publish + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"released-org"}"""), + ).andExpect(status().isCreated) + + val putBody = mapOf("config" to sampleOrganismConfig) + mockMvc.perform( + put("/api/admin/config/organisms/released-org/draft") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(putBody)), + ).andExpect(status().isOk) + mockMvc.perform( + post("/api/admin/config/organisms/released-org/publish").withAuth(jwtForLoculusAdministrator), + ).andExpect(status().isOk) + + mockMvc.perform( + put("/api/admin/config/organisms/released-org/draft") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(putBody)), + ) + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.error", equalTo("draft_scope_mismatch"))) + } + + @Test + fun `Operations on a released organism - setMetadataFieldDisplay then publish bumps to v2`() { + // Setup: create, draft, publish v1 + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"ops-org"}"""), + ).andExpect(status().isCreated) + mockMvc.perform( + put("/api/admin/config/organisms/ops-org/draft") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(mapOf("config" to sampleOrganismConfig))), + ).andExpect(status().isOk) + mockMvc.perform( + post("/api/admin/config/organisms/ops-org/publish").withAuth(jwtForLoculusAdministrator), + ).andExpect(status().isOk) + + // Append a setMetadataFieldDisplay op + val opBody = """ + { "operations": [ + { "type": "setMetadataFieldDisplay", + "payload": { "field": "country", "displayName": "Country of origin" } } + ] } + """.trimIndent() + mockMvc.perform( + post("/api/admin/config/organisms/ops-org/draft/operations") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(opBody), + ).andExpect(status().isOk) + + // Publish v2 + mockMvc.perform( + post("/api/admin/config/organisms/ops-org/publish").withAuth(jwtForLoculusAdministrator), + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(2))) + .andExpect(jsonPath("$.previousVersion", equalTo(1))) + + // Public read shows the new displayName + mockMvc.perform(get("/api/config/organisms/ops-org")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(2))) + .andExpect( + jsonPath( + "$.config.schema.metadata[?(@.name == 'country')].displayName", + equalTo(listOf("Country of origin")), + ), + ) + } + + @Test + fun `Append ops on an unreleased organism returns 403`() { + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"unreleased-org"}"""), + ).andExpect(status().isCreated) + + val opBody = """ + { "operations": [ + { "type": "setMetadataFieldDisplay", + "payload": { "field": "country", "displayName": "X" } } + ] } + """.trimIndent() + mockMvc.perform( + post("/api/admin/config/organisms/unreleased-org/draft/operations") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(opBody), + ) + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.error", equalTo("draft_scope_mismatch"))) + } + + @Test + fun `Discard draft is no-op when no draft exists`() { + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"empty-draft-test"}"""), + ).andExpect(status().isCreated) + mockMvc.perform( + delete("/api/admin/config/organisms/empty-draft-test/draft").withAuth(jwtForLoculusAdministrator), + ).andExpect(status().isNoContent) + } + + @Test + fun `Optimistic concurrency - If-Match mismatch returns 409`() { + // Create and put a draft (rev=1) + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"cc-test"}"""), + ).andExpect(status().isCreated) + mockMvc.perform( + put("/api/admin/config/organisms/cc-test/draft") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(mapOf("config" to sampleOrganismConfig))), + ).andExpect(status().isOk) + + // Second PUT with wrong If-Match + mockMvc.perform( + put("/api/admin/config/organisms/cc-test/draft") + .withAuth(jwtForLoculusAdministrator) + .header("If-Match", "99") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(mapOf("config" to sampleOrganismConfig))), + ) + .andExpect(status().isConflict) + .andExpect(jsonPath("$.error", equalTo("revision_conflict"))) + } + + @Test + fun `Instance config publish path - change branding, get v2`() { + // Append a setInstanceBranding op + val opBody = """ + { "operations": [ + { "type": "setInstanceBranding", + "payload": { "name": "Pathoplexus" } } + ] } + """.trimIndent() + mockMvc.perform( + post("/api/admin/config/instance/draft/operations") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(opBody), + ).andExpect(status().isOk) + + // Publish + mockMvc.perform(post("/api/admin/config/instance/publish").withAuth(jwtForLoculusAdministrator)) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(2))) + + // Public read returns the new name + mockMvc.perform(get("/api/config/instance")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(2))) + .andExpect(jsonPath("$.config.name", equalTo("Pathoplexus"))) + } + + @Test + fun `Unknown operation type returns 400`() { + // Have to be on a released organism for the operations endpoint + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"unknown-op-test"}"""), + ).andExpect(status().isCreated) + mockMvc.perform( + put("/api/admin/config/organisms/unknown-op-test/draft") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(mapOf("config" to sampleOrganismConfig))), + ).andExpect(status().isOk) + mockMvc.perform( + post("/api/admin/config/organisms/unknown-op-test/publish").withAuth(jwtForLoculusAdministrator), + ).andExpect(status().isOk) + + mockMvc.perform( + post("/api/admin/config/organisms/unknown-op-test/draft/operations") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content( + """ + { "operations": [ + { "type": "doesNotExist", "payload": {} } + ] } + """.trimIndent(), + ), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.error", equalTo("unknown_operation"))) + } + + @Test + fun `Empty operation batch returns 400 and does not create a draft`() { + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"empty-op-test"}"""), + ).andExpect(status().isCreated) + mockMvc.perform( + put("/api/admin/config/organisms/empty-op-test/draft") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(mapOf("config" to sampleOrganismConfig))), + ).andExpect(status().isOk) + mockMvc.perform( + post("/api/admin/config/organisms/empty-op-test/publish").withAuth(jwtForLoculusAdministrator), + ).andExpect(status().isOk) + + mockMvc.perform( + post("/api/admin/config/organisms/empty-op-test/draft/operations") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"operations":[]}"""), + ) + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.error", equalTo("bad_request"))) + + mockMvc.perform(get("/api/admin/config/organisms/empty-op-test/draft").withAuth(jwtForLoculusAdministrator)) + .andExpect(status().isNoContent) + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/config/BackendSpringConfigTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/BackendSpringConfigTest.kt deleted file mode 100644 index 213085e07c..0000000000 --- a/backend/src/test/kotlin/org/loculus/backend/config/BackendSpringConfigTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.loculus.backend.config - -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.`is` -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.loculus.backend.controller.DEFAULT_ORGANISM - -class BackendSpringConfigTest { - - @Test - fun `GIVEN an empty config THEN the it is valid`() { - val conf = backendConfig(emptyList(), EarliestReleaseDate(false, emptyList())) - - val errors = validateEarliestReleaseDateFields(conf) - - assertTrue(errors.isEmpty()) - } - - @Test - fun `GIVEN a config with earliestReleaseDate configured with existing date fields THEN it is valid`() { - val conf = backendConfig( - listOf( - Metadata("foo", MetadataType.DATE), - Metadata("bar", MetadataType.DATE), - ), - EarliestReleaseDate(true, listOf("foo", "bar")), - ) - - val errors = validateEarliestReleaseDateFields(conf) - - assertTrue(errors.isEmpty()) - } - - @Test - fun `GIVEN a config with a missing external field in earliestReleaseDate THEN it is invalid`() { - val conf = backendConfig( - listOf( - Metadata("foo", MetadataType.DATE), - ), - EarliestReleaseDate(true, listOf("foo", "bar")), - ) - - val errors = validateEarliestReleaseDateFields(conf) - - assertThat(errors.size, `is`(1)) - } - - @Test - fun `GIVEN a config with an external field with incorrect type in earliestReleaseDate THEN it is invalid`() { - val conf = backendConfig( - listOf( - Metadata("foo", MetadataType.DATE), - Metadata("bar", MetadataType.STRING), - ), - EarliestReleaseDate(true, listOf("foo", "bar")), - ) - - val errors = validateEarliestReleaseDateFields(conf) - - assertThat(errors.size, `is`(1)) - } -} - -fun backendConfig(metadataList: List, earliestReleaseDate: EarliestReleaseDate) = BackendConfig( - organisms = mapOf( - DEFAULT_ORGANISM to InstanceConfig( - schema = Schema(DEFAULT_ORGANISM, metadataList, earliestReleaseDate = earliestReleaseDate), - referenceGenome = ReferenceGenome(emptyList(), emptyList()), - ), - ), - accessionPrefix = "FOO_", - dataUseTerms = DataUseTerms(true, null), - websiteUrl = "https://example.com", - backendUrl = "http://foo.com", -) diff --git a/backend/src/test/kotlin/org/loculus/backend/config/InstanceConfigSerializationTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/InstanceConfigSerializationTest.kt new file mode 100644 index 0000000000..953199c616 --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/InstanceConfigSerializationTest.kt @@ -0,0 +1,92 @@ +package org.loculus.backend.config + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +/** + * Guards the hand-maintained mirrors of the default instance config: the Kotlin + * [DEFAULT_INSTANCE_CONFIG] object, the [DEFAULT_INSTANCE_CONFIG_JSON] literal, + * and (indirectly) the canonical Zod schema / DB seed. If a new InstanceConfig + * field is added without updating the JSON literal, this test fails. + */ +class InstanceConfigSerializationTest { + private val objectMapper = jacksonObjectMapper() + + @Test + fun `DEFAULT_INSTANCE_CONFIG_JSON deserializes to DEFAULT_INSTANCE_CONFIG`() { + val parsed = objectMapper.readValue(DEFAULT_INSTANCE_CONFIG_JSON) + assertThat(parsed).isEqualTo(DEFAULT_INSTANCE_CONFIG) + } + + @Test + fun `DEFAULT_INSTANCE_CONFIG serializes to DEFAULT_INSTANCE_CONFIG_JSON`() { + val serialized = objectMapper.writeValueAsString(DEFAULT_INSTANCE_CONFIG) + assertThat(serialized).isEqualTo(DEFAULT_INSTANCE_CONFIG_JSON) + } + + @Test + fun `overview config round-trips`() { + val config = DEFAULT_INSTANCE_CONFIG.copy( + views = mapOf( + "overview" to ViewConfig( + displayName = "Overview", + query = """select accessionVersion, country from "ebola"""", + schema = """ + schema: + instanceName: Overview + opennessLevel: OPEN + metadata: + - name: accessionVersion + type: string + - name: country + type: string + primaryKey: accessionVersion + features: + - name: generalizedAdvancedQuery + """.trimIndent(), + tableColumns = listOf("organism", "country"), + ), + "test-organisms" to ViewConfig( + displayName = "Test organisms", + query = """select accessionVersion, country from "dummy-organism"""", + schema = """ + schema: + instanceName: Test organisms + opennessLevel: OPEN + metadata: + - name: accessionVersion + type: string + - name: country + type: string + primaryKey: accessionVersion + features: + - name: generalizedAdvancedQuery + """.trimIndent(), + tableColumns = listOf("organism", "country"), + ), + ), + overview = OverviewConfig( + displayName = "Overview", + query = """select accessionVersion, country from "ebola"""", + schema = """ + schema: + instanceName: Overview + opennessLevel: OPEN + metadata: + - name: accessionVersion + type: string + - name: country + type: string + primaryKey: accessionVersion + features: + - name: generalizedAdvancedQuery + """.trimIndent(), + tableColumns = listOf("organism", "country"), + ), + ) + val roundTripped = objectMapper.readValue(objectMapper.writeValueAsString(config)) + assertThat(roundTripped).isEqualTo(config) + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/config/PreprocessingConfigEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/PreprocessingConfigEndpointTest.kt new file mode 100644 index 0000000000..0a492b3130 --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/PreprocessingConfigEndpointTest.kt @@ -0,0 +1,139 @@ +package org.loculus.backend.config + +import com.fasterxml.jackson.databind.ObjectMapper +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.containsInAnyOrder +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.Test +import org.loculus.backend.controller.EndpointTest +import org.loculus.backend.controller.jwtForDefaultUser +import org.loculus.backend.controller.jwtForLoculusAdministrator +import org.loculus.backend.controller.withAuth +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.MediaType +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +@EndpointTest +class PreprocessingConfigEndpointTest(@Autowired val mockMvc: MockMvc, @Autowired val objectMapper: ObjectMapper) { + + private val sampleConfigFile = """ + nextclade_dataset_name: nextstrain/ebola/sudan + batch_size: 100 + processing_spec: + country: + function: identity + inputs: { input: country } + """.trimIndent() + + private fun createOrganism(key: String) { + mockMvc.perform( + post("/api/admin/config/organisms") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.APPLICATION_JSON) + .content("""{"key":"$key"}"""), + ).andExpect(status().isCreated) + } + + private fun putConfig(key: String, version: Int, body: String) = mockMvc.perform( + put("/api/admin/config/organisms/$key/preprocessing/$version") + .withAuth(jwtForLoculusAdministrator) + .contentType(MediaType.TEXT_PLAIN) + .content(body), + ) + + @Test + fun `PUT preprocessing config requires admin auth`() { + createOrganism("auth-org") + + // Unauthenticated write is rejected (Spring returns 4xx for the + // anonymous state-changing request). + mockMvc.perform( + put("/api/admin/config/organisms/auth-org/preprocessing/1") + .contentType(MediaType.TEXT_PLAIN) + .content("x"), + ).andExpect(status().is4xxClientError) + + // An authenticated non-admin user is forbidden. + mockMvc.perform( + put("/api/admin/config/organisms/auth-org/preprocessing/1") + .withAuth(jwtForDefaultUser) + .contentType(MediaType.TEXT_PLAIN) + .content("x"), + ).andExpect(status().isForbidden) + } + + @Test + fun `public GET returns 404 when no config file is set`() { + createOrganism("empty-prepro-org") + mockMvc.perform(get("/api/config/organisms/empty-prepro-org/preprocessing/1")) + .andExpect(status().isNotFound) + } + + @Test + fun `full flow - PUT, public GET returns raw text, list shows version, DELETE removes it`() { + createOrganism("prepro-org") + + putConfig("prepro-org", 1, sampleConfigFile).andExpect(status().isNoContent) + + // Public read returns the exact bytes as text/plain + mockMvc.perform(get("/api/config/organisms/prepro-org/preprocessing/1")) + .andExpect(status().isOk) + .andExpect(content().string(sampleConfigFile)) + + // Listing includes the version + mockMvc.perform(get("/api/config/organisms/prepro-org/preprocessing")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.versions[0].pipelineVersion", equalTo(1))) + .andExpect(jsonPath("$.versions[0].updatedBy").exists()) + + // Delete then GET 404 + mockMvc.perform( + delete("/api/admin/config/organisms/prepro-org/preprocessing/1").withAuth(jwtForLoculusAdministrator), + ).andExpect(status().isNoContent) + mockMvc.perform(get("/api/config/organisms/prepro-org/preprocessing/1")) + .andExpect(status().isNotFound) + } + + @Test + fun `PUT replaces existing content`() { + createOrganism("replace-org") + putConfig("replace-org", 1, "first").andExpect(status().isNoContent) + putConfig("replace-org", 1, "second").andExpect(status().isNoContent) + mockMvc.perform(get("/api/config/organisms/replace-org/preprocessing/1")) + .andExpect(status().isOk) + .andExpect(content().string("second")) + } + + @Test + fun `pipeline versions are independent`() { + createOrganism("multi-version-org") + putConfig("multi-version-org", 1, "config-v1").andExpect(status().isNoContent) + putConfig("multi-version-org", 2, "config-v2").andExpect(status().isNoContent) + + mockMvc.perform(get("/api/config/organisms/multi-version-org/preprocessing/1")) + .andExpect(content().string("config-v1")) + mockMvc.perform(get("/api/config/organisms/multi-version-org/preprocessing/2")) + .andExpect(content().string("config-v2")) + + val result = mockMvc.perform(get("/api/config/organisms/multi-version-org/preprocessing")) + .andExpect(status().isOk) + .andReturn() + val versions = objectMapper.readTree(result.response.contentAsString)["versions"] + .map { it["pipelineVersion"].asLong() } + assertThat(versions, containsInAnyOrder(1L, 2L)) + } + + @Test + fun `PUT on a non-existent organism returns 404`() { + putConfig("no-such-organism", 1, "x") + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.error", equalTo("organism_not_found"))) + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/config/PublicConfigEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/PublicConfigEndpointTest.kt new file mode 100644 index 0000000000..0a8af52fea --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/PublicConfigEndpointTest.kt @@ -0,0 +1,66 @@ +package org.loculus.backend.config + +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.Test +import org.loculus.backend.controller.EndpointTest +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +@EndpointTest +class PublicConfigEndpointTest(@Autowired val mockMvc: MockMvc) { + + @Test + fun `GET instance returns the default fixture variant`() { + mockMvc.perform(get("/api/config/instance")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(1))) + .andExpect(jsonPath("$.config.name", equalTo("Loculus"))) + .andExpect(jsonPath("$.config.accessionPrefix", equalTo("LOC_"))) + .andExpect(jsonPath("$.config.dataUseTerms.enabled", equalTo(true))) + } + + @Test + fun `GET instance with explicit version 1 returns the same as latest`() { + mockMvc.perform(get("/api/config/instance").param("version", "1")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.version", equalTo(1))) + .andExpect(jsonPath("$.config.name", equalTo("Loculus"))) + } + + @Test + fun `GET instance with unknown version returns 404`() { + mockMvc.perform(get("/api/config/instance").param("version", "9999")) + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.error", equalTo("version_not_found"))) + } + + @Test + fun `GET organisms returns the default fixture organisms`() { + mockMvc.perform(get("/api/config/organisms")) + .andExpect(status().isOk) + .andExpect(jsonPath("$.organisms.length()", equalTo(3))) + .andExpect( + jsonPath( + "$.organisms[?(@.key == 'dummyOrganism')].displayName", + equalTo(listOf("Displayed test organism")), + ), + ) + } + + @Test + fun `GET unknown organism returns 404`() { + mockMvc.perform(get("/api/config/organisms/nonexistent")) + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.error", equalTo("organism_not_found"))) + } + + @Test + fun `GET endpoints are open - no auth required`() { + // No withAuth() call. + mockMvc.perform(get("/api/config/instance")).andExpect(status().isOk) + mockMvc.perform(get("/api/config/organisms")).andExpect(status().isOk) + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/config/QueryOpenApiCustomizerTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/QueryOpenApiCustomizerTest.kt new file mode 100644 index 0000000000..4d7acb28a8 --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/QueryOpenApiCustomizerTest.kt @@ -0,0 +1,100 @@ +package org.loculus.backend.config + +import io.mockk.every +import io.mockk.mockk +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.Operation +import io.swagger.v3.oas.models.PathItem +import io.swagger.v3.oas.models.Paths +import io.swagger.v3.oas.models.media.StringSchema +import io.swagger.v3.oas.models.parameters.Parameter +import io.swagger.v3.oas.models.parameters.RequestBody +import kotlinx.datetime.LocalDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.loculus.backend.config.service.ConfigService + +class QueryOpenApiCustomizerTest { + @Test + fun `view query OpenAPI exposes sequence endpoints only when configured`() { + val configService = mockk() + every { configService.listReleasedOrganisms() } returns emptyList() + every { configService.getInstanceConfig() } returns ConfigService.VersionedInstance( + version = 1, + publishedAt = LocalDateTime(2026, 6, 11, 0, 0), + publishedBy = "test", + config = DEFAULT_INSTANCE_CONFIG.copy( + views = mapOf( + "overview" to ViewConfig( + displayName = "Overview", + query = """select accessionVersion, organism from "enteroviruses"""", + schema = schema("Overview"), + tableColumns = listOf("organism"), + lapisUrl = "http://lapis-overview", + ), + "real-organisms" to ViewConfig( + displayName = "Real organisms", + query = """select accessionVersion, organism from "west-nile"""", + schema = schema("Real organisms"), + tableColumns = listOf("organism"), + sequenceData = ViewSequenceData( + unalignedNucleotideSequences = ViewUnalignedNucleotideSequences( + enabled = true, + segments = listOf("main", "L"), + ), + ), + lapisUrl = "http://lapis-real-organisms", + ), + ), + ), + ) + + val openApi = OpenAPI().paths( + Paths() + .addPathItem("/query/{organism}/{versionGroup}/metadata", genericPathItem()) + .addPathItem("/query/{organism}/{versionGroup}/aggregated", genericPathItem()) + .addPathItem("/query/{organism}/{versionGroup}/sequences", genericPathItem()) + .addPathItem("/query/{organism}/{versionGroup}/sequences/{segment}", genericPathItem()) + .addPathItem("/query/{organism}/{versionGroup}/sequencesAligned", genericPathItem()), + ) + + QueryOpenApiCustomizer() + .organismSpecificQueryOpenApiCustomizer(configService) + .customise(openApi) + + assertThat(openApi.paths).containsKey("/query/overview/{versionGroup}/metadata") + assertThat(openApi.paths).containsKey("/query/overview/{versionGroup}/aggregated") + assertThat(openApi.paths).doesNotContainKey("/query/overview/{versionGroup}/sequences") + assertThat(openApi.paths).containsKey("/query/real-organisms/{versionGroup}/metadata") + assertThat(openApi.paths).containsKey("/query/real-organisms/{versionGroup}/aggregated") + assertThat(openApi.paths).containsKey("/query/real-organisms/{versionGroup}/sequences") + assertThat(openApi.paths).containsKey("/query/real-organisms/{versionGroup}/sequences/{segment}") + assertThat(openApi.paths).doesNotContainKey("/query/real-organisms/{versionGroup}/sequencesAligned") + } + + private fun schema(instanceName: String) = """ + schema: + instanceName: $instanceName + opennessLevel: OPEN + metadata: + - name: accessionVersion + type: string + - name: organism + type: string + primaryKey: accessionVersion + features: + - name: generalizedAdvancedQuery + """.trimIndent() + + private fun genericPathItem() = PathItem() + .post( + Operation() + .parameters( + listOf( + Parameter().name("organism").`in`("path").schema(StringSchema()), + Parameter().name("versionGroup").`in`("path").schema(StringSchema()), + ), + ) + .requestBody(RequestBody()), + ) +} diff --git a/backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixtures.kt b/backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixtures.kt new file mode 100644 index 0000000000..df7f4566b9 --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixtures.kt @@ -0,0 +1,140 @@ +package org.loculus.backend.config.fixtures + +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.deleteAll +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.dbtables.ConfigAuditLogTable +import org.loculus.backend.config.dbtables.ConfigInstanceDraftTable +import org.loculus.backend.config.dbtables.ConfigInstanceStateTable +import org.loculus.backend.config.dbtables.ConfigInstanceVersionsTable +import org.loculus.backend.config.dbtables.ConfigOrganismDraftsTable +import org.loculus.backend.config.dbtables.ConfigOrganismVersionsTable +import org.loculus.backend.config.dbtables.ConfigOrganismsTable +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.service.submission.dbtables.CurrentProcessingPipelineTable +import org.loculus.backend.utils.DateProvider +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import java.io.File +import javax.sql.DataSource + +class ConfigFixtures( + private val configService: ConfigService, + private val dateProvider: DateProvider, + dataSource: DataSource, +) { + + // Fixtures are loaded from JUnit callbacks (e.g. EndpointTestExtension.beforeEach) that run + // outside any Spring-managed transaction, so a bare `transaction {}` would fall back to + // Exposed's global default database. That default can be repointed at a mock DataSource by a + // SpringBootTestWithoutDatabase context running earlier in the same JVM, which then makes the + // fixture load fail with "no answer found for DataSource.getConnection()". Bind explicitly to + // this context's real datasource instead. + private val database = Database.connect(dataSource) + + fun loadDefault() = loadVariant("default") + + fun loadVariant(name: String) { + val variantDir = File(FIXTURES_ROOT, name) + require(variantDir.isDirectory) { "No fixture variant directory: ${variantDir.absolutePath}" } + + val instanceFile = File(variantDir, "instance.yaml") + require(instanceFile.isFile) { "Missing instance.yaml in ${variantDir.absolutePath}" } + val instance = yamlMapper.readValue(instanceFile, InstanceConfig::class.java) + + val organismsDir = File(variantDir, "organisms") + require(organismsDir.isDirectory) { "Missing organisms directory in ${variantDir.absolutePath}" } + val organismFiles = organismsDir.listFiles { _, fn -> fn.endsWith(".yaml") }?.sortedBy { it.name } + ?: emptyList() + + val now = dateProvider.getCurrentDateTime() + + transaction(database) { + ConfigAuditLogTable.deleteAll() + ConfigInstanceDraftTable.deleteAll() + ConfigOrganismDraftsTable.deleteAll() + ConfigOrganismVersionsTable.deleteAll() + ConfigOrganismsTable.deleteAll() + ConfigInstanceStateTable.deleteAll() + ConfigInstanceVersionsTable.deleteAll() + CurrentProcessingPipelineTable.deleteAll() + + ConfigInstanceVersionsTable.insert { + it[ConfigInstanceVersionsTable.versionColumn] = 1L + it[ConfigInstanceVersionsTable.configColumn] = instance + it[ConfigInstanceVersionsTable.publishedAtColumn] = now + it[ConfigInstanceVersionsTable.publishedByColumn] = SYSTEM_ACTOR + } + ConfigInstanceStateTable.insert { + it[ConfigInstanceStateTable.singletonColumn] = true + it[ConfigInstanceStateTable.currentVersionColumn] = 1L + } + + for (file in organismFiles) { + val key = file.nameWithoutExtension + val config = yamlMapper.readValue(file, OrganismConfig::class.java) + + ConfigOrganismsTable.insert { + it[ConfigOrganismsTable.keyColumn] = key + it[ConfigOrganismsTable.statusColumn] = "released" + it[ConfigOrganismsTable.currentVersionColumn] = 1L + it[ConfigOrganismsTable.deployedColumn] = true + it[ConfigOrganismsTable.createdAtColumn] = now + it[ConfigOrganismsTable.createdByColumn] = SYSTEM_ACTOR + it[ConfigOrganismsTable.firstPublishedAtColumn] = now + it[ConfigOrganismsTable.lastPublishedAtColumn] = now + } + ConfigOrganismVersionsTable.insert { + it[ConfigOrganismVersionsTable.organismKeyColumn] = key + it[ConfigOrganismVersionsTable.versionColumn] = 1L + it[ConfigOrganismVersionsTable.configColumn] = config + it[ConfigOrganismVersionsTable.publishedAtColumn] = now + it[ConfigOrganismVersionsTable.publishedByColumn] = SYSTEM_ACTOR + } + CurrentProcessingPipelineTable.setV1ForOrganismsIfNotExist(listOf(key), now) + } + } + configService.invalidateCache() + } + + fun setInstanceConfig(config: InstanceConfig) { + transaction(database) { + val currentVersion = ConfigInstanceStateTable.selectAll().single()[ + ConfigInstanceStateTable.currentVersionColumn, + ] ?: error("no current_version on config_instance_state") + ConfigInstanceVersionsTable.update({ ConfigInstanceVersionsTable.versionColumn eq currentVersion }) { + it[ConfigInstanceVersionsTable.configColumn] = config + } + } + configService.invalidateCache() + } + + companion object { + private const val FIXTURES_ROOT = "src/test/resources/fixtures" + private const val SYSTEM_ACTOR = "system" + + private val yamlMapper: ObjectMapper = ObjectMapper(YAMLFactory()) + .registerKotlinModule() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + } +} + +@TestConfiguration +class ConfigFixturesConfig { + @Bean + fun configFixtures( + @Autowired configService: ConfigService, + @Autowired dateProvider: DateProvider, + @Autowired dataSource: DataSource, + ): ConfigFixtures = ConfigFixtures(configService, dateProvider, dataSource) +} diff --git a/backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixturesTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixturesTest.kt new file mode 100644 index 0000000000..7dbe27a3c1 --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/fixtures/ConfigFixturesTest.kt @@ -0,0 +1,80 @@ +package org.loculus.backend.config.fixtures + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.containsInAnyOrder +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.controller.EndpointTest +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Import + +@EndpointTest +@Import(ConfigFixturesConfig::class) +class ConfigFixturesTest( + @Autowired private val fixtures: ConfigFixtures, + @Autowired private val configService: ConfigService, +) { + + @Test + fun `loads the default variant`() { + fixtures.loadDefault() + + val instance = configService.getInstanceConfig().config + assertThat(instance.name, equalTo("Loculus")) + assertThat(instance.accessionPrefix, equalTo("LOC_")) + assertThat(instance.dataUseTerms.enabled, equalTo(true)) + + val organisms = configService.listOrganismKeys() + assertThat( + organisms, + containsInAnyOrder("dummyOrganism", "otherOrganism", "dummyOrganismWithoutConsensusSequences"), + ) + + val dummy = configService.getOrganismConfig("dummyOrganism").config + assertThat(dummy, notNullValue()) + assertThat(dummy.displayName, equalTo("Displayed test organism")) + assertThat(dummy.schema.organismName, equalTo("Test")) + assertThat( + dummy.schema.metadata.map { it.name }, + containsInAnyOrder( + "date", "dateSubmitted", "region", "country", "division", "host", "age", "sex", + "pangoLineage", "qc", "booleanColumn", "insdcAccessionFull", "other_db_accession", + ), + ) + } + + @Test + fun `loads the single-segment variant`() { + fixtures.loadVariant("single-segment") + + val organisms = configService.listOrganismKeys() + assertThat(organisms, containsInAnyOrder("dummyOrganism")) + + val dummy = configService.getOrganismConfig("dummyOrganism").config + assertThat(dummy.referenceGenome.nucleotideSequences.single().sequence, equalTo("ACGT")) + } + + @Test + fun `loads the data-use-terms-disabled variant`() { + fixtures.loadVariant("data-use-terms-disabled") + + val instance = configService.getInstanceConfig().config + assertThat(instance.dataUseTerms.enabled, equalTo(false)) + } + + @Test + fun `loads the s3 variant`() { + fixtures.loadVariant("s3") + + val instance = configService.getInstanceConfig().config + assertThat(instance.fileSharing.outputFileUrlType.toString(), equalTo("s3")) + + val keys = configService.listOrganismKeys() + assertThat( + keys, + containsInAnyOrder("dummyOrganism", "otherOrganism", "dummyOrganismWithoutConsensusSequences"), + ) + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/config/operations/OperationHandlerTest.kt b/backend/src/test/kotlin/org/loculus/backend/config/operations/OperationHandlerTest.kt new file mode 100644 index 0000000000..0dccceb38e --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/config/operations/OperationHandlerTest.kt @@ -0,0 +1,294 @@ +package org.loculus.backend.config.operations + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.contains +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasSize +import org.hamcrest.Matchers.instanceOf +import org.junit.jupiter.api.Test +import org.loculus.backend.config.DataUseTerms +import org.loculus.backend.config.FileSharing +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.LinkOut +import org.loculus.backend.config.Metadata +import org.loculus.backend.config.MetadataType +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.OrganismImage +import org.loculus.backend.config.ReferenceGenome +import org.loculus.backend.config.Schema +import org.loculus.backend.config.operations.handlers.AddLinkOutHandler +import org.loculus.backend.config.operations.handlers.AddLinkOutPayload +import org.loculus.backend.config.operations.handlers.AddOptionalMetadataFieldHandler +import org.loculus.backend.config.operations.handlers.AddOptionalMetadataFieldPayload +import org.loculus.backend.config.operations.handlers.RemoveLinkOutHandler +import org.loculus.backend.config.operations.handlers.RemoveLinkOutPayload +import org.loculus.backend.config.operations.handlers.ReorderMetadataFieldsHandler +import org.loculus.backend.config.operations.handlers.ReorderMetadataFieldsPayload +import org.loculus.backend.config.operations.handlers.SetInstanceBrandingHandler +import org.loculus.backend.config.operations.handlers.SetInstanceBrandingPayload +import org.loculus.backend.config.operations.handlers.SetMetadataFieldDisplayHandler +import org.loculus.backend.config.operations.handlers.SetMetadataFieldDisplayPayload +import org.loculus.backend.config.operations.handlers.SetOrganismDisplayHandler +import org.loculus.backend.config.operations.handlers.SetOrganismDisplayPayload +import org.loculus.backend.config.operations.handlers.UpdateLinkOutHandler +import org.loculus.backend.config.operations.handlers.UpdateLinkOutPayload + +class OperationHandlerTest { + + private val instance = InstanceConfig( + name = "Loculus", + accessionPrefix = "LOC_", + dataUseTerms = DataUseTerms(enabled = false, urls = null), + fileSharing = FileSharing(), + ) + + private val organism = OrganismConfig( + schema = Schema( + organismName = "Test", + metadata = listOf( + Metadata(name = "country", type = MetadataType.STRING), + Metadata(name = "date", type = MetadataType.DATE, required = true), + ), + ), + referenceGenome = ReferenceGenome(emptyList(), emptyList()), + ) + + @Test + fun `SetInstanceBranding sets the name`() { + val handler = SetInstanceBrandingHandler() + val payload = SetInstanceBrandingPayload(name = "Pathoplexus") + val draft = ConfigDocument.Instance(instance) + + assertThat(handler.validate(payload, draft), instanceOf(ValidationResult.Valid::class.java)) + val result = handler.apply(payload, draft) as ConfigDocument.Instance + assertThat(result.config.name, equalTo("Pathoplexus")) + assertThat(result.config.accessionPrefix, equalTo("LOC_")) // untouched + } + + @Test + fun `SetInstanceBranding rejects blank name`() { + val handler = SetInstanceBrandingHandler() + val result = handler.validate(SetInstanceBrandingPayload(name = " "), ConfigDocument.Instance(instance)) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + val errors = (result as ValidationResult.Invalid).errors + assertThat(errors.first().path, equalTo("name")) + } + + @Test + fun `SetInstanceBranding rejects organism-scope draft`() { + val handler = SetInstanceBrandingHandler() + val result = handler.validate( + SetInstanceBrandingPayload(name = "X"), + ConfigDocument.Organism(organism), + ) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + } + + @Test + fun `SetMetadataFieldDisplay updates displayName`() { + val handler = SetMetadataFieldDisplayHandler() + val payload = SetMetadataFieldDisplayPayload(field = "country", displayName = "Country of origin") + val draft = ConfigDocument.Organism(organism) + + assertThat(handler.validate(payload, draft), instanceOf(ValidationResult.Valid::class.java)) + val result = handler.apply(payload, draft) as ConfigDocument.Organism + val updated = result.config.schema.metadata.single { it.name == "country" } + assertThat(updated.displayName, equalTo("Country of origin")) + } + + @Test + fun `SetMetadataFieldDisplay rejects nonexistent field`() { + val handler = SetMetadataFieldDisplayHandler() + val result = handler.validate( + SetMetadataFieldDisplayPayload(field = "nonexistent", displayName = "X"), + ConfigDocument.Organism(organism), + ) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + val errors = (result as ValidationResult.Invalid).errors + assertThat(errors.first().path, equalTo("field")) + } + + @Test + fun `SetOrganismDisplay updates top-level fields and leaves schema_organismName alone`() { + val handler = SetOrganismDisplayHandler() + val payload = SetOrganismDisplayPayload( + displayName = "Sudan ebolavirus", + description = "A virus", + image = OrganismImage(url = "/img/sudan.png"), + ) + val draft = ConfigDocument.Organism(organism) + + assertThat(handler.validate(payload, draft), instanceOf(ValidationResult.Valid::class.java)) + val result = handler.apply(payload, draft) as ConfigDocument.Organism + assertThat(result.config.displayName, equalTo("Sudan ebolavirus")) + assertThat(result.config.description, equalTo("A virus")) + assertThat(result.config.image?.url, equalTo("/img/sudan.png")) + // Schema.organismName is no longer mutated by this op; it retains the + // value from the initial PUT. + assertThat(result.config.schema.organismName, equalTo(organism.schema.organismName)) + } + + @Test + fun `SetOrganismDisplay rejects blank display values`() { + val handler = SetOrganismDisplayHandler() + val result = handler.validate( + SetOrganismDisplayPayload(displayName = " ", image = OrganismImage(url = "")), + ConfigDocument.Organism(organism), + ) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + assertThat((result as ValidationResult.Invalid).errors.map { it.path }, contains("displayName", "image.url")) + } + + @Test + fun `ReorderMetadataFields reorders the existing list`() { + val handler = ReorderMetadataFieldsHandler() + val payload = ReorderMetadataFieldsPayload(order = listOf("date", "country")) + val draft = ConfigDocument.Organism(organism) + + assertThat(handler.validate(payload, draft), instanceOf(ValidationResult.Valid::class.java)) + val result = handler.apply(payload, draft) as ConfigDocument.Organism + assertThat(result.config.schema.metadata.map { it.name }, contains("date", "country")) + } + + @Test + fun `ReorderMetadataFields rejects missing or extra fields`() { + val handler = ReorderMetadataFieldsHandler() + val draft = ConfigDocument.Organism(organism) + + val missing = handler.validate(ReorderMetadataFieldsPayload(order = listOf("country")), draft) + assertThat(missing, instanceOf(ValidationResult.Invalid::class.java)) + assertThat((missing as ValidationResult.Invalid).errors.first().message, equalTo("missing fields: date")) + + val extra = handler.validate( + ReorderMetadataFieldsPayload(order = listOf("country", "date", "extra")), + draft, + ) + assertThat(extra, instanceOf(ValidationResult.Invalid::class.java)) + assertThat((extra as ValidationResult.Invalid).errors.first().message, equalTo("unknown fields: extra")) + + val dupes = handler.validate( + ReorderMetadataFieldsPayload(order = listOf("country", "country")), + draft, + ) + assertThat(dupes, instanceOf(ValidationResult.Invalid::class.java)) + } + + @Test + fun `AddLinkOut adds and rejects duplicates`() { + val handler = AddLinkOutHandler() + val link = LinkOut(name = "NCBI", url = "https://x/{accession}") + val draft = ConfigDocument.Organism(organism) + + assertThat(handler.validate(AddLinkOutPayload(link), draft), instanceOf(ValidationResult.Valid::class.java)) + val after = handler.apply(AddLinkOutPayload(link), draft) as ConfigDocument.Organism + assertThat(after.config.schema.linkOuts.map { it.name }, contains("NCBI")) + + val dupe = handler.validate(AddLinkOutPayload(link), after) + assertThat(dupe, instanceOf(ValidationResult.Invalid::class.java)) + assertThat((dupe as ValidationResult.Invalid).errors.first().path, equalTo("linkOut.name")) + } + + @Test + fun `AddLinkOut rejects blank fields`() { + val handler = AddLinkOutHandler() + val result = handler.validate( + AddLinkOutPayload( + LinkOut(name = "", url = "", maxNumberOfRecommendedEntries = 0), + ), + ConfigDocument.Organism(organism), + ) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + val errors = (result as ValidationResult.Invalid).errors + assertThat(errors, hasSize(3)) + } + + @Test + fun `UpdateLinkOut updates fields by name`() { + val handler = UpdateLinkOutHandler() + val existing = organism.copy( + schema = organism.schema.copy( + linkOuts = listOf(LinkOut(name = "NCBI", url = "https://x")), + ), + ) + val draft = ConfigDocument.Organism(existing) + val payload = UpdateLinkOutPayload(name = "NCBI", url = "https://new") + + assertThat(handler.validate(payload, draft), instanceOf(ValidationResult.Valid::class.java)) + val result = handler.apply(payload, draft) as ConfigDocument.Organism + val updated = result.config.schema.linkOuts.single() + assertThat(updated.url, equalTo("https://new")) + assertThat(updated.name, equalTo("NCBI")) // untouched + } + + @Test + fun `UpdateLinkOut rejects unknown name`() { + val handler = UpdateLinkOutHandler() + val result = handler.validate( + UpdateLinkOutPayload(name = "nope", url = "https://x", maxNumberOfRecommendedEntries = 0), + ConfigDocument.Organism(organism), + ) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + val paths = (result as ValidationResult.Invalid).errors.map { it.path } + assertThat(paths, contains("name", "maxNumberOfRecommendedEntries")) + } + + @Test + fun `RemoveLinkOut removes by name`() { + val handler = RemoveLinkOutHandler() + val existing = organism.copy( + schema = organism.schema.copy( + linkOuts = listOf(LinkOut(name = "NCBI", url = "https://x")), + ), + ) + val draft = ConfigDocument.Organism(existing) + + assertThat( + handler.validate(RemoveLinkOutPayload(name = "NCBI"), draft), + instanceOf(ValidationResult.Valid::class.java), + ) + val result = handler.apply(RemoveLinkOutPayload(name = "NCBI"), draft) as ConfigDocument.Organism + assertThat(result.config.schema.linkOuts, hasSize(0)) + } + + @Test + fun `RemoveLinkOut rejects unknown name`() { + val handler = RemoveLinkOutHandler() + val result = handler.validate( + RemoveLinkOutPayload(name = "nope"), + ConfigDocument.Organism(organism), + ) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + } + + @Test + fun `AddOptionalMetadataField appends a new optional field`() { + val handler = AddOptionalMetadataFieldHandler() + val payload = AddOptionalMetadataFieldPayload( + name = "host", + type = MetadataType.STRING, + displayName = "Host", + ) + val draft = ConfigDocument.Organism(organism) + + assertThat(handler.validate(payload, draft), instanceOf(ValidationResult.Valid::class.java)) + val result = handler.apply(payload, draft) as ConfigDocument.Organism + val added = result.config.schema.metadata.last() + assertThat(added.name, equalTo("host")) + assertThat(added.required, equalTo(false)) + assertThat(added.displayName, equalTo("Host")) + } + + @Test + fun `AddOptionalMetadataField rejects duplicate name`() { + val handler = AddOptionalMetadataFieldHandler() + val result = handler.validate( + AddOptionalMetadataFieldPayload(name = "country", type = MetadataType.STRING), + ConfigDocument.Organism(organism), + ) + assertThat(result, instanceOf(ValidationResult.Invalid::class.java)) + assertThat( + (result as ValidationResult.Invalid).errors.first().message, + equalTo("field 'country' already exists"), + ) + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/EndpointTestExtension.kt b/backend/src/test/kotlin/org/loculus/backend/controller/EndpointTestExtension.kt index f22ec458bb..58b7a07741 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/EndpointTestExtension.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/EndpointTestExtension.kt @@ -15,6 +15,16 @@ import org.junit.platform.launcher.TestPlan import org.loculus.backend.api.Address import org.loculus.backend.api.NewGroup import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.dbtables.CONFIG_AUDIT_LOG_TABLE_NAME +import org.loculus.backend.config.dbtables.CONFIG_INSTANCE_DRAFT_TABLE_NAME +import org.loculus.backend.config.dbtables.CONFIG_INSTANCE_STATE_TABLE_NAME +import org.loculus.backend.config.dbtables.CONFIG_INSTANCE_VERSIONS_TABLE_NAME +import org.loculus.backend.config.dbtables.CONFIG_ORGANISMS_TABLE_NAME +import org.loculus.backend.config.dbtables.CONFIG_ORGANISM_DRAFTS_TABLE_NAME +import org.loculus.backend.config.dbtables.CONFIG_ORGANISM_VERSIONS_TABLE_NAME +import org.loculus.backend.config.fixtures.ConfigFixtures +import org.loculus.backend.config.fixtures.ConfigFixturesConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.datauseterms.DataUseTermsControllerClient import org.loculus.backend.controller.files.FilesClient import org.loculus.backend.controller.groupmanagement.GroupManagementControllerClient @@ -37,12 +47,14 @@ import org.springframework.context.annotation.Import import org.springframework.core.annotation.AliasFor import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.junit.jupiter.SpringExtension /** * The main annotation for tests. It also loads the [EndpointTestExtension], which initializes - * a PostgreSQL test container. - * You can set additional properties to - for example - override the backend config file, like in - * [org.loculus.backend.controller.submission.GetReleasedDataDataUseTermsDisabledEndpointTest]. + * a PostgreSQL test container. By default the extension loads the `default` fixture variant + * before each test; tests that need a different organism set autowire [ConfigFixtures] and + * call [ConfigFixtures.loadVariant] in their own `@BeforeEach` (see + * [org.loculus.backend.controller.submission.GetReleasedDataDataUseTermsDisabledEndpointTest]). */ @Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @@ -59,14 +71,13 @@ import org.springframework.test.context.ActiveProfiles SeqSetCitationsControllerClient::class, PublicJwtKeyConfig::class, FilesClient::class, + ConfigFixturesConfig::class, ) annotation class EndpointTest(@get:AliasFor(annotation = SpringBootTest::class) val properties: Array = []) -const val SINGLE_SEGMENTED_REFERENCE_GENOME = "src/test/resources/backend_config_single_segment.json" - -const val DATA_USE_TERMS_DISABLED_CONFIG = "src/test/resources/backend_config_data_use_terms_disabled.json" - -const val S3_CONFIG = "src/test/resources/backend_config_s3.json" +const val SINGLE_SEGMENT_VARIANT = "single-segment" +const val DATA_USE_TERMS_DISABLED_VARIANT = "data-use-terms-disabled" +const val S3_VARIANT = "s3" const val SPRING_DATASOURCE_URL = "spring.datasource.url" const val SPRING_DATASOURCE_USERNAME = "spring.datasource.username" @@ -211,6 +222,10 @@ class EndpointTestExtension : override fun beforeEach(context: ExtensionContext) { log.debug("Clearing database") env.postgres.exec(clearDatabaseStatement()) + + val springContext = runCatching { SpringExtension.getApplicationContext(context) }.getOrNull() + springContext?.getBean(ConfigService::class.java)?.invalidateCache() + springContext?.getBean(ConfigFixtures::class.java)?.loadDefault() } override fun testPlanExecutionFinished(testPlan: TestPlan) { @@ -240,13 +255,20 @@ private fun clearDatabaseStatement(): String = """ $DATA_USE_TERMS_TABLE_NAME, $CURRENT_PROCESSING_PIPELINE_TABLE_NAME, $FILES_TABLE_NAME, + $CONFIG_AUDIT_LOG_TABLE_NAME, + $CONFIG_INSTANCE_DRAFT_TABLE_NAME, + $CONFIG_ORGANISM_DRAFTS_TABLE_NAME, + $CONFIG_ORGANISM_VERSIONS_TABLE_NAME, + $CONFIG_ORGANISMS_TABLE_NAME, + $CONFIG_INSTANCE_STATE_TABLE_NAME, + $CONFIG_INSTANCE_VERSIONS_TABLE_NAME, external_metadata, seqsets, seqset_records, seqset_to_records, audit_log, table_update_tracker - cascade; + restart identity cascade; alter sequence $ACCESSION_SEQUENCE_NAME restart with 1; alter sequence groups_table_group_id_seq restart with 1; alter sequence seqset_id_sequence restart with 1; @@ -254,10 +276,6 @@ private fun clearDatabaseStatement(): String = """ alter sequence seqset_to_records_seqset_record_id_seq restart with 1; alter sequence user_groups_table_id_seq restart with 1; alter sequence audit_log_id_seq restart with 1; - insert into $CURRENT_PROCESSING_PIPELINE_TABLE_NAME values - (1, now(), '$DEFAULT_ORGANISM'), - (1, now(), '$OTHER_ORGANISM'), - (1, now(), '$ORGANISM_WITHOUT_CONSENSUS_SEQUENCES'); """ private fun createBucket(endpoint: String, accessKey: String, secretKey: String, region: String, bucket: String) { diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/ExceptionHandlerTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/ExceptionHandlerTest.kt index d8b9a519e8..9a9b25f234 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/ExceptionHandlerTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/ExceptionHandlerTest.kt @@ -119,9 +119,13 @@ class ExceptionHandlerWithMockedModelTest(@Autowired val mockMvc: MockMvc) { @MockkBean lateinit var submitModel: SubmitModel + @MockkBean + lateinit var configService: org.loculus.backend.config.service.ConfigService + @Test fun `WHEN I submit a request with invalid organism THEN it should return a descriptive error message`() { every { submitModel.processSubmissions(any(), any(), any()) } returns validResponse + every { configService.listOrganismKeys() } returns emptySet() mockMvc.perform( multipart("/unknownOrganism/submit") diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/OpenApiSplitControllerTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/OpenApiSplitControllerTest.kt new file mode 100644 index 0000000000..048e73ae6a --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/controller/OpenApiSplitControllerTest.kt @@ -0,0 +1,59 @@ +package org.loculus.backend.controller + +import io.mockk.every +import io.mockk.mockk +import io.swagger.v3.core.util.Json +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.PathItem +import io.swagger.v3.oas.models.Paths +import io.swagger.v3.oas.models.info.Info +import jakarta.servlet.http.HttpServletRequest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springdoc.webmvc.api.OpenApiWebMvcResource + +class OpenApiSplitControllerTest { + + private val request = mockk(relaxed = true) + + @Test + fun `query JSON contains only paths for the requested organism`() { + val controller = OpenApiSplitController(openApiResource()) + + val response = controller.queryJson(request, "real-organisms") + val openApi = Json.mapper().readValue(response.body, OpenAPI::class.java) + + assertThat(openApi.paths.keys).containsExactly("/query/real-organisms/{versionGroup}/metadata") + } + + @Test + fun `general JSON excludes query paths`() { + val controller = OpenApiSplitController(openApiResource()) + + val response = controller.generalJson(request) + val openApi = Json.mapper().readValue(response.body, OpenAPI::class.java) + + assertThat(openApi.paths.keys).containsExactly("/{organism}/submit") + } + + private fun openApiResource(): OpenApiWebMvcResource { + val resource = mockk() + every { resource.openapiJson(any(), any(), any()) } returns Json.mapper().writeValueAsBytes( + OpenAPI() + .info(Info().title("Loculus").version("test")) + .paths( + Paths() + .addPathItem( + "/query/overview/{versionGroup}/metadata", + PathItem().get(io.swagger.v3.oas.models.Operation()), + ) + .addPathItem( + "/query/real-organisms/{versionGroup}/metadata", + PathItem().get(io.swagger.v3.oas.models.Operation()), + ) + .addPathItem("/{organism}/submit", PathItem().post(io.swagger.v3.oas.models.Operation())), + ), + ) + return resource + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/QueryControllerTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/QueryControllerTest.kt new file mode 100644 index 0000000000..96558a323b --- /dev/null +++ b/backend/src/test/kotlin/org/loculus/backend/controller/QueryControllerTest.kt @@ -0,0 +1,270 @@ +package org.loculus.backend.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import com.ninjasquad.springmockk.MockkBean +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.loculus.backend.SpringBootTestWithoutDatabase +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.config.service.OrganismNotFoundException +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody +import java.net.URLDecoder +import java.nio.charset.StandardCharsets + +private const val ORGANISM = DEFAULT_ORGANISM +private const val LAPIS_URL = "http://lapis.test" + +@SpringBootTestWithoutDatabase +@AutoConfigureMockMvc +class QueryControllerTest(@Autowired val mockMvc: MockMvc, @Autowired val objectMapper: ObjectMapper) { + + @MockkBean + lateinit var lapisProxyService: LapisProxyService + + @MockkBean(relaxed = true) + lateinit var configService: ConfigService + + private val okResponse: ResponseEntity = ResponseEntity.ok( + StreamingResponseBody { it.write("{}".toByteArray()) }, + ) + + @BeforeEach + fun setUp() { + every { lapisProxyService.proxyPost(any(), any(), any(), any()) } returns okResponse + every { lapisProxyService.proxyGet(any(), any(), any(), any()) } returns okResponse + + val organismConfig = mockk { every { lapisUrl } returns LAPIS_URL } + every { configService.getOrganismConfig(ORGANISM) } returns + mockk { every { config } returns organismConfig } + every { configService.getOrganismConfig("unknownOrganism") } throws OrganismNotFoundException("unknownOrganism") + } + + @Test + fun `unauthenticated request is allowed (query endpoints are open)`() { + mockMvc.perform( + post("/query/$ORGANISM/current/metadata") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + } + + @Test + fun `current injects versionStatus LATEST_VERSION into body`() { + val bodySlot = slot() + every { lapisProxyService.proxyPost(any(), any(), capture(bodySlot), any()) } returns okResponse + + mockMvc.perform( + post("/query/$ORGANISM/current/metadata") + .contentType(MediaType.APPLICATION_JSON) + .content("""{"limit": 10}"""), + ).andExpect(status().isOk) + + val forwarded = objectMapper.readTree(bodySlot.captured) + assert(forwarded.get("versionStatus")?.asText() == "LATEST_VERSION") { + "Expected versionStatus=LATEST_VERSION in forwarded body, got: $forwarded" + } + assert(forwarded.get("limit")?.asInt() == 10) { + "Expected limit=10 preserved in forwarded body, got: $forwarded" + } + } + + @Test + fun `allVersions does not inject versionStatus`() { + val bodySlot = slot() + every { lapisProxyService.proxyPost(any(), any(), capture(bodySlot), any()) } returns okResponse + + mockMvc.perform( + post("/query/$ORGANISM/allVersions/metadata") + .contentType(MediaType.APPLICATION_JSON) + .content("""{"limit": 5}"""), + ).andExpect(status().isOk) + + val forwarded = objectMapper.readTree(bodySlot.captured) + assert(forwarded.get("versionStatus") == null) { + "Expected no versionStatus in forwarded body for allVersions, got: $forwarded" + } + } + + @Test + fun `null body is handled as empty object`() { + val bodySlot = slot() + every { lapisProxyService.proxyPost(any(), any(), capture(bodySlot), any()) } returns okResponse + + mockMvc.perform( + post("/query/$ORGANISM/current/aggregated") + .contentType(MediaType.APPLICATION_JSON), + ).andExpect(status().isOk) + + val forwarded = objectMapper.readTree(bodySlot.captured) + assert(forwarded.get("versionStatus")?.asText() == "LATEST_VERSION") { + "Expected versionStatus injected into empty body, got: $forwarded" + } + } + + @Test + fun `no group visibility filter is injected (auth off)`() { + val bodySlot = slot() + every { lapisProxyService.proxyPost(any(), any(), capture(bodySlot), any()) } returns okResponse + + mockMvc.perform( + post("/query/$ORGANISM/current/metadata") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + val forwarded = objectMapper.readTree(bodySlot.captured) + assert(forwarded.get("groupId") == null) { + "Expected no groupId filter in forwarded body, got: $forwarded" + } + assert(forwarded.get("advancedQuery") == null) { + "Expected no advancedQuery visibility filter in forwarded body, got: $forwarded" + } + } + + @Test + fun `GET query preserves advancedQuery and injects versionStatus without group filter`() { + val querySlot = slot() + every { lapisProxyService.proxyGet(any(), any(), capture(querySlot), any()) } returns okResponse + + mockMvc.perform( + get("/query/$ORGANISM/current/aggregated?fields=country&advancedQuery=country=Germany"), + ).andExpect(status().isOk) + + val forwarded = URLDecoder.decode(querySlot.captured, StandardCharsets.UTF_8) + assert(forwarded.contains("versionStatus=LATEST_VERSION")) { + "Expected versionStatus in forwarded query, got: $forwarded" + } + assert(forwarded.contains("advancedQuery=country=Germany")) { + "Expected advancedQuery preserved unchanged in forwarded query, got: $forwarded" + } + assert(!forwarded.contains("groupId")) { + "Expected no groupId filter in forwarded query, got: $forwarded" + } + } + + @Test + fun `unknown organism returns 404`() { + mockMvc.perform( + post("/query/unknownOrganism/current/metadata") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isNotFound) + } + + @Test + fun `unknown versionGroup returns 404`() { + mockMvc.perform( + post("/query/$ORGANISM/badVersionGroup/metadata") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isNotFound) + } + + @Test + fun `metadata routes to sample details`() { + mockMvc.perform( + post("/query/$ORGANISM/current/metadata") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyPost(eq(LAPIS_URL), eq("/sample/details"), any(), any()) } + } + + @Test + fun `aggregated routes to sample aggregated`() { + mockMvc.perform( + post("/query/$ORGANISM/current/aggregated") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyPost(any(), eq("/sample/aggregated"), any(), any()) } + } + + @Test + fun `aggregated GET routes to sample aggregated`() { + mockMvc.perform( + get("/query/$ORGANISM/current/aggregated?fields=country"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyGet(any(), eq("/sample/aggregated"), any(), any()) } + } + + @Test + fun `sequencesAligned mutations routes to nucleotideMutations (literal wins over variable)`() { + mockMvc.perform( + post("/query/$ORGANISM/current/sequencesAligned/mutations") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyPost(any(), eq("/sample/nucleotideMutations"), any(), any()) } + } + + @Test + fun `sequencesAligned mutations GET routes to nucleotideMutations`() { + mockMvc.perform( + get("/query/$ORGANISM/current/sequencesAligned/mutations"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyGet(any(), eq("/sample/nucleotideMutations"), any(), any()) } + } + + @Test + fun `sequencesAligned with referenceName routes to alignedNucleotideSequences with segment`() { + mockMvc.perform( + post("/query/$ORGANISM/current/sequencesAligned/main") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyPost(any(), eq("/sample/alignedNucleotideSequences/main"), any(), any()) } + } + + @Test + fun `sequences with segment routes to unalignedNucleotideSequences with segment`() { + mockMvc.perform( + post("/query/$ORGANISM/current/sequences/main") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyPost(any(), eq("/sample/unalignedNucleotideSequences/main"), any(), any()) } + } + + @Test + fun `translations with geneName routes to alignedAminoAcidSequences`() { + mockMvc.perform( + post("/query/$ORGANISM/current/translations/S") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyPost(any(), eq("/sample/alignedAminoAcidSequences/S"), any(), any()) } + } + + @Test + fun `translations mutations routes to aminoAcidMutations`() { + mockMvc.perform( + post("/query/$ORGANISM/current/translations/S/mutations") + .contentType(MediaType.APPLICATION_JSON) + .content("{}"), + ).andExpect(status().isOk) + + verify { lapisProxyService.proxyPost(any(), eq("/sample/aminoAcidMutations"), any(), any()) } + } +} diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/RequestAuthorization.kt b/backend/src/test/kotlin/org/loculus/backend/controller/RequestAuthorization.kt index 0d6d28429f..6f9a8febd9 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/RequestAuthorization.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/RequestAuthorization.kt @@ -2,6 +2,7 @@ package org.loculus.backend.controller import io.jsonwebtoken.Jwts import org.loculus.backend.auth.Roles.EXTERNAL_METADATA_UPDATER +import org.loculus.backend.auth.Roles.LOCULUS_ADMINISTRATOR import org.loculus.backend.auth.Roles.PREPROCESSING_PIPELINE import org.loculus.backend.auth.Roles.SUPER_USER import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder @@ -17,6 +18,7 @@ val jwtForAlternativeUser = generateJwtFor(ALTERNATIVE_DEFAULT_USER_NAME) val jwtForProcessingPipeline = generateJwtFor("preprocessing_pipeline", listOf(PREPROCESSING_PIPELINE)) val jwtForExternalMetadataUpdatePipeline = generateJwtFor("external_metadata_updater", listOf(EXTERNAL_METADATA_UPDATER)) +val jwtForLoculusAdministrator = generateJwtFor(LOCULUS_ADMINISTRATOR, listOf(LOCULUS_ADMINISTRATOR)) val jwtForSuperUser = generateJwtFor(SUPER_USER_NAME, listOf(SUPER_USER)) fun generateJwtFor(username: String, roles: List = emptyList()): String = Jwts.builder() diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/admin/PipelineStatisticsEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/admin/PipelineStatisticsEndpointTest.kt index 39d8a0c800..646cc14437 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/admin/PipelineStatisticsEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/admin/PipelineStatisticsEndpointTest.kt @@ -3,6 +3,7 @@ package org.loculus.backend.controller.admin import org.junit.jupiter.api.Test import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.jwtForDefaultUser +import org.loculus.backend.controller.jwtForLoculusAdministrator import org.loculus.backend.controller.jwtForSuperUser import org.loculus.backend.controller.withAuth import org.springframework.beans.factory.annotation.Autowired @@ -19,14 +20,20 @@ class PipelineStatisticsEndpointTest(@Autowired val mockMvc: MockMvc) { } @Test - fun `WHEN non superuser THEN forbidden`() { + fun `WHEN non administrator THEN forbidden`() { mockMvc.perform(get("/admin/pipeline-statistics").withAuth(jwtForDefaultUser)) .andExpect(status().isForbidden) } @Test - fun `WHEN superuser THEN ok`() { + fun `WHEN superuser THEN forbidden`() { mockMvc.perform(get("/admin/pipeline-statistics").withAuth(jwtForSuperUser)) + .andExpect(status().isForbidden) + } + + @Test + fun `WHEN loculus administrator THEN ok`() { + mockMvc.perform(get("/admin/pipeline-statistics").withAuth(jwtForLoculusAdministrator)) .andExpect(status().isOk) } } diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/files/CompleteMultipartUploadEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/files/CompleteMultipartUploadEndpointTest.kt index befb4f00ff..0706117ab9 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/files/CompleteMultipartUploadEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/files/CompleteMultipartUploadEndpointTest.kt @@ -4,11 +4,11 @@ import org.hamcrest.CoreMatchers.containsString import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.FileIdAndEtags -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.DEFAULT_GROUP import org.loculus.backend.controller.DEFAULT_MULTIPART_FILE_PARTS import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.groupmanagement.GroupManagementControllerClient import org.loculus.backend.controller.groupmanagement.andGetGroupId import org.loculus.backend.controller.jwtForDefaultUser @@ -17,17 +17,21 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class CompleteMultipartUploadEndpointTest( @Autowired private val groupManagementClient: GroupManagementControllerClient, @Autowired private val filesClient: FilesClient, @Autowired val convenienceClient: SubmissionConvenienceClient, + @Autowired private val configFixtures: ConfigFixtures, ) { var groupId: Int = 0 + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @BeforeEach fun prepareNewGroup() { groupId = groupManagementClient diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/files/GetFilesEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/files/GetFilesEndpointTest.kt index e17382501c..af8108cef6 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/files/GetFilesEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/files/GetFilesEndpointTest.kt @@ -4,11 +4,12 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.FileIdAndName -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.jwtForAlternativeUser import org.loculus.backend.controller.jwtForDefaultUser import org.loculus.backend.controller.submission.PreparedProcessedData @@ -23,14 +24,19 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import java.net.http.HttpResponse -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class GetFilesEndpointTest( @Autowired private val submissionConvenienceClient: SubmissionConvenienceClient, @Autowired private val filesClient: FilesClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `GIVEN an unpublished file WHEN requesting without auth THEN an error is raised`() { submissionConvenienceClient.submitDefaultFiles(includeFileMapping = true) diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestMultipartUploadEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestMultipartUploadEndpointTest.kt index db76f8af45..0c8fc928b7 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestMultipartUploadEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestMultipartUploadEndpointTest.kt @@ -7,10 +7,11 @@ import org.apache.http.entity.ContentType import org.apache.http.impl.client.HttpClients import org.hamcrest.CoreMatchers.`is` import org.hamcrest.MatcherAssert.assertThat +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.groupmanagement.GroupManagementControllerClient import org.loculus.backend.controller.groupmanagement.andGetGroupId import org.loculus.backend.controller.jwtForAlternativeUser @@ -18,15 +19,19 @@ import org.loculus.backend.controller.jwtForProcessingPipeline import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class RequestMultipartUploadEndpointTest( @Autowired private val client: FilesClient, @Autowired private val groupManagementClient: GroupManagementControllerClient, @Autowired private val objectMapper: ObjectMapper, + @Autowired private val configFixtures: ConfigFixtures, ) { + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `GIVEN a request for 2 URLs and 3 parts THEN returns a response with 2x3 URLs`() { val groupId = groupManagementClient.createNewGroup().andGetGroupId() diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestUploadEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestUploadEndpointTest.kt index ff795c4c76..f4ae6e0ee5 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestUploadEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/files/RequestUploadEndpointTest.kt @@ -9,10 +9,11 @@ import org.hamcrest.CoreMatchers.`is` import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.containsString import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.generateJwtFor import org.loculus.backend.controller.groupmanagement.GroupManagementControllerClient import org.loculus.backend.controller.groupmanagement.andGetGroupId @@ -25,15 +26,19 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class RequestUploadEndpointTest( @Autowired private val client: FilesClient, @Autowired private val groupManagementClient: GroupManagementControllerClient, @Autowired private val objectMapper: ObjectMapper, + @Autowired private val configFixtures: ConfigFixtures, ) { + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `GIVEN a request for three URLs THEN returns a response with three URLs`() { val groupId = groupManagementClient.createNewGroup().andGetGroupId() diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/ApproveProcessedDataEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/ApproveProcessedDataEndpointTest.kt index ad3bdbfd46..1c31085928 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/ApproveProcessedDataEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/ApproveProcessedDataEndpointTest.kt @@ -5,6 +5,7 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.hasSize import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.AccessionVersion import org.loculus.backend.api.ApproveDataScope.ALL @@ -14,13 +15,13 @@ import org.loculus.backend.api.Status.IN_PROCESSING import org.loculus.backend.api.Status.PROCESSED import org.loculus.backend.api.SubmittedProcessedData import org.loculus.backend.api.toFileIdAndName -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.ALTERNATIVE_DEFAULT_USER_NAME import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.DEFAULT_USER_NAME import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.OTHER_ORGANISM -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.assertHasError import org.loculus.backend.controller.assertStatusIs import org.loculus.backend.controller.expectUnauthorizedResponse @@ -39,15 +40,19 @@ import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class ApproveProcessedDataEndpointTest( @Autowired val client: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, @Autowired val objectMapper: ObjectMapper, + @Autowired private val configFixtures: ConfigFixtures, ) { + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `GIVEN invalid authorization token THEN returns 401 Unauthorized`() { expectUnauthorizedResponse(isModifyingRequest = true) { diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/ExtractUnprocessedDataEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/ExtractUnprocessedDataEndpointTest.kt index 2842635f5c..61626cf1fa 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/ExtractUnprocessedDataEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/ExtractUnprocessedDataEndpointTest.kt @@ -15,6 +15,7 @@ import org.hamcrest.Matchers.hasProperty import org.hamcrest.Matchers.hasSize import org.hamcrest.Matchers.matchesRegex import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.FileIdAndNameAndReadUrl import org.loculus.backend.api.GeneticSequence @@ -24,13 +25,14 @@ import org.loculus.backend.api.SubmittedContentWithFileUrls import org.loculus.backend.api.SubmittedData import org.loculus.backend.api.UnprocessedData import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.DEFAULT_SIMPLE_FILE_CONTENT import org.loculus.backend.controller.DEFAULT_USER_NAME import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.ORGANISM_WITHOUT_CONSENSUS_SEQUENCES import org.loculus.backend.controller.OTHER_ORGANISM -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.assertStatusIs import org.loculus.backend.controller.expectForbiddenResponse import org.loculus.backend.controller.expectNdjsonAndGetContent @@ -51,14 +53,19 @@ import java.net.http.HttpResponse @EndpointTest( properties = [ "${BackendSpringProperty.STREAM_BATCH_SIZE}=2", - "${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG", ], ) class ExtractUnprocessedDataEndpointTest( @Autowired val convenienceClient: SubmissionConvenienceClient, @Autowired val client: SubmissionControllerClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `GIVEN invalid authorization token THEN returns 401 Unauthorized`() { expectUnauthorizedResponse(isModifyingRequest = true) { diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataDataUseTermsDisabledEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataDataUseTermsDisabledEndpointTest.kt index 3615f42a2b..9cb86a1b80 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataDataUseTermsDisabledEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataDataUseTermsDisabledEndpointTest.kt @@ -16,9 +16,9 @@ import org.junit.jupiter.api.Test import org.keycloak.representations.idm.UserRepresentation import org.loculus.backend.api.GeneticSequence import org.loculus.backend.api.ProcessedData -import org.loculus.backend.config.BackendConfig -import org.loculus.backend.config.BackendSpringProperty -import org.loculus.backend.controller.DATA_USE_TERMS_DISABLED_CONFIG +import org.loculus.backend.config.fixtures.ConfigFixtures +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.controller.DATA_USE_TERMS_DISABLED_VARIANT import org.loculus.backend.controller.DEFAULT_GROUP import org.loculus.backend.controller.DEFAULT_GROUP_CHANGED import org.loculus.backend.controller.DEFAULT_GROUP_NAME_CHANGED @@ -37,15 +37,19 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import kotlin.time.Clock -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$DATA_USE_TERMS_DISABLED_CONFIG"], -) +@EndpointTest class GetReleasedDataDataUseTermsDisabledEndpointTest( @Autowired private val convenienceClient: SubmissionConvenienceClient, @Autowired private val submissionControllerClient: SubmissionControllerClient, @Autowired private val groupClient: GroupManagementControllerClient, - @Autowired private val backendConfig: BackendConfig, + @Autowired private val configService: ConfigService, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadDataUseTermsDisabledFixture() { + configFixtures.loadVariant(DATA_USE_TERMS_DISABLED_VARIANT) + } private val currentDate = Clock.System.now().toLocalDateTime(DateProvider.timeZone).date.toString() @MockkBean @@ -58,7 +62,7 @@ class GetReleasedDataDataUseTermsDisabledEndpointTest( @Test fun `config has been read and data use terms are configured to be off`() { - assertThat(backendConfig.dataUseTerms.enabled, `is`(false)) + assertThat(configService.getInstanceConfig().config.dataUseTerms.enabled, `is`(false)) } @Test diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataEndpointTest.kt index f7460ce158..af702aeb98 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataEndpointTest.kt @@ -42,10 +42,9 @@ import org.loculus.backend.api.DataUseTermsChangeRequest import org.loculus.backend.api.ReleasedData import org.loculus.backend.api.Status import org.loculus.backend.api.VersionStatus -import org.loculus.backend.config.BackendConfig -import org.loculus.backend.config.BackendSpringProperty import org.loculus.backend.config.DataUseTermsUrls -import org.loculus.backend.config.readBackendConfig +import org.loculus.backend.config.fixtures.ConfigFixtures +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.DEFAULT_GROUP import org.loculus.backend.controller.DEFAULT_GROUP_CHANGED import org.loculus.backend.controller.DEFAULT_GROUP_NAME @@ -584,7 +583,24 @@ class GetReleasedDataEndpointWithDataUseTermsUrlTest( @Autowired val dataUseTermsClient: DataUseTermsControllerClient, @Autowired val submissionControllerClient: SubmissionControllerClient, @Autowired var dateProvider: DateProvider, + @Autowired val configFixtures: ConfigFixtures, + @Autowired val configService: ConfigService, ) { + @BeforeEach + fun installDataUseTermsUrls() { + val current = configService.getInstanceConfig().config + configFixtures.setInstanceConfig( + current.copy( + dataUseTerms = current.dataUseTerms.copy( + urls = DataUseTermsUrls( + open = OPEN_DATA_USE_TERMS_URL, + restricted = RESTRICTED_DATA_USE_TERMS_URL, + ), + ), + ), + ) + } + @Test fun `GIVEN sequence entry WHEN I change data use terms THEN returns updated data use terms`() { every { dateProvider.getCurrentInstant() } answers { callOriginal() } @@ -773,22 +789,8 @@ class GetReleasedDataEndpointWithDataUseTermsUrlTest( @TestConfiguration class GetReleasedDataEndpointWithDataUseTermsUrlTestConfig { - @Bean - @Primary - fun configWithModifiedDataUseTermsUrl( - objectMapper: ObjectMapper, - @Value("\${${BackendSpringProperty.BACKEND_CONFIG_PATH}}") configPath: String, - ): BackendConfig { - val originalConfig = readBackendConfig(objectMapper = objectMapper, configPath = configPath) - return originalConfig.copy( - dataUseTerms = originalConfig.dataUseTerms.copy( - urls = DataUseTermsUrls( - open = OPEN_DATA_USE_TERMS_URL, - restricted = RESTRICTED_DATA_USE_TERMS_URL, - ), - ), - ) - } + // Data-use-terms URLs are installed per-test via ConfigFixtures.setInstanceConfig; see + // installDataUseTermsUrls @BeforeEach above. @Bean @Primary diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataFileSharingEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataFileSharingEndpointTest.kt index d667f764e5..7ee0c763f3 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataFileSharingEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/GetReleasedDataFileSharingEndpointTest.kt @@ -7,13 +7,14 @@ import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.hasKey import org.hamcrest.Matchers.hasSize import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.FileIdAndName import org.loculus.backend.api.ReleasedData -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.DEFAULT_GROUP import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.datauseterms.DataUseTermsControllerClient import org.loculus.backend.controller.expectNdjsonAndGetContent import org.loculus.backend.controller.files.FilesClient @@ -24,9 +25,7 @@ import org.loculus.backend.controller.jwtForDefaultUser import org.loculus.backend.service.submission.SubmissionDatabaseService import org.springframework.beans.factory.annotation.Autowired -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class GetReleasedDataFileSharingEndpointTest( @Autowired private val convenienceClient: SubmissionConvenienceClient, @Autowired private val submissionControllerClient: SubmissionControllerClient, @@ -35,7 +34,13 @@ class GetReleasedDataFileSharingEndpointTest( @Autowired private val submissionDatabaseService: SubmissionDatabaseService, @Autowired private val groupManagementClient: GroupManagementControllerClient, @Autowired private val filesClient: FilesClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } private val objectMapper = jacksonObjectMapper() @Test diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/ReviseEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/ReviseEndpointTest.kt index 61dbc1c8e5..1cf00586fd 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/ReviseEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/ReviseEndpointTest.kt @@ -7,6 +7,7 @@ import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.hasProperty import org.hamcrest.Matchers.hasSize import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -17,7 +18,7 @@ import org.loculus.backend.api.Status.APPROVED_FOR_RELEASE import org.loculus.backend.api.Status.PROCESSED import org.loculus.backend.api.Status.RECEIVED import org.loculus.backend.api.UnprocessedData -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.DEFAULT_MULTIPART_FILE_PARTS import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.DEFAULT_SIMPLE_FILE_CONTENT @@ -25,7 +26,7 @@ import org.loculus.backend.controller.DEFAULT_USER_NAME import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.ORGANISM_WITHOUT_CONSENSUS_SEQUENCES import org.loculus.backend.controller.OTHER_ORGANISM -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.SUPER_USER_NAME import org.loculus.backend.controller.assertStatusIs import org.loculus.backend.controller.expectNdjsonAndGetContent @@ -51,15 +52,20 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import java.util.UUID -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class ReviseEndpointTest( @Autowired val client: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, @Autowired val groupManagementClient: GroupManagementControllerClient, @Autowired val filesClient: FilesClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `GIVEN invalid authorization token THEN returns 401 Unauthorized`() { expectUnauthorizedResponse(isModifyingRequest = true) { diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionConvenienceClient.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionConvenienceClient.kt index 9a52684cdb..8989c8bd90 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionConvenienceClient.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionConvenienceClient.kt @@ -22,7 +22,7 @@ import org.loculus.backend.api.SubmissionIdMapping import org.loculus.backend.api.SubmittedData import org.loculus.backend.api.SubmittedProcessedData import org.loculus.backend.api.UnprocessedData -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.DEFAULT_GROUP import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.DEFAULT_PIPELINE_VERSION @@ -57,7 +57,7 @@ data class SubmissionResult( class SubmissionConvenienceClient( private val groupManagementClient: GroupManagementControllerClient, - private val backendConfig: BackendConfig, + private val configService: ConfigService, private val client: SubmissionControllerClient, private val filesClient: FilesClient, private val objectMapper: ObjectMapper, @@ -74,13 +74,13 @@ class SubmissionConvenienceClient( .createNewGroup(group = DEFAULT_GROUP, jwt = generateJwtFor(username)) .andGetGroupId() - val instanceConfig = backendConfig.getInstanceConfig(Organism(organism)) + val organismConfig = configService.getOrganismConfig(Organism(organism)).config - val isMultiSegmented = instanceConfig + val isMultiSegmented = organismConfig .referenceGenome .nucleotideSequences.size > 1 - val doesNotAllowConsensusSequenceFile = !instanceConfig.schema + val doesNotAllowConsensusSequenceFile = !organismConfig.schema .submissionDataTypes .consensusSequences diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionJourneyWithFilesTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionJourneyWithFilesTest.kt index 229ac291fb..d125ef2249 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionJourneyWithFilesTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmissionJourneyWithFilesTest.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.module.kotlin.readValue import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.hasItems import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.AccessionVersion import org.loculus.backend.api.FileIdAndEtags @@ -12,14 +13,14 @@ import org.loculus.backend.api.Status.APPROVED_FOR_RELEASE import org.loculus.backend.api.Status.IN_PROCESSING import org.loculus.backend.api.Status.PROCESSED import org.loculus.backend.api.Status.RECEIVED -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.DEFAULT_GROUP import org.loculus.backend.controller.DEFAULT_MULTIPART_FILE_CONTENT import org.loculus.backend.controller.DEFAULT_MULTIPART_FILE_PARTS import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.DEFAULT_SIMPLE_FILE_CONTENT import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.assertHasError import org.loculus.backend.controller.assertStatusIs import org.loculus.backend.controller.files.FilesClient @@ -38,15 +39,20 @@ import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class SubmissionJourneyWithFilesTest( @Autowired val submissionControllerClient: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, @Autowired val groupManagementClient: GroupManagementControllerClient, @Autowired val filesClient: FilesClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `Simple file upload, submission, processing and approval, ending in get-released-data`() { val groupId = groupManagementClient diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEditedSequenceEntryVersionEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEditedSequenceEntryVersionEndpointTest.kt index fffac17786..936173d240 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEditedSequenceEntryVersionEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEditedSequenceEntryVersionEndpointTest.kt @@ -6,16 +6,17 @@ import org.hamcrest.Matchers.anEmptyMap import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.not +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.EditedSequenceEntryData import org.loculus.backend.api.FileIdAndName import org.loculus.backend.api.Status import org.loculus.backend.api.SubmittedData -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.DEFAULT_USER_NAME import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.OTHER_ORGANISM -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.assertHasError import org.loculus.backend.controller.assertStatusIs import org.loculus.backend.controller.expectUnauthorizedResponse @@ -30,16 +31,20 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import java.util.UUID -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class SubmitEditedSequenceEntryVersionEndpointTest( @Autowired val client: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, @Autowired val groupManagementClient: GroupManagementControllerClient, @Autowired val filesClient: FilesClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Test fun `GIVEN invalid authorization token THEN returns 401 Unauthorized`() { expectUnauthorizedResponse(isModifyingRequest = true) { diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointDataUseTermsDisabledTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointDataUseTermsDisabledTest.kt index 89ae7972ab..9788cdf750 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointDataUseTermsDisabledTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointDataUseTermsDisabledTest.kt @@ -5,9 +5,9 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.containsString import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.loculus.backend.config.BackendConfig -import org.loculus.backend.config.BackendSpringProperty -import org.loculus.backend.controller.DATA_USE_TERMS_DISABLED_CONFIG +import org.loculus.backend.config.fixtures.ConfigFixtures +import org.loculus.backend.config.service.ConfigService +import org.loculus.backend.controller.DATA_USE_TERMS_DISABLED_VARIANT import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.groupmanagement.GroupManagementControllerClient @@ -20,14 +20,18 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$DATA_USE_TERMS_DISABLED_CONFIG"], -) +@EndpointTest class SubmitEndpointDataUseTermsDisabledTest( @Autowired val submissionControllerClient: SubmissionControllerClient, - @Autowired val backendConfig: BackendConfig, + @Autowired val configService: ConfigService, @Autowired val groupManagementClient: GroupManagementControllerClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadDataUseTermsDisabledFixture() { + configFixtures.loadVariant(DATA_USE_TERMS_DISABLED_VARIANT) + } var groupId: Int = 0 @BeforeEach @@ -37,7 +41,7 @@ class SubmitEndpointDataUseTermsDisabledTest( @Test fun `config has been read and data use terms are configured to be off`() { - assertThat(backendConfig.dataUseTerms.enabled, `is`(false)) + assertThat(configService.getInstanceConfig().config.dataUseTerms.enabled, `is`(false)) } @Test @@ -52,7 +56,9 @@ class SubmitEndpointDataUseTermsDisabledTest( .andExpect(content().contentType(APPLICATION_JSON_VALUE)) .andExpect(jsonPath("\$.length()").value(NUMBER_OF_SEQUENCES)) .andExpect(jsonPath("\$[0].submissionId").value("custom0")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) } } diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointFileSharingTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointFileSharingTest.kt index 0b80727d8a..775223833b 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointFileSharingTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointFileSharingTest.kt @@ -5,12 +5,12 @@ import org.hamcrest.Matchers.containsString import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.FileIdAndName -import org.loculus.backend.config.BackendConfig -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.DEFAULT_SIMPLE_FILE_CONTENT import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.files.FilesClient import org.loculus.backend.controller.files.andGetFileIds import org.loculus.backend.controller.files.andGetFileIdsAndUrls @@ -27,16 +27,20 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import java.util.* -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class SubmitEndpointFileSharingTest( @Autowired val submissionControllerClient: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, @Autowired val filesClient: FilesClient, - @Autowired val backendConfig: BackendConfig, + @Autowired val configService: ConfigService, @Autowired val groupManagementClient: GroupManagementControllerClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } var groupId: Int = 0 @BeforeEach @@ -62,7 +66,9 @@ class SubmitEndpointFileSharingTest( .andExpect(content().contentType(APPLICATION_JSON_VALUE)) .andExpect(jsonPath("\$.length()").value(NUMBER_OF_SEQUENCES)) .andExpect(jsonPath("\$[0].submissionId").value("custom0")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) } diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointMultipartFileSharingTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointMultipartFileSharingTest.kt index 1fc11e08f0..255cf70ed8 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointMultipartFileSharingTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointMultipartFileSharingTest.kt @@ -5,12 +5,12 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.loculus.backend.api.FileIdAndEtags import org.loculus.backend.api.FileIdAndName -import org.loculus.backend.config.BackendConfig -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.DEFAULT_MULTIPART_FILE_PARTS import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.EndpointTest -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.files.FilesClient import org.loculus.backend.controller.files.andGetFileIdsAndMultipartUrls import org.loculus.backend.controller.groupmanagement.GroupManagementControllerClient @@ -24,16 +24,20 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import java.util.* -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class SubmitEndpointMultipartFileSharingTest( @Autowired val submissionControllerClient: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, @Autowired val filesClient: FilesClient, - @Autowired val backendConfig: BackendConfig, + @Autowired val configService: ConfigService, @Autowired val groupManagementClient: GroupManagementControllerClient, + @Autowired private val configFixtures: ConfigFixtures, ) { + + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } var groupId: Int = 0 @BeforeEach @@ -71,7 +75,9 @@ class SubmitEndpointMultipartFileSharingTest( .andExpect(content().contentType(APPLICATION_JSON_VALUE)) .andExpect(jsonPath("\$.length()").value(NUMBER_OF_SEQUENCES)) .andExpect(jsonPath("\$[0].submissionId").value("custom0")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) } diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointTest.kt index 9f23083bc4..ffd2ea4804 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitEndpointTest.kt @@ -16,7 +16,7 @@ import org.junit.jupiter.params.provider.MethodSource import org.loculus.backend.api.DataUseTerms import org.loculus.backend.api.FileIdAndName import org.loculus.backend.api.Organism -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.ORGANISM_WITHOUT_CONSENSUS_SEQUENCES @@ -49,7 +49,7 @@ import kotlin.time.Clock class SubmitEndpointTest( @Autowired val submissionControllerClient: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, - @Autowired val backendConfig: BackendConfig, + @Autowired val configService: ConfigService, @Autowired val groupManagementClient: GroupManagementControllerClient, ) { var groupId: Int = 0 @@ -137,7 +137,9 @@ class SubmitEndpointTest( .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(jsonPath("\$.length()").value(NUMBER_OF_SEQUENCES)) .andExpect(jsonPath("\$[0].submissionId").value("custom0")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) } @@ -153,7 +155,9 @@ class SubmitEndpointTest( .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(jsonPath("\$.length()").value(NUMBER_OF_SEQUENCES)) .andExpect(jsonPath("\$[0].submissionId").value("custom0")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) } @@ -256,7 +260,9 @@ class SubmitEndpointTest( .andExpect(content().contentType(APPLICATION_JSON)) .andExpect(jsonPath("\$.length()").value(NUMBER_OF_SEQUENCES)) .andExpect(jsonPath("\$[0].submissionId").value("custom0")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) } @@ -574,7 +580,9 @@ class SubmitEndpointTest( .andExpect(content().contentType(APPLICATION_JSON_VALUE)) .andExpect(jsonPath("\$.length()").value(2)) .andExpect(jsonPath("\$[0].submissionId").value("header1")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) val unalignedNucleotideSequences = convenienceClient.extractUnprocessedData()[0] @@ -613,7 +621,9 @@ class SubmitEndpointTest( .andExpect(content().contentType(APPLICATION_JSON_VALUE)) .andExpect(jsonPath("\$.length()").value(2)) .andExpect(jsonPath("\$[0].submissionId").value("header1")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) val unalignedNucleotideSequences = convenienceClient.extractUnprocessedData(organism = OTHER_ORGANISM)[0] @@ -650,7 +660,9 @@ class SubmitEndpointTest( .andExpect(content().contentType(APPLICATION_JSON_VALUE)) .andExpect(jsonPath("\$.length()").value(2)) .andExpect(jsonPath("\$[0].submissionId").value("header1")) - .andExpect(jsonPath("\$[0].accession", containsString(backendConfig.accessionPrefix))) + .andExpect( + jsonPath("\$[0].accession", containsString(configService.getInstanceConfig().config.accessionPrefix)), + ) .andExpect(jsonPath("\$[0].version").value(1)) val unalignedNucleotideSequences = convenienceClient.extractUnprocessedData(organism = OTHER_ORGANISM)[0] diff --git a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitProcessedDataEndpointTest.kt b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitProcessedDataEndpointTest.kt index bfa72ea650..a52cedad69 100644 --- a/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitProcessedDataEndpointTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/controller/submission/SubmitProcessedDataEndpointTest.kt @@ -11,6 +11,7 @@ import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.hasEntry import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource @@ -22,7 +23,7 @@ import org.loculus.backend.api.Organism import org.loculus.backend.api.Status import org.loculus.backend.api.SubmittedProcessedData import org.loculus.backend.api.UnprocessedData -import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.fixtures.ConfigFixtures import org.loculus.backend.controller.DEFAULT_GROUP import org.loculus.backend.controller.DEFAULT_MULTIPART_FILE_PARTS import org.loculus.backend.controller.DEFAULT_ORGANISM @@ -30,7 +31,7 @@ import org.loculus.backend.controller.DEFAULT_SIMPLE_FILE_CONTENT import org.loculus.backend.controller.DUMMY_ORGANISM_MAIN_SEQUENCE import org.loculus.backend.controller.EndpointTest import org.loculus.backend.controller.OTHER_ORGANISM -import org.loculus.backend.controller.S3_CONFIG +import org.loculus.backend.controller.S3_VARIANT import org.loculus.backend.controller.assertHasError import org.loculus.backend.controller.assertStatusIs import org.loculus.backend.controller.expectForbiddenResponse @@ -58,9 +59,7 @@ import java.net.http.HttpRequest import java.net.http.HttpResponse import java.util.UUID -@EndpointTest( - properties = ["${BackendSpringProperty.BACKEND_CONFIG_PATH}=$S3_CONFIG"], -) +@EndpointTest class SubmitProcessedDataEndpointTest( @Autowired val submissionControllerClient: SubmissionControllerClient, @Autowired val convenienceClient: SubmissionConvenienceClient, @@ -68,8 +67,14 @@ class SubmitProcessedDataEndpointTest( @Autowired val useNewerProcessingPipelineVersionTask: UseNewerProcessingPipelineVersionTask, @Autowired val submissionDatabaseService: SubmissionDatabaseService, @Autowired val objectMapper: ObjectMapper, + @Autowired private val configFixtures: ConfigFixtures, ) { + @BeforeEach + fun loadS3Fixture() { + configFixtures.loadVariant(S3_VARIANT) + } + @Autowired private lateinit var filesClient: FilesClient diff --git a/backend/src/test/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberServiceTest.kt b/backend/src/test/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberServiceTest.kt index 211f496df7..209c176e49 100644 --- a/backend/src/test/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberServiceTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/service/GenerateAccessionFromNumberServiceTest.kt @@ -1,25 +1,38 @@ package org.loculus.backend.service +import io.mockk.every +import io.mockk.mockk +import kotlinx.datetime.LocalDateTime import org.hamcrest.CoreMatchers.`is` import org.hamcrest.MatcherAssert.assertThat import org.junit.jupiter.api.Test -import org.loculus.backend.config.BackendConfig import org.loculus.backend.config.DataUseTerms +import org.loculus.backend.config.FileSharing +import org.loculus.backend.config.InstanceConfig +import org.loculus.backend.config.service.ConfigService import java.lang.Math.random import kotlin.math.pow const val PREFIX = "LOC_" class GenerateAccessionFromNumberServiceTest { - private val accessionFromNumberService = GenerateAccessionFromNumberService( - BackendConfig( - websiteUrl = "https://example.com", - backendUrl = "http://foo.com", - accessionPrefix = PREFIX, - organisms = emptyMap(), - dataUseTerms = DataUseTerms(true, null), - ), - ) + private val configService: ConfigService = mockk() + + init { + every { configService.getInstanceConfig() } returns ConfigService.VersionedInstance( + version = 1L, + publishedAt = LocalDateTime(2024, 1, 1, 0, 0), + publishedBy = "test", + config = InstanceConfig( + name = "Loculus", + accessionPrefix = PREFIX, + dataUseTerms = DataUseTerms(true, null), + fileSharing = FileSharing(), + ), + ) + } + + private val accessionFromNumberService = GenerateAccessionFromNumberService(configService) @Test fun `GIVEN sequence numbers and prefix THEN returns custom ids that are padded to 6 digits`() { diff --git a/backend/src/test/kotlin/org/loculus/backend/service/ProcessedMetadataPostprocessorTest.kt b/backend/src/test/kotlin/org/loculus/backend/service/ProcessedMetadataPostprocessorTest.kt index 2213082e02..de1ff71041 100644 --- a/backend/src/test/kotlin/org/loculus/backend/service/ProcessedMetadataPostprocessorTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/service/ProcessedMetadataPostprocessorTest.kt @@ -2,35 +2,54 @@ package org.loculus.backend.service import com.fasterxml.jackson.databind.node.NullNode import com.fasterxml.jackson.databind.node.TextNode +import io.mockk.every +import io.mockk.mockk +import kotlinx.datetime.LocalDateTime import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.hasKey import org.hamcrest.Matchers.not import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import org.loculus.backend.SpringBootTestWithoutDatabase import org.loculus.backend.api.Organism import org.loculus.backend.api.ProcessedData -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.Metadata +import org.loculus.backend.config.MetadataType +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.ReferenceGenome +import org.loculus.backend.config.Schema +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.service.submission.ProcessedMetadataPostprocessor -import org.springframework.beans.factory.annotation.Autowired -@SpringBootTestWithoutDatabase -class ProcessedMetadataPostprocessorTest( - @Autowired private val processedMetadataPostprocessor: ProcessedMetadataPostprocessor, - @Autowired private val backendConfig: BackendConfig, -) { +class ProcessedMetadataPostprocessorTest { + + private val configService: ConfigService = mockk() + private val processedMetadataPostprocessor = ProcessedMetadataPostprocessor(configService) @Test fun `Processed Metadata Postprocessor correctly round trips metadata`() { - val organism = Organism(backendConfig.organisms.keys.first()) - val configuredFields = backendConfig.getInstanceConfig(organism).schema.metadata.map { it.name } - require(configuredFields.size >= 2) { "Test requires at least 2 configured metadata fields" } - - val configuredPresent = configuredFields[0] - val configuredNull = configuredFields[1] + val organism = Organism("dummy") + val configuredPresent = "country" + val configuredNull = "date" val unconfiguredPresent = "unconfigured_present" val unconfiguredNull = "unconfigured_null" + every { configService.getOrganismConfig(organism) } returns ConfigService.VersionedOrganism( + key = organism.name, + version = 1L, + publishedAt = LocalDateTime(2024, 1, 1, 0, 0), + publishedBy = "test", + config = OrganismConfig( + schema = Schema( + organismName = "Test", + metadata = listOf( + Metadata(name = configuredPresent, type = MetadataType.STRING), + Metadata(name = configuredNull, type = MetadataType.DATE), + ), + ), + referenceGenome = ReferenceGenome(emptyList(), emptyList()), + ), + ) + val testData = ProcessedData( metadata = mapOf( configuredPresent to TextNode("value1"), @@ -50,14 +69,12 @@ class ProcessedMetadataPostprocessorTest( val condensed = processedMetadataPostprocessor.stripNullValuesFromMetadata(testData) val expanded = processedMetadataPostprocessor.filterOutExtraFieldsAndAddNulls(condensed, organism) - // Check storage assertThat(condensed.metadata, not(hasKey(configuredNull))) assertThat(condensed.metadata, not(hasKey(unconfiguredNull))) assertThat(condensed.metadata, hasKey(configuredPresent)) assertThat(condensed.metadata, hasKey(unconfiguredPresent)) assertEquals(condensed.metadata[configuredPresent], testData.metadata[configuredPresent]) - // Check storage retrieval assertEquals(expanded.metadata[configuredPresent], testData.metadata[configuredPresent]) assertEquals(expanded.metadata[configuredNull], testData.metadata[configuredNull]) assertThat(expanded.metadata, not(hasKey(unconfiguredPresent))) diff --git a/backend/src/test/kotlin/org/loculus/backend/service/ProcessedSequencesPostprocessorTest.kt b/backend/src/test/kotlin/org/loculus/backend/service/ProcessedSequencesPostprocessorTest.kt index 5d77d31d45..b8fb87f9cf 100644 --- a/backend/src/test/kotlin/org/loculus/backend/service/ProcessedSequencesPostprocessorTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/service/ProcessedSequencesPostprocessorTest.kt @@ -1,17 +1,22 @@ package org.loculus.backend.service +import io.mockk.every +import io.mockk.mockk +import kotlinx.datetime.LocalDateTime import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.hasKey import org.hamcrest.Matchers.not import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import org.loculus.backend.SpringBootTestWithoutDatabase import org.loculus.backend.api.Insertion import org.loculus.backend.api.Organism import org.loculus.backend.api.ProcessedData -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.ReferenceGenome +import org.loculus.backend.config.ReferenceSequence +import org.loculus.backend.config.Schema +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.service.submission.ProcessedSequencesPostprocessor -import org.springframework.beans.factory.annotation.Autowired fun assertMapStorage(actual: Map, expected: Map, presentSeg: K, absentSegs: List) { absentSegs.forEach { key -> @@ -30,32 +35,41 @@ fun assertMapRetrieval(actual: Map, expected: Map, presentKey } } -@SpringBootTestWithoutDatabase -class ProcessedSequencesPostprocessorTest( - @Autowired private val processedSequencesPostprocessor: ProcessedSequencesPostprocessor, - @Autowired private val backendConfig: BackendConfig, -) { +class ProcessedSequencesPostprocessorTest { + + private val configService: ConfigService = mockk() + private val processedSequencesPostprocessor = ProcessedSequencesPostprocessor(configService) @Test fun `Processed Sequences Postprocessor correctly round trips sequences`() { - val organism = Organism("otherOrganism") - val configuredSequences = backendConfig.getInstanceConfig(organism).referenceGenome.nucleotideSequences - .map { it.name } - .sorted() - require(configuredSequences.size >= 2) { "Test requires at least 2 configured sequences" } + val organism = Organism("multiSegment") + val configuredPresentSeg = "seg1" + val configuredNullSeg = "seg2" + val configuredPresentGene = "gene1" + val configuredNullGene = "gene2" - val configuredGenes = backendConfig.getInstanceConfig(organism).referenceGenome.genes - .map { it.name } - .sorted() - require(configuredGenes.size >= 2) { "Test requires at least 2 configured genes" } + every { configService.getOrganismConfig(organism) } returns ConfigService.VersionedOrganism( + key = organism.name, + version = 1L, + publishedAt = LocalDateTime(2024, 1, 1, 0, 0), + publishedBy = "test", + config = OrganismConfig( + schema = Schema(organismName = "Test", metadata = emptyList()), + referenceGenome = ReferenceGenome( + nucleotideSequences = listOf( + ReferenceSequence(configuredPresentSeg, "AAAA"), + ReferenceSequence(configuredNullSeg, "CCCC"), + ), + genes = listOf( + ReferenceSequence(configuredPresentGene, "MMMM"), + ReferenceSequence(configuredNullGene, "AAAA"), + ), + ), + ), + ) - val configuredPresentSeg = configuredSequences[0] - val configuredNullSeg = configuredSequences[1] val unconfiguredPresentSeg = "unconfigured_present" val unconfiguredNullSeg = "unconfigured_null" - - val configuredPresentGene = configuredGenes[0] - val configuredNullGene = configuredGenes[1] val unconfiguredPresentGene = "unconfigured_present" val unconfiguredNullGene = "unconfigured_null" @@ -111,7 +125,6 @@ class ProcessedSequencesPostprocessorTest( val retrievalPresentGenes = listOf(configuredPresentGene, configuredNullGene) val retrievalAbsentGenes = listOf(unconfiguredPresentGene, unconfiguredNullGene) - // Check storage assertMapStorage( condensed.unalignedNucleotideSequences, testData.unalignedNucleotideSequences, @@ -133,7 +146,6 @@ class ProcessedSequencesPostprocessorTest( ) assertMapStorage(condensed.aminoAcidInsertions, testData.aminoAcidInsertions, presentGene, absentGenes) - // Check retrieval assertMapRetrieval( expanded.unalignedNucleotideSequences, testData.unalignedNucleotideSequences, diff --git a/backend/src/test/kotlin/org/loculus/backend/service/files/EnabledS3ServiceTest.kt b/backend/src/test/kotlin/org/loculus/backend/service/files/EnabledS3ServiceTest.kt index cea84ec62c..edf97a28ee 100644 --- a/backend/src/test/kotlin/org/loculus/backend/service/files/EnabledS3ServiceTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/service/files/EnabledS3ServiceTest.kt @@ -3,8 +3,11 @@ package org.loculus.backend.service.files import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.loculus.backend.config.BackendSpringProperty +import org.loculus.backend.config.S3BucketConfig +import org.loculus.backend.config.S3Config import org.loculus.backend.controller.EndpointTest import org.springframework.beans.factory.annotation.Autowired +import java.net.URI import java.util.UUID @EndpointTest( @@ -18,4 +21,27 @@ class EnabledS3ServiceTest(@Autowired val s3Service: S3Service) { s3Service.createUrlToUploadPrivateFile(UUID.randomUUID()) } } + + @Test + fun `WHEN calling createUrlToUploadPrivateFile with internal endpoint THEN URL uses internal endpoint`() { + val service = S3Service( + S3Config( + true, + S3BucketConfig( + endpoint = "http://external.example", + internalEndpoint = "http://internal.example", + region = "us-east-1", + bucket = "bucket", + accessKey = "access", + secretKey = "secret", + ), + ), + ) + + val externalUrl = service.createUrlToUploadPrivateFile(UUID.randomUUID(), useInternalEndpoint = false) + val internalUrl = service.createUrlToUploadPrivateFile(UUID.randomUUID(), useInternalEndpoint = true) + + org.junit.jupiter.api.Assertions.assertEquals("external.example", URI.create(externalUrl).host) + org.junit.jupiter.api.Assertions.assertEquals("internal.example", URI.create(internalUrl).host) + } } diff --git a/backend/src/test/kotlin/org/loculus/backend/service/submission/CompressionDictServiceTest.kt b/backend/src/test/kotlin/org/loculus/backend/service/submission/CompressionDictServiceTest.kt index 6da4f4c223..b4757c9ab5 100644 --- a/backend/src/test/kotlin/org/loculus/backend/service/submission/CompressionDictServiceTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/service/submission/CompressionDictServiceTest.kt @@ -4,6 +4,14 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.`is` import org.junit.jupiter.api.Test import org.loculus.backend.api.Organism +import org.loculus.backend.config.Metadata +import org.loculus.backend.config.MetadataType +import org.loculus.backend.config.OrganismConfig +import org.loculus.backend.config.ReferenceGenome +import org.loculus.backend.config.ReferenceSequence +import org.loculus.backend.config.Schema +import org.loculus.backend.config.service.DraftService +import org.loculus.backend.config.service.OrganismAdminService import org.loculus.backend.controller.DEFAULT_ORGANISM import org.loculus.backend.controller.DUMMY_ORGANISM_MAIN_SEQUENCE import org.loculus.backend.controller.EndpointTest @@ -11,7 +19,11 @@ import org.loculus.backend.controller.OTHER_ORGANISM import org.springframework.beans.factory.annotation.Autowired @EndpointTest -class CompressionDictServiceTest(@Autowired private val underTest: CompressionDictService) { +class CompressionDictServiceTest( + @Autowired private val underTest: CompressionDictService, + @Autowired private val organismAdminService: OrganismAdminService, + @Autowired private val draftService: DraftService, +) { @Test fun `gets dict by segment name and id`() { val bySegment = underTest.getDictForSegmentOrGene(Organism(DEFAULT_ORGANISM), "main")!! @@ -33,4 +45,31 @@ class CompressionDictServiceTest(@Autowired private val underTest: CompressionDi assertThat(byId, `is`(forUnalignedSequence.dict)) } + + @Test + fun `gets dict for organism published after cache population`() { + underTest.getDictForSegmentOrGene(Organism(DEFAULT_ORGANISM), "main") + + organismAdminService.createOrganism("runtimeOrganism", "test") + draftService.putOrganismDraft("runtimeOrganism", runtimeOrganismConfig, null, "test") + draftService.publishOrganism("runtimeOrganism", "test") + + val organism = Organism("runtimeOrganism") + val bySegment = underTest.getDictForSegmentOrGene(organism, "main")!! + val unaligned = underTest.getDictForUnalignedSequence(organism)!! + + assertThat(bySegment.dict, `is`("GATTACA".toByteArray())) + assertThat(unaligned.dict, `is`("GATTACA".toByteArray())) + } + + private val runtimeOrganismConfig = OrganismConfig( + schema = Schema( + organismName = "Runtime organism", + metadata = listOf(Metadata(name = "date", type = MetadataType.DATE, required = true)), + ), + referenceGenome = ReferenceGenome( + nucleotideSequences = listOf(ReferenceSequence("main", "GATTACA")), + genes = emptyList(), + ), + ) } diff --git a/backend/src/test/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProviderTest.kt b/backend/src/test/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProviderTest.kt index 3785f3af8f..2dadb27910 100644 --- a/backend/src/test/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProviderTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/service/submission/EmptyProcessedDataProviderTest.kt @@ -1,18 +1,20 @@ package org.loculus.backend.service.submission import com.fasterxml.jackson.databind.node.NullNode +import io.mockk.every +import io.mockk.mockk +import kotlinx.datetime.LocalDateTime import org.hamcrest.CoreMatchers.`is` import org.hamcrest.MatcherAssert.assertThat import org.junit.jupiter.api.Test import org.loculus.backend.api.Organism -import org.loculus.backend.config.BackendConfig -import org.loculus.backend.config.DataUseTerms -import org.loculus.backend.config.InstanceConfig import org.loculus.backend.config.Metadata import org.loculus.backend.config.MetadataType +import org.loculus.backend.config.OrganismConfig import org.loculus.backend.config.ReferenceGenome import org.loculus.backend.config.ReferenceSequence import org.loculus.backend.config.Schema +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.DEFAULT_ORGANISM private const val FIRST_METADATA_FIELD = "required" @@ -23,14 +25,20 @@ private const val FIRST_AMINO_ACID_SEQUENCE = "firstAminoAcidSequence" private const val SECOND_AMINO_ACID_SEQUENCE = "secondAminoAcidSequence" class EmptyProcessedDataProviderTest { - private val underTest = EmptyProcessedDataProvider( - BackendConfig( - accessionPrefix = "LOC_", - organisms = mapOf( - DEFAULT_ORGANISM to InstanceConfig( + private val configService: ConfigService = mockk() + private val underTest = EmptyProcessedDataProvider(configService) + + init { + every { configService.getOrganismConfig(Organism(DEFAULT_ORGANISM)) } returns + ConfigService.VersionedOrganism( + key = DEFAULT_ORGANISM, + version = 1L, + publishedAt = LocalDateTime(2024, 1, 1, 0, 0), + publishedBy = "test", + config = OrganismConfig( schema = Schema( - FIRST_NUCLEOTIDE_SEQUENCE, - listOf( + organismName = FIRST_NUCLEOTIDE_SEQUENCE, + metadata = listOf( Metadata(name = FIRST_METADATA_FIELD, type = MetadataType.STRING, required = true), Metadata(name = SECOND_METADATA_FIELD, type = MetadataType.DATE, required = false), ), @@ -46,12 +54,8 @@ class EmptyProcessedDataProviderTest { ), ), ), - ), - dataUseTerms = DataUseTerms(true, null), - websiteUrl = "example.com", - backendUrl = "http://dummy-backend.com", - ), - ) + ) + } @Test fun `GIVEN backend config for schema THEN returns processed data with all fields and sequences empty`() { diff --git a/backend/src/test/kotlin/org/loculus/backend/service/submission/ValidateFileNameTest.kt b/backend/src/test/kotlin/org/loculus/backend/service/submission/ValidateFileNameTest.kt index 92c839462e..a3d31a1704 100644 --- a/backend/src/test/kotlin/org/loculus/backend/service/submission/ValidateFileNameTest.kt +++ b/backend/src/test/kotlin/org/loculus/backend/service/submission/ValidateFileNameTest.kt @@ -6,17 +6,17 @@ import org.junit.jupiter.api.assertThrows import org.loculus.backend.api.FileCategory import org.loculus.backend.api.FileCategoryFilesMap import org.loculus.backend.api.FileIdAndName -import org.loculus.backend.config.BackendConfig +import org.loculus.backend.config.service.ConfigService import org.loculus.backend.controller.UnprocessableEntityException import org.loculus.backend.service.files.FilesDatabaseService import org.loculus.backend.service.files.S3Service import java.util.UUID class ValidateFileNameTest { - private val backendConfig: BackendConfig = mockk() + private val configService: ConfigService = mockk() private val s3Service: S3Service = mockk() private val filesDatabaseService: FilesDatabaseService = mockk() - private val validator = FileMappingPreconditionValidator(backendConfig, s3Service, filesDatabaseService) + private val validator = FileMappingPreconditionValidator(configService, s3Service, filesDatabaseService) private fun createFileMapping(category: FileCategory, filenames: List): FileCategoryFilesMap { val files = filenames.map { FileIdAndName(UUID.randomUUID(), it) } diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties index 2eae6c6bda..0a58a40026 100644 --- a/backend/src/test/resources/application.properties +++ b/backend/src/test/resources/application.properties @@ -1,5 +1,6 @@ spring.config.import=file:src/main/resources/application.properties -loculus.config.path=src/test/resources/backend_config.json +loculus.backend.website-url=https://example.com +loculus.backend.backend-url=http://dummy-backend.com loculus.enable-seqsets=true crossref.endpoint=dummy diff --git a/backend/src/test/resources/backend_config.json b/backend/src/test/resources/backend_config.json deleted file mode 100644 index 8ecc50dfa0..0000000000 --- a/backend/src/test/resources/backend_config.json +++ /dev/null @@ -1,238 +0,0 @@ -{ - "accessionPrefix": "LOC_", - "websiteUrl": "https://example.com", - "backendUrl": "http://dummy-backend.com", - "organisms": { - "dummyOrganism": { - "referenceGenome": { - "nucleotideSequences": [ - { - "name": "main", - "sequence": "ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCT" - } - ], - "genes": [ - { - "name": "someLongGene", - "sequence": "AAAAAAAAAAAAAAAAAAAAAAAAA" - }, - { - "name": "someShortGene", - "sequence": "MADS" - } - ] - }, - "schema": { - "organismName": "Test", - "allowSubmissionOfConsensusSequences": true, - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "dateSubmitted", - "type": "date" - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - }, - { - "name": "age", - "type": "int" - }, - { - "name": "sex", - "type": "string", - "autocomplete": true - }, - { - "name": "pangoLineage", - "type": "string", - "autocomplete": true - }, - { - "name": "qc", - "type": "float" - }, - { - "name": "booleanColumn", - "type": "boolean" - }, - { - "name": "insdcAccessionFull", - "type": "string" - }, - { - "name": "other_db_accession", - "type": "string" - } - ], - "externalMetadata": [ - { - "name": "insdcAccessionFull", - "type": "string", - "externalMetadataUpdater": "ena" - }, - { - "name": "other_db_accession", - "type": "string", - "externalMetadataUpdater": "other_db" - } - ] - } - }, - "otherOrganism": { - "referenceGenome": { - "nucleotideSequences": [ - { - "name": "notOnlySegment", - "sequence": "ATCG" - }, - { - "name": "secondSegment", - "sequence": "AAAAAAAAAAAAAAAA" - } - ], - "genes": [ - { - "name": "someLongGene", - "sequence": "AAAAAAAAAAAAAAAAAAAAAAAAA" - }, - { - "name": "someShortGene", - "sequence": "MADS" - } - ] - }, - "schema": { - "organismName": "Test", - "allowSubmissionOfConsensusSequences": true, - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "dateSubmitted", - "type": "date" - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "specialOtherField", - "type": "string", - "required": false - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - }, - { - "name": "age", - "type": "int" - }, - { - "name": "sex", - "type": "string", - "autocomplete": true - }, - { - "name": "pangoLineage", - "type": "string", - "autocomplete": true - }, - { - "name": "qc", - "type": "float" - } - ] - } - }, - "dummyOrganismWithoutConsensusSequences": { - "referenceGenome": { - "nucleotideSequences": [], - "genes": [] - }, - "schema": { - "organismName": "Test without consensus sequences", - "submissionDataTypes": { - "consensusSequences": false - }, - "files": [], - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - } - ] - } - } - }, - "dataUseTerms": { - "enabled": true - }, - "s3": { - "enabled": false - } -} diff --git a/backend/src/test/resources/backend_config_data_use_terms_disabled.json b/backend/src/test/resources/backend_config_data_use_terms_disabled.json deleted file mode 100644 index 7cc9c8ab56..0000000000 --- a/backend/src/test/resources/backend_config_data_use_terms_disabled.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "accessionPrefix": "LOC_", - "websiteUrl": "https://example.com", - "backendUrl": "http://dummy-backend.com", - "organisms": { - "dummyOrganism": { - "referenceGenome": { - "nucleotideSequences": [ - { - "name": "main", - "sequence": "ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCT" - } - ], - "genes": [ - { - "name": "someLongGene", - "sequence": "AAAAAAAAAAAAAAAAAAAAAAAAA" - }, - { - "name": "someShortGene", - "sequence": "MADS" - } - ] - }, - "schema": { - "organismName": "Test", - "allowSubmissionOfConsensusSequences": true, - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "dateSubmitted", - "type": "date" - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - }, - { - "name": "age", - "type": "int" - }, - { - "name": "sex", - "type": "string", - "autocomplete": true - }, - { - "name": "pangoLineage", - "type": "string", - "autocomplete": true - }, - { - "name": "qc", - "type": "float" - }, - { - "name": "booleanColumn", - "type": "boolean" - }, - { - "name": "insdcAccessionFull", - "type": "string" - }, - { - "name": "other_db_accession", - "type": "string" - } - ], - "externalMetadata": [ - { - "name": "insdcAccessionFull", - "type": "string", - "externalMetadataUpdater": "ena" - }, - { - "name": "other_db_accession", - "type": "string", - "externalMetadataUpdater": "other_db" - } - ] - } - } - }, - "dataUseTerms": { - "enabled": false - }, - "s3": { - "enabled": false - } -} diff --git a/backend/src/test/resources/backend_config_s3.json b/backend/src/test/resources/backend_config_s3.json deleted file mode 100644 index 0d962e4499..0000000000 --- a/backend/src/test/resources/backend_config_s3.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "accessionPrefix": "LOC_", - "websiteUrl": "https://example.com", - "backendUrl": "http://dummy-backend.com", - "organisms": { - "dummyOrganism": { - "referenceGenome": { - "nucleotideSequences": [ - { - "name": "main", - "sequence": "ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCT" - } - ], - "genes": [ - { - "name": "someLongGene", - "sequence": "AAAAAAAAAAAAAAAAAAAAAAAAA" - }, - { - "name": "someShortGene", - "sequence": "MADS" - } - ] - }, - "schema": { - "organismName": "Test", - "allowSubmissionOfConsensusSequences": true, - "submissionDataTypes": { - "consensusSequences": true, - "files": { - "enabled": true, - "categories": [ - { - "name": "myFileCategory" - }, - { - "name": "myOtherFileCategory" - } - ] - } - }, - "files": [ - { - "name": "myFileCategory" - }, - { - "name": "myOtherFileCategory" - }, - { - "name": "myProcessedOnlyFileCategory" - } - ], - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "dateSubmitted", - "type": "date" - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - }, - { - "name": "age", - "type": "int" - }, - { - "name": "sex", - "type": "string", - "autocomplete": true - }, - { - "name": "pangoLineage", - "type": "string", - "autocomplete": true - }, - { - "name": "qc", - "type": "float" - }, - { - "name": "booleanColumn", - "type": "boolean" - }, - { - "name": "insdcAccessionFull", - "type": "string" - }, - { - "name": "other_db_accession", - "type": "string" - } - ], - "externalMetadata": [ - { - "name": "insdcAccessionFull", - "type": "string", - "externalMetadataUpdater": "ena" - }, - { - "name": "other_db_accession", - "type": "string", - "externalMetadataUpdater": "other_db" - } - ] - } - }, - "otherOrganism": { - "referenceGenome": { - "nucleotideSequences": [ - { - "name": "notOnlySegment", - "sequence": "ATCG" - }, - { - "name": "secondSegment", - "sequence": "AAAAAAAAAAAAAAAA" - } - ], - "genes": [ - { - "name": "someLongGene", - "sequence": "AAAAAAAAAAAAAAAAAAAAAAAAA" - }, - { - "name": "someShortGene", - "sequence": "MADS" - } - ] - }, - "schema": { - "organismName": "Test", - "allowSubmissionOfConsensusSequences": true, - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "dateSubmitted", - "type": "date" - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "specialOtherField", - "type": "string", - "required": false - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - }, - { - "name": "age", - "type": "int" - }, - { - "name": "sex", - "type": "string", - "autocomplete": true - }, - { - "name": "pangoLineage", - "type": "string", - "autocomplete": true - }, - { - "name": "qc", - "type": "float" - } - ] - } - }, - "dummyOrganismWithoutConsensusSequences": { - "referenceGenome": { - "nucleotideSequences": [], - "genes": [] - }, - "schema": { - "organismName": "Test without consensus sequences", - "submissionDataTypes": { - "consensusSequences": false - }, - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - } - ] - } - } - }, - "dataUseTerms": { - "enabled": true - }, - "fileSharing": { - "outputFileUrlType": "s3" - }, - "s3": { - "enabled": true, - "bucket": { - "endpoint": "dummyendpoint.com", - "region": "dummyregion", - "bucket": "dummybucket", - "accessKey": "dummyaccesskey", - "secretKey": "dummysecretkey" - } - } -} diff --git a/backend/src/test/resources/backend_config_single_segment.json b/backend/src/test/resources/backend_config_single_segment.json deleted file mode 100644 index a55a66479c..0000000000 --- a/backend/src/test/resources/backend_config_single_segment.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "accessionPrefix": "LOC_", - "websiteUrl": "https://example.com", - "backendUrl": "http://dummy-backend.com", - "dataUseTerms": { - "enabled": true - }, - "organisms": { - "dummyOrganism": { - "referenceGenome": { - "nucleotideSequences": [ - { - "name": "main", - "sequence": "ACGT" - } - ], - "genes": [ - { - "name": "someLongGene", - "sequence": "AAAAAAAAAAAAAAAAAAAAAAAAA" - }, - { - "name": "someShortGene", - "sequence": "MADS" - } - ] - }, - "schema": { - "organismName": "Test", - "metadata": [ - { - "name": "date", - "type": "date", - "required": true - }, - { - "name": "dateSubmitted", - "type": "date" - }, - { - "name": "region", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "country", - "type": "string", - "autocomplete": true, - "required": true - }, - { - "name": "division", - "type": "string", - "autocomplete": true - }, - { - "name": "host", - "type": "string", - "autocomplete": true - }, - { - "name": "age", - "type": "int" - }, - { - "name": "sex", - "type": "string", - "autocomplete": true - }, - { - "name": "pangoLineage", - "type": "string", - "autocomplete": true - }, - { - "name": "qc", - "type": "float" - } - ] - } - } - }, - "s3": { - "enabled": false - } -} diff --git a/backend/src/test/resources/fixtures/data-use-terms-disabled/instance.yaml b/backend/src/test/resources/fixtures/data-use-terms-disabled/instance.yaml new file mode 100644 index 0000000000..3d5e87191b --- /dev/null +++ b/backend/src/test/resources/fixtures/data-use-terms-disabled/instance.yaml @@ -0,0 +1,7 @@ +name: Loculus +accessionPrefix: LOC_ +dataUseTerms: + enabled: false + urls: null +fileSharing: + outputFileUrlType: website diff --git a/backend/src/test/resources/fixtures/data-use-terms-disabled/organisms/dummyOrganism.yaml b/backend/src/test/resources/fixtures/data-use-terms-disabled/organisms/dummyOrganism.yaml new file mode 100644 index 0000000000..b392bd7fe5 --- /dev/null +++ b/backend/src/test/resources/fixtures/data-use-terms-disabled/organisms/dummyOrganism.yaml @@ -0,0 +1,28 @@ +schema: + organismName: Test + metadata: + - {name: date, type: date, required: true} + - {name: dateSubmitted, type: date} + - {name: region, type: string, autocomplete: true, required: true} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} + - {name: age, type: int} + - {name: sex, type: string, autocomplete: true} + - {name: pangoLineage, type: string, autocomplete: true} + - {name: qc, type: float} + - {name: booleanColumn, type: boolean} + - {name: insdcAccessionFull, type: string} + - {name: other_db_accession, type: string} + externalMetadata: + - {name: insdcAccessionFull, type: string, externalMetadataUpdater: ena} + - {name: other_db_accession, type: string, externalMetadataUpdater: other_db} +referenceGenome: + nucleotideSequences: + - name: main + sequence: ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCT + genes: + - name: someLongGene + sequence: AAAAAAAAAAAAAAAAAAAAAAAAA + - name: someShortGene + sequence: MADS diff --git a/backend/src/test/resources/fixtures/default/instance.yaml b/backend/src/test/resources/fixtures/default/instance.yaml new file mode 100644 index 0000000000..bb956e80ba --- /dev/null +++ b/backend/src/test/resources/fixtures/default/instance.yaml @@ -0,0 +1,7 @@ +name: Loculus +accessionPrefix: LOC_ +dataUseTerms: + enabled: true + urls: null +fileSharing: + outputFileUrlType: website diff --git a/backend/src/test/resources/fixtures/default/organisms/dummyOrganism.yaml b/backend/src/test/resources/fixtures/default/organisms/dummyOrganism.yaml new file mode 100644 index 0000000000..278797c6f6 --- /dev/null +++ b/backend/src/test/resources/fixtures/default/organisms/dummyOrganism.yaml @@ -0,0 +1,29 @@ +schema: + organismName: Test + metadata: + - {name: date, type: date, required: true} + - {name: dateSubmitted, type: date} + - {name: region, type: string, autocomplete: true, required: true} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} + - {name: age, type: int} + - {name: sex, type: string, autocomplete: true} + - {name: pangoLineage, type: string, autocomplete: true} + - {name: qc, type: float} + - {name: booleanColumn, type: boolean} + - {name: insdcAccessionFull, type: string} + - {name: other_db_accession, type: string} + externalMetadata: + - {name: insdcAccessionFull, type: string, externalMetadataUpdater: ena} + - {name: other_db_accession, type: string, externalMetadataUpdater: other_db} +referenceGenome: + nucleotideSequences: + - name: main + sequence: ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCT + genes: + - name: someLongGene + sequence: AAAAAAAAAAAAAAAAAAAAAAAAA + - name: someShortGene + sequence: MADS +displayName: Displayed test organism diff --git a/backend/src/test/resources/fixtures/default/organisms/dummyOrganismWithoutConsensusSequences.yaml b/backend/src/test/resources/fixtures/default/organisms/dummyOrganismWithoutConsensusSequences.yaml new file mode 100644 index 0000000000..97607201fb --- /dev/null +++ b/backend/src/test/resources/fixtures/default/organisms/dummyOrganismWithoutConsensusSequences.yaml @@ -0,0 +1,14 @@ +schema: + organismName: Test without consensus sequences + submissionDataTypes: + consensusSequences: false + files: [] + metadata: + - {name: date, type: date, required: true} + - {name: region, type: string, autocomplete: true, required: true} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} +referenceGenome: + nucleotideSequences: [] + genes: [] diff --git a/backend/src/test/resources/fixtures/default/organisms/otherOrganism.yaml b/backend/src/test/resources/fixtures/default/organisms/otherOrganism.yaml new file mode 100644 index 0000000000..215208135e --- /dev/null +++ b/backend/src/test/resources/fixtures/default/organisms/otherOrganism.yaml @@ -0,0 +1,25 @@ +schema: + organismName: Test + metadata: + - {name: date, type: date, required: true} + - {name: dateSubmitted, type: date} + - {name: region, type: string, autocomplete: true, required: true} + - {name: specialOtherField, type: string, required: false} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} + - {name: age, type: int} + - {name: sex, type: string, autocomplete: true} + - {name: pangoLineage, type: string, autocomplete: true} + - {name: qc, type: float} +referenceGenome: + nucleotideSequences: + - name: notOnlySegment + sequence: ATCG + - name: secondSegment + sequence: AAAAAAAAAAAAAAAA + genes: + - name: someLongGene + sequence: AAAAAAAAAAAAAAAAAAAAAAAAA + - name: someShortGene + sequence: MADS diff --git a/backend/src/test/resources/fixtures/s3/instance.yaml b/backend/src/test/resources/fixtures/s3/instance.yaml new file mode 100644 index 0000000000..737a192f2f --- /dev/null +++ b/backend/src/test/resources/fixtures/s3/instance.yaml @@ -0,0 +1,7 @@ +name: Loculus +accessionPrefix: LOC_ +dataUseTerms: + enabled: true + urls: null +fileSharing: + outputFileUrlType: s3 diff --git a/backend/src/test/resources/fixtures/s3/organisms/dummyOrganism.yaml b/backend/src/test/resources/fixtures/s3/organisms/dummyOrganism.yaml new file mode 100644 index 0000000000..ae5527a159 --- /dev/null +++ b/backend/src/test/resources/fixtures/s3/organisms/dummyOrganism.yaml @@ -0,0 +1,39 @@ +schema: + organismName: Test + submissionDataTypes: + consensusSequences: true + files: + enabled: true + categories: + - {name: myFileCategory} + - {name: myOtherFileCategory} + files: + - {name: myFileCategory} + - {name: myOtherFileCategory} + - {name: myProcessedOnlyFileCategory} + metadata: + - {name: date, type: date, required: true} + - {name: dateSubmitted, type: date} + - {name: region, type: string, autocomplete: true, required: true} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} + - {name: age, type: int} + - {name: sex, type: string, autocomplete: true} + - {name: pangoLineage, type: string, autocomplete: true} + - {name: qc, type: float} + - {name: booleanColumn, type: boolean} + - {name: insdcAccessionFull, type: string} + - {name: other_db_accession, type: string} + externalMetadata: + - {name: insdcAccessionFull, type: string, externalMetadataUpdater: ena} + - {name: other_db_accession, type: string, externalMetadataUpdater: other_db} +referenceGenome: + nucleotideSequences: + - name: main + sequence: ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCT + genes: + - name: someLongGene + sequence: AAAAAAAAAAAAAAAAAAAAAAAAA + - name: someShortGene + sequence: MADS diff --git a/backend/src/test/resources/fixtures/s3/organisms/dummyOrganismWithoutConsensusSequences.yaml b/backend/src/test/resources/fixtures/s3/organisms/dummyOrganismWithoutConsensusSequences.yaml new file mode 100644 index 0000000000..79f8279691 --- /dev/null +++ b/backend/src/test/resources/fixtures/s3/organisms/dummyOrganismWithoutConsensusSequences.yaml @@ -0,0 +1,13 @@ +schema: + organismName: Test without consensus sequences + submissionDataTypes: + consensusSequences: false + metadata: + - {name: date, type: date, required: true} + - {name: region, type: string, autocomplete: true, required: true} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} +referenceGenome: + nucleotideSequences: [] + genes: [] diff --git a/backend/src/test/resources/fixtures/s3/organisms/otherOrganism.yaml b/backend/src/test/resources/fixtures/s3/organisms/otherOrganism.yaml new file mode 100644 index 0000000000..215208135e --- /dev/null +++ b/backend/src/test/resources/fixtures/s3/organisms/otherOrganism.yaml @@ -0,0 +1,25 @@ +schema: + organismName: Test + metadata: + - {name: date, type: date, required: true} + - {name: dateSubmitted, type: date} + - {name: region, type: string, autocomplete: true, required: true} + - {name: specialOtherField, type: string, required: false} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} + - {name: age, type: int} + - {name: sex, type: string, autocomplete: true} + - {name: pangoLineage, type: string, autocomplete: true} + - {name: qc, type: float} +referenceGenome: + nucleotideSequences: + - name: notOnlySegment + sequence: ATCG + - name: secondSegment + sequence: AAAAAAAAAAAAAAAA + genes: + - name: someLongGene + sequence: AAAAAAAAAAAAAAAAAAAAAAAAA + - name: someShortGene + sequence: MADS diff --git a/backend/src/test/resources/fixtures/single-segment/instance.yaml b/backend/src/test/resources/fixtures/single-segment/instance.yaml new file mode 100644 index 0000000000..bb956e80ba --- /dev/null +++ b/backend/src/test/resources/fixtures/single-segment/instance.yaml @@ -0,0 +1,7 @@ +name: Loculus +accessionPrefix: LOC_ +dataUseTerms: + enabled: true + urls: null +fileSharing: + outputFileUrlType: website diff --git a/backend/src/test/resources/fixtures/single-segment/organisms/dummyOrganism.yaml b/backend/src/test/resources/fixtures/single-segment/organisms/dummyOrganism.yaml new file mode 100644 index 0000000000..4994bfd009 --- /dev/null +++ b/backend/src/test/resources/fixtures/single-segment/organisms/dummyOrganism.yaml @@ -0,0 +1,22 @@ +schema: + organismName: Test + metadata: + - {name: date, type: date, required: true} + - {name: dateSubmitted, type: date} + - {name: region, type: string, autocomplete: true, required: true} + - {name: country, type: string, autocomplete: true, required: true} + - {name: division, type: string, autocomplete: true} + - {name: host, type: string, autocomplete: true} + - {name: age, type: int} + - {name: sex, type: string, autocomplete: true} + - {name: pangoLineage, type: string, autocomplete: true} + - {name: qc, type: float} +referenceGenome: + nucleotideSequences: + - name: main + sequence: ACGT + genes: + - name: someLongGene + sequence: AAAAAAAAAAAAAAAAAAAAAAAAA + - name: someShortGene + sequence: MADS diff --git a/backend/start_dev.sh b/backend/start_dev.sh index df18ded510..3571e5bcb1 100755 --- a/backend/start_dev.sh +++ b/backend/start_dev.sh @@ -4,7 +4,8 @@ args=$(printf "%s " \ "--spring.datasource.url=jdbc:postgresql://localhost:5432/loculus" \ "--spring.datasource.username=postgres" \ "--spring.datasource.password=unsecure" \ - "--loculus.config.path=../website/tests/config/backend_config.json" \ + "--loculus.backend.website-url=http://localhost:3000" \ + "--loculus.backend.backend-url=http://localhost:8079" \ "--loculus.debug-mode=true" \ "--loculus.enable-seqsets=true" \ "--spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8083/realms/loculus/protocol/openid-connect/certs" \ diff --git a/build-local-images.sh b/build-local-images.sh new file mode 100755 index 0000000000..adb1c849dc --- /dev/null +++ b/build-local-images.sh @@ -0,0 +1,210 @@ +#!/usr/bin/env bash +# +# Build every Loculus Docker image locally and import them into the running +# k3d cluster. Useful for running a fully local instance without depending on +# any image pushed to ghcr.io. +# +# Pairs with `./deploy.py helm --branch local --for-e2e --enablePreprocessing +# --enableIngest` — the chart's docker-tag helper turns `--branch local` into +# the literal tag `local`, so every container ends up running the image +# tagged here. +# +# Usage: +# ./build-local-images.sh # build + import everything +# ./build-local-images.sh --skip-import # build only (no k3d import) +# ./build-local-images.sh --dev # everything EXCEPT backend + website +# # (use when running those in your IDE +# # alongside `./deploy.py helm --dev`) +# ./build-local-images.sh backend website # build just the listed components +# +# Components (each is one Docker image): backend, website, config-loader, +# config-adapter, config-processor, loculus-silo, keycloakify, +# preprocessing-nextclade, preprocessing-dummy, ingest, ena-submission, +# ena-submission-flyway, taxonomy-service. +# +# The upstream LAPIS image (ghcr.io/genspectrum/lapis) is NOT built locally; +# k3d pulls it from the public registry on first use. + +set -euo pipefail + +TAG="local" +CLUSTER="${LOCULUS_K3D_CLUSTER:-testCluster}" +SKIP_IMPORT=0 +DEV_MODE=0 + +# Parse flags. +COMPONENTS=() +for arg in "$@"; do + case "$arg" in + --skip-import) SKIP_IMPORT=1 ;; + --dev) DEV_MODE=1 ;; + --help|-h) + sed -n '2,/^set -euo/{/^set -euo/d;p;}' "$0" | sed 's/^# \{0,1\}//' + exit 0 + ;; + *) COMPONENTS+=("$arg") ;; + esac +done + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT" + +ALL_COMPONENTS=( + backend + website + config-loader + config-adapter + config-processor + loculus-silo + keycloakify + preprocessing-nextclade + preprocessing-dummy + ingest + ena-submission + ena-submission-flyway + taxonomy-service +) + +# `--dev` is the IDE workflow: backend + website run on the host, not in the +# cluster, so we skip them by default. Explicit positional components still win. +DEV_COMPONENTS=( + config-loader + config-adapter + config-processor + loculus-silo + keycloakify + preprocessing-nextclade + preprocessing-dummy + ingest + ena-submission + ena-submission-flyway + taxonomy-service +) + +if [[ ${#COMPONENTS[@]} -eq 0 ]]; then + if [[ "$DEV_MODE" -eq 1 ]]; then + COMPONENTS=("${DEV_COMPONENTS[@]}") + else + COMPONENTS=("${ALL_COMPONENTS[@]}") + fi +fi + +build_backend() { + echo ">>> Building backend (Gradle bootJar + Docker)" + (cd backend && ./gradlew --no-daemon bootJar) + docker build -t "ghcr.io/loculus-project/backend:${TAG}" backend +} + +build_website() { + echo ">>> Building website" + # Website re-exports canonical Zod schemas from `../../../config-tools/...`, + # so the website Dockerfile pulls config-tools in via a named build context. + docker build \ + -t "ghcr.io/loculus-project/website:${TAG}" \ + --build-context config-tools=./config-tools \ + website +} + +build_config_loader() { + echo ">>> Building config-loader" + docker build -f config-tools/Dockerfile.loader \ + -t "ghcr.io/loculus-project/config-loader:${TAG}" config-tools +} + +build_config_adapter() { + echo ">>> Building config-adapter" + docker build -f config-tools/Dockerfile.adapter \ + -t "ghcr.io/loculus-project/config-adapter:${TAG}" config-tools +} + +build_config_processor() { + echo ">>> Building config-processor" + docker build -t "ghcr.io/loculus-project/config-processor:${TAG}" kubernetes/config-processor +} + +build_loculus_silo() { + echo ">>> Building loculus-silo" + docker build -t "ghcr.io/loculus-project/loculus-silo:${TAG}" loculus-silo +} + +build_keycloakify() { + echo ">>> Building keycloakify" + docker build -t "ghcr.io/loculus-project/keycloakify:${TAG}" keycloak/keycloakify +} + +build_preprocessing_nextclade() { + echo ">>> Building preprocessing-nextclade" + docker build -t "ghcr.io/loculus-project/preprocessing-nextclade:${TAG}" preprocessing/nextclade +} + +build_preprocessing_dummy() { + echo ">>> Building preprocessing-dummy" + docker build -t "ghcr.io/loculus-project/preprocessing-dummy:${TAG}" preprocessing/dummy +} + +build_ingest() { + echo ">>> Building ingest" + docker build -t "ghcr.io/loculus-project/ingest:${TAG}" ingest +} + +build_ena_submission() { + echo ">>> Building ena-submission" + docker build -t "ghcr.io/loculus-project/ena-submission:${TAG}" ena-submission +} + +build_ena_submission_flyway() { + echo ">>> Building ena-submission-flyway" + docker build -t "ghcr.io/loculus-project/ena-submission-flyway:${TAG}" ena-submission/flyway +} + +build_taxonomy_service() { + echo ">>> Building taxonomy-service" + docker build -t "ghcr.io/loculus-project/taxonomy-service:${TAG}" taxonomy/taxonomy_service +} + +import_image() { + local image="$1" + if [[ "$SKIP_IMPORT" -eq 1 ]]; then return; fi + echo ">>> Importing $image into k3d cluster '$CLUSTER'" + k3d image import "$image" --cluster "$CLUSTER" +} + +# Map component name → build function + image name to import. +for component in "${COMPONENTS[@]}"; do + case "$component" in + backend) build_backend ; import_image "ghcr.io/loculus-project/backend:${TAG}" ;; + website) build_website ; import_image "ghcr.io/loculus-project/website:${TAG}" ;; + config-loader) build_config_loader ; import_image "ghcr.io/loculus-project/config-loader:${TAG}" ;; + config-adapter) build_config_adapter ; import_image "ghcr.io/loculus-project/config-adapter:${TAG}" ;; + config-processor) build_config_processor ; import_image "ghcr.io/loculus-project/config-processor:${TAG}" ;; + loculus-silo) build_loculus_silo ; import_image "ghcr.io/loculus-project/loculus-silo:${TAG}" ;; + keycloakify) build_keycloakify ; import_image "ghcr.io/loculus-project/keycloakify:${TAG}" ;; + preprocessing-nextclade) build_preprocessing_nextclade ; import_image "ghcr.io/loculus-project/preprocessing-nextclade:${TAG}" ;; + preprocessing-dummy) build_preprocessing_dummy ; import_image "ghcr.io/loculus-project/preprocessing-dummy:${TAG}" ;; + ingest) build_ingest ; import_image "ghcr.io/loculus-project/ingest:${TAG}" ;; + ena-submission) build_ena_submission ; import_image "ghcr.io/loculus-project/ena-submission:${TAG}" ;; + ena-submission-flyway) build_ena_submission_flyway ; import_image "ghcr.io/loculus-project/ena-submission-flyway:${TAG}" ;; + taxonomy-service) build_taxonomy_service ; import_image "ghcr.io/loculus-project/taxonomy-service:${TAG}" ;; + *) + echo "Unknown component: $component" >&2 + echo "Available: ${ALL_COMPONENTS[*]}" >&2 + exit 2 + ;; + esac +done + +echo "" +if [[ "$DEV_MODE" -eq 1 ]]; then + echo "Done (--dev: backend + website not built). Deploy with:" + echo " ./deploy.py --verbose helm --branch local --dev --enablePreprocessing --enableIngest" + echo "" + echo "Then start backend + website in your IDE pointing at the in-cluster services" + echo "(Postgres on :5432, Keycloak on :8083, LAPIS on :8080, S3 on :8084)." +else + echo "Done. Deploy with:" + echo " ./deploy.py --verbose helm --branch local --for-e2e --enablePreprocessing --enableIngest" +fi +echo "" +echo "Note: '--branch local' makes the chart reference the ':local' image tag AND" +echo "applies kubernetes/loculus/values_local_images.yaml (imagePullPolicy=IfNotPresent)" +echo "so kubelet uses the k3d-imported images instead of trying to pull from ghcr.io." diff --git a/check_preview.sh b/check_preview.sh new file mode 100755 index 0000000000..d69d4dd583 --- /dev/null +++ b/check_preview.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# +# Compact verification for a local Loculus preview. +# +# Usage: +# ./check_preview.sh + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT" + +DOCS_PORT="${LOCULUS_DOCS_PORT:-3001}" +CHECK_DOCS=1 + +while [[ $# -gt 0 ]]; do + case "$1" in + --skip-docs) CHECK_DOCS=0 ;; + -h|--help) + sed -n '2,/^set -euo/{/^set -euo/d;p;}' "$0" | sed 's/^# \{0,1\}//' + echo "Options:" + echo " --skip-docs Do not verify the docs server." + exit 0 + ;; + *) echo "Unknown argument: $1" >&2; exit 2 ;; + esac + shift +done + +need() { + command -v "$1" >/dev/null 2>&1 || { + echo "Missing required command: $1" >&2 + exit 127 + } +} + +need kubectl +need curl +need jq + +ok() { + printf 'ok %s\n' "$*" +} + +fail() { + printf 'fail %s\n' "$*" >&2 + exit 1 +} + +section() { + printf '\n%s\n' "$*" +} + +section "Pods" +bad_pods="$(kubectl get pods --no-headers | awk '$3 != "Running" && $3 != "Completed" {print}')" +if [[ -n "$bad_pods" ]]; then + echo "$bad_pods" >&2 + fail "some pods are not Running or Completed" +fi +kubectl get pods --no-headers | awk ' + { counts[$3]++ } + END { + for (status in counts) printf "%s=%d ", status, counts[status]; + printf "\n"; + } +' +ok "pod statuses" + +section "Jobs" +kubectl get jobs +if kubectl get job/loculus-config-loader >/dev/null 2>&1; then + kubectl wait --for=condition=complete job/loculus-config-loader --timeout=5s >/dev/null + ok "config loader completed" +else + ok "config loader job no longer present" +fi + +section "Endpoints" +curl -fsS http://127.0.0.1:3000/ >/dev/null +ok "website" +curl -fsS http://127.0.0.1:8079/ >/dev/null +ok "backend" +if [[ "$CHECK_DOCS" -eq 1 ]]; then + curl -fsS "http://127.0.0.1:${DOCS_PORT}/" >/dev/null + ok "docs" +fi +lapis_count="$(curl -fsS 'http://127.0.0.1:8080/cchf/sample/details?limit=1' | jq '.data | length')" +[[ "$lapis_count" -ge 0 ]] || fail "LAPIS did not return a data array" +ok "LAPIS" +co_infection_count="$(curl -fsS 'http://127.0.0.1:8079/query/co-infections/current/metadata?limit=100' | jq '.data | length')" +[[ "$co_infection_count" -ge 30 ]] || fail "expected at least 30 co-infection rows, got $co_infection_count" +seeded_co_infection_count="$(curl -fsS 'http://127.0.0.1:8079/query/co-infections/current/metadata?specimenCollectorSampleId=coinfection-preview-001&limit=10' | jq '.data | length')" +[[ "$seeded_co_infection_count" -eq 2 ]] || fail "expected seeded co-infection sample to return 2 rows, got $seeded_co_infection_count" +ok "co-infections view" + +section "Auth" +token_type="$( + curl -fsS -X POST 'http://127.0.0.1:8083/realms/loculus/protocol/openid-connect/token' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + --data 'client_id=backend-client&grant_type=password&username=testuser&password=testuser' \ + | jq -r '.token_type' +)" +[[ "$token_type" == "Bearer" ]] || fail "testuser password grant failed" +ok "testuser password grant" + +section "Config" +instance="$(curl -fsS http://127.0.0.1:8079/api/config/instance)" +file_url_type="$(jq -r '.config.fileSharing.outputFileUrlType' <<<"$instance")" +[[ "$file_url_type" == "backend" ]] || fail "file sharing outputFileUrlType is '$file_url_type'" +ok "file sharing uses backend URLs" + +organism_summary="$(curl -fsS http://127.0.0.1:8079/api/config/organisms | jq -r '"organisms=" + ((.organisms | length) | tostring) + " " + ((.organisms | map(.key) | join(",")))')" +echo "$organism_summary" +[[ "$organism_summary" == organisms=8* ]] || fail "expected 8 configured organisms" +ok "organism config" + +section "Database" +kubectl exec deploy/loculus-database -- psql -U postgres -d loculus -c \ + "select organism, count(*) filter (where approver is not null) approved, count(*) filter (where released_at is not null) released, count(*) total from sequence_entries group by organism order by organism;" +kubectl exec deploy/loculus-database -- psql -U postgres -d loculus -c \ + "select organism, processing_status, count(*) from (select e.organism, p.processing_status from sequence_entries_preprocessed_data p join sequence_entries e on e.accession=p.accession and e.version=p.version) s group by organism, processing_status order by organism, processing_status;" +kubectl exec deploy/loculus-database -- psql -U postgres -d loculus -c \ + "select e.organism, count(*) filter (where e.approver is not null) approved, count(*) filter (where e.released_at is not null) released, count(*) total from sequence_entries e join groups_table g on e.group_id=g.group_id where g.group_name='Co-infection preview data' group by e.organism order by e.organism;" + +section "Summary" +ok "preview verification completed" diff --git a/config-tools/Dockerfile.adapter b/config-tools/Dockerfile.adapter new file mode 100644 index 0000000000..bfd8ae2fa1 --- /dev/null +++ b/config-tools/Dockerfile.adapter @@ -0,0 +1,37 @@ +# syntax=docker/dockerfile:1 +# +# loculus-config-adapter: init-container that fetches a pinned organism config +# from the Loculus public API and writes SILO/LAPIS/preprocessing files into +# /loculus-config (override via LOCULUS_CONFIG_OUTPUT_DIR). +# +# Two-stage build keeps the runtime image small. + +# ── Stage 1: install deps + typecheck ────────────────────────────────────────── +FROM node:22-alpine AS builder +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm ci + +COPY tsconfig.json ./ +COPY src ./src + +RUN npm run check-types + +# ── Stage 2: runtime image ───────────────────────────────────────────────────── +FROM node:22-alpine +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev && npm install --no-save tsx@^4.19.0 + +COPY --from=builder /app/src ./src +COPY --from=builder /app/tsconfig.json ./tsconfig.json + +# Runs as root by default: as a k8s init container we write to a shared +# emptyDir mount point that kubelet creates owned by root. The container is +# short-lived (exits as soon as the render is done), so dropping privileges +# here would just require a securityContext override on the deployment side +# without giving us meaningful isolation. + +ENTRYPOINT ["npx", "tsx", "/app/src/adapter/cli.ts"] diff --git a/config-tools/Dockerfile.loader b/config-tools/Dockerfile.loader new file mode 100644 index 0000000000..0e55c1a868 --- /dev/null +++ b/config-tools/Dockerfile.loader @@ -0,0 +1,40 @@ +# syntax=docker/dockerfile:1 +# +# loculus-config-loader: posts fixture YAML to the Loculus admin config API. +# Used by: +# - Local dev (replaces backend/import_local_test_config.py) +# - Phase-3 Helm post-install Job +# +# Two-stage build keeps the runtime image small (no tsc, no devDeps). + +# ── Stage 1: install deps + build via tsx-style execution ────────────────────── +FROM node:22-alpine AS builder +WORKDIR /app + +# Install full deps (incl. dev) for typecheck + tests. +COPY package.json package-lock.json* ./ +RUN npm ci + +COPY tsconfig.json ./ +COPY src ./src + +# Sanity-check the build artefacts in CI by typechecking and running tests. +# Comment out if image-build time becomes an issue. +RUN npm run check-types + +# ── Stage 2: runtime image ───────────────────────────────────────────────────── +FROM node:22-alpine +WORKDIR /app + +# Production deps only — tsx is the runtime executor (no separate build step). +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev && npm install --no-save tsx@^4.19.0 + +COPY --from=builder /app/src ./src +COPY --from=builder /app/tsconfig.json ./tsconfig.json + +# Drop privileges. +RUN addgroup -S loculus && adduser -S loculus -G loculus +USER loculus + +ENTRYPOINT ["npx", "tsx", "/app/src/loader/cli.ts"] diff --git a/config-tools/README.md b/config-tools/README.md new file mode 100644 index 0000000000..3f7e1581ee --- /dev/null +++ b/config-tools/README.md @@ -0,0 +1,176 @@ +# @loculus/config-tools + +Shared canonical config schemas (Zod) and CLIs for the Loculus DB-backed config system. + +This package backs: + +- **`website/`** — consumes the canonical Zod schemas (via re-export in `website/src/types/loculusConfig.ts`). +- **`loculus-config-loader`** (this package) — posts fixture YAML to the backend admin API. +- **`loculus-config-adapter`** (planned, Phase 2.2) — init-container that renders SILO/LAPIS / preprocessing config from `/api/config/...`. + +## Layout + +``` +config-tools/ +├── package.json +├── tsconfig.json +├── vitest.config.ts +├── Dockerfile.loader +└── src/ + ├── index.ts # re-exports all schemas + ├── schema/ + │ ├── canonicalConfig.ts # mirrors backend/.../config/Config.kt + InstanceConfig.kt + │ └── adminApi.ts # mirrors AdminConfigController response DTOs + └── loader/ + ├── cli.ts # `loculus-config-loader` entry point + ├── fixtures.ts # reads instance.yaml + organisms/*.yaml + ├── adminClient.ts # HTTP client (Bearer auth + If-Match) + ├── compare.ts # deep-equal for idempotency + ├── publish.ts # orchestrator + └── publish.spec.ts +``` + +## Fixture format + +The loader expects a directory like: + +``` +/ +├── instance.yaml # validates against canonicalInstanceConfig +└── organisms/ + ├── .yaml # validates against canonicalOrganismConfig + └── .yaml +``` + +Each organism's `key` is taken from its filename (without the `.yaml` extension). +Backend test fixtures under `backend/src/test/resources/fixtures//` are +in this exact shape (the fixtures `ConfigFixtures.kt` consumes), so they double +as loader input for local dev. + +## Adapter: `loculus-config-adapter` + +Init-container that fetches a pinned organism's config from the Loculus public +API and renders the files SILO + LAPIS + preprocessing expect. + +### Outputs + +- `database_config.yaml` — SILO database config (was `_siloDatabaseConfig.tpl`) +- `reference_genomes.json` — flat nucleotide+gene shape with multi-segment / multi-reference disambiguation (was `_merged-reference-genomes.tpl`) +- `preprocessing_config.yaml` — per-field preprocessing pipeline spec (was `_preprocessingFromValues.tpl`) +- `lineage_definitions.json` — the `{ lineageSystem: { pipelineVersion: URL } }` map taken from the DB instance config's `lineageSystemDefinitions`, filtered to the systems this organism references (always written, possibly `{}`). The adapter does **not** download the definitions; the silo-importer reads this file and downloads them itself at import time (it knows the pipeline version of the data it imports). + +### CLI usage + +``` +loculus-config-adapter \ + --backend-url http://localhost:8079 \ + --organism ebola-sudan \ + --organism-version 1 \ + [--instance-version 4] # default: latest + [--output-dir /loculus-config] # default: /loculus-config +``` + +Environment fallbacks: `LOCULUS_BACKEND_URL`, `LOCULUS_ORGANISM_KEY`, +`LOCULUS_ORGANISM_CONFIG_VERSION`, `LOCULUS_INSTANCE_CONFIG_VERSION`, +`LOCULUS_CONFIG_OUTPUT_DIR`. + +### Consistency + +The adapter writes each output file directly into the output dir; it never +removes the dir, because in Kubernetes that dir is the mount point of a shared +`emptyDir` the container can't delete. Consistency comes from init-container +ordering instead: the adapter runs to completion before SILO/LAPIS starts, so +the main container only ever sees a complete render. + +### Docker + +```bash +docker build -f config-tools/Dockerfile.adapter -t loculus-config-adapter:dev config-tools/ +``` + +The Phase 3.5 Helm work wires this as an init container on each per-organism +SILO + LAPIS Deployment. + +## Loader: `loculus-config-loader` + +Replaces the legacy `backend/import_local_test_config.py` script. + +### CLI usage + +``` +loculus-config-loader \ + --backend-url http://localhost:8079 \ + --fixtures backend/src/test/resources/fixtures/default \ + --admin-token "$LOCULUS_ADMIN_TOKEN" \ + [--mode idempotent|fresh-only|republish] \ + [--dry-run] +``` + +Environment variables work too: `LOCULUS_BACKEND_URL`, `LOCULUS_FIXTURES_DIR`, +`LOCULUS_ADMIN_TOKEN`, `LOCULUS_ADMIN_TOKEN_FILE`. + +### Modes + +- `idempotent` (default) — for each fixture entry, skip if the backend already + matches; otherwise create-then-publish. Fails if an *already-released* + organism's draft has diverged from the fixture (use `--mode republish` to + override; not yet implemented for that case). +- `fresh-only` — fails if any fixture key already exists in the backend. Right + for Helm post-install hooks where a fresh cluster shouldn't paper over half-state. +- `republish` — placeholder; not yet implemented for released organisms. + +### Local-dev usage + +To seed a local backend with the default test fixtures (replaces the old +`import_local_test_config.py` flow): + +```bash +cd config-tools +npm install +npm run loader -- \ + --backend-url http://localhost:8079 \ + --fixtures ../backend/src/test/resources/fixtures/default \ + --admin-token "$LOCULUS_ADMIN_TOKEN" +``` + +Use a Keycloak token for a user with the `loculus_administrator` realm role. + +### Helm usage (planned, Phase 3.4) + +The Helm chart's post-install Job will mount a ConfigMap of YAML fixtures and +invoke `loculus-config-loader --mode fresh-only` against the in-cluster backend. + +## Schemas + +Importable from `@loculus/config-tools`: + +```ts +import { + canonicalInstanceConfig, + canonicalOrganismConfig, + organismDraftResponse, + publishResponse, + operationRequest, +} from '@loculus/config-tools'; +``` + +(In this repo, the website imports them via a relative re-export at +`website/src/types/loculusConfig.ts`; no workspace setup is required.) + +## Tests + +```bash +cd config-tools +CI=1 npm test +``` + +Unit tests use Vitest with a mocked global `fetch`. + +## Docker + +```bash +docker build -f config-tools/Dockerfile.loader -t loculus-config-loader:dev config-tools/ +``` + +The image runs `npx tsx src/loader/cli.ts` as `ENTRYPOINT`; pass loader flags as +the `args` array in your Kubernetes job spec or as `CMD` for `docker run`. diff --git a/config-tools/package-lock.json b/config-tools/package-lock.json new file mode 100644 index 0000000000..22c9cf8c80 --- /dev/null +++ b/config-tools/package-lock.json @@ -0,0 +1,2200 @@ +{ + "name": "@loculus/config-tools", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@loculus/config-tools", + "version": "0.0.1", + "dependencies": { + "yaml": "^2.9.0", + "zod": "^3.25.67" + }, + "bin": { + "loculus-config-adapter": "dist/adapter/cli.js", + "loculus-config-loader": "dist/loader/cli.js" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "tsx": "^4.19.0", + "typescript": "^5.9.0", + "vitest": "^3.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", + "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tsx": { + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz", + "integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/config-tools/package.json b/config-tools/package.json new file mode 100644 index 0000000000..14b86d48da --- /dev/null +++ b/config-tools/package.json @@ -0,0 +1,29 @@ +{ + "name": "@loculus/config-tools", + "version": "0.0.1", + "type": "module", + "description": "Shared canonical config schemas and CLIs (loader/adapter) for the Loculus DB-backed config system", + "private": true, + "scripts": { + "check-types": "tsc --noEmit", + "test": "vitest", + "test-fast": "vitest --bail=1", + "build": "tsc", + "loader": "tsx src/loader/cli.ts", + "adapter": "tsx src/adapter/cli.ts" + }, + "bin": { + "loculus-config-loader": "./dist/loader/cli.js", + "loculus-config-adapter": "./dist/adapter/cli.js" + }, + "dependencies": { + "yaml": "^2.9.0", + "zod": "^3.25.67" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "tsx": "^4.19.0", + "typescript": "^5.9.0", + "vitest": "^3.0.0" + } +} diff --git a/config-tools/src/adapter/cli.ts b/config-tools/src/adapter/cli.ts new file mode 100644 index 0000000000..707cc60576 --- /dev/null +++ b/config-tools/src/adapter/cli.ts @@ -0,0 +1,303 @@ +#!/usr/bin/env node +// Init container that fetches one organism's pinned config from the public API +// and renders the files SILO + LAPIS + preprocessing expect. +import { mkdir, writeFile } from 'node:fs/promises'; +import { join } from 'node:path'; + +import { stringify as yamlStringify } from 'yaml'; + +import { mergeReferenceGenomes } from './referenceGenomes.ts'; +import { + overviewQuery, + overviewReferenceGenomes, + overviewSequenceConfig, + overviewSiloDatabaseConfigYaml, + overviewSiloPreprocessingConfig, +} from './overviewConfig.ts'; +import { toSiloDatabaseConfig } from './siloDatabaseConfig.ts'; +import { toSiloPreprocessingConfig } from './siloPreprocessingConfig.ts'; +import { lineageDefinitionsForOrganism } from './lineageSystems.ts'; +import { + canonicalInstanceResponse, + canonicalOrganismResponse, + type CanonicalInstanceResponse, + type CanonicalOrganismResponse, +} from '../schema/canonicalConfig.ts'; + +type RenderMode = 'organism' | 'overview'; + +interface ParsedArgs { + mode: RenderMode; + backendUrl: string; + organismKey: string; + viewKey: string; + organismVersion: number; + instanceVersion: number | null; + outputDir: string; +} + +function usage(): string { + return `loculus-config-adapter [options] + +Required: + --backend-url Base URL of the Loculus backend + --organism Organism key to render (organism mode) + --organism-version Pinned organism config version (organism mode) + +Optional: + --mode Render mode (default: organism). In overview + mode only the instance config is rendered into + the cross-organism overview SILO config + importer + field list; --organism is not required. + --instance-version Pinned instance config version (default: latest) + --view-key SQL-backed view key (overview mode, default: overview) + --output-dir Output directory (default: /loculus-config) + --help Show this message + +Environment fallbacks: LOCULUS_BACKEND_URL, LOCULUS_ORGANISM_KEY, +LOCULUS_ORGANISM_CONFIG_VERSION, LOCULUS_INSTANCE_CONFIG_VERSION, +LOCULUS_VIEW_KEY, LOCULUS_CONFIG_OUTPUT_DIR. +`; +} + +function parseArgs(argv: string[]): ParsedArgs { + const env = process.env; + let mode: RenderMode = env.LOCULUS_CONFIG_MODE === 'overview' ? 'overview' : 'organism'; + let backendUrl = env.LOCULUS_BACKEND_URL; + let organismKey = env.LOCULUS_ORGANISM_KEY; + let viewKey = env.LOCULUS_VIEW_KEY ?? 'overview'; + let organismVersion = env.LOCULUS_ORGANISM_CONFIG_VERSION; + let instanceVersion = env.LOCULUS_INSTANCE_CONFIG_VERSION; + let outputDir = env.LOCULUS_CONFIG_OUTPUT_DIR ?? '/loculus-config'; + + for (let i = 2; i < argv.length; i++) { + const arg = argv[i]; + const next = (): string => { + const v = argv[++i]; + if (v === undefined) throw new Error(`Missing value for ${arg}`); + return v; + }; + switch (arg) { + case '--mode': { + const v = next(); + if (v !== 'organism' && v !== 'overview') throw new Error(`Invalid --mode "${v}"`); + mode = v; + break; + } + case '--backend-url': + backendUrl = next(); + break; + case '--organism': + organismKey = next(); + break; + case '--view-key': + viewKey = next(); + break; + case '--organism-version': + organismVersion = next(); + break; + case '--instance-version': + instanceVersion = next(); + break; + case '--output-dir': + outputDir = next(); + break; + case '--help': + case '-h': + console.log(usage()); + process.exit(0); + break; + default: + throw new Error(`Unknown argument ${arg}\n\n${usage()}`); + } + } + + if (backendUrl === undefined || backendUrl === '') throw new Error('Missing --backend-url'); + + let parsedInstanceVersion: number | null = null; + if (instanceVersion !== undefined && instanceVersion !== '') { + const v = Number.parseInt(instanceVersion, 10); + if (Number.isNaN(v) || v < 1) throw new Error(`Invalid --instance-version "${instanceVersion}"`); + parsedInstanceVersion = v; + } + + // The overview render only needs the instance config (no single organism). + if (mode === 'overview') { + if (viewKey === '') throw new Error('Missing --view-key'); + return { + mode, + backendUrl, + organismKey: '', + viewKey, + organismVersion: 0, + instanceVersion: parsedInstanceVersion, + outputDir, + }; + } + + if (organismKey === undefined || organismKey === '') throw new Error('Missing --organism'); + if (organismVersion === undefined || organismVersion === '') { + throw new Error('Missing --organism-version (the adapter requires pinned config for reproducible renders)'); + } + const parsedOrgVersion = Number.parseInt(organismVersion, 10); + if (Number.isNaN(parsedOrgVersion) || parsedOrgVersion < 1) { + throw new Error(`Invalid --organism-version "${organismVersion}"`); + } + + return { + mode, + backendUrl, + organismKey, + viewKey, + organismVersion: parsedOrgVersion, + instanceVersion: parsedInstanceVersion, + outputDir, + }; +} + +async function fetchJson(url: string): Promise { + let response: Response; + try { + response = await fetch(url, { + method: 'GET', + // eslint-disable-next-line @typescript-eslint/naming-convention + headers: { Accept: 'application/json' }, + }); + } catch (e) { + throw new Error(`Network error fetching ${url}: ${(e as Error).message}`); + } + if (!response.ok) { + throw new Error(`status ${response.status} fetching ${url}`); + } + return response.json(); +} + +async function fetchInstance(backendUrl: string, version: number | null): Promise { + const base = backendUrl.replace(/\/$/, ''); + const url = version === null ? `${base}/api/config/instance` : `${base}/api/config/instance?version=${version}`; + return canonicalInstanceResponse.parse(await fetchJson(url)); +} + +async function fetchOrganism(backendUrl: string, key: string, version: number): Promise { + const base = backendUrl.replace(/\/$/, ''); + const url = `${base}/api/config/organisms/${encodeURIComponent(key)}?version=${version}`; + return canonicalOrganismResponse.parse(await fetchJson(url)); +} + +async function writeOutputs(outputDir: string, files: Map): Promise { + // Atomicity comes from init-container ordering, not from rename-into-place: + // we never `rm` the output dir because in k8s it is the mount point of a + // shared emptyDir, which the container can't remove. + await mkdir(outputDir, { recursive: true }); + for (const [name, content] of files) { + await writeFile(join(outputDir, name), content, 'utf8'); + } +} + +async function renderOverview(args: ParsedArgs): Promise { + let instance: CanonicalInstanceResponse; + try { + instance = await fetchInstance(args.backendUrl, args.instanceVersion); + } catch (e) { + console.error(`Failed to fetch instance config: ${(e as Error).message}`); + return 3; + } + console.log(`Rendering view ${args.viewKey} config against instance v${instance.version}…`); + + const files = new Map(); + try { + files.set('database_config.yaml', overviewSiloDatabaseConfigYaml(instance.config, args.viewKey)); + files.set('reference_genomes.json', JSON.stringify(overviewReferenceGenomes(instance.config, args.viewKey), null, 2)); + files.set( + 'view_sequence_config.json', + JSON.stringify(overviewSequenceConfig(instance.config, args.viewKey), null, 2), + ); + files.set('preprocessing_config.yaml', yamlStringify(overviewSiloPreprocessingConfig())); + files.set('overview_query.sql', overviewQuery(instance.config, args.viewKey)); + } catch (e) { + console.error(`Render failed: ${(e as Error).message}`); + return 4; + } + + try { + await writeOutputs(args.outputDir, files); + } catch (e) { + console.error(`Render/write failed: ${(e as Error).message}`); + return 4; + } + console.log( + `Wrote ${args.outputDir}/{database_config.yaml,reference_genomes.json,preprocessing_config.yaml,` + + `view_sequence_config.json,overview_query.sql}`, + ); + return 0; +} + +async function main(): Promise { + let args: ParsedArgs; + try { + args = parseArgs(process.argv); + } catch (e) { + console.error((e as Error).message); + return 2; + } + + if (args.mode === 'overview') { + return renderOverview(args); + } + + let instance: CanonicalInstanceResponse; + let organism: CanonicalOrganismResponse; + try { + [instance, organism] = await Promise.all([ + fetchInstance(args.backendUrl, args.instanceVersion), + fetchOrganism(args.backendUrl, args.organismKey, args.organismVersion), + ]); + } catch (e) { + console.error(`Failed to fetch config: ${(e as Error).message}`); + return 3; + } + + console.log( + `Rendering for ${args.organismKey} v${organism.version} against instance v${instance.version}…`, + ); + + // SILO + LAPIS files only. The Loculus preprocessing pipeline is external + // and fetches its config from the backend itself (see + // config-architecture/61_preprocessingPipeline.md); the adapter does not + // render a pipeline-specific file. + const files = new Map(); + try { + files.set('database_config.yaml', yamlStringify(toSiloDatabaseConfig(instance.config, organism.config))); + files.set('reference_genomes.json', JSON.stringify(mergeReferenceGenomes(organism.config), null, 2)); + files.set('preprocessing_config.yaml', yamlStringify(toSiloPreprocessingConfig(organism.config))); + // Lineage-definition URLs come from the DB instance config. The adapter + // forwards them; the silo-importer downloads the actual files at import + // time (it knows the pipeline version of the data it's importing). + // Always written (possibly `{}`) so the silo-importer's mount always finds a file. + const lineageDefinitions = lineageDefinitionsForOrganism(instance.config, organism.config); + files.set('lineage_definitions.json', JSON.stringify(lineageDefinitions, null, 2)); + } catch (e) { + console.error(`Render failed: ${(e as Error).message}`); + return 4; + } + + try { + await writeOutputs(args.outputDir, files); + } catch (e) { + console.error(`Render/write failed: ${(e as Error).message}`); + return 4; + } + console.log( + `Wrote ${args.outputDir}/{database_config.yaml,reference_genomes.json,` + + `preprocessing_config.yaml,lineage_definitions.json}`, + ); + return 0; +} + +main().then( + (code) => process.exit(code), + (e) => { + console.error('Unhandled error:', e); + process.exit(99); + }, +); diff --git a/config-tools/src/adapter/commonMetadata.ts b/config-tools/src/adapter/commonMetadata.ts new file mode 100644 index 0000000000..2d5dfa4435 --- /dev/null +++ b/config-tools/src/adapter/commonMetadata.ts @@ -0,0 +1,211 @@ +// TS port of `_common-metadata.tpl`. The system metadata fields that get +// concatenated onto every organism's metadata before being fed to SILO and +// preprocessing. +import type { CanonicalInstanceConfig } from '../schema/canonicalConfig.ts'; + +export type CommonMetadataField = Record & { name: string; type: string }; + +export function commonMetadata(instance: CanonicalInstanceConfig): CommonMetadataField[] { + const { name, accessionPrefix, dataUseTerms } = instance; + + const fields: CommonMetadataField[] = [ + { + name: 'accessionVersion', + displayName: 'Accession version', + type: 'string', + notSearchable: true, + hideOnSequenceDetailsPage: true, + includeInDownloadsByDefault: true, + definition: `The ${name} \`accession\` and \`version\`, uniquely identifying the specific version of the sequence record (e.g. \`${accessionPrefix}000001.1\`).`, + }, + { + name: 'accession', + displayName: 'Accession', + type: 'string', + notSearchable: true, + hideOnSequenceDetailsPage: true, + definition: `A unique identifier assigned to the sequence record by ${name} (e.g. \`${accessionPrefix}000001\`).`, + }, + { + name: 'version', + displayName: 'Version', + type: 'int', + hideOnSequenceDetailsPage: true, + definition: + 'The version number of the sequence record, incremented each time the sequence is revised.', + }, + { + name: 'submissionId', + displayName: 'Submission ID', + type: 'string', + header: 'Submission details', + orderOnDetailsPage: 5000, + substringSearch: true, + includeInDownloadsByDefault: true, + definition: 'The sample identifier provided by the submitter.', + }, + { + name: 'isRevocation', + displayName: 'Is revocation', + type: 'boolean', + autocomplete: true, + hideOnSequenceDetailsPage: true, + definition: 'Indicator of whether the sequence record is revoked.', + }, + { + name: 'submitter', + displayName: 'Submitter', + type: 'string', + generateIndex: true, + autocomplete: true, + hideOnSequenceDetailsPage: true, + header: 'Submission details', + orderOnDetailsPage: 5010, + definition: `Name of the ${name} user who submitted the sequence record.`, + }, + { + name: 'groupName', + type: 'string', + generateIndex: true, + autocomplete: true, + header: 'Submission details', + displayName: 'Submitting group', + includeInDownloadsByDefault: true, + orderOnDetailsPage: 5020, + definition: 'Name of the group that submitted the sequence record.', + customDisplay: { type: 'submittingGroup', displayGroup: 'group' }, + }, + { + name: 'groupId', + type: 'int', + autocomplete: true, + header: 'Submission details', + displayName: 'Submitting group (numeric ID)', + orderOnDetailsPage: 5030, + definition: 'Numeric ID of the group that submitted the sequence record.', + customDisplay: { type: 'submittingGroup', displayGroup: 'group' }, + }, + { + name: 'submittedAtTimestamp', + type: 'timestamp', + displayName: 'Date submitted', + header: 'Submission details', + orderOnDetailsPage: 5040, + definition: `Date and time on which the sequence record was submitted to ${name}.`, + }, + { + name: 'submittedDate', + type: 'string', + hideOnSequenceDetailsPage: true, + generateIndex: true, + autocomplete: true, + displayName: 'Date submitted (exact)', + orderOnDetailsPage: 5050, + definition: `Date on which the sequence record was submitted to ${name}.`, + }, + { + name: 'releasedAtTimestamp', + type: 'timestamp', + displayName: 'Date released', + header: 'Submission details', + columnWidth: 100, + orderOnDetailsPage: 5060, + definition: `Date and time on which the sequence record was released on ${name}.`, + }, + { + name: 'releasedDate', + type: 'string', + hideOnSequenceDetailsPage: true, + generateIndex: true, + autocomplete: true, + displayName: 'Date released (exact)', + columnWidth: 100, + orderOnDetailsPage: 5070, + definition: `Date on which the sequence record was released on ${name}.`, + }, + ]; + + if (dataUseTerms.enabled) { + fields.push( + { + name: 'dataUseTerms', + type: 'string', + generateIndex: true, + autocomplete: true, + displayName: 'Data use terms', + initiallyVisible: true, + includeInDownloadsByDefault: true, + definition: + 'The terms under which the sequence record may be used; either `OPEN` or `RESTRICTED`.', + customDisplay: { type: 'dataUseTerms' }, + header: 'Data use terms', + orderOnDetailsPage: 610, + orderInSearchDisplay: 10, + }, + { + name: 'dataUseTermsRestrictedUntil', + type: 'date', + displayName: 'Data use terms restricted until', + hideOnSequenceDetailsPage: true, + header: 'Data use terms', + orderOnDetailsPage: 620, + definition: 'The date until which the sequence record is restricted use.', + }, + { + name: 'dataBecameOpenAt', + type: 'date', + displayName: 'Date data became open', + hideOnSequenceDetailsPage: true, + header: 'Data use terms', + orderOnDetailsPage: 625, + definition: + 'The date on which the sequence record transitioned from restricted to open access.', + }, + ); + if (dataUseTerms.urls !== null && dataUseTerms.urls !== undefined) { + fields.push({ + name: 'dataUseTermsUrl', + displayName: 'Data use terms URL', + type: 'string', + notSearchable: true, + header: 'Data use terms', + includeInDownloadsByDefault: true, + definition: + 'Link to the full text of the data use terms applicable to the sequence record.', + customDisplay: { type: 'link', url: '__value__' }, + orderOnDetailsPage: 630, + }); + } + } + + fields.push( + { + name: 'versionStatus', + displayName: 'Version status', + type: 'string', + autocomplete: true, + hideOnSequenceDetailsPage: true, + definition: + 'Indicates whether this is the latest version of the sequence record (`LATEST_VERSION`), an earlier version (`REVISED`), or has been revoked (`REVOKED`).', + }, + { + name: 'versionComment', + type: 'string', + displayName: 'Version comment', + header: 'Submission details', + orderOnDetailsPage: 5000, + definition: + 'Reason for revising sequences, or other general comments concerning a specific version.', + }, + { + name: 'pipelineVersion', + displayName: 'Pipeline version', + type: 'int', + notSearchable: true, + hideOnSequenceDetailsPage: true, + definition: 'The version of the processing pipeline used to process the sequence record.', + }, + ); + + return fields; +} diff --git a/config-tools/src/adapter/lineageSystems.ts b/config-tools/src/adapter/lineageSystems.ts new file mode 100644 index 0000000000..e3ccd3644e --- /dev/null +++ b/config-tools/src/adapter/lineageSystems.ts @@ -0,0 +1,31 @@ +import type { CanonicalInstanceConfig, CanonicalOrganismConfig } from '../schema/canonicalConfig.ts'; + +export function lineageSystemsForOrganism(organism: CanonicalOrganismConfig): string[] { + const seen = new Set(); + for (const field of organism.schema.metadata) { + const ls = field.lineageSystem; + if (ls !== null && ls !== undefined && ls !== '') seen.add(ls); + } + return [...seen].sort(); +} + +// URLs are forwarded from the instance config; the silo-importer downloads the +// actual definition files at import time. Throws if a referenced system is undefined. +export function lineageDefinitionsForOrganism( + instance: CanonicalInstanceConfig, + organism: CanonicalOrganismConfig, +): Record> { + const definitions = instance.lineageSystemDefinitions ?? {}; + const result: Record> = {}; + for (const system of lineageSystemsForOrganism(organism)) { + const urlsByVersion = definitions[system]; + if (urlsByVersion === undefined || urlsByVersion === null) { + throw new Error( + `Organism references lineage system "${system}" but the instance config has no ` + + `lineageSystemDefinitions entry for it.`, + ); + } + result[system] = urlsByVersion; + } + return result; +} diff --git a/config-tools/src/adapter/overviewConfig.spec.ts b/config-tools/src/adapter/overviewConfig.spec.ts new file mode 100644 index 0000000000..74c84798e0 --- /dev/null +++ b/config-tools/src/adapter/overviewConfig.spec.ts @@ -0,0 +1,168 @@ +import { describe, expect, it } from 'vitest'; + +import { + overviewQuery, + overviewReferenceGenomes, + overviewSequenceConfig, + overviewSiloDatabaseConfigYaml, +} from './overviewConfig.ts'; +import { + canonicalInstanceConfig, + type CanonicalInstanceConfig, +} from '../schema/canonicalConfig.ts'; + +function instanceWithOverview(overview: unknown): CanonicalInstanceConfig { + return canonicalInstanceConfig.parse({ + name: 'Test', + accessionPrefix: 'LOC_', + dataUseTerms: { enabled: false, urls: null }, + fileSharing: { outputFileUrlType: 'website' }, + overview, + }); +} + +function instanceWithViews(views: unknown): CanonicalInstanceConfig { + return canonicalInstanceConfig.parse({ + name: 'Test', + accessionPrefix: 'LOC_', + dataUseTerms: { enabled: false, urls: null }, + fileSharing: { outputFileUrlType: 'website' }, + views, + }); +} + +const schema = ` +schema: + instanceName: Overview + opennessLevel: OPEN + metadata: + - name: accessionVersion + type: string + - name: organism + displayName: Organism + type: string + generateIndex: true + autocomplete: true + header: Sample details + - name: country + type: string + - name: submittedAtTimestamp + displayName: Date submitted + type: timestamp + primaryKey: accessionVersion + features: + - name: generalizedAdvancedQuery +`; + +const overview = { + displayName: 'Overview', + query: 'select accessionVersion, organism, country from "enteroviruses"', + schema, + tableColumns: ['organism', 'country'], +}; + +describe('overview adapter config', () => { + it('passes through the configured SQL query with a trailing newline', () => { + expect(overviewQuery(instanceWithOverview(overview))).toBe( + 'select accessionVersion, organism, country from "enteroviruses"\n', + ); + }); + + it('renders a selected SQL-backed view', () => { + const instance = instanceWithViews({ + 'real-organisms': { + ...overview, + displayName: 'Real organisms', + query: 'select accessionVersion from "west-nile"', + }, + }); + + expect(overviewQuery(instance, 'real-organisms')).toBe( + 'select accessionVersion from "west-nile"\n', + ); + expect( + overviewSiloDatabaseConfigYaml(instance, 'real-organisms'), + ).toContain('instanceName: Overview'); + }); + + it('renders empty sequence config for metadata-only views', () => { + const instance = instanceWithOverview(overview); + + expect(overviewReferenceGenomes(instance)).toEqual({ + nucleotideSequences: [], + genes: [], + }); + expect(overviewSequenceConfig(instance)).toEqual({ + unalignedNucleotideSequences: { + enabled: false, + segments: [], + sourceSegments: {}, + }, + }); + }); + + it('renders unaligned nucleotide segment config for sequence-enabled views', () => { + const instance = instanceWithViews({ + 'real-organisms': { + ...overview, + sequenceData: { + unalignedNucleotideSequences: { + enabled: true, + segments: ['main', 'L'], + sourceSegments: { + main: { + 'west-nile': 'genome', + }, + }, + }, + }, + }, + }); + + expect(overviewReferenceGenomes(instance, 'real-organisms')).toEqual({ + nucleotideSequences: [ + { name: 'main', sequence: 'N' }, + { name: 'L', sequence: 'N' }, + ], + genes: [], + }); + expect(overviewSequenceConfig(instance, 'real-organisms')).toEqual({ + unalignedNucleotideSequences: { + enabled: true, + segments: ['main', 'L'], + sourceSegments: { + main: { + 'west-nile': 'genome', + }, + }, + }, + }); + }); + + it('passes through a valid manual SILO database config YAML', () => { + const rendered = overviewSiloDatabaseConfigYaml( + instanceWithOverview(overview), + ); + expect(rendered).toContain('instanceName: Overview'); + expect(rendered).toContain('primaryKey: accessionVersion'); + expect(rendered).toContain('name: submittedAtTimestamp'); + expect(rendered).toContain('type: int'); + expect(rendered).toContain('generateIndex: true'); + expect(rendered).not.toContain('displayName'); + expect(rendered).not.toContain('autocomplete'); + expect(rendered.endsWith('\n')).toBe(true); + }); + + it('rejects a manual schema without the expected primary key', () => { + const invalid = { + ...overview, + schema: schema.replace( + 'primaryKey: accessionVersion', + 'primaryKey: accession', + ), + }; + expect(() => + overviewSiloDatabaseConfigYaml(instanceWithOverview(invalid)), + ).toThrow('overview.schema.schema.primaryKey must be accessionVersion'); + }); +}); diff --git a/config-tools/src/adapter/overviewConfig.ts b/config-tools/src/adapter/overviewConfig.ts new file mode 100644 index 0000000000..2adcb97582 --- /dev/null +++ b/config-tools/src/adapter/overviewConfig.ts @@ -0,0 +1,204 @@ +// Renders the cross-organism overview SILO/LAPIS config + importer inputs from +// the instance config's SQL-backed view sections. View rows are produced by an +// admin-provided SQL query and can optionally include unaligned nucleotide +// sequences. +import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'; + +import type { CanonicalInstanceConfig } from '../schema/canonicalConfig.ts'; +import type { CanonicalView } from '../schema/canonicalConfig.ts'; + +export type OverviewSequenceConfig = { + unalignedNucleotideSequences: { + enabled: boolean; + segments: string[]; + sourceSegments: Record>; + }; +}; + +export function overviewReferenceGenomes( + instance: CanonicalInstanceConfig, + viewKey = 'overview', +): { nucleotideSequences: { name: string; sequence: string }[]; genes: [] } { + return { + nucleotideSequences: overviewSequenceSegments(instance, viewKey).map( + (segment) => ({ + name: segment, + // Synthetic placeholder: view sequence support only needs segment + // names for unaligned LAPIS endpoints, not aligned reference bases. + sequence: 'N', + }), + ), + genes: [], + }; +} + +export function overviewSequenceConfig( + instance: CanonicalInstanceConfig, + viewKey = 'overview', +): OverviewSequenceConfig { + const sequenceConfig = getView(instance, viewKey).sequenceData + ?.unalignedNucleotideSequences; + const enabled = + sequenceConfig?.enabled === true && + (sequenceConfig.segments ?? []).length > 0; + return { + unalignedNucleotideSequences: { + enabled, + segments: enabled ? [...sequenceConfig.segments] : [], + sourceSegments: enabled + ? (sequenceConfig.sourceSegments ?? {}) + : {}, + }, + }; +} + +/** SILO preprocessing config for a SQL-backed view instance. */ +export function overviewSiloPreprocessingConfig(): { + inputDirectory: string; + outputDirectory: string; + ndjsonInputFilename: string; + referenceGenomeFilename: string; +} { + return { + inputDirectory: '/preprocessing/input', + outputDirectory: '/preprocessing/output', + ndjsonInputFilename: 'data.ndjson.zst', + referenceGenomeFilename: 'reference_genomes.json', + }; +} + +function overviewSequenceSegments( + instance: CanonicalInstanceConfig, + viewKey: string, +): string[] { + const sequenceConfig = getView(instance, viewKey).sequenceData + ?.unalignedNucleotideSequences; + if (sequenceConfig?.enabled !== true) return []; + return sequenceConfig.segments ?? []; +} + +export function overviewQuery( + instance: CanonicalInstanceConfig, + viewKey = 'overview', +): string { + const query = getView(instance, viewKey).query?.trim(); + if (!query) { + throw new Error(`views.${viewKey}.query is required`); + } + return `${query}\n`; +} + +export function overviewSiloDatabaseConfigYaml( + instance: CanonicalInstanceConfig, + viewKey = 'overview', +): string { + const schema = getView(instance, viewKey).schema?.trim(); + if (!schema) { + throw new Error(`views.${viewKey}.schema is required`); + } + return stringifyYaml(toOverviewSiloDatabaseConfig(schema)); +} + +function getView( + instance: CanonicalInstanceConfig, + viewKey: string, +): CanonicalView { + const view = + instance.views?.[viewKey] ?? + (viewKey === 'overview' ? instance.overview : undefined); + if (view === null || view === undefined) { + throw new Error(`View "${viewKey}" is not configured`); + } + return view; +} + +function toOverviewSiloDatabaseConfig(schema: string): { + schema: { + instanceName: string; + opennessLevel: 'OPEN'; + metadata: { + name: string; + type: string; + generateIndex?: true; + generateLineageIndex?: string; + }[]; + primaryKey: 'accessionVersion'; + features: { name: string }[]; + }; +} { + const parsed = parseYaml(schema) as unknown; + if (!isRecord(parsed)) { + throw new Error('overview.schema must be a YAML object'); + } + const schemaNode = parsed.schema; + if (!isRecord(schemaNode)) { + throw new Error('overview.schema must contain a schema object'); + } + if (!Array.isArray(schemaNode.metadata)) { + throw new Error('overview.schema.schema.metadata must be an array'); + } + if (schemaNode.primaryKey !== 'accessionVersion') { + throw new Error( + 'overview.schema.schema.primaryKey must be accessionVersion', + ); + } + return { + schema: { + instanceName: + typeof schemaNode.instanceName === 'string' + ? schemaNode.instanceName + : 'Cross-organism overview', + opennessLevel: 'OPEN', + metadata: schemaNode.metadata.map(toSiloMetadataEntry), + primaryKey: 'accessionVersion', + features: Array.isArray(schemaNode.features) + ? schemaNode.features.filter(isFeatureEntry) + : [{ name: 'generalizedAdvancedQuery' }], + }, + }; +} + +function toSiloMetadataEntry(field: unknown): { + name: string; + type: string; + generateIndex?: true; + generateLineageIndex?: string; +} { + if (!isRecord(field) || typeof field.name !== 'string') { + throw new Error( + 'overview.schema.schema.metadata entries must have a name', + ); + } + const entry: { + name: string; + type: string; + generateIndex?: true; + generateLineageIndex?: string; + } = { + name: field.name, + type: toSiloType(field.type), + }; + if (field.generateIndex === true) { + entry.generateIndex = true; + } + if (typeof field.lineageSystem === 'string' && field.lineageSystem !== '') { + entry.generateIndex = true; + entry.generateLineageIndex = field.lineageSystem; + } + return entry; +} + +function toSiloType(type: unknown): string { + if (type === 'timestamp') return 'int'; + if (type === 'authors') return 'string'; + if (typeof type === 'string' && type !== '') return type; + return 'string'; +} + +function isFeatureEntry(value: unknown): value is { name: string } { + return isRecord(value) && typeof value.name === 'string'; +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} diff --git a/config-tools/src/adapter/referenceGenomes.ts b/config-tools/src/adapter/referenceGenomes.ts new file mode 100644 index 0000000000..06d27473c6 --- /dev/null +++ b/config-tools/src/adapter/referenceGenomes.ts @@ -0,0 +1,71 @@ +// TS port of `_merged-reference-genomes.tpl`: flattens the canonical +// per-segment/per-reference shape into SILO's flat input. +import type { CanonicalOrganismConfig } from '../schema/canonicalConfig.ts'; + +export interface MergedReferenceGenomes { + nucleotideSequences: { name: string; sequence: string }[]; + genes: { name: string; sequence: string }[]; +} + +export interface SegmentInfo { + segments: string[]; + displayNames: Record; +} + +function nucleotideName(segmentName: string, referenceName: string, singleSegment: boolean, singleReference: boolean): string { + if (singleReference) return segmentName; + if (singleSegment) return referenceName; + return `${segmentName}-${referenceName}`; +} + +export function mergeReferenceGenomes(organism: CanonicalOrganismConfig): MergedReferenceGenomes { + const segments = organism.referenceGenomes; + if (segments === null || segments === undefined || segments.length === 0) { + return { + nucleotideSequences: organism.referenceGenome.nucleotideSequences.map((s) => ({ + name: s.name, + sequence: s.sequence, + })), + genes: organism.referenceGenome.genes.map((g) => ({ name: g.name, sequence: g.sequence })), + }; + } + + const nucleotideSequences: { name: string; sequence: string }[] = []; + const genes: { name: string; sequence: string }[] = []; + const singleSegment = segments.length === 1; + + for (const segment of segments) { + const singleReference = segment.references.length === 1; + for (const reference of segment.references) { + const nucName = nucleotideName(segment.name, reference.name, singleSegment, singleReference); + nucleotideSequences.push({ name: nucName, sequence: reference.sequence }); + + if (reference.genes !== null && reference.genes !== undefined) { + for (const gene of reference.genes) { + const geneName = singleReference ? gene.name : `${gene.name}-${reference.name}`; + genes.push({ name: geneName, sequence: gene.sequence }); + } + } + } + } + return { nucleotideSequences, genes }; +} + +export function getNucleotideSegmentInfo(organism: CanonicalOrganismConfig): SegmentInfo { + const segments = organism.referenceGenomes; + if (segments === null || segments === undefined || segments.length === 0) { + const names = organism.referenceGenome.nucleotideSequences.map((s) => s.name); + return { + segments: [...names].sort(), + displayNames: {}, + }; + } + const segmentNames = segments.map((s) => s.name); + const displayNames: Record = {}; + for (const segment of segments) { + if (segment.displayName !== null && segment.displayName !== undefined) { + displayNames[segment.name] = segment.displayName; + } + } + return { segments: [...segmentNames].sort(), displayNames }; +} diff --git a/config-tools/src/adapter/renderers.spec.ts b/config-tools/src/adapter/renderers.spec.ts new file mode 100644 index 0000000000..944fbc2e04 --- /dev/null +++ b/config-tools/src/adapter/renderers.spec.ts @@ -0,0 +1,253 @@ +import { describe, expect, it } from 'vitest'; + +import { commonMetadata } from './commonMetadata.ts'; +import { lineageDefinitionsForOrganism, lineageSystemsForOrganism } from './lineageSystems.ts'; +import { getNucleotideSegmentInfo, mergeReferenceGenomes } from './referenceGenomes.ts'; +import { toSiloDatabaseConfig } from './siloDatabaseConfig.ts'; +import { + canonicalInstanceConfig, + canonicalOrganismConfig, + type CanonicalInstanceConfig, + type CanonicalOrganismConfig, +} from '../schema/canonicalConfig.ts'; + +const baseInstance: CanonicalInstanceConfig = canonicalInstanceConfig.parse({ + name: 'Loculus', + accessionPrefix: 'LOC_', + dataUseTerms: { enabled: false, urls: null }, + fileSharing: { outputFileUrlType: 'website' }, +}); + +const singleSegmentSingleRef: CanonicalOrganismConfig = canonicalOrganismConfig.parse({ + schema: { + organismName: 'Test virus', + metadata: [ + { name: 'date', type: 'date', required: true }, + { name: 'country', type: 'string', generateIndex: true, autocomplete: true }, + { name: 'pangoLineage', type: 'string', lineageSystem: 'pangoLineage' }, + ], + }, + referenceGenome: { + nucleotideSequences: [{ name: 'main', sequence: 'ATCG' }], + genes: [{ name: 'spike', sequence: 'MM' }], + }, +}); + +const multiSegmentMultiRef: CanonicalOrganismConfig = canonicalOrganismConfig.parse({ + schema: { + organismName: 'CCHF', + metadata: [ + { name: 'date', type: 'date', required: true }, + { name: 'coverage', type: 'float', perSegment: true }, + ], + }, + referenceGenome: { nucleotideSequences: [], genes: [] }, + referenceGenomes: [ + { + name: 'L', + references: [ + { name: 'YP_001', sequence: 'AAA', genes: [{ name: 'RdRp', sequence: 'MM' }] }, + { name: 'YP_002', sequence: 'CCC' }, + ], + }, + { + name: 'M', + references: [{ name: 'YP_003', sequence: 'GGG' }], + }, + ], +}); + +describe('commonMetadata', () => { + it('renders the base 15 system fields when dataUseTerms is disabled', () => { + const fields = commonMetadata(baseInstance); + const names = fields.map((f) => f.name); + expect(names).toEqual([ + 'accessionVersion', + 'accession', + 'version', + 'submissionId', + 'isRevocation', + 'submitter', + 'groupName', + 'groupId', + 'submittedAtTimestamp', + 'submittedDate', + 'releasedAtTimestamp', + 'releasedDate', + 'versionStatus', + 'versionComment', + 'pipelineVersion', + ]); + }); + + it('adds the three dataUseTerms fields when enabled but no urls set', () => { + const fields = commonMetadata({ ...baseInstance, dataUseTerms: { enabled: true, urls: null } }); + const names = fields.map((f) => f.name); + expect(names).toContain('dataUseTerms'); + expect(names).toContain('dataUseTermsRestrictedUntil'); + expect(names).toContain('dataBecameOpenAt'); + expect(names).not.toContain('dataUseTermsUrl'); + }); + + it('adds the dataUseTermsUrl field when urls are set', () => { + const fields = commonMetadata({ + ...baseInstance, + dataUseTerms: { enabled: true, urls: { open: 'http://o', restricted: 'http://r' } }, + }); + expect(fields.map((f) => f.name)).toContain('dataUseTermsUrl'); + }); + + it('uses the instance name + accessionPrefix in the accessionVersion definition', () => { + const f = commonMetadata(baseInstance).find((m) => m.name === 'accessionVersion'); + expect(f?.definition).toContain('Loculus'); + expect(f?.definition).toContain('LOC_000001.1'); + }); +}); + +describe('mergeReferenceGenomes', () => { + it('keeps names unchanged for single-segment single-reference', () => { + const m = mergeReferenceGenomes(singleSegmentSingleRef); + expect(m.nucleotideSequences).toEqual([{ name: 'main', sequence: 'ATCG' }]); + expect(m.genes).toEqual([{ name: 'spike', sequence: 'MM' }]); + }); + + it('applies the multi-segment + multi-reference disambiguation', () => { + const m = mergeReferenceGenomes(multiSegmentMultiRef); + // L has two refs, M has one. Multi-segment AND multi-reference (for L) → "segment-reference"; + // M is single-reference → just "M". + expect(m.nucleotideSequences.map((s) => s.name)).toEqual(['L-YP_001', 'L-YP_002', 'M']); + // Genes only come from references that declared them. L/YP_001 has RdRp; multi-reference suffix. + expect(m.genes).toEqual([{ name: 'RdRp-YP_001', sequence: 'MM' }]); + }); + + it('falls back to the simple referenceGenome shape when referenceGenomes is empty', () => { + const m = mergeReferenceGenomes(singleSegmentSingleRef); + expect(m.nucleotideSequences[0].sequence).toBe('ATCG'); + }); +}); + +describe('getNucleotideSegmentInfo', () => { + it('sorts segments alphabetically and pulls displayNames', () => { + const info = getNucleotideSegmentInfo({ + ...multiSegmentMultiRef, + referenceGenomes: [ + { name: 'M', displayName: 'Medium', references: [{ name: 'r1', sequence: 'g' }] }, + { name: 'L', references: [{ name: 'r2', sequence: 'g' }] }, + ], + }); + expect(info.segments).toEqual(['L', 'M']); + expect(info.displayNames).toEqual({ M: 'Medium' }); + }); +}); + +describe('toSiloDatabaseConfig', () => { + it('emits all common + organism metadata with name + (translated) type', () => { + const silo = toSiloDatabaseConfig(baseInstance, singleSegmentSingleRef); + expect(silo.schema.instanceName).toBe('Test virus'); + expect(silo.schema.primaryKey).toBe('accessionVersion'); + expect(silo.schema.features).toEqual([{ name: 'generalizedAdvancedQuery' }]); + + const names = silo.schema.metadata.map((m) => m.name); + // common fields + organism fields + expect(names).toContain('accessionVersion'); + expect(names).toContain('submittedAtTimestamp'); + expect(names).toContain('country'); + expect(names).toContain('pangoLineage'); + + // timestamp → int + const ts = silo.schema.metadata.find((m) => m.name === 'submittedAtTimestamp'); + expect(ts?.type).toBe('int'); + + // generateIndex passed through; lineageSystem → generateIndex + generateLineageIndex + const country = silo.schema.metadata.find((m) => m.name === 'country'); + expect(country?.generateIndex).toBe(true); + const lineage = silo.schema.metadata.find((m) => m.name === 'pangoLineage'); + expect(lineage?.generateIndex).toBe(true); + expect(lineage?.generateLineageIndex).toBe('pangoLineage'); + }); + + it('emits per-segment fields one entry per (sorted) segment name', () => { + const silo = toSiloDatabaseConfig(baseInstance, multiSegmentMultiRef); + const coverageNames = silo.schema.metadata.filter((m) => m.name.startsWith('coverage')).map((m) => m.name); + expect(coverageNames).toEqual(['coverage_L', 'coverage_M']); + // non-perSegment fields stay unsegmented + expect(silo.schema.metadata.some((m) => m.name === 'date')).toBe(true); + expect(silo.schema.metadata.some((m) => m.name === 'date_L')).toBe(false); + }); + + it('appends file categories as plain string fields', () => { + const withFiles = canonicalOrganismConfig.parse({ + ...singleSegmentSingleRef, + schema: { ...singleSegmentSingleRef.schema, files: [{ name: 'rawReads' }, { name: 'qcReport' }] }, + }); + const silo = toSiloDatabaseConfig(baseInstance, withFiles); + const fileEntries = silo.schema.metadata.filter((m) => m.name === 'rawReads' || m.name === 'qcReport'); + expect(fileEntries).toHaveLength(2); + expect(fileEntries.every((e) => e.type === 'string')).toBe(true); + }); +}); + +describe('lineageSystemsForOrganism', () => { + it('returns the unique set of lineageSystem keys, sorted', () => { + const organism = canonicalOrganismConfig.parse({ + ...singleSegmentSingleRef, + schema: { + ...singleSegmentSingleRef.schema, + metadata: [ + { name: 'a', type: 'string', lineageSystem: 'pangoLineage' }, + { name: 'b', type: 'string', lineageSystem: 'nextclade' }, + { name: 'c', type: 'string', lineageSystem: 'pangoLineage' }, + { name: 'd', type: 'string' }, + ], + }, + }); + expect(lineageSystemsForOrganism(organism)).toEqual(['nextclade', 'pangoLineage']); + }); + + it('picks up the lineageSystem from any metadata field on the organism', () => { + // singleSegmentSingleRef has `pangoLineage` with `lineageSystem: pangoLineage` + expect(lineageSystemsForOrganism(singleSegmentSingleRef)).toEqual(['pangoLineage']); + }); + + it('returns [] for an organism with no lineageSystem fields', () => { + const organism = canonicalOrganismConfig.parse({ + ...singleSegmentSingleRef, + schema: { + ...singleSegmentSingleRef.schema, + metadata: [{ name: 'date', type: 'date' }], + }, + }); + expect(lineageSystemsForOrganism(organism)).toEqual([]); + }); +}); + +describe('lineageDefinitionsForOrganism', () => { + const instanceWithDefs: CanonicalInstanceConfig = canonicalInstanceConfig.parse({ + ...baseInstance, + lineageSystemDefinitions: { + pangoLineage: { '1': 'https://example.org/v1.yaml', '2': 'https://example.org/v2.yaml' }, + unused: { '1': 'https://example.org/unused.yaml' }, + }, + }); + + it('forwards the per-version URL map only for the systems the organism references', () => { + // singleSegmentSingleRef references only `pangoLineage`. + expect(lineageDefinitionsForOrganism(instanceWithDefs, singleSegmentSingleRef)).toEqual({ + pangoLineage: { '1': 'https://example.org/v1.yaml', '2': 'https://example.org/v2.yaml' }, + }); + }); + + it('returns {} for an organism with no lineage systems', () => { + const organism = canonicalOrganismConfig.parse({ + ...singleSegmentSingleRef, + schema: { ...singleSegmentSingleRef.schema, metadata: [{ name: 'date', type: 'date' }] }, + }); + expect(lineageDefinitionsForOrganism(instanceWithDefs, organism)).toEqual({}); + }); + + it('throws when the organism references a system the instance does not define', () => { + expect(() => lineageDefinitionsForOrganism(baseInstance, singleSegmentSingleRef)).toThrow( + /lineageSystemDefinitions entry/, + ); + }); +}); diff --git a/config-tools/src/adapter/siloDatabaseConfig.ts b/config-tools/src/adapter/siloDatabaseConfig.ts new file mode 100644 index 0000000000..00cd74d174 --- /dev/null +++ b/config-tools/src/adapter/siloDatabaseConfig.ts @@ -0,0 +1,92 @@ +// TS port of `_siloDatabaseConfig.tpl` — renders `database_config.yaml` for SILO. +import { commonMetadata, type CommonMetadataField } from './commonMetadata.ts'; +import { getNucleotideSegmentInfo } from './referenceGenomes.ts'; +import type { + CanonicalInstanceConfig, + CanonicalOrganismConfig, +} from '../schema/canonicalConfig.ts'; + +export interface SiloDatabaseConfig { + schema: { + instanceName: string; + opennessLevel: 'OPEN'; + metadata: SiloMetadataEntry[]; + primaryKey: 'accessionVersion'; + features: { name: string }[]; + }; +} + +export interface SiloMetadataEntry { + type: string; + name: string; + generateIndex?: boolean; + generateLineageIndex?: string; +} + +// SILO only accepts string/int/float/date/boolean; translate the richer set. +function siloType(canonicalType: string): string { + if (canonicalType === 'timestamp') return 'int'; + if (canonicalType === 'authors') return 'string'; + return canonicalType; +} + +function shared(entry: SchemaLikeField): Omit { + const out: Omit = { type: siloType(entry.type ?? 'string') }; + if (entry.generateIndex === true) out.generateIndex = true; + if (entry.lineageSystem !== null && entry.lineageSystem !== undefined && entry.lineageSystem !== '') { + out.generateIndex = true; + out.generateLineageIndex = entry.lineageSystem; + } + return out; +} + +interface SchemaLikeField { + name: string; + type?: string | null; + perSegment?: boolean | null; + generateIndex?: boolean | null; + lineageSystem?: string | null; +} + +export function toSiloDatabaseConfig( + instance: CanonicalInstanceConfig, + organism: CanonicalOrganismConfig, +): SiloDatabaseConfig { + const { segments } = getNucleotideSegmentInfo(organism); + const isSegmented = segments.length > 1; + + const common = commonMetadata(instance) as unknown as SchemaLikeField[]; + const commonNames = new Set(common.map((c) => c.name)); + const organismMetadata = (organism.schema.metadata as unknown as SchemaLikeField[]).filter( + (m) => !commonNames.has(m.name), + ); + const allMetadata: SchemaLikeField[] = [...common, ...organismMetadata]; + + const out: SiloMetadataEntry[] = []; + for (const entry of allMetadata) { + const base = shared(entry); + if (isSegmented && entry.perSegment === true) { + for (const segment of segments) { + out.push({ ...base, name: `${entry.name}_${segment}` }); + } + } else { + out.push({ ...base, name: entry.name }); + } + } + + for (const file of organism.schema.files) { + out.push({ type: 'string', name: file.name }); + } + + return { + schema: { + instanceName: organism.schema.organismName, + opennessLevel: 'OPEN', + metadata: out, + primaryKey: 'accessionVersion', + features: [{ name: 'generalizedAdvancedQuery' }], + }, + }; +} + +export type { CommonMetadataField }; diff --git a/config-tools/src/adapter/siloPreprocessingConfig.ts b/config-tools/src/adapter/siloPreprocessingConfig.ts new file mode 100644 index 0000000000..2375f6b7fd --- /dev/null +++ b/config-tools/src/adapter/siloPreprocessingConfig.ts @@ -0,0 +1,26 @@ +// SILO's own preprocessing config (input locations + lineage definitions to +// load); distinct from the external Loculus preprocessing pipeline. +import { lineageSystemsForOrganism } from './lineageSystems.ts'; +import type { CanonicalOrganismConfig } from '../schema/canonicalConfig.ts'; + +export interface SiloPreprocessingConfig { + inputDirectory: string; + outputDirectory: string; + ndjsonInputFilename: string; + referenceGenomeFilename: string; + lineageDefinitionFilenames?: string[]; +} + +export function toSiloPreprocessingConfig(organism: CanonicalOrganismConfig): SiloPreprocessingConfig { + const lineageSystems = lineageSystemsForOrganism(organism); + const config: SiloPreprocessingConfig = { + inputDirectory: '/preprocessing/input', + outputDirectory: '/preprocessing/output', + ndjsonInputFilename: 'data.ndjson.zst', + referenceGenomeFilename: 'reference_genomes.json', + }; + if (lineageSystems.length > 0) { + config.lineageDefinitionFilenames = lineageSystems.map((s) => `${s}.yaml`); + } + return config; +} diff --git a/config-tools/src/index.ts b/config-tools/src/index.ts new file mode 100644 index 0000000000..bbafeb81c5 --- /dev/null +++ b/config-tools/src/index.ts @@ -0,0 +1,3 @@ +export * from './schema/canonicalConfig.ts'; +export * from './schema/adminApi.ts'; +export { commonMetadata, type CommonMetadataField } from './adapter/commonMetadata.ts'; diff --git a/config-tools/src/loader/adminClient.ts b/config-tools/src/loader/adminClient.ts new file mode 100644 index 0000000000..e07f7721d9 --- /dev/null +++ b/config-tools/src/loader/adminClient.ts @@ -0,0 +1,188 @@ +// Minimal admin-API client used by the loader CLI. Kept separate from the +// website's `adminConfigClient.ts` to avoid coupling the loader to website +// source paths; the two share Zod schemas via `@loculus/config-tools`. +import { + adminApiError, + adminOrganismsListResponse, + draftMutationResponse, + instanceDraftResponse, + organismDraftResponse, + organismListing, + publishResponse, + type AdminApiError, + type AdminOrganismsListResponse, + type DraftMutationResponse, + type InstanceDraftResponse, + type OrganismDraftResponse, + type OrganismListing, + type PublishResponse, +} from '../schema/adminApi.ts'; +import { + canonicalInstanceConfig, + type CanonicalInstanceConfig, + type CanonicalOrganismConfig, +} from '../schema/canonicalConfig.ts'; + +export class AdminApiHttpError extends Error { + constructor( + readonly status: number, + readonly body: AdminApiError, + ) { + super(`admin api ${status} ${body.error}${body.message !== undefined ? `: ${body.message}` : ''}`); + this.name = 'AdminApiHttpError'; + } +} + +function parseResponseBody(text: string): unknown { + if (text.length === 0) return null; + try { + return JSON.parse(text); + } catch { + return { error: 'invalid_json', message: text.slice(0, 256) } satisfies AdminApiError; + } +} + +function httpError(status: number, parsed: unknown): AdminApiHttpError { + const errBody = adminApiError.safeParse(parsed); + return new AdminApiHttpError( + status, + errBody.success + ? errBody.data + : { error: 'unexpected_error', message: typeof parsed === 'string' ? parsed : undefined }, + ); +} + +export interface AdminClientOptions { + backendUrl: string; + accessToken: string; +} + +export class LoaderAdminClient { + private readonly backendUrl: string; + private readonly accessToken: string; + + constructor(options: AdminClientOptions) { + this.backendUrl = options.backendUrl.replace(/\/$/, ''); + this.accessToken = options.accessToken; + } + + private async send( + method: 'GET' | 'POST' | 'PUT' | 'DELETE', + path: string, + body?: unknown, + ifMatch?: number, + ): Promise<{ status: number; body: unknown }> { + const url = `${this.backendUrl}${path}`; + const headers: Record = { + // eslint-disable-next-line @typescript-eslint/naming-convention + Authorization: `Bearer ${this.accessToken}`, + // eslint-disable-next-line @typescript-eslint/naming-convention + Accept: 'application/json', + }; + if (body !== undefined) headers['Content-Type'] = 'application/json'; + if (ifMatch !== undefined) headers['If-Match'] = String(ifMatch); + + const response = await fetch(url, { + method, + headers, + body: body === undefined ? undefined : JSON.stringify(body), + }); + if (response.status === 204) { + return { status: 204, body: null }; + } + const parsed = parseResponseBody(await response.text()); + if (!response.ok) throw httpError(response.status, parsed); + return { status: response.status, body: parsed }; + } + + async listOrganisms(): Promise { + const { body } = await this.send('GET', '/api/admin/config/organisms'); + return adminOrganismsListResponse.parse(body); + } + + async createOrganism(key: string): Promise { + const { body } = await this.send('POST', '/api/admin/config/organisms', { key }); + return organismListing.parse(body); + } + + async getOrganismDraft(key: string): Promise { + const { status, body } = await this.send( + 'GET', + `/api/admin/config/organisms/${encodeURIComponent(key)}/draft`, + ); + if (status === 204) return null; + return organismDraftResponse.parse(body); + } + + async putOrganismDraft( + key: string, + config: CanonicalOrganismConfig, + ifMatch?: number, + ): Promise { + const { body } = await this.send( + 'PUT', + `/api/admin/config/organisms/${encodeURIComponent(key)}/draft`, + { config }, + ifMatch, + ); + return draftMutationResponse.parse(body); + } + + async publishOrganism(key: string): Promise { + const { body } = await this.send('POST', `/api/admin/config/organisms/${encodeURIComponent(key)}/publish`); + return publishResponse.parse(body); + } + + async markOrganismDeployed(key: string): Promise { + const { body } = await this.send( + 'POST', + `/api/admin/config/organisms/${encodeURIComponent(key)}/mark-deployed`, + ); + return organismListing.parse(body); + } + + async getInstanceDraft(): Promise { + const { status, body } = await this.send('GET', '/api/admin/config/instance/draft'); + if (status === 204) return null; + return instanceDraftResponse.parse(body); + } + + async putInstanceDraft(config: CanonicalInstanceConfig, ifMatch?: number): Promise { + const { body } = await this.send('PUT', '/api/admin/config/instance/draft', { config }, ifMatch); + return draftMutationResponse.parse(body); + } + + async publishInstance(): Promise { + const { body } = await this.send('POST', '/api/admin/config/instance/publish'); + return publishResponse.parse(body); + } + + // Latest published instance config (public read API), used for the loader's idempotency check. + // Returns null if the response can't be parsed, so a schema drift degrades to "republish" rather + // than aborting the loader. + async getPublishedInstanceConfig(): Promise { + const { body } = await this.send('GET', '/api/config/instance'); + const parsed = canonicalInstanceConfig.safeParse((body as { config?: unknown })?.config); + return parsed.success ? parsed.data : null; + } + + // The body is raw text, so this bypasses the JSON `send` helper. + async setPreprocessingConfig(key: string, pipelineVersion: number, content: string): Promise { + const url = + `${this.backendUrl}/api/admin/config/organisms/${encodeURIComponent(key)}` + + `/preprocessing/${pipelineVersion}`; + const response = await fetch(url, { + method: 'PUT', + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + Authorization: `Bearer ${this.accessToken}`, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Content-Type': 'text/plain', + }, + body: content, + }); + if (!response.ok && response.status !== 204) { + throw httpError(response.status, parseResponseBody(await response.text())); + } + } +} diff --git a/config-tools/src/loader/cli.ts b/config-tools/src/loader/cli.ts new file mode 100644 index 0000000000..b543915c16 --- /dev/null +++ b/config-tools/src/loader/cli.ts @@ -0,0 +1,151 @@ +#!/usr/bin/env node +// Posts fixture YAML to the Loculus admin config API. See ./publish.ts for the +// per-mode semantics. +import { readFile } from 'node:fs/promises'; + +import { loadFixtures } from './fixtures.ts'; +import { LoaderAdminClient } from './adminClient.ts'; +import { type LoaderMode, publishFixtures, summariseResult } from './publish.ts'; + +interface ParsedArgs { + backendUrl: string; + accessToken: string; + fixturesDir: string; + mode: LoaderMode; + dryRun: boolean; +} + +function usage(): string { + return `loculus-config-loader [options] + +Required: + --backend-url Base URL of the Loculus backend (e.g. http://localhost:8079) + --fixtures Directory containing instance.yaml + organisms/*.yaml + --admin-token Keycloak access token (loculus_administrator role required) + --admin-token-file ...or read the token from a file (one of --admin-token or --admin-token-file required) + +Optional: + --mode Default: idempotent + --dry-run Print what would happen without making changes + --help Show this message + +Environment variables (used when the matching flag is omitted): + LOCULUS_BACKEND_URL, LOCULUS_FIXTURES_DIR, LOCULUS_ADMIN_TOKEN, LOCULUS_ADMIN_TOKEN_FILE +`; +} + +async function parseArgs(argv: string[]): Promise { + const args: Partial & { tokenFile?: string } = { + backendUrl: process.env.LOCULUS_BACKEND_URL, + fixturesDir: process.env.LOCULUS_FIXTURES_DIR, + accessToken: process.env.LOCULUS_ADMIN_TOKEN, + tokenFile: process.env.LOCULUS_ADMIN_TOKEN_FILE, + mode: 'idempotent', + dryRun: false, + }; + + for (let i = 2; i < argv.length; i++) { + const arg = argv[i]; + const next = (): string => { + const v = argv[++i]; + if (v === undefined) throw new Error(`Missing value for ${arg}`); + return v; + }; + switch (arg) { + case '--backend-url': + args.backendUrl = next(); + break; + case '--fixtures': + args.fixturesDir = next(); + break; + case '--admin-token': + args.accessToken = next(); + break; + case '--admin-token-file': + args.tokenFile = next(); + break; + case '--mode': { + const m = next(); + if (m !== 'idempotent' && m !== 'fresh-only') { + throw new Error(`Unknown --mode ${m}; expected idempotent|fresh-only`); + } + args.mode = m; + break; + } + case '--dry-run': + args.dryRun = true; + break; + case '--help': + case '-h': + console.log(usage()); + process.exit(0); + break; + default: + throw new Error(`Unknown argument ${arg}\n\n${usage()}`); + } + } + + if (args.backendUrl === undefined) throw new Error('Missing --backend-url (or LOCULUS_BACKEND_URL)'); + if (args.fixturesDir === undefined) throw new Error('Missing --fixtures (or LOCULUS_FIXTURES_DIR)'); + if (args.accessToken === undefined && args.tokenFile !== undefined) { + args.accessToken = (await readFile(args.tokenFile, 'utf8')).trim(); + } + if (args.accessToken === undefined) { + throw new Error('Missing --admin-token / --admin-token-file (or LOCULUS_ADMIN_TOKEN[_FILE])'); + } + return args as ParsedArgs; +} + +async function main(): Promise { + let args: ParsedArgs; + try { + args = await parseArgs(process.argv); + } catch (e) { + console.error((e as Error).message); + return 2; + } + + let fixtures; + try { + fixtures = await loadFixtures(args.fixturesDir); + } catch (e) { + console.error(`Failed to load fixtures from ${args.fixturesDir}:`); + console.error((e as Error).message); + return 3; + } + console.log( + `Loaded fixtures: 1 instance config + ${fixtures.organisms.size} organism(s) (${[ + ...fixtures.organisms.keys(), + ].join(', ')})`, + ); + + const client = new LoaderAdminClient({ + backendUrl: args.backendUrl, + accessToken: args.accessToken, + }); + + const result = await publishFixtures(client, fixtures, { + mode: args.mode, + dryRun: args.dryRun, + }); + + console.log('\n' + summariseResult(result)); + + if (result.hadFailures) { + console.error('\nLoader finished with failures:'); + if (result.instance.status === 'failed') console.error(` instance: ${result.instance.reason}`); + for (const o of result.organisms.filter((x) => x.status === 'failed')) { + console.error(` ${o.key}: ${o.reason}`); + } + return 1; + } + return 0; +} + +main().then( + (code) => process.exit(code), + (e) => { + console.error('Unhandled error:', e); + process.exit(99); + }, +); diff --git a/config-tools/src/loader/compare.ts b/config-tools/src/loader/compare.ts new file mode 100644 index 0000000000..0021e26a01 --- /dev/null +++ b/config-tools/src/loader/compare.ts @@ -0,0 +1,34 @@ +// Deep equality used for the loader's idempotency check. `null` and `undefined` +// are treated as equivalent because backend defaults often resurface fields as +// `null` that the fixture YAML leaves unset. +export function deepEqualIgnoringUndefined(a: unknown, b: unknown): boolean { + if (a === b) return true; + if (a === null || a === undefined) return b === null || b === undefined; + if (b === null || b === undefined) return false; + if (typeof a !== typeof b) return false; + + if (Array.isArray(a)) { + if (!Array.isArray(b)) return false; + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (!deepEqualIgnoringUndefined(a[i], b[i])) return false; + } + return true; + } + + if (typeof a === 'object') { + if (typeof b !== 'object' || Array.isArray(b)) return false; + const aRec = a as Record; + const bRec = b as Record; + const aKeys = new Set(Object.keys(aRec).filter((k) => aRec[k] !== undefined && aRec[k] !== null)); + const bKeys = new Set(Object.keys(bRec).filter((k) => bRec[k] !== undefined && bRec[k] !== null)); + if (aKeys.size !== bKeys.size) return false; + for (const k of aKeys) { + if (!bKeys.has(k)) return false; + if (!deepEqualIgnoringUndefined(aRec[k], bRec[k])) return false; + } + return true; + } + + return Object.is(a, b); +} diff --git a/config-tools/src/loader/fixtures.ts b/config-tools/src/loader/fixtures.ts new file mode 100644 index 0000000000..e1c9a634ea --- /dev/null +++ b/config-tools/src/loader/fixtures.ts @@ -0,0 +1,137 @@ +// Reads loader fixtures from disk: an `instance.yaml` plus an `organisms/` +// directory. Validates each file against the canonical Zod schema. +import { readFile, readdir } from 'node:fs/promises'; +import { join } from 'node:path'; + +import { parse as parseYaml } from 'yaml'; +import { z } from 'zod'; + +import { + canonicalInstanceConfig, + canonicalOrganismConfig, + type CanonicalInstanceConfig, + type CanonicalOrganismConfig, +} from '../schema/canonicalConfig.ts'; + +export interface LoadedFixtures { + instance: CanonicalInstanceConfig; + organisms: Map; + // Opaque config files keyed by organism then pipeline version, read verbatim + // from `preprocessing//.`. + preprocessingConfigs: Map>; +} + +async function readYamlFile(path: string, parser: (raw: unknown) => T): Promise { + const raw = await readFile(path, 'utf8'); + let parsed: unknown; + try { + parsed = parseYaml(raw); + } catch (e) { + throw new Error(`Failed to parse YAML at ${path}: ${(e as Error).message}`); + } + try { + return parser(parsed); + } catch (e) { + if (e instanceof z.ZodError) { + throw new Error( + `Schema validation failed for ${path}:\n` + + e.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\n'), + ); + } + throw e; + } +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function normalizeLegacyMetadataAdd(raw: unknown): unknown { + if (!isRecord(raw) || !isRecord(raw.schema)) return raw; + const { schema } = raw; + if (!Array.isArray(schema.metadata) || !Array.isArray(schema.metadataAdd)) return raw; + + const metadataByName = new Map>(); + for (const field of schema.metadata) { + if (isRecord(field) && typeof field.name === 'string') { + metadataByName.set(field.name, field); + } + } + + for (const field of schema.metadataAdd) { + if (!isRecord(field) || typeof field.name !== 'string') continue; + const existing = metadataByName.get(field.name); + if (existing !== undefined) { + Object.assign(existing, field); + } + } + + delete schema.metadataAdd; + return raw; +} + +export async function loadFixtures(dir: string): Promise { + const instancePath = join(dir, 'instance.yaml'); + const instance = await readYamlFile(instancePath, (raw) => canonicalInstanceConfig.parse(raw)); + + const organismsDir = join(dir, 'organisms'); + let organismFiles: string[]; + try { + organismFiles = (await readdir(organismsDir)) + .filter((f) => f.endsWith('.yaml') || f.endsWith('.yml')) + .sort(); + } catch (e) { + if ((e as NodeJS.ErrnoException).code === 'ENOENT') { + return { instance, organisms: new Map(), preprocessingConfigs: new Map() }; + } + throw e; + } + + const organisms = new Map(); + for (const filename of organismFiles) { + const key = filename.replace(/\.ya?ml$/, ''); + const config = await readYamlFile(join(organismsDir, filename), (raw) => + canonicalOrganismConfig.parse(normalizeLegacyMetadataAdd(raw)), + ); + organisms.set(key, config); + } + + const preprocessingConfigs = await loadPreprocessingConfigs(join(dir, 'preprocessing')); + return { instance, organisms, preprocessingConfigs }; +} + +/** + * Reads `preprocessing//.` files verbatim. The file + * stem must be the integer pipeline version; the content is opaque and stored + * as-is. Returns an empty map if the directory is absent. + */ +async function loadPreprocessingConfigs(preprocessingDir: string): Promise>> { + const result = new Map>(); + let organismDirs: import('node:fs').Dirent[]; + try { + organismDirs = (await readdir(preprocessingDir, { withFileTypes: true })).filter((d) => d.isDirectory()); + } catch (e) { + if ((e as NodeJS.ErrnoException).code === 'ENOENT') return result; + throw e; + } + + for (const organismDir of organismDirs.sort((a, b) => a.name.localeCompare(b.name))) { + const organismKey = organismDir.name; + const versionFiles = (await readdir(join(preprocessingDir, organismKey))).sort(); + const byVersion = new Map(); + for (const filename of versionFiles) { + const stem = filename.replace(/\.[^.]+$/, ''); + const pipelineVersion = Number(stem); + if (!Number.isInteger(pipelineVersion) || pipelineVersion < 1) { + throw new Error( + `Invalid preprocessing config filename '${filename}' in ${organismKey}: ` + + `the file stem must be a positive integer pipeline version (e.g. '1.yaml').`, + ); + } + const content = await readFile(join(preprocessingDir, organismKey, filename), 'utf8'); + byVersion.set(pipelineVersion, content); + } + if (byVersion.size > 0) result.set(organismKey, byVersion); + } + return result; +} diff --git a/config-tools/src/loader/publish.spec.ts b/config-tools/src/loader/publish.spec.ts new file mode 100644 index 0000000000..38027b437c --- /dev/null +++ b/config-tools/src/loader/publish.spec.ts @@ -0,0 +1,234 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { LoaderAdminClient } from './adminClient.ts'; +import type { LoadedFixtures } from './fixtures.ts'; +import { publishFixtures } from './publish.ts'; +import { + canonicalInstanceConfig, + canonicalOrganismConfig, + type CanonicalInstanceConfig, + type CanonicalOrganismConfig, +} from '../schema/canonicalConfig.ts'; + +const backendUrl = 'https://backend.test'; + +function jsonResponse(body: unknown, status = 200): Response { + return new Response(JSON.stringify(body), { + status, + // eslint-disable-next-line @typescript-eslint/naming-convention + headers: { 'Content-Type': 'application/json' }, + }); +} + +const sampleInstance: CanonicalInstanceConfig = canonicalInstanceConfig.parse({ + name: 'Loculus', + accessionPrefix: 'LOC_', + dataUseTerms: { enabled: false, urls: null }, + fileSharing: { outputFileUrlType: 'website' }, +}); + +const sampleOrganism: CanonicalOrganismConfig = canonicalOrganismConfig.parse({ + schema: { organismName: 'Test', metadata: [{ name: 'date', type: 'date' }] }, + referenceGenome: { nucleotideSequences: [{ name: 'main', sequence: 'ATG' }], genes: [] }, +}); + +const sampleFixtures: LoadedFixtures = { + instance: sampleInstance, + organisms: new Map([['new-organism', sampleOrganism]]), + preprocessingConfigs: new Map(), +}; + +describe('publishFixtures', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('creates + publishes a fresh organism end-to-end in idempotent mode', async () => { + // Mock backend: empty initial state for instance draft and organism listing, + // then accept the PUT/POST/publish sequence for both instance and the new organism. + const fetchMock = vi.spyOn(globalThis, 'fetch'); + fetchMock + // instance draft fetch: 204 (no draft yet) + .mockResolvedValueOnce(new Response(null, { status: 204 })) + // published instance config differs from the fixture → not idempotent-skippable + .mockResolvedValueOnce(jsonResponse({ config: { ...sampleInstance, name: 'Different instance' } })) + // instance PUT draft → revision 1 + .mockResolvedValueOnce(jsonResponse({ revision: 1 })) + // instance publish → v1 + .mockResolvedValueOnce(jsonResponse({ version: 1, previousVersion: null, publishedAt: 'now', publishedBy: 'loader' })) + // organism list → empty + .mockResolvedValueOnce(jsonResponse({ organisms: [] })) + // POST create organism + .mockResolvedValueOnce(jsonResponse({ key: 'new-organism', status: 'unreleased', currentVersion: null, deployed: false })) + // PUT draft + .mockResolvedValueOnce(jsonResponse({ revision: 1 })) + // publish → v1 + .mockResolvedValueOnce(jsonResponse({ version: 1, previousVersion: null, publishedAt: 'now', publishedBy: 'loader' })) + // mark deployed + .mockResolvedValueOnce(jsonResponse({ key: 'new-organism', status: 'released', currentVersion: 1, deployed: true })); + + const client = new LoaderAdminClient({ backendUrl, accessToken: 'TOKEN' }); + const result = await publishFixtures(client, sampleFixtures, { + mode: 'idempotent', + dryRun: false, + log: () => undefined, + }); + + expect(result.hadFailures).toBe(false); + expect(result.instance.status).toBe('published-new-version'); + expect(result.organisms[0].status).toBe('created'); + expect(result.organisms[0].version).toBe(1); + }); + + it('fresh-only mode fails fast when an organism already exists', async () => { + const fetchMock = vi.spyOn(globalThis, 'fetch'); + fetchMock + // instance draft fetch: 204 + .mockResolvedValueOnce(new Response(null, { status: 204 })) + // instance PUT + publish (instance is allowed in fresh-only since the migration default isn't "customised") + .mockResolvedValueOnce(jsonResponse({ revision: 1 })) + .mockResolvedValueOnce(jsonResponse({ version: 1, previousVersion: null, publishedAt: 'now', publishedBy: 'loader' })) + // organism list shows the target key already released + .mockResolvedValueOnce( + jsonResponse({ + organisms: [{ key: 'new-organism', status: 'released', currentVersion: 1, deployed: true }], + }), + ); + + const client = new LoaderAdminClient({ backendUrl, accessToken: 'TOKEN' }); + const result = await publishFixtures(client, sampleFixtures, { + mode: 'fresh-only', + dryRun: false, + log: () => undefined, + }); + + expect(result.hadFailures).toBe(true); + expect(result.organisms[0].status).toBe('failed'); + expect(result.organisms[0].reason).toContain('fresh-only'); + }); + + it('idempotent mode skips an organism whose existing release already matches the fixture', async () => { + const fetchMock = vi.spyOn(globalThis, 'fetch'); + fetchMock + // instance draft: existing draft equal to target → publish it + .mockResolvedValueOnce(jsonResponse({ config: sampleInstance, baseVersion: 1, revision: 1 })) + .mockResolvedValueOnce(jsonResponse({ version: 2, previousVersion: 1, publishedAt: 'now', publishedBy: 'loader' })) + // organism list: already released + .mockResolvedValueOnce( + jsonResponse({ + organisms: [{ key: 'new-organism', status: 'released', currentVersion: 1, deployed: true }], + }), + ) + // organism draft: 204 → no pending changes, idempotent skip + .mockResolvedValueOnce(new Response(null, { status: 204 })); + + const client = new LoaderAdminClient({ backendUrl, accessToken: 'TOKEN' }); + const result = await publishFixtures(client, sampleFixtures, { + mode: 'idempotent', + dryRun: false, + log: () => undefined, + }); + + expect(result.hadFailures).toBe(false); + expect(result.organisms[0].status).toBe('skipped-equal'); + }); + + it('idempotent mode skips re-publishing the instance when the published config already matches', async () => { + const fetchMock = vi.spyOn(globalThis, 'fetch'); + fetchMock + // instance draft fetch: 204 (no draft) + .mockResolvedValueOnce(new Response(null, { status: 204 })) + // published instance config equals the fixture → idempotent skip, no PUT/publish + .mockResolvedValueOnce(jsonResponse({ config: sampleInstance })) + // organism list: already released and matching + .mockResolvedValueOnce( + jsonResponse({ + organisms: [{ key: 'new-organism', status: 'released', currentVersion: 1, deployed: true }], + }), + ) + // organism draft: 204 → idempotent skip + .mockResolvedValueOnce(new Response(null, { status: 204 })); + + const client = new LoaderAdminClient({ backendUrl, accessToken: 'TOKEN' }); + const result = await publishFixtures(client, sampleFixtures, { + mode: 'idempotent', + dryRun: false, + log: () => undefined, + }); + + expect(result.hadFailures).toBe(false); + expect(result.instance.status).toBe('skipped-equal'); + // No instance PUT/publish happened — only GETs for the instance. + const methodsCalled = fetchMock.mock.calls.map((c) => (c[1] as RequestInit | undefined)?.method); + expect(methodsCalled.every((m) => m === 'GET' || m === undefined)).toBe(true); + }); + + it('seeds preprocessing config files after publishing the organism', async () => { + const fetchMock = vi.spyOn(globalThis, 'fetch'); + fetchMock + // instance draft fetch: 204 (no draft yet) + .mockResolvedValueOnce(new Response(null, { status: 204 })) + // published instance config differs from the fixture → not idempotent-skippable + .mockResolvedValueOnce(jsonResponse({ config: { ...sampleInstance, name: 'Different instance' } })) + // instance PUT draft → revision 1 + .mockResolvedValueOnce(jsonResponse({ revision: 1 })) + // instance publish → v1 + .mockResolvedValueOnce(jsonResponse({ version: 1, previousVersion: null, publishedAt: 'now', publishedBy: 'loader' })) + // organism list → empty + .mockResolvedValueOnce(jsonResponse({ organisms: [] })) + // POST create organism + .mockResolvedValueOnce(jsonResponse({ key: 'new-organism', status: 'unreleased', currentVersion: null, deployed: false })) + // PUT draft + .mockResolvedValueOnce(jsonResponse({ revision: 1 })) + // publish → v1 + .mockResolvedValueOnce(jsonResponse({ version: 1, previousVersion: null, publishedAt: 'now', publishedBy: 'loader' })) + // mark deployed + .mockResolvedValueOnce(jsonResponse({ key: 'new-organism', status: 'released', currentVersion: 1, deployed: true })) + // PUT preprocessing config v1 → 204 + .mockResolvedValueOnce(new Response(null, { status: 204 })); + + const fixtures: LoadedFixtures = { + instance: sampleInstance, + organisms: new Map([['new-organism', sampleOrganism]]), + preprocessingConfigs: new Map([['new-organism', new Map([[1, 'batch_size: 50\n']])]]), + }; + + const client = new LoaderAdminClient({ backendUrl, accessToken: 'TOKEN' }); + const result = await publishFixtures(client, fixtures, { + mode: 'idempotent', + dryRun: false, + log: () => undefined, + }); + + expect(result.hadFailures).toBe(false); + const lastCall = fetchMock.mock.calls.at(-1)!; + expect(lastCall[0]).toBe(`${backendUrl}/api/admin/config/organisms/new-organism/preprocessing/1`); + const init = lastCall[1] as RequestInit; + expect(init.method).toBe('PUT'); + expect(init.body).toBe('batch_size: 50\n'); + expect((init.headers as Record)['Content-Type']).toBe('text/plain'); + }); + + it('dry-run mode makes no PUT/POST/publish calls', async () => { + const fetchMock = vi.spyOn(globalThis, 'fetch'); + fetchMock + // instance draft fetch: 204 + .mockResolvedValueOnce(new Response(null, { status: 204 })) + // published instance config differs → would PUT+publish, but dry-run short-circuits + .mockResolvedValueOnce(jsonResponse({ config: { ...sampleInstance, name: 'Different instance' } })) + // organism list: empty + .mockResolvedValueOnce(jsonResponse({ organisms: [] })); + + const client = new LoaderAdminClient({ backendUrl, accessToken: 'TOKEN' }); + const result = await publishFixtures(client, sampleFixtures, { + mode: 'idempotent', + dryRun: true, + log: () => undefined, + }); + + expect(result.hadFailures).toBe(false); + // Only the two GETs happened — no PUTs / POSTs. + const methodsCalled = fetchMock.mock.calls.map((c) => (c[1] as RequestInit | undefined)?.method); + expect(methodsCalled.every((m) => m === 'GET' || m === undefined)).toBe(true); + }); +}); diff --git a/config-tools/src/loader/publish.ts b/config-tools/src/loader/publish.ts new file mode 100644 index 0000000000..d756c55d40 --- /dev/null +++ b/config-tools/src/loader/publish.ts @@ -0,0 +1,247 @@ +// Modes: +// - idempotent (default): skip exact matches; fail if a fixture diverges from +// what's already published. +// - fresh-only: fail if any fixture organism already exists. For Helm +// post-install. +import type { LoadedFixtures } from './fixtures.ts'; +import { LoaderAdminClient, AdminApiHttpError } from './adminClient.ts'; +import { deepEqualIgnoringUndefined } from './compare.ts'; +import type { + CanonicalInstanceConfig, + CanonicalOrganismConfig, +} from '../schema/canonicalConfig.ts'; + +export type LoaderMode = 'idempotent' | 'fresh-only'; + +export interface PublishOptions { + mode: LoaderMode; + dryRun: boolean; + log?: (msg: string) => void; +} + +export interface OrganismResult { + key: string; + status: 'created' | 'skipped-equal' | 'published-new-version' | 'failed'; + version?: number; + reason?: string; +} + +export interface InstanceResult { + status: 'skipped-equal' | 'published-new-version' | 'failed'; + version?: number; + reason?: string; +} + +export interface LoaderResult { + instance: InstanceResult; + organisms: OrganismResult[]; + hadFailures: boolean; +} + +export async function publishFixtures( + client: LoaderAdminClient, + fixtures: LoadedFixtures, + options: PublishOptions, +): Promise { + const log = options.log ?? ((msg) => console.log(msg)); + + const instanceResult = await publishInstance(client, fixtures.instance, options, log); + const organismResults: OrganismResult[] = []; + let seedingFailed = false; + for (const [key, organismConfig] of fixtures.organisms) { + const result = await publishOrganism(client, key, organismConfig, options, log); + organismResults.push(result); + if (result.status !== 'failed') { + const ok = await seedPreprocessingConfigs( + client, + key, + fixtures.preprocessingConfigs.get(key), + options, + log, + ); + if (!ok) seedingFailed = true; + } + } + + const hadFailures = + instanceResult.status === 'failed' || organismResults.some((r) => r.status === 'failed') || seedingFailed; + return { instance: instanceResult, organisms: organismResults, hadFailures }; +} + +/** + * PUTs each opaque preprocessing config file for an organism. Idempotent: a PUT + * replaces the current value. Returns false if any write failed. + */ +async function seedPreprocessingConfigs( + client: LoaderAdminClient, + key: string, + configs: Map | undefined, + options: PublishOptions, + log: (msg: string) => void, +): Promise { + if (configs === undefined || configs.size === 0) return true; + for (const [version, content] of [...configs.entries()].sort((a, b) => a[0] - b[0])) { + if (options.dryRun) { + log(`[dry-run] ${key}: would set preprocessing config for pipeline version ${version}`); + continue; + } + try { + await client.setPreprocessingConfig(key, version, content); + log(`${key}: set preprocessing config for pipeline version ${version}`); + } catch (e) { + const reason = e instanceof AdminApiHttpError ? `${e.body.error}: ${e.body.message ?? ''}` : String(e); + log(`${key}: FAILED to set preprocessing config for pipeline version ${version} — ${reason}`); + return false; + } + } + return true; +} + +async function publishInstance( + client: LoaderAdminClient, + target: CanonicalInstanceConfig, + options: PublishOptions, + log: (msg: string) => void, +): Promise { + try { + const draft = await client.getInstanceDraft(); + if (draft !== null) { + // fresh-only forbids any pre-existing draft, matching the organism contract. + if (options.mode === 'fresh-only') { + return { + status: 'failed', + reason: 'instance draft already exists; fresh-only mode forbids existing state', + }; + } + if (!deepEqualIgnoringUndefined(draft.config, target)) { + // A divergent draft means manual edits the loader must not silently clobber. + return { + status: 'failed', + reason: 'instance has a divergent draft; resolve it in the admin UI', + }; + } + if (options.dryRun) { + log('[dry-run] instance: would publish existing matching draft'); + return { status: 'published-new-version' }; + } + const result = await client.publishInstance(); + log(`instance: published v${result.version} from existing draft`); + return { status: 'published-new-version', version: result.version }; + } + + // No draft. Unlike organisms, the instance always has a published version (seeded by the + // migration), so "no draft" does not by itself imply the fixture is already applied. In + // idempotent mode compare against the published config and skip when it already matches; + // fresh-only always publishes (the freshly-seeded default is not the fixture). + if (options.mode === 'idempotent') { + const published = await client.getPublishedInstanceConfig(); + if (published !== null && deepEqualIgnoringUndefined(published, target)) { + log('instance: published config already matches fixture — skipping'); + return { status: 'skipped-equal' }; + } + } + + if (options.dryRun) { + log('[dry-run] instance: would PUT draft + publish'); + return { status: 'published-new-version' }; + } + await client.putInstanceDraft(target); + const result = await client.publishInstance(); + log(`instance: published v${result.version}`); + return { status: 'published-new-version', version: result.version }; + } catch (e) { + const reason = e instanceof AdminApiHttpError ? `${e.body.error}: ${e.body.message ?? ''}` : String(e); + log(`instance: FAILED — ${reason}`); + return { status: 'failed', reason }; + } +} + +async function publishOrganism( + client: LoaderAdminClient, + key: string, + target: CanonicalOrganismConfig, + options: PublishOptions, + log: (msg: string) => void, +): Promise { + try { + const all = await client.listOrganisms(); + const existing = all.organisms.find((o) => o.key === key); + + if (existing === undefined) { + if (options.dryRun) { + log(`[dry-run] ${key}: would create + publish v1`); + return { key, status: 'created' }; + } + await client.createOrganism(key); + await client.putOrganismDraft(key, target); + const result = await client.publishOrganism(key); + await client.markOrganismDeployed(key); + log(`${key}: created, published v1, and marked deployed`); + return { key, status: 'created', version: result.version }; + } + + if (options.mode === 'fresh-only') { + return { + key, + status: 'failed', + reason: 'organism already exists; fresh-only mode forbids existing state', + }; + } + + if (existing.status === 'unreleased') { + if (options.dryRun) { + log(`[dry-run] ${key}: would PUT draft on existing unreleased organism + publish v1`); + return { key, status: 'created' }; + } + await client.putOrganismDraft(key, target); + const result = await client.publishOrganism(key); + await client.markOrganismDeployed(key); + log(`${key}: published v1 (unreleased → released) and marked deployed`); + return { key, status: 'created', version: result.version }; + } + + const draft = await client.getOrganismDraft(key); + const currentConfig = draft?.config; + if (currentConfig !== undefined && deepEqualIgnoringUndefined(currentConfig, target)) { + if (options.dryRun) { + log(`[dry-run] ${key}: draft already matches — would publish next version`); + return { key, status: 'published-new-version' }; + } + const result = await client.publishOrganism(key); + log(`${key}: published v${result.version} from existing matching draft`); + return { key, status: 'published-new-version', version: result.version }; + } + + if (draft === null) { + // Absence of a draft is treated as "already in the target state" — + // the loader cannot compare against the published version directly. + log(`${key}: no draft; assuming current published version matches — skipping`); + return { key, status: 'skipped-equal' }; + } + + // Released organism whose draft diverges from the fixture: the loader + // does not overwrite a released config — resolve it in the admin UI. + return { + key, + status: 'failed', + reason: 'released organism has a divergent draft; resolve it in the admin UI', + }; + } catch (e) { + const reason = e instanceof AdminApiHttpError ? `${e.body.error}: ${e.body.message ?? ''}` : String(e); + log(`${key}: FAILED — ${reason}`); + return { key, status: 'failed', reason }; + } +} + +export function summariseResult(result: LoaderResult): string { + const lines: string[] = []; + lines.push(`Instance: ${result.instance.status}` + (result.instance.version ? ` (v${result.instance.version})` : '')); + const counts = result.organisms.reduce>((acc, r) => { + acc[r.status] = (acc[r.status] ?? 0) + 1; + return acc; + }, {}); + for (const [status, n] of Object.entries(counts)) { + lines.push(`Organisms ${status}: ${n}`); + } + return lines.join('\n'); +} diff --git a/config-tools/src/schema/adminApi.ts b/config-tools/src/schema/adminApi.ts new file mode 100644 index 0000000000..bf7f99ed00 --- /dev/null +++ b/config-tools/src/schema/adminApi.ts @@ -0,0 +1,110 @@ +// Admin-only response shapes for `/api/admin/config/...`. Mirrors backend DTOs +// in AdminConfigController.kt / AuditLogService.kt / ConfigService.kt. +import { z } from 'zod'; + +import { canonicalInstanceConfig, canonicalOrganismConfig } from './canonicalConfig.ts'; + +export const organismListing = z.object({ + key: z.string(), + status: z.enum(['unreleased', 'released']), + currentVersion: z.number().nullable(), + deployed: z.boolean(), +}); +export type OrganismListing = z.infer; + +export const adminOrganismsListResponse = z.object({ + organisms: z.array(organismListing), +}); +export type AdminOrganismsListResponse = z.infer; + +export const pendingOp = z.object({ + opType: z.string(), + summary: z.string(), + appliedAt: z.string(), + appliedBy: z.string(), +}); +export type PendingOp = z.infer; + +export const organismDraftResponse = z.object({ + config: canonicalOrganismConfig, + baseVersion: z.number().nullable(), + revision: z.number(), + operations: z.array(pendingOp), +}); +export type OrganismDraftResponse = z.infer; + +export const instanceDraftResponse = z.object({ + config: canonicalInstanceConfig, + baseVersion: z.number().nullable(), + revision: z.number(), +}); +export type InstanceDraftResponse = z.infer; + +export const draftMutationResponse = z.object({ + revision: z.number(), +}); +export type DraftMutationResponse = z.infer; + +export const publishResponse = z.object({ + version: z.number(), + previousVersion: z.number().nullable(), + publishedAt: z.string(), + publishedBy: z.string(), +}); +export type PublishResponse = z.infer; + +export const versionListing = z.object({ + version: z.number(), + publishedAt: z.string(), + publishedBy: z.string(), +}); +export type VersionListing = z.infer; + +export const versionsResponse = z.object({ + versions: z.array(versionListing), +}); +export type VersionsResponse = z.infer; + +export const auditEntry = z.object({ + id: z.number(), + occurredAt: z.string(), + actor: z.string(), + scope: z.enum(['instance', 'organism']), + organismKey: z.string().nullable(), + action: z.enum(['organism_create', 'document_replace', 'op_append', 'publish', 'mark_deployed', 'discard_draft']), + details: z.record(z.string(), z.unknown()).nullable(), + resultVersion: z.number().nullable(), +}); +export type AuditEntry = z.infer; + +export const auditResponse = z.object({ + entries: z.array(auditEntry), +}); +export type AuditResponse = z.infer; + +export const preprocessingConfigVersion = z.object({ + pipelineVersion: z.number(), + updatedAt: z.string(), + updatedBy: z.string(), +}); +export type PreprocessingConfigVersion = z.infer; + +export const preprocessingConfigListResponse = z.object({ + versions: z.array(preprocessingConfigVersion), +}); +export type PreprocessingConfigListResponse = z.infer; + +export const operationRequest = z.object({ + type: z.string(), + payload: z.record(z.string(), z.unknown()), +}); +export type OperationRequest = z.infer; + +// `errors` is populated only by `operation_validation_failed`. +export const adminApiError = z.object({ + error: z.string(), + message: z.string().optional(), + opType: z.string().optional(), + errors: z.array(z.object({ path: z.string(), message: z.string() })).optional(), +}); +export type AdminApiError = z.infer; diff --git a/config-tools/src/schema/canonicalConfig.ts b/config-tools/src/schema/canonicalConfig.ts new file mode 100644 index 0000000000..76c7158f4d --- /dev/null +++ b/config-tools/src/schema/canonicalConfig.ts @@ -0,0 +1,340 @@ +// Canonical config shapes returned by `/api/config/...`. Mirror the Kotlin +// types in `backend/.../config/Config.kt` + `InstanceConfig.kt`; keep these in +// lock-step (widen on the backend side first). +import { z } from 'zod'; + +export const metadataPossibleTypes = z.enum([ + 'string', + 'date', + 'int', + 'float', + 'number', + 'timestamp', + 'boolean', + 'authors', +] as const); +export type MetadataType = z.infer; + +export const orderDirection = z.enum(['ascending', 'descending']); +export type OrderDirection = z.infer; + +export const canonicalLogo = z.object({ + url: z.string(), + alt: z.string().optional().nullable(), + width: z.number().optional().nullable(), + height: z.number().optional().nullable(), +}); + +export const canonicalSupportContact = z.object({ + email: z.string().optional().nullable(), + url: z.string().optional().nullable(), +}); + +export const canonicalFileSharing = z.object({ + outputFileUrlType: z.enum(['website', 'backend', 's3']).default('website'), +}); + +export const canonicalDataUseTermsUrls = z + .object({ + open: z.string(), + restricted: z.string(), + }) + .nullable(); + +export const canonicalDataUseTerms = z.object({ + enabled: z.boolean(), + urls: canonicalDataUseTermsUrls, +}); + +export const canonicalFieldToDisplay = z.object({ + field: z.string(), + displayName: z.string(), +}); + +export const canonicalSeqSetGraph = z.object({ + name: z.string(), + displayName: z.string(), + type: z.enum(['date', 'category']), + fields: z.array(z.string()), +}); + +export const canonicalGithubSequenceFlagging = z.object({ + organization: z.string(), + repository: z.string(), + issueTemplate: z.string().optional().nullable(), +}); + +export const canonicalSequenceFlagging = z.object({ + github: canonicalGithubSequenceFlagging, +}); + +export const canonicalViewUnalignedNucleotideSequences = z.object({ + enabled: z.boolean().default(false), + segments: z.array(z.string()).default([]), + sourceSegments: z.record(z.string(), z.record(z.string(), z.string())).default({}), +}); + +export const canonicalViewSequenceData = z.object({ + unalignedNucleotideSequences: canonicalViewUnalignedNucleotideSequences.optional().nullable(), +}); + +/** + * Instance-level configuration for SQL-backed views. Admins provide a SQL query + * over per-organism DuckDB views and a manual SILO database_config.yaml schema + * for the query output. Views are metadata-only unless sequenceData opts in to + * unaligned nucleotide sequences. + */ +export const canonicalView = z.object({ + displayName: z.string().default('Overview'), + query: z.string().min(1), + schema: z.string().min(1), + tableColumns: z.array(z.string()).default([]), + sequenceData: canonicalViewSequenceData.optional().nullable(), + // Upstream LAPIS base URL for the view instance (used by the backend proxy). + lapisUrl: z.string().optional().nullable(), +}); + +export const canonicalOverview = canonicalView; +export type CanonicalOverview = z.infer; +export type CanonicalView = z.infer; + +export const canonicalInstanceConfig = z.object({ + name: z.string(), + accessionPrefix: z.string(), + dataUseTerms: canonicalDataUseTerms, + fileSharing: canonicalFileSharing, + description: z.string().optional().nullable(), + logo: canonicalLogo.optional().nullable(), + supportContact: canonicalSupportContact.optional().nullable(), + bannerMessage: z.string().optional().nullable(), + bannerMessageURL: z.string().optional().nullable(), + submissionBannerMessage: z.string().optional().nullable(), + submissionBannerMessageURL: z.string().optional().nullable(), + welcomeMessageHTML: z.string().optional().nullable(), + additionalHeadHTML: z.string().optional().nullable(), + gitHubEditLink: z.string().optional().nullable(), + gitHubMainUrl: z.string().optional().nullable(), + gitHubIssuesUrl: z.string().optional().nullable(), + issuesEmail: z.string().optional().nullable(), + enableSeqSets: z.boolean().default(false), + seqSetsFieldsToDisplay: z.array(canonicalFieldToDisplay).optional().nullable(), + seqSetsGraphs: z.array(canonicalSeqSetGraph).optional().nullable(), + enableLoginNavigationItem: z.boolean().default(true), + enableSubmissionNavigationItem: z.boolean().default(true), + enableSubmissionPages: z.boolean().default(true), + dataUseTermsAgreementHTML: z.string().optional().nullable(), + sequenceFlagging: canonicalSequenceFlagging.optional().nullable(), + dateFieldForGroupGraph: z.string().optional().nullable(), + // Map: lineage system key → pipeline version (string) → definition-file URL. + lineageSystemDefinitions: z.record(z.string(), z.record(z.string(), z.string())).optional().nullable(), + // SQL-backed metadata-only views, keyed by public route/proxy key. + views: z.record(z.string(), canonicalView).default({}), + // Legacy single overview table / LAPIS instance configuration. + overview: canonicalOverview.optional().nullable(), +}); +export type CanonicalInstanceConfig = z.infer; + +export const canonicalInstanceResponse = z.object({ + version: z.number(), + publishedAt: z.string(), + config: canonicalInstanceConfig, + readOnlyMode: z.boolean().default(false), +}); +export type CanonicalInstanceResponse = z.infer; + +export const canonicalRangeBound = z.enum(['lower', 'upper']); +export const canonicalRangeOverlapSearch = z.object({ + rangeName: z.string(), + rangeDisplayName: z.string(), + bound: canonicalRangeBound, +}); + +export const canonicalCustomDisplay = z.record(z.string(), z.unknown()); + +export const canonicalMetadata = z.object({ + name: z.string(), + type: metadataPossibleTypes, + required: z.boolean().optional(), + displayName: z.string().optional().nullable(), + description: z.string().optional().nullable(), + definition: z.string().optional().nullable(), + header: z.string().optional().nullable(), + hidden: z.boolean().optional().nullable(), + customDisplay: canonicalCustomDisplay.optional().nullable(), + autocomplete: z.boolean().optional().nullable(), + notSearchable: z.boolean().optional().nullable(), + noInput: z.boolean().optional().nullable(), + hideInSearchResultsTable: z.boolean().optional().nullable(), + initiallyVisible: z.boolean().optional().nullable(), + hideOnSequenceDetailsPage: z.boolean().optional().nullable(), + rangeSearch: z.boolean().optional().nullable(), + rangeOverlapSearch: canonicalRangeOverlapSearch.optional().nullable(), + substringSearch: z.boolean().optional().nullable(), + lineageSearch: z.boolean().optional().nullable(), + columnWidth: z.number().optional().nullable(), + order: z.number().optional().nullable(), + orderOnDetailsPage: z.number().optional().nullable(), + orderInSearchDisplay: z.number().optional().nullable(), + includeInDownloadsByDefault: z.boolean().optional().nullable(), + onlyForReference: z.string().optional().nullable(), + isSequenceFilter: z.boolean().optional().nullable(), + relatesToSegment: z.string().optional().nullable(), + percentage: z.boolean().optional().nullable(), + // Adapter-side fields used to render SILO config. + perSegment: z.boolean().optional().nullable(), + lineageSystem: z.string().optional().nullable(), + generateIndex: z.boolean().optional().nullable(), + oneHeader: z.boolean().optional().nullable(), + options: z.array(z.object({ name: z.string() })).optional().nullable(), + ingest: z.string().optional().nullable(), + ontology_id: z.string().optional().nullable(), +}); + +export const canonicalExternalMetadata = z.object({ + externalMetadataUpdater: z.string(), + name: z.string(), + type: metadataPossibleTypes, + required: z.boolean().optional(), +}); + +export const canonicalInputFieldOption = z.object({ name: z.string() }); +export const canonicalInputField = z.object({ + name: z.string(), + displayName: z.string().optional().nullable(), + noEdit: z.boolean().optional().nullable(), + required: z.boolean().optional().nullable(), + definition: z.string().optional().nullable(), + example: z.union([z.string(), z.number()]).optional().nullable(), + guidance: z.string().optional().nullable(), + desired: z.boolean().optional().nullable(), + options: z.array(canonicalInputFieldOption).optional().nullable(), +}); + +export const canonicalMultiFieldSearch = z.object({ + name: z.string(), + displayName: z.string(), + fields: z.array(z.string()), + orderInSearchDisplay: z.number().optional().nullable(), +}); + +export const canonicalLinkOut = z.object({ + name: z.string(), + url: z.string(), + maxNumberOfRecommendedEntries: z.number().int().positive().optional().nullable(), + onlyForReferences: z.record(z.string(), z.string()).optional().nullable(), + category: z.string().optional().nullable(), +}); + +export const canonicalFileCategory = z.object({ + name: z.string(), + displayName: z.string().optional().nullable(), +}); + +export const canonicalFilesSubmissionDataType = z.object({ + enabled: z.boolean(), + categories: z.array(canonicalFileCategory), +}); + +export const canonicalSubmissionDataTypes = z.object({ + consensusSequences: z.boolean().default(true), + // Whether aligned nucleotide sequences / mutations / insertions are available (LAPIS query API support) + alignedNucleotideSequences: z.boolean().default(true), + maxSequencesPerEntry: z.number().int().optional().nullable(), + files: canonicalFilesSubmissionDataType.optional().nullable(), +}); + +export const canonicalEarliestReleaseDate = z.object({ + enabled: z.boolean().default(false), + externalFields: z.array(z.string()), +}); + +export const canonicalSchema = z.object({ + // Legacy. The original required name field from the pre-DB Helm config + // schema. Still read by `website/src/services/configTransform.ts` (maps + // it onto the website-internal Schema.organismName) and by the + // SILO database config (`instanceName`). New code should prefer the + // top-level `OrganismConfig.displayName` and treat this as a backward- + // compat fallback; this field will be removed once all consumers + // migrate. + organismName: z.string(), + image: z.string().optional().nullable(), + metadata: z.array(canonicalMetadata), + externalMetadata: z.array(canonicalExternalMetadata).default([]), + metadataTemplate: z.array(z.string()).optional().nullable(), + inputFields: z.array(canonicalInputField).default([]), + tableColumns: z.array(z.string()).default([]), + primaryKey: z.string().optional().nullable(), + defaultOrderBy: z.string().optional().nullable(), + defaultOrder: orderDirection.optional().nullable(), + earliestReleaseDate: canonicalEarliestReleaseDate.optional(), + submissionDataTypes: canonicalSubmissionDataTypes.optional(), + files: z.array(canonicalFileCategory).default([]), + loadSequencesAutomatically: z.boolean().optional().nullable(), + richFastaHeaderFields: z.array(z.string()).optional().nullable(), + linkOuts: z.array(canonicalLinkOut).default([]), + referenceIdentifierField: z.string().optional().nullable(), + multiFieldSearches: z.array(canonicalMultiFieldSearch).optional().nullable(), +}); +export type CanonicalSchema = z.infer; + +export const canonicalReferenceGene = z.object({ name: z.string(), sequence: z.string() }); +export const canonicalReference = z.object({ + name: z.string(), + displayName: z.string().optional().nullable(), + sequence: z.string(), + insdcAccessionFull: z.string().optional().nullable(), + genes: z.array(canonicalReferenceGene).optional().nullable(), +}); +export const canonicalReferenceGenomeSegment = z.object({ + name: z.string(), + displayName: z.string().optional().nullable(), + references: z.array(canonicalReference), +}); + +export const canonicalReferenceSequence = z.object({ name: z.string(), sequence: z.string() }); +export const canonicalReferenceGenome = z.object({ + nucleotideSequences: z.array(canonicalReferenceSequence), + genes: z.array(canonicalReferenceSequence), +}); +export type CanonicalReferenceGenome = z.infer; + +export const canonicalOrganismImage = z.object({ url: z.string() }); + +export const canonicalOrganismConfig = z.object({ + schema: canonicalSchema, + referenceGenome: canonicalReferenceGenome, + /** + * Human-facing display name for this organism. The canonical name field + * for new code; preferred over the legacy `schema.organismName`. + */ + displayName: z.string().optional().nullable(), + description: z.string().optional().nullable(), + image: canonicalOrganismImage.optional().nullable(), + referenceGenomes: z.array(canonicalReferenceGenomeSegment).optional().nullable(), + /** + * Upstream LAPIS base URL for this organism, used by the backend's LAPIS + * proxy / query API to forward requests. Set by deployment tooling; the + * proxy returns 404 when it is absent. + */ + lapisUrl: z.string().optional().nullable(), +}); +export type CanonicalOrganismConfig = z.infer; + +export const canonicalOrganismResponse = z.object({ + key: z.string(), + version: z.number(), + publishedAt: z.string(), + config: canonicalOrganismConfig, +}); +export type CanonicalOrganismResponse = z.infer; + +export const canonicalOrganismsListResponse = z.object({ + organisms: z.array( + z.object({ + key: z.string(), + displayName: z.string(), + currentVersion: z.number(), + }), + ), +}); +export type CanonicalOrganismsListResponse = z.infer; diff --git a/config-tools/tsconfig.json b/config-tools/tsconfig.json new file mode 100644 index 0000000000..7fb57a5371 --- /dev/null +++ b/config-tools/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022"], + "outDir": "dist", + "rootDir": "src", + "strict": true, + "noImplicitAny": true, + "noImplicitOverride": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "skipLibCheck": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "verbatimModuleSyntax": true, + "isolatedModules": true + }, + "include": ["src/**/*"] +} diff --git a/config-tools/vitest.config.ts b/config-tools/vitest.config.ts new file mode 100644 index 0000000000..4bddc572e1 --- /dev/null +++ b/config-tools/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['src/**/*.spec.ts'], + environment: 'node', + }, +}); diff --git a/deploy.py b/deploy.py index 332c2efce0..09f76d3c9c 100755 --- a/deploy.py +++ b/deploy.py @@ -255,6 +255,17 @@ def handle_helm(): # noqa: C901 if args.for_e2e or args.dev: parameters += ["-f", HELM_CHART_DIR / "values_e2e_and_dev.yaml"] parameters += ["--skip-schema-validation"] + # Helm waits for post-install hooks (the config-loader Job is one). + # On a fresh cluster, Keycloak's cold start can take longer than + # Helm's default 5-minute hook wait. Give it 15 minutes of headroom. + if not args.template: + parameters += ["--timeout", "15m"] + # `--branch local` is the convention for running locally-built images imported + # into k3d via `./build-local-images.sh`. Without IfNotPresent kubelet would + # try to fetch `:local` from ghcr.io and fail with 403, even though the image + # is sitting right there in the cluster's containerd. + if branch == "local": + parameters += ["-f", HELM_CHART_DIR / "values_local_images.yaml"] if args.sha: parameters += ["--set", f"sha={args.sha[:7]}"] @@ -357,17 +368,9 @@ def generate_configs(from_live, live_host, enable_ena, values_files=None): values_files=values_files, ) - website_config_path = temp_dir_path / "website_config.json" - generate_config( - helm_chart, - "templates/loculus-website-config.yaml", - website_config_path, - codespace_name, - from_live, - live_host, - values_files=values_files, - ) - + # website_config.json is no longer generated: organism domain config now + # lives in the backend DB and the website fetches it per request via + # configMiddleware. Only the technical runtime_config.json is still needed. runtime_config_path = temp_dir_path / "runtime_config.json" generate_config( helm_chart, @@ -408,19 +411,10 @@ def generate_configs(from_live, live_host, enable_ena, values_files=None): values_files=values_files, ) - prepro_configmap_path = temp_dir_path / "preprocessing-config.yaml" - prepro_template_path = "templates/loculus-preprocessing-config.yaml" - prepro_configout_path = temp_dir_path / "preprocessing-config.yaml" - generate_config( - helm_chart, - prepro_template_path, - prepro_configmap_path, - codespace_name, - from_live, - live_host, - prepro_configout_path, - values_files=values_files, - ) + # The Loculus preprocessing pipeline config is no longer rendered from Helm: + # the pipeline fetches its opaque config file and organism metadata directly + # from the backend (see config-architecture/61_preprocessingPipeline.md), so + # the old templates/loculus-preprocessing-config.yaml has been removed. run_command( [ diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 8c1fa42aea..c9595230ea 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -61,6 +61,15 @@ export default defineConfig({ items: [ { label: 'Getting started', link: '/for-administrators/getting-started/' }, { label: 'My first Loculus', link: '/for-administrators/my-first-loculus/' }, + { label: 'Local development instance', link: '/for-administrators/local-dev-instance/' }, + { + label: 'Configuration', + items: [ + 'for-administrators/configuration-system', + 'for-administrators/managing-configuration', + 'for-administrators/rolling-out-organism-config', + ], + }, { label: 'Setup with k3d and nginx', link: '/for-administrators/setup-with-k3d-and-nginx/' }, { label: 'Setup with Kubernetes', link: '/for-administrators/setup-with-kubernetes/' }, { label: 'Schema designs', link: '/for-administrators/schema-designs/' }, @@ -70,6 +79,7 @@ export default defineConfig({ 'for-administrators/pipeline-concept', 'for-administrators/existing-preprocessing-pipelines', 'for-administrators/build-new-preprocessing-pipeline', + 'for-administrators/configure-pipeline-admin-panel', ], }, { label: 'Configuring extra files', link: '/for-administrators/configuring-extra-files/' }, diff --git a/docs/src/content/docs/for-administrators/build-new-preprocessing-pipeline.md b/docs/src/content/docs/for-administrators/build-new-preprocessing-pipeline.md index 239cfccf6d..7b264d243f 100644 --- a/docs/src/content/docs/for-administrators/build-new-preprocessing-pipeline.md +++ b/docs/src/content/docs/for-administrators/build-new-preprocessing-pipeline.md @@ -16,28 +16,27 @@ To call the backend, the preprocessing pipeline needs to use an account with the The [preprocessing pipeline specification](https://github.com/loculus-project/loculus/blob/main/preprocessing/specification.md) describes the interface between a pipeline and the [Loculus backend server](../../reference/glossary.md#backend). -## Deployment with Kubernetes and Helm +## Deployment -If you use [Kubernetes and Helm to deploy Loculus](../setup-with-kubernetes/) and have a Docker image of your pipeline, you can configure it to be used in the `preprocessing` field ([reference](http://localhost:4321/reference/helm-chart-config/#organism-type)). In that field, you have to specify the `version` of the pipeline and the `image` name. Additionally, you can provide a list of `args` and values for the `configFile`. +A preprocessing pipeline is simply a process that authenticates with the `preprocessing_pipeline` role, polls the backend for unprocessed data, processes it, and submits the results back. **How and where you run it is entirely up to you** — a long-running service, a scheduled job, any orchestrator, or a managed/cloud service. It only needs network access to the backend (and Keycloak). The sections below describe how _our_ Helm chart does it, but none of this is mandatory. -The pipeline will be started with the following arguments +### With Kubernetes and Helm + +If you use [Kubernetes and Helm to deploy Loculus](../setup-with-kubernetes/) and have a Docker image of your pipeline, you can configure it in the `preprocessing` field ([reference](../../reference/helm-chart-config/#organism-type)). Specify the `version` of the pipeline and the `image` name, and optionally a list of `args`. + +The pipeline will be started with the following arguments: ``` { values from the args field } --backend-host={ backend host }/{ organism } --keycloak-host={ Keycloak host } --pipeline-version={ pipeline version } +--organism={ organism } --keycloak-password={ Keycloak password } ``` -If `configFile` is set, the `preprocessing-config.yaml` will be created, mounted onto the container and added as an argument. [This template](https://github.com/loculus-project/loculus/blob/c723c562ed2ca4a0252b3899fd375dab9a652c5a/kubernetes/loculus/templates/loculus-preprocessing-config.yaml#L12) specifies how the content of the file will be generated. - -The config is then added as an argument using - -``` - --config=/etc/config/preprocessing-config.yaml -``` +The Loculus Helm chart creates a user for the pipeline (username `preprocessing_pipeline`); its password is provided through the `--keycloak-password` argument shown above. For further detail you can view the [Helm template code](https://github.com/loculus-project/loculus/blob/main/kubernetes/loculus/templates/loculus-preprocessing-deployment.yaml). -For further information on how arguments are passed you can view the [Helm template code](https://github.com/loculus-project/loculus/blob/c723c562ed2ca4a0252b3899fd375dab9a652c5a/kubernetes/loculus/templates/loculus-preprocessing-deployment.yaml#L43) where this is defined. +### Providing per-organism configuration -The Loculus Helm chart will create a user for the pipeline. The username is `preprocessing_pipeline` and the password will be provided, as shown above, through the arguments. +Loculus does **not** mount a config file into your pipeline. (Earlier versions generated a `preprocessing-config.yaml` from a `configFile` field in `values.yaml` and mounted it via `--config`; that mechanism has been removed.) If your pipeline needs per-organism configuration, it can fetch it from the backend's public config API — see [Configuring pipelines in the admin panel](../configure-pipeline-admin-panel/). That, too, is optional: many pipelines need no per-organism config (the [dummy pipeline](https://github.com/loculus-project/loculus/tree/main/preprocessing/dummy) reads none), and you can equally bake configuration into your image or pass it through `args`/environment variables. Whatever config you store in the admin panel is opaque text that the backend serves verbatim; your pipeline decides what to do with it. diff --git a/docs/src/content/docs/for-administrators/configuration-system.md b/docs/src/content/docs/for-administrators/configuration-system.md new file mode 100644 index 0000000000..0755497a8c --- /dev/null +++ b/docs/src/content/docs/for-administrators/configuration-system.md @@ -0,0 +1,53 @@ +--- +title: Configuration system +description: How Loculus configuration is structured — the database-backed domain config vs. deployment config +--- + +Loculus configuration is split into two layers, with a deliberate boundary between them: + +| Layer | Examples | Where it lives | How you change it | +| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| **Domain config** | Organism schemas, metadata fields, reference genomes, link-outs, lineage system definitions; instance name, branding, banners, GitHub links, `dataUseTerms`, `fileSharing`, feature toggles | The backend **database** (`config_*` tables — versioned, with an audit log) | The [admin dashboard](managing-configuration/) or the `/api/admin/config/...` API | +| **Deployment / infrastructure config** | Service URLs, image tags, resource requests, replica counts, secrets, which pipelines run | Helm `values.yaml` (and `runtime_config.json` for the website) | Edit the files; redeploy | + +The split is deliberate: domain config changes routinely and benefits from versioning + an audit trail, while deployment config changes rarely and belongs in version control with the rest of the deployment. Secrets and infrastructure never enter the database-backed config, which is why its read API can be public. + +This page explains the concepts. For the step-by-step admin workflow (creating organisms, editing, publishing, rollout, the API), see [Managing configuration](managing-configuration/). + +## The two domain-config documents + +- **Instance config** — one document for the whole instance: name, accession prefix, logo, banners, GitHub URLs, `dataUseTerms`, `fileSharing`, feature toggles, lineage system definitions, etc. +- **Organism config** — one document per organism: its `schema` (metadata fields, table columns, primary key, …), reference genome(s) (single- or multi-segment, multi-reference), file categories, link-outs, and display fields. + +Each document has its own independent, versioned history. + +## Versioned, with drafts + +Every published config version is **immutable**. You never edit a published version in place — instead you work on a **draft** and then publish it as a new version: + +1. Open an organism (or the instance) for editing — the system loads the current draft, or starts a fresh one from the published version. +2. Make changes — replacing the whole document (for a brand-new, unreleased organism) or appending small, named, validated **operations** (for a released organism), such as `setOrganismDisplay`, `addLinkOut`, or `reorderMetadataFields`. +3. Review the pending operations. +4. **Publish** — this writes a new immutable version and clears the draft (or discard the draft to throw the changes away). + +Every change is recorded in an **audit log** (who changed what, when). A released config can only be changed through this operations + publish path; there is no way to silently mutate it. + +## Where each component gets its config + +The database is the single source of truth; the components read from it in different ways: + +- **Backend + website** read the config from the database directly (the website fetches `/api/config/...` on each server-side request and fails closed if the backend is unreachable). Newly published config is picked up without a restart. +- **SILO + LAPIS** do not read the database directly. Each per-organism SILO/LAPIS pod runs a `config-adapter` init container that fetches the **pinned** organism config version (set by `configVersion` in `values.yaml`) from the public API and renders the files SILO/LAPIS expect. Publishing a new version therefore does **not** automatically restart SILO/LAPIS — see [Managing configuration → Update an organism](managing-configuration/#3-update-an-organism) for the rollout step. +- **Preprocessing pipelines** are external and fetch what they need from the public API themselves. They may optionally read an opaque per-organism config file you store in Loculus — see [Configuring pipelines in the admin panel](configure-pipeline-admin-panel/). + +## What is _not_ in the database-backed config + +Deployment and infrastructure settings stay in Helm `values.yaml`: service URLs, image tags/versions, replicas, resource limits, secrets (as Kubernetes Secrets), and the per-organism scaffolding that declares which pipelines run and which `configVersion` each SILO/LAPIS pod pins. See the [Helm chart config reference](../../reference/helm-chart-config/). + +> Migration note: the ingest and ENA-submission pipelines still read some organism fields (schema/metadata and reference-genome segment names — but not the reference sequences, which now live only in the database) from `values.yaml`; those legacy fields remain there until those pipelines also move to the database-backed config. + +## Where to go next + +- [Managing configuration](managing-configuration/) — the practical admin-dashboard + API guide. +- [Configuring pipelines in the admin panel](configure-pipeline-admin-panel/) — the optional preprocessing config-file feature. +- [Helm chart config reference](../../reference/helm-chart-config/) — the deployment-side `values.yaml` fields. diff --git a/docs/src/content/docs/for-administrators/configure-pipeline-admin-panel.md b/docs/src/content/docs/for-administrators/configure-pipeline-admin-panel.md new file mode 100644 index 0000000000..7aae507da9 --- /dev/null +++ b/docs/src/content/docs/for-administrators/configure-pipeline-admin-panel.md @@ -0,0 +1,49 @@ +--- +title: Configuring pipelines in the admin panel +description: Optionally store a preprocessing pipeline's config file in Loculus and serve it over the public config API +--- + +Loculus can optionally store a preprocessing pipeline's **config file** in its database, per organism and per pipeline version, and serve it over the public config API. You can edit this file in the admin panel. + +:::caution[This is entirely optional] +You do **not** have to use the admin panel — or store any pipeline config in Loculus at all — to run a preprocessing pipeline. Pipelines are [external to Loculus and fully customizable](../pipeline-concept/): how you configure and run them is up to you. This feature is just a convenient place to keep a pipeline's config so it can be edited without redeploying. The Loculus-maintained [Nextclade pipeline](../existing-preprocessing-pipelines/) does use it, but that is a choice of that pipeline — your own pipeline can ignore it completely and take its configuration from environment variables, command-line arguments, a baked-in file, or anywhere else. +::: + +## What it is + +- **One free-form text config file per (organism, pipeline version).** The format is whatever your pipeline expects (YAML, JSON, TOML, plain text — anything). +- **Loculus stores it verbatim and never parses or interprets it.** It is an opaque text channel; the meaning is entirely between you and your pipeline. +- **Served over a public, unauthenticated endpoint:** `GET /api/config/organisms/{organismKey}/preprocessing/{pipelineVersion}` returns the raw text (404 if none is configured). A pipeline fetches it itself. +- **Edited in the admin panel** (requires the `loculus_administrator` role). Editing is a **direct save** — there is no draft/publish/version flow and no history; the current text is simply what the endpoint serves. +- **It is not versioned** as part of the organism config, so it is independent of the SILO/LAPIS config rollout. + +:::danger[Never put secrets in the config file] +The endpoint is public. Credentials (e.g. the pipeline's Keycloak password), API keys, and certificates must **never** be placed here — they belong in your deployment's secret mechanism (in our Helm chart, a Kubernetes Secret passed as an argument). +::: + +## Storing config here does not run anything + +This is the most important point: + +:::caution[Adding a config file is not enough to run preprocessing] +Adding a pipeline version and a config file in the admin panel only **stores text**. It does not start, schedule, or deploy any pipeline. You must separately **run** a preprocessing pipeline — a process authenticated with the [`preprocessing_pipeline` role](../build-new-preprocessing-pipeline/#authentication) — that polls the backend for unprocessed data, processes it, and submits the results back. Whether and how that pipeline reads the config file you stored here is up to the pipeline. +::: + +Running and hosting the pipeline is the administrator's responsibility, by whatever means you prefer: + +- Our Helm chart deploys the in-repo pipelines as Kubernetes Deployments, and the [config loader](../configuration-system/) seeds these config files into the backend from fixtures. This is how our previews work — but it is **one option, not a requirement**. +- You can equally run your pipeline as a long-running service, a scheduled job, a different orchestrator, or a managed/cloud service — anywhere it can reach the backend — and feed it configuration however you like. + +## Using the editor + +1. Open `/admin/config/` on your Loculus host (requires the `loculus_administrator` realm role in Keycloak). +2. Go to **Organisms**, choose an organism, and open the **Preprocessing** section. +3. **Add a pipeline version** — the number must match the `--pipeline-version` your running pipeline uses (the backend tracks which version processed which sequences, and several versions can run in parallel, e.g. during a reprocessing migration). +4. Paste the config text into the editor and **Save**. The change takes effect immediately; the public endpoint serves it right away. +5. Your running pipeline picks it up the next time it fetches its config (typically on start-up). + +To remove a config file, use **Remove** on that version. An organism may have no config files at all — that is perfectly valid (for example, the [dummy pipeline](https://github.com/loculus-project/loculus/tree/main/preprocessing/dummy) reads none). + +## How the Nextclade pipeline uses it + +For reference, the Loculus-maintained Nextclade pipeline opts in to this feature. When deployed by our Helm chart it is started with `--organism`, `--pipeline-version`, and `--backend-host`; from those it derives the backend root and fetches `GET /api/config/organisms/{organism}/preprocessing/{pipelineVersion}`, parsing the result (nextclade dataset, alignment settings, metadata processing specs, …) as its configuration. It also fetches the organism's metadata from the public config API and applies identity processing to any field the config file does not explicitly handle. See [Existing pipelines](../existing-preprocessing-pipelines/) and [Building your own pipeline](../build-new-preprocessing-pipeline/). diff --git a/docs/src/content/docs/for-administrators/configuring-extra-files.md b/docs/src/content/docs/for-administrators/configuring-extra-files.md index a493ac2291..1759efc8a2 100644 --- a/docs/src/content/docs/for-administrators/configuring-extra-files.md +++ b/docs/src/content/docs/for-administrators/configuring-extra-files.md @@ -68,6 +68,10 @@ secrets: ### Configuring file submission +:::note[Where this config now lives] +Enabling S3 (above) is a deployment setting and stays in `values.yaml`. The **file categories** shown below are part of an organism's `schema`, which is now stored in the [database-backed configuration system](../configuration-system/) — edit it through the admin dashboard (or the `kubernetes/loculus/fixtures/` files seeded by the config loader), not in `values.yaml`. The YAML shape shown here is still correct; only its location has changed. +::: + Users can submit files along with sequence metadata and sequences (or also instead of sequences). For this, you need to enable the `files` submission type, and configure at least one file category that users can submit: diff --git a/docs/src/content/docs/for-administrators/data-use-terms.mdx b/docs/src/content/docs/for-administrators/data-use-terms.mdx index d44f569f38..23330f6d79 100644 --- a/docs/src/content/docs/for-administrators/data-use-terms.mdx +++ b/docs/src/content/docs/for-administrators/data-use-terms.mdx @@ -14,6 +14,13 @@ import { Aside } from '@astrojs/starlight/components'; Loculus comes with built-in handling of data use terms for submitted data, which means that data can either be _open_ or _restricted_. You can define, what restricted means yourself. Users can submit data as restricted, but they have to give a date at which point the sequences become open, this date can at most be one year from the submission date. When data use terms are enabled, you can also filter for only open or only restricted data, and when downloading you will have to accept the data use terms. The same applies to API usage. + + + You can then enable data use terms like this: ```yaml diff --git a/docs/src/content/docs/for-administrators/existing-preprocessing-pipelines.md b/docs/src/content/docs/for-administrators/existing-preprocessing-pipelines.md index 6a27b0dc0e..8a7cf31ca5 100644 --- a/docs/src/content/docs/for-administrators/existing-preprocessing-pipelines.md +++ b/docs/src/content/docs/for-administrators/existing-preprocessing-pipelines.md @@ -12,15 +12,15 @@ _Maintained by the Loculus team_ This pipeline supports all [schemas](../../reference/glossary/#schema) where each segment has one unique reference that it should be aligned to, e.g. the [one organism, multi-segment schema](../schema-designs#one-organism-for-everything), the [multi-organism schema](../schema-designs#multiple-clearly-separated-organisms-each-with-one-reference) and even the [no alignment schema](../schema-designs#no-aligments-at-all). -Given a nextclade dataset this pipeline uses [nextclade run](https://docs.nextstrain.org/projects/nextclade/en/stable/user/nextclade-cli/reference.html#nextclade-run) for alignment, mutation calling, quality checks and (optionally) annotation file generation. When no nextclade dataset is given the pipeline will not do any sequence validation and only perform metadata checks (see below). The pipeline requires a [Nextclade dataset](https://docs.nextstrain.org/projects/nextclade/en/stable/user/datasets.html) with the same reference genome as the one used by Loculus, `nextclade` will also perform clade assignment and phylogenetic placement if the `dataset` includes this information. To use this pipeline for new pathogens, check if there is already an existing nextclade dataset for that pathogen [here](https://github.com/nextstrain/nextclade_data/tree/master/data), or follow the steps in the [dataset creation guide](https://github.com/nextstrain/nextclade_data/blob/master/docs/dataset-creation-guide.md) to create a new dataset. For example for mpox we use [nextstrain/mpox/all-clades](https://github.com/nextstrain/nextclade_data/tree/master/data/nextstrain/mpox/all-clades), defined in the `values.yaml` as: +Given a nextclade dataset this pipeline uses [nextclade run](https://docs.nextstrain.org/projects/nextclade/en/stable/user/nextclade-cli/reference.html#nextclade-run) for alignment, mutation calling, quality checks and (optionally) annotation file generation. When no nextclade dataset is given the pipeline will not do any sequence validation and only perform metadata checks (see below). The pipeline requires a [Nextclade dataset](https://docs.nextstrain.org/projects/nextclade/en/stable/user/datasets.html) with the same reference genome as the one used by Loculus, `nextclade` will also perform clade assignment and phylogenetic placement if the `dataset` includes this information. To use this pipeline for new pathogens, check if there is already an existing nextclade dataset for that pathogen [here](https://github.com/nextstrain/nextclade_data/tree/master/data), or follow the steps in the [dataset creation guide](https://github.com/nextstrain/nextclade_data/blob/master/docs/dataset-creation-guide.md) to create a new dataset. + +This pipeline reads its configuration — the nextclade dataset, alignment settings and metadata processing — from a config file that Loculus serves over its public config API. You provide that config file [via the admin panel](../configure-pipeline-admin-panel/) (or seed it with the config loader, as our Helm chart does). For example, to use [nextstrain/mpox/all-clades](https://github.com/nextstrain/nextclade_data/tree/master/data/nextstrain/mpox/all-clades) for mpox, the config file contains: ```yaml -preprocessing: - - configFile: - nextclade_dataset_name: nextstrain/mpox/all-clades +nextclade_dataset_name: nextstrain/mpox/all-clades ``` -Additionally the pipeline performs checks on the metadata fields. The checks are defined by custom preprocessing functions in the `values.yaml` file. These checks can be applied to and customized for other metadata fields, see [Preprocessing Checks](https://github.com/loculus-project/loculus/blob/main/preprocessing/nextclade/README.md#preprocessing-checks) for more info. +Additionally the pipeline performs checks on the metadata fields. These checks are defined as custom preprocessing functions in the same config file (the `processing_spec`). They can be applied to and customized for other metadata fields, see [Preprocessing Checks](https://github.com/loculus-project/loculus/blob/main/preprocessing/nextclade/README.md#preprocessing-checks) for more info. In the default configuration the pipeline performs: diff --git a/docs/src/content/docs/for-administrators/getting-started.md b/docs/src/content/docs/for-administrators/getting-started.md index fad6ef931e..3ee8e70faa 100644 --- a/docs/src/content/docs/for-administrators/getting-started.md +++ b/docs/src/content/docs/for-administrators/getting-started.md @@ -11,7 +11,7 @@ Although Loculus is in principle stable and can be used in production, we plan t ## Define your schema -The first step to setting up a new Loculus instance is to define its schema. You can read about [a few example schemas](../schema-designs). +The first step to setting up a new Loculus instance is to define its schema. You can read about [a few example schemas](../schema-designs). Schemas and the rest of the organism + instance domain config are managed through Loculus's database-backed configuration system — see [Configuration system](../configuration-system) for how it is structured, seeded, and edited. ## Choose a preprocessing pipeline diff --git a/docs/src/content/docs/for-administrators/local-dev-instance.md b/docs/src/content/docs/for-administrators/local-dev-instance.md new file mode 100644 index 0000000000..45162f28df --- /dev/null +++ b/docs/src/content/docs/for-administrators/local-dev-instance.md @@ -0,0 +1,308 @@ +--- +title: Local development instance +description: How to spin up a feature-complete local Loculus instance, matching the dev/preview environments +--- + +This guide walks you through standing up a Loculus instance on your own machine that matches the functionality of the dev and preview environments: same default organisms, automatic INSDC ingest, full preprocessing pipelines, and the new DB-backed config system seeded from `kubernetes/loculus/fixtures/`. + +There are two flavours of local dev, depending on what you're modifying: + +- **All-in-cluster** — every component runs in k3d, you use the published `ghcr.io/loculus-project/*` images. Closest to the preview environment. Right for trying things end-to-end without writing code. +- **`--dev` (IDE) mode** — Postgres + Keycloak + SILO + LAPIS + preprocessing + ingest run in k3d; **backend and website run on your host in your IDE**. Right when you're modifying backend or website code, since rebuild + restart is instant. + +Both flavours can run entirely offline once images are local — there's a section on building all images locally below. + +> If you're new to Loculus and just want a minimal "hello world" tutorial, start with [My first Loculus](../my-first-loculus). Come back here when you want a full-fidelity local instance. + +## What you'll get + +- All 8 default organisms (`cchf`, `cchf-multi-ref`, `dummy-organism`, `dummy-organism-with-files`, `ebola-sudan`, `enteroviruses`, `not-aligned-organism`, `west-nile`), seeded from `kubernetes/loculus/fixtures/` via the `loculus-config-loader`. +- The Nextclade-based preprocessing pipeline running for each organism. +- The INSDC ingest pipeline running against NCBI Datasets. +- SILO + LAPIS pods per organism, with their config rendered by the `loculus-config-adapter` init container fetching from the backend. +- Keycloak with pre-seeded test accounts, including `superuser` (password `superuser`) for curation workflows and `loculus_administrator` (password `loculus_administrator`) for the admin dashboard. + +## System requirements + +- Linux or macOS with Docker support. Tested on Ubuntu 24.04 and macOS 14. +- At least 8 GB RAM and 4 CPU cores free for the cluster (preprocessing + ingest are memory-hungry). +- ~20 GB free disk space (Docker images + SILO data dirs). + +## 1. Install prerequisites + +| Tool | Why | Install | +| ------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| Docker | k3d runs Kubernetes inside Docker | | +| k3d | Lightweight Kubernetes-in-Docker | `curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh \| bash` | +| kubectl | Cluster CLI | | +| Helm | Chart deployer | | +| Python 3.9+ with `pyyaml` | `./deploy.py` wrapper | `pip install pyyaml` | +| Node 22+, npm | For `config-tools/` (loader CLI), and optionally for running the website in your IDE | | +| Java 21+, Gradle | For running the backend in your IDE | | + +## 2. Clone the repo + +```bash +git clone https://github.com/loculus-project/loculus.git +cd loculus +``` + +All subsequent commands assume you're in the repo root. + +## Flavour A — `--dev` (IDE) mode + +This is the workflow when you're actively modifying backend or website code. The cluster runs everything else; the backend + website you're working on run on your host in your IDE and pick up changes on rebuild. + +### A.1 Create the cluster (no backend/website port-forwards) + +```bash +./deploy.py --verbose cluster --dev +``` + +`--dev` removes the port-forwards for 3000 (website) and 8079 (backend) so those host ports are free for your IDE-run servers. Postgres (5432), Keycloak (8083), LAPIS (8080), and S3 (8084) are still forwarded, so the IDE-run backend can reach them. + +### A.2 Deploy the in-cluster stack + +```bash +./deploy.py --verbose helm \ + --branch latest \ + --dev \ + --enablePreprocessing \ + --enableIngest +``` + +What the flags do: + +- `--branch latest` — uses the `:latest` tag for each image (built from `main`). +- `--dev` — sets `disableBackend=true` and `disableWebsite=true`, plus pulls in `kubernetes/loculus/values_e2e_and_dev.yaml` (test accounts, deterministic raw passwords, debug flags). +- `--enablePreprocessing` / `--enableIngest` — turn on the preprocessing + ingest deployments (off by default; needed to match preview functionality). + +In `--dev` mode the chart **skips the `loculus-config-loader` Job entirely** — it would race the IDE backend startup. You'll run the loader manually in step A.4. + +### A.3 Start backend + website in your IDE + +The backend needs Postgres + Keycloak from the cluster. The repo ships `backend/start_dev.sh` which already points at `localhost:5432` (Postgres) and `localhost:8083` (Keycloak) — those are the k3d port-forwards: + +```bash +cd backend && ./start_dev.sh +``` + +(Or run `org.loculus.backend.BackendApplication` from your IDE with the equivalent `--spring.*` args — see the script.) + +Once the backend is up at , start the website. From `website/`: + +```bash +../generate_local_test_config.sh # writes website/tests/config/{runtime,website,backend}_config.json +npm install # one-time +CONFIG_DIR=$(pwd)/tests/config npm run dev +``` + +The website is then at . + +### A.4 Seed organisms via the loader CLI + +The cluster's Helm Job didn't run (intentional, see A.2). Run the loader from your host once the IDE backend is up: + +```bash +cd config-tools +npm install # one-time + +TOKEN=$(curl -sS \ + -d "username=loculus_administrator&password=loculus_administrator&grant_type=password&client_id=backend-client" \ + http://localhost:8083/realms/loculus/protocol/openid-connect/token \ + | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])") + +npm run loader -- \ + --backend-url http://localhost:8079 \ + --fixtures ../kubernetes/loculus/fixtures \ + --admin-token "$TOKEN" +``` + +The default mode is `idempotent`, so the command is safe to re-run after editing fixtures. It will skip exact matches and fail loudly on drift. + +Expected output ends with: + +```text +Instance: published-new-version (v2) +Organisms created: 8 +``` + +After this completes, the SILO/LAPIS pods (which until now were sitting in `Init` waiting on `/api/config/organisms`) will unblock automatically: each pod's `config-adapter` init container retries the fetch on a short interval, and once the organism config is reachable on the IDE backend at `host.k3d.internal:8079`, it writes `database_config.yaml` etc. and SILO + LAPIS start. + +### A.5 Check the website and admin dashboard + +- Public site: — all 8 organisms appear in the dropdown. +- Admin dashboard: — log in as `loculus_administrator` / `loculus_administrator`. See [Managing configuration](../managing-configuration) for the full walkthrough. + +Preprocessing + ingest pods run automatically; expect the first ingested batches to appear under e.g. after 5-10 minutes. + +### A.6 Iterate + +- **Backend code changes:** stop `start_dev.sh`, save your edit, restart. The cluster components automatically reconnect to the new backend instance. +- **Website code changes:** `npm run dev` is hot-reload; just save. +- **Fixture changes:** re-run the loader (step A.4). Idempotent mode skips unchanged organisms and fails on drift — to push drifted fixtures, manually delete the affected organism via the admin UI first (or accept that breaking changes need the clone-and-replace workflow described in [Managing configuration §3](../managing-configuration#3-update-an-organism)). + +## Flavour B — all-in-cluster + +Use this when you just want a working full instance without touching code. + +### B.1 Create the cluster (with backend + website port-forwards) + +```bash +./deploy.py --verbose cluster +``` + +### B.2 Deploy + +```bash +./deploy.py --verbose helm \ + --branch latest \ + --for-e2e \ + --enablePreprocessing \ + --enableIngest +``` + +`--for-e2e` applies `kubernetes/loculus/values_e2e_and_dev.yaml` (test accounts + deterministic service-account passwords needed by the loader Job + debug flags), without setting `disableBackend`. The post-install loader Job runs automatically and seeds the 8 organisms. + +### B.3 Wait and open + +```bash +kubectl get pods +``` + +When everything is `Running` (or `Completed` for the loader Job), open . Log in to the admin dashboard as `loculus_administrator` / `loculus_administrator`. + +Pod readiness order: Postgres → Keycloak (~1-2 minutes; everything else crash-loops while waiting) → backend (runs Flyway including the V2.0 `config_*` migration) → `loculus-config-loader-` Job (completes in ~10s once backend is ready) → SILO/LAPIS init containers unblock → website ready. + +## Run entirely from local builds (offline / unpushed-changes) + +By default `./deploy.py helm --branch latest` pulls images from `ghcr.io/loculus-project/*:latest` — the images CI built from `main`. If you want to run a fully local instance with unpushed changes (or just offline), build the images locally, import them into k3d, and tell Helm to use the `local` tag. + +### Build + import + +```bash +# Flavour A (--dev): build everything except backend + website (you run those in IDE). +./build-local-images.sh --dev + +# Flavour B (all-in-cluster): build everything. +./build-local-images.sh +``` + +The script tags every image as `ghcr.io/loculus-project/:local` and `k3d image import`s it into the `testCluster` cluster. Re-run for any single component as needed: + +```bash +./build-local-images.sh config-adapter +./build-local-images.sh preprocessing-nextclade ingest +``` + +(See `./build-local-images.sh --help` for the full component list.) + +The only image still pulled from a remote registry is `ghcr.io/genspectrum/lapis` (upstream LAPIS) — k3d pulls it once and caches. + +### Deploy with the `local` tag + +```bash +# Flavour A (--dev) +./deploy.py --verbose helm --branch local --dev --enablePreprocessing --enableIngest + +# Flavour B +./deploy.py --verbose helm --branch local --for-e2e --enablePreprocessing --enableIngest +``` + +Two things happen automatically when `--branch local` is detected: + +1. The chart's docker-tag helper resolves `--branch local` to the literal `local` tag, so every container references the image you just built (e.g. `ghcr.io/loculus-project/config-adapter:local`). +2. `deploy.py` applies `kubernetes/loculus/values_local_images.yaml` on top of the chart, which switches `imagePullPolicy` for every Loculus-built image from `Always` to `IfNotPresent`. **This is critical** — without it kubelet still tries to pull `:local` from ghcr.io (because `Always` means always), gets a 403 ("anonymous token, status 403"), and the pod stays in `ImagePullBackOff` forever even though the image is already in containerd. + +If you ever see `Failed to pull image "ghcr.io/loculus-project/<...>:<...>": ... 403 Forbidden`, the fix is either: + +- You deployed without `--branch local` (the default `--branch latest` references the public ghcr.io `:latest` tag, which only exists once CI has pushed it). Rebuild + redeploy: `./build-local-images.sh --dev && ./deploy.py helm --branch local --dev --enablePreprocessing --enableIngest`. +- The image isn't in containerd yet — rerun `./build-local-images.sh `. + +### After editing one component + +You don't have to rebuild everything. The two-step pattern is: + +```bash +./build-local-images.sh # rebuild + reimport one image +kubectl rollout restart deploy/loculus-<...> # restart the matching deployment +``` + +For per-organism components (e.g. `loculus-preprocessing-ebola-sudan-v1-0`) you'll need to restart each affected deployment, or `helm upgrade` to roll all of them. + +## Customising the instance + +### Add or modify an organism + +The DB-backed config is the source of truth. Use the admin dashboard at — see [Managing configuration §2-3](../managing-configuration#2-create-and-launch-a-new-organism). + +For SILO/LAPIS to spin up a per-organism pod for a brand-new organism, you also need to add it to `kubernetes/loculus/values.yaml` under `defaultOrganisms` (so the chart templates the pods) and `helm upgrade`. The website picks up new organisms automatically on the next page load. + +### Re-seed config from fixtures + +After editing `kubernetes/loculus/fixtures/`: + +```bash +cd config-tools +npm run loader -- \ + --backend-url http://localhost:8079 \ + --fixtures ../kubernetes/loculus/fixtures \ + --admin-token "$TOKEN" +``` + +Default mode is `idempotent`. To wipe and start clean, delete the relevant organism rows via the admin UI first (or in Flavour B, `./deploy.py helm --uninstall` + start over). + +### Reset just the data (keep config) + +```bash +kubectl exec deployment/loculus-database -- \ + psql -U postgres -d loculus -c "TRUNCATE sequence_entries CASCADE;" +``` + +## Common issues + +### `--dev`: SILO/LAPIS pod stuck in `Init` + +Cause: the `config-adapter` init container can't reach `/api/config/organisms/?version=1`. In `--dev` mode this almost always means either (a) the IDE backend isn't running, or (b) the loader hasn't been run yet (A.4). + +```bash +kubectl logs -c config-adapter- +``` + +### Loader fails with "organism already exists" + +The loader's default mode (`idempotent`) won't overwrite drifted released organisms. Two paths: + +- Edit the fixture YAML to match the released config (so it's a no-op skip). +- Delete the affected organism via the admin dashboard and re-run the loader. + +### Loader Job in Flavour B fails + +Check `kubectl logs job/loculus-config-loader`. Most common cause: Keycloak wasn't ready when the Job ran. Delete the Job and reapply: + +```bash +kubectl delete job loculus-config-loader +./deploy.py --verbose helm --branch latest --for-e2e --enablePreprocessing --enableIngest +``` + +### Port 5432 already in use + +You probably have a local Postgres running on 5432. Stop it, or edit `./deploy.py` to map a different host port. + +### Keycloak slow start + +Keycloak takes 1-2 minutes to start. Dependent pods crash-loop until it's ready, then automatically recover. No action needed. + +### Ingest pulls nothing + +The INSDC ingest pipeline polls NCBI Datasets, which is slow at startup (downloads reference datasets first). Logs show progress. + +## Reference + +- `./deploy.py --help` — full deploy script options. +- `./build-local-images.sh --help` — local-image build script. +- `kubernetes/loculus/values.yaml` — per-organism scaffolding (configVersion, preprocessing + ingest pipeline declarations, replicas). +- `kubernetes/loculus/fixtures/` — canonical DB-backed config (the source of truth the loader posts; matches what the admin dashboard edits). +- `kubernetes/loculus/values_e2e_and_dev.yaml` — overrides applied by `--for-e2e` and `--dev` (test accounts, raw service-account passwords incl. `configLoaderUserPassword`, debug flags). +- `config-tools/README.md` — loader/adapter CLI usage. +- [Configuration system](../configuration-system) — concepts; and [Managing configuration](../managing-configuration) — the full admin guide. diff --git a/docs/src/content/docs/for-administrators/managing-configuration.md b/docs/src/content/docs/for-administrators/managing-configuration.md new file mode 100644 index 0000000000..e93b16769e --- /dev/null +++ b/docs/src/content/docs/for-administrators/managing-configuration.md @@ -0,0 +1,284 @@ +--- +title: Managing configuration +description: Editing the database-backed Loculus config through the admin dashboard and API +--- + +This is the practical guide to working with the database-backed configuration system: editing it through the admin dashboard, creating and updating organisms, rolling changes out to SILO and LAPIS, and scripting via the API. For the concepts behind it (the config layers, versioning model, and where each component reads config), start with the [Configuration system overview](../configuration-system/). For deployment-specific details after publishing an organism config, see [Rolling out organism config changes](../rolling-out-organism-config/). + +A quick recap of the model used throughout this page: domain config (organism + instance settings) lives in the backend database as **immutable, versioned** documents; you edit a **draft** and **publish** it as a new version; a **released** organism can only be changed through small, named **operations** (e.g. `setOrganismDisplay`, `addLinkOut`, `reorderMetadataFields`); and every change is recorded in an audit log. Preprocessing-pipeline config is a separate, opt-in feature — see [Configuring pipelines in the admin panel](../configure-pipeline-admin-panel/) — and is not part of the versioned organism config. + +## 1. The admin dashboard + +### Access + +The dashboard is at `/admin/config/` on your Loculus host. Access requires the `loculus_administrator` realm role in Keycloak. This role is separate from `super_user`; `super_user` is intended for curation powers such as acting on sequence entries across groups, not for changing instance configuration. + +- An "Admin" link appears in the top navigation only when the logged-in user has the role. +- Anyone without the role gets a 403 when navigating to any `/admin/...` path. + +To grant the role to a user: + +1. Open the Keycloak admin console (e.g. `http://localhost:8083/admin/master/console/` in local dev). +2. Select the `loculus` realm. +3. Open the user → **Role mapping** → assign `loculus_administrator`. + +The local development setup ships with a `loculus_administrator` account (password `loculus_administrator`) that already has the role when test accounts are enabled. + +### Layout + +The sidebar has three sections: + +- **Instance** — edit the instance-level config (branding, banners, dataUseTerms, github links, etc.). +- **Organisms** — list all organisms (released + unreleased), create new ones, copy from existing ones, edit, view JSON, browse version history. +- **Audit** — the full audit log across all scopes. + +Each organism also has its own per-organism pages: + +| Page | URL pattern | Purpose | +| ----------------- | --------------------------------------- | ---------------------------------------------------------------------------------------- | +| Document editor | `/admin/config/organisms//draft` | Raw JSON editor; used only for **unreleased** organisms | +| Operations editor | `/admin/config/organisms//edit` | Typed forms; used for **released** organisms | +| JSON viewer | `/admin/config/organisms//json` | Read-only formatted JSON of the published config + any current draft, with a Copy button | +| History | `/admin/config/organisms//history` | Published versions + audit log filtered to this organism | + +Trying to open the wrong-status page (e.g. the document editor on a released organism) automatically redirects to the right one. + +### Useful patterns + +- **Inspect a config without editing.** Use the **View JSON** action in the listing. Both the published version and the current draft (if any) are shown side-by-side with Copy buttons. +- **Validate before publishing.** The document editor performs client-side Zod parse + safeParse before sending the PUT; obvious schema errors surface inline. The backend validates again on publish; server-side errors are surfaced with the offending field path. +- **Concurrent editing.** The dashboard uses optimistic concurrency via `If-Match` revisions. If two admins edit the same draft, the second submit triggers a 409 → reload-toast UX: the dashboard re-fetches the draft and asks the admin to redo their change. No silent overwrites. +- **Discarding pending operations.** The operations editor has a "Discard pending operations" button that clears the draft without publishing. + +## 2. Create and launch a new organism + +This is the canonical path for a brand-new organism. + +### Step 1 — Create an organism row + +In the admin dashboard: + +1. Go to **Organisms** in the sidebar. +2. Scroll down to **Create new organism**. +3. Enter a key (lowercase letters, digits, hyphens; must be unique). +4. Optionally pick **Copy config from** to seed the draft with another released organism's config. Useful when adapting an existing organism setup — the source's `schema.organismName` is replaced with the new key and `displayName` is cleared so the new organism doesn't accidentally show as the source. +5. Click **Create**. + +You land in the document editor at `/admin/config/organisms//draft`. The organism is now `unreleased` — it exists as a row but has no published config yet. + +### Step 2 — Fill in the draft + +The document editor is a plain monospace JSON textarea. Paste or edit the full `OrganismConfig` document. Minimal shape: + +```json +{ + "schema": { + "organismName": "Example virus", + "metadata": [ + { "name": "date", "type": "date", "required": true }, + { "name": "country", "type": "string", "autocomplete": true, "generateIndex": true } + ], + "tableColumns": ["date", "country"], + "primaryKey": "accessionVersion", + "defaultOrderBy": "submittedAtTimestamp", + "defaultOrder": "descending" + }, + "referenceGenome": { + "nucleotideSequences": [{ "name": "main", "sequence": "ATCG..." }], + "genes": [] + } +} +``` + +For richer configs (multi-segment, multi-reference, link-outs, preprocessing, lineage systems) see the canonical schema in `config-tools/src/schema/canonicalConfig.ts`. The easiest starting point is to copy from an existing organism — either via the **Copy config from** select on the create dialog, or by opening another organism's **View JSON** page. + +Click **Save draft** as you go. Each save bumps the draft revision. + +### Step 3 — Publish v1 + +When the draft looks right, click **Publish v1**. The backend validates the full document, writes it as the immutable version 1, and clears the draft. The organism flips from `unreleased` to `released`. + +A modal appears with the published version, the exact config pin to update (`organisms.example-virus.configVersion=1`), a values.yaml example for GitOps/ArgoCD deployments, and a direct Helm example. The organism is now released but **not deployed**, so it is hidden from public organism lists until SILO/LAPIS are ready and an administrator marks it deployed. See [Rolling out organism config changes](../rolling-out-organism-config/) for how to choose the right workflow for your deployment. + +### Step 4 — Roll out SILO + LAPIS for the new organism + +This is the deployment-side step. SILO and LAPIS are templated per organism, so to start serving the new organism you need to: + +1. Add the new organism to the values file used by the deployment, usually under `organisms` for environment-specific values or under `defaultOrganisms` in the in-repository defaults, with `configVersion: 1`. The per-organism scaffolding in `values.yaml` is deployment config: enabled flag, pinned config version, pipeline deployments, replicas, resource settings, and the pieces still needed by ingest/ENA-submission. The organism schema, metadata fields, reference sequences, link-outs, and file categories remain in the database-backed config. +2. Apply the values change. For GitOps/ArgoCD deployments, commit and push the values change and let ArgoCD sync. For direct Helm deployments, run a `helm upgrade` with the same configVersion pin. The dedicated [rollout guide](../rolling-out-organism-config/) shows both forms. + +After the rollout, the `config-adapter` init container on each new SILO/LAPIS pod fetches `/api/config/organisms/example-virus?version=1` from the backend, renders `database_config.yaml`, `reference_genomes.json`, and `preprocessing_config.yaml` into the shared volume, and SILO/LAPIS start up against the new organism. + +After the rollout is complete, check the organism's LAPIS endpoint and then return to **Admin → Organisms** and click **Mark deployed**. The public website will then include the organism in navigation and organism lists on the next request. + +## 3. Update an organism + +How you update a released organism depends on whether the change is **non-breaking** (display-only, additive) or **breaking** (changes that require SILO/LAPIS to re-ingest data). + +### Non-breaking change — operations only + +Examples: rename a display name, reorder metadata fields, add a link-out, change a field's header or description. + +1. Go to **Organisms → edit** for the released organism. +2. The page lists the currently pending operations (if any) and shows one form per supported operation type. Available forms include: + - **Set organism display** — displayName, organismName, image, description. + - **Add optional metadata field** — name + type + optional display name (must be `required: false`; this is enforced as non-breaking by the validator). + - **Set metadata field display** — pick a field, change displayName / header / description. + - **Reorder metadata fields** — arrow buttons to reorder. + - **Link-outs** — add a new link-out, or remove an existing one by name. +3. Each form's **Apply** button posts one operation; the operation appears in the **Pending operations** list at the top. +4. When all the operations look right, click **Publish**. The backend re-validates and writes a new immutable version. + +These changes don't need a SILO/LAPIS rollout — the website re-derives its view model on the next request, picking up the new published config automatically. The pinned `configVersion` on SILO/LAPIS pods can stay at the previous version; the schema-level differences between the two versions don't affect the SILO data files. + +### Breaking change — requires a SILO/LAPIS rollout + +Examples: adding a metadata field that should be indexed (`generateIndex`), changing the reference genome, changing a field type, adding a lineage system, changing the per-segment shape — anything that affects what SILO indexes or how the preprocessing pipeline parses input. + +The operation registry is intentionally narrow today, so most breaking changes don't have a dedicated admin-UI form yet. Two paths: + +**A. Clone-and-replace via the admin dashboard.** Because the document editor only operates on **unreleased** organisms, the workflow for a breaking change is to create a new organism whose config differs: + +1. Create a new organism with a different key (e.g. add a version suffix). +2. Use **Copy config from** to seed it from the existing one. +3. Edit the JSON to reflect the breaking change. +4. Publish v1 of the new organism. +5. Roll out SILO/LAPIS for the new organism (per [§2 step 4](#step-4--roll-out-silo--lapis-for-the-new-organism)) and migrate users / external links to the new key. + +**B. Use the API directly** to append the matching operation handler (for handlers that exist in the backend registry but don't have a UI form yet). See the API reference below. + +After publishing the new version: + +1. The publish modal shows the new version number and the exact config pin to update, for example `organisms..configVersion=`. +2. Update the values file or Helm override that your deployment uses. For GitOps/ArgoCD deployments, commit and push the values change. For direct Helm deployments, run the `helm upgrade` command shown in the modal after replacing the release and chart placeholders. +3. This rolls the per-organism SILO and LAPIS Deployments. The `config-adapter` init container on each new pod fetches the new pinned version from the backend and re-renders `database_config.yaml` / `reference_genomes.json` / `preprocessing_config.yaml`. SILO then re-imports the data. +4. Once SILO is healthy on the new version, the change is live. + +While SILO is re-importing, queries hit the previous version's data on the still-running pod (Kubernetes does a rolling update). There's no manual downtime, but expect a brief period where the new SILO pod is in `Init` waiting on the adapter + the re-import. + +### Tracking what changed + +The **History** page for the organism (`/admin/config/organisms//history`) lists every published version + every audit-log entry (who applied which operation, when). The top-level **Audit** page does the same across all scopes. + +Each version's stored config is browsable via the API: `GET /api/config/organisms/?version=N`. The **View JSON** page in the dashboard shows the currently-published version and the current draft (if any). + +## API reference (for scripting + advanced cases) + +For situations where the admin dashboard doesn't yet have a form — or when you want to script bulk changes — the same operations are available via HTTP. + +### Get an access token + +```bash +KEYCLOAK_URL="http://localhost:8083" + +ACCESS_TOKEN=$( + curl --fail-with-body -sS \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "username=loculus_administrator" \ + -d "password=loculus_administrator" \ + -d "grant_type=password" \ + -d "client_id=backend-client" \ + "$KEYCLOAK_URL/realms/loculus/protocol/openid-connect/token" \ + | jq -r ".access_token" +) +``` + +The user must have the `loculus_administrator` realm role. + +### Public read API (no auth) + +| Endpoint | Purpose | +| --------------------------------------------- | ------------------------------------------------------------------------------- | +| `GET /api/config/instance[?version=N]` | Latest instance config, or a pinned version. Includes top-level `readOnlyMode`. | +| `GET /api/config/organisms` | List released and deployed organisms with their current versions. | +| `GET /api/config/organisms/{key}[?version=N]` | One organism's config, latest or pinned. | + +### Admin write API (`Bearer` loculus_administrator token) + +| Endpoint | Purpose | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `GET /api/admin/config/organisms` | List all organisms including unreleased and not-deployed organisms. | +| `POST /api/admin/config/organisms` `{key}` | Create a new unreleased organism. | +| `GET /api/admin/config/organisms/{key}/draft` | Get the current draft (204 if none). | +| `PUT /api/admin/config/organisms/{key}/draft` | **Unreleased only.** Replace the full draft document. Use `If-Match: ` for optimistic concurrency. | +| `POST /api/admin/config/organisms/{key}/draft/operations` | **Released only.** Append operation(s); body `{operations: [{type, payload}]}`. | +| `DELETE /api/admin/config/organisms/{key}/draft` | Discard the draft. | +| `POST /api/admin/config/organisms/{key}/publish` | Publish the draft as a new immutable version. | +| `POST /api/admin/config/organisms/{key}/mark-deployed` | Mark a released organism deployed after SILO/LAPIS have been rolled out and checked. | +| `GET /api/admin/config/organisms/{key}/versions` | List versions of one organism. | +| `GET /api/admin/config/instance/draft` | Get the current instance draft (204 if none). | +| `PUT /api/admin/config/instance/draft` | Replace the full instance draft document. | +| `POST /api/admin/config/instance/draft/operations` | Append operation(s). | +| `POST /api/admin/config/instance/publish` | Publish the instance draft. | +| `GET /api/admin/config/audit[?organism=]` | Audit log entries (filter by organism or all). | + +### End-to-end script: create + publish a new organism + +```bash +BACKEND_URL="http://localhost:8079" +ORGANISM="example-virus" + +# 1. Create row. +curl --fail-with-body -sS \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"key\":\"$ORGANISM\"}" \ + "$BACKEND_URL/api/admin/config/organisms" + +# 2. Put the full draft (config in a file). +curl --fail-with-body -sS -X PUT \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d @organism-config.json \ + "$BACKEND_URL/api/admin/config/organisms/$ORGANISM/draft" + +# 3. Publish v1. +curl --fail-with-body -sS -X POST \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + "$BACKEND_URL/api/admin/config/organisms/$ORGANISM/publish" +``` + +`organism-config.json` is the same shape the admin dashboard's document editor shows, wrapped in `{"config": ...}`: + +```json +{ + "config": { + "schema": { "organismName": "Example virus", "metadata": [] }, + "referenceGenome": { "nucleotideSequences": [], "genes": [] } + } +} +``` + +### Append an operation + +```bash +curl --fail-with-body -sS -X POST \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "operations": [ + { + "type": "setOrganismDisplay", + "payload": {"displayName": "Example virus", "description": "..." } + } + ] + }' \ + "$BACKEND_URL/api/admin/config/organisms/$ORGANISM/draft/operations" +``` + +The exact operation types currently in the registry: `setInstanceBranding`, `setMetadataFieldDisplay`, `setOrganismDisplay`, `reorderMetadataFields`, `addLinkOut`, `updateLinkOut`, `removeLinkOut`, `addOptionalMetadataField`. The Swagger UI at `/swagger-ui/index.html` is the authoritative reference for the current schema. + +### Bulk load fixtures + +The `loculus-config-loader` CLI (in `config-tools/`) reads a directory of fixture YAMLs (`instance.yaml` + `organisms/*.yaml`) and posts them via the admin API. Useful for bootstrapping a fresh instance or restoring config from version control. + +```bash +cd config-tools +npm install +npm run loader -- \ + --backend-url http://localhost:8079 \ + --fixtures ../kubernetes/loculus/fixtures \ + --admin-token "$ACCESS_TOKEN" +``` + +Modes: `idempotent` (default, skip exact matches), `fresh-only` (fail if anything already exists; used by the Helm post-install Job). See `config-tools/README.md` for full usage. diff --git a/docs/src/content/docs/for-administrators/my-first-loculus.md b/docs/src/content/docs/for-administrators/my-first-loculus.md index 3373500d96..36007817a7 100644 --- a/docs/src/content/docs/for-administrators/my-first-loculus.md +++ b/docs/src/content/docs/for-administrators/my-first-loculus.md @@ -5,6 +5,12 @@ description: Experimenting with a Loculus interface running in a local mini Kube This tutorial will guide you through setting up a test instance for Loculus locally, running on a mini Kubernetes cluster. You'll learn how to install dependencies, deploy Loculus, configure a custom organism, and submit sample data. By the end, you'll have a Loculus database running on your machine, providing hands-on experience of how things work (but the setup will not be suitable for production use). +:::tip[Want a feature-complete dev instance?] +If you just want a local Loculus instance that matches the dev/preview environments (all 8 default organisms, ingest, preprocessing, the admin dashboard), follow [Local development instance](../local-dev-instance) instead. That guide gets you a working full-stack instance in a few commands without writing any custom YAML. + +This page stays useful if you want to learn the system by walking through it manually. +::: + :::note[System requirements] This tutorial is intended for Linux. It has been tested on a fresh Ubuntu installation running on a DigitalOcean droplet (though you will find it simpler if you are able to run it locally.) @@ -97,99 +103,39 @@ Once the pods are running, you can access Loculus locally - the website will be curl http://127.0.0.1:3000 ``` -## Reconfiguring Loculus +## Customizing the configuration -Now that we know we can get Loculus working we can tweak it. +Your instance already ships with a set of default organisms — you'll see them in the organism dropdown on the website. Beyond that you can customize the instance: change its name and branding, and add or edit organisms. -Let's create a new configuration. +Configuration like this is **no longer set in `values.yaml`**. Organism and instance domain config now lives in Loculus's database-backed configuration system, edited through the **admin dashboard** at `/admin/config/`: -Create a new file (for now we will call it `custom_values.yaml` and it can be in your current working directory) with the following content: +- To understand how configuration is structured (the config layers, versioning, and where each component reads it), read [Configuration system](../configuration-system/). +- For the step-by-step admin workflow — changing the instance name, creating and publishing a new organism, and rolling it out to SILO/LAPIS — see [Managing configuration](../managing-configuration/). -```yaml -name: 'My awesome database' -``` +The admin dashboard requires a user with the `loculus_administrator` role. The quickest way to get a fully-featured local instance that already includes such an account (`loculus_administrator` / `loculus_administrator`) along with all the default organisms, ingest, and preprocessing is the [Local development instance](../local-dev-instance/) guide — that is the recommended path once you want to go beyond this minimal walkthrough. -Now we can "upgrade" the existing Loculus with this configuration: +## Submitting some data -```bash -helm upgrade loculus ./kubernetes/loculus --set environment=local --set branch=latest --set disableIngest=true --set disableEnaSubmission=true -f custom_values.yaml -``` +Let's submit a little data and watch it flow through the system. For that we need a login, so enable the built-in test accounts. Create a `custom_values.yaml`: -Again you can check the status of the pods with `kubectl get pods` and once they are all running you can check the website again at `http://127.0.0.1:3000`. - -You should find that the name of the database has changed to "My awesome database"! - -### Configuring an organism - -Loculus ships with some default organisms, but you probably want to overwrite these with your own. - -Let's edit the `custom_values.yaml` file to the following: - - ```yaml -name: 'Angelovirus DB' -organisms: - angelovirus: - schema: - organismName: 'Angelovirus' - metadata: - - name: country - type: string - initiallyVisible: true - - name: city - type: string - initiallyVisible: true - website: - tableColumns: - - country - - city - defaultOrder: descending - defaultOrderBy: country - preprocessing: - - version: 1 - image: ghcr.io/loculus-project/preprocessing-nextclade - args: - - 'prepro' - configFile: - log_level: DEBUG - batch_size: 100 - segments: - - name: main - references: - - name: singleReference - genes: [] - referenceGenomes: - - name: main - references: - - name: singleReference - sequence: 'NNN' # We are not performing alignment here, so this sequence doesn't matter createTestAccounts: true ``` - - -Because we have enabled the `createTestAccounts` option, we need to delete the existing keycloak database to ensure that the test users are added. - -First we need to run `kubectl get pods` to get the name of the keycloak pod, which will be something like `loculus-keycloak-database-665b964c6b-gm9t5` (but with the random string at the end being different). -Then we can delete the pod with `kubectl delete pod loculus-keycloak-database-[the rest of the pod name]`. - -:::tip - -If you struggled with deleting the pod, an alternative approach would be to delete the entire helm release with `helm delete loculus` and then re-run the `helm install` command (`helm install loculus ./kubernetes/loculus --set environment=local --set branch=latest --set disableIngest=true --set disableEnaSubmission=true -f custom_values.yaml`). - -::: - -Now we can upgrade the Loculus installation again: +The accounts are created when Keycloak first initialises, so delete the Keycloak database pod to have it re-seeded (find its name with `kubectl get pods`), then upgrade: ```bash +kubectl delete pod loculus-keycloak-database-<...> helm upgrade loculus ./kubernetes/loculus --set environment=local --set branch=latest --set disableIngest=true --set disableEnaSubmission=true -f custom_values.yaml ``` -### Testing it out with some data +:::tip +If deleting the pod is fiddly, you can instead `helm delete loculus` and re-run the original `helm install ... -f custom_values.yaml`. +::: -While that's getting ready, let's create some data to submit. +Once the pods are running again, open `http://localhost:3000` and log in with username `testuser`, password `testuser`. -First let's make our sequence file, which we might name `sequences.fasta`: +Go to **Submit** and choose one of the default organisms. The submission page offers a **metadata template** download for the selected organism — that lists exactly the columns the organism expects and is the easiest way to prepare a valid metadata file. Fill in a row or two, and prepare a matching FASTA, e.g. `sequences.fasta`: ```txt >sample1 @@ -198,33 +144,17 @@ ATGGGATTTTGGCATATATATACGA GCAGAGAGAGATACGTATATATATA ``` -Then our metadata file, which we might name `metadata.tsv`: - -
-
-id	city	country
-sample1	Paris	France
-sample2	Bogota	Colombia
-
-
- :::warning - -The metadata file must be tab-separated (TSV) -- sometimes code editors will try to convert tabs to a number of spaces, causing confusion. - +The metadata file must be tab-separated (TSV) — some editors silently convert tabs to spaces, which causes confusing errors. ::: -Now we can check everything is running with `kubectl get pods` and once it is, we can open up the website at `http://localhost:3000` again. Because we enabled the `createTestAccounts` option, you should be able to log in with the username `testuser` and password `testuser`. - -You can then go to `Submit`. You will be prompted to create a submitting group. - :::note -To successfully create a submitting group you will need to be able to access `127.0.0.1` on port `8079` (if you are running this on a remote machine you will need to set up port forwarding for this port too!). +You will first be prompted to create a submitting group. To create one you need to be able to reach the backend on `127.0.0.1:8079` (set up port forwarding too if you're on a remote machine). ::: -Once you have created a submitting group, you can submit your data. You will need to upload the `sequences.fasta` and `metadata.tsv` files. You can then select the organism you created earlier (`Angelovirus`) and submit the data. +Upload the metadata and sequence files and submit. Your sequences appear on the **Review** page, where you can release them. After a moment, refresh the **Search** page and your data should appear. **🎉 You've submitted and released your first sequences!** -You should find that they appear on your Review page and you can choose to release them. If you wait a minute and then refresh the Search page you should find your sequences have appeared! **🎉 We've released the first data for our new database!** +(If the organism dropdown is empty, the config loader that seeds the default organisms didn't run in this minimal install — the [Local development instance](../local-dev-instance/) guide is the reliable way to get a fully-seeded instance.) ### Cleaning up diff --git a/docs/src/content/docs/for-administrators/pipeline-concept.md b/docs/src/content/docs/for-administrators/pipeline-concept.md index 9f2195fbec..f5e1b36580 100644 --- a/docs/src/content/docs/for-administrators/pipeline-concept.md +++ b/docs/src/content/docs/for-administrators/pipeline-concept.md @@ -6,6 +6,10 @@ Before submitted user data is available for review and release, it is first proc Using an [existing pipeline](../existing-preprocessing-pipelines/) is the fastest way to get started with Loculus, but it is also easy to develop new pipelines that use custom tooling and logic. For a very brief guide on how to build a new pipeline, please see [here](../build-new-preprocessing-pipeline/). +:::note[Pipelines are external and customizable] +A preprocessing pipeline is a separate program that talks to the Loculus backend; Loculus does not prescribe how you configure or run it. Loculus _optionally_ offers a place to store a pipeline's config file (per organism and pipeline version), editable in the admin panel — see [Configuring pipelines in the admin panel](../configure-pipeline-admin-panel/). Using it is not mandatory: you can run and configure your pipeline however you like. +::: + ## Tasks The preprocessing pipeline receives the user submitted data and then validates and enriches this data. @@ -23,6 +27,6 @@ While the exact functionality depends on the specific pipeline, generally a pipe ## Pipeline versions -As the preprocessing logic might change over time, preprocessing pipelines are versioned (You specify the pipeline version under `.preprocessing.version`). +As the preprocessing logic might change over time, preprocessing pipelines are versioned. Each running pipeline declares which version it is via the `--pipeline-version` argument (in our Helm chart this comes from `preprocessing[].version` in `values.yaml`). Several versions can run in parallel — for example while migrating to a new version. The backend keeps track of which sequences have successfully been processed with which pipeline version. Once all data for an organism has successfully been processed with a new version, that version will also automatically be served to users. diff --git a/docs/src/content/docs/for-administrators/rolling-out-organism-config.md b/docs/src/content/docs/for-administrators/rolling-out-organism-config.md new file mode 100644 index 0000000000..2a9ca70fd1 --- /dev/null +++ b/docs/src/content/docs/for-administrators/rolling-out-organism-config.md @@ -0,0 +1,96 @@ +--- +title: Rolling out organism config changes +description: How to apply published organism config versions to SILO and LAPIS +--- + +Publishing an organism config writes a new immutable version to the Loculus backend database. The backend and website read the latest published config directly from the backend API, so they do not need a rollout for ordinary config publishes. SILO and LAPIS are different: each organism's SILO/LAPIS pods are pinned to one organism config version by Helm values, and their `config-adapter` init container fetches that pinned version when a pod starts. + +That means an organism config change has two steps when it affects SILO/LAPIS: publish the new version in the admin dashboard, then update the deployment's pinned `configVersion` and roll the organism's SILO/LAPIS pods. Instance-level config changes and display-only organism changes usually do not need this second step. + +## What value to change + +After publishing an organism config, the admin dashboard shows: + +- the organism key, for example `cchf-multi-ref`; +- the newly published config version, for example `3`; +- the value to update, for example `organisms.cchf-multi-ref.configVersion=3`. + +The chart templates read `.Values.organisms` when it is set, otherwise they fall back to `.Values.defaultOrganisms`. Most production-style installations should keep instance-specific organism deployment settings in their own values file under `organisms`. The in-repository default fixtures live under `defaultOrganisms`. + +For an existing organism, update only the deployment-level pin: + +```yaml +organisms: + cchf-multi-ref: + configVersion: 3 +``` + +If your deployment still uses `defaultOrganisms`, update the same field there: + +```yaml +defaultOrganisms: + cchf-multi-ref: + configVersion: 3 +``` + +Do not copy the organism schema, metadata fields, reference sequences, or link-outs into Helm values. Those are domain config and live in the database-backed configuration system. Helm values only hold deployment-level scaffolding such as the config pin, enabled flag, pipeline deployments, replicas, and resource settings. + +## GitOps / ArgoCD workflow + +For GitOps-managed deployments, do not run `helm upgrade` by hand. Change the values file in the repository that ArgoCD watches: + +1. Find the values file for the environment, for example a preview values file or a Pathoplexus environment values file. +2. Update the organism's `configVersion` to the newly published version. +3. Commit and push the change through the normal review process. +4. Let ArgoCD sync the application, or trigger a sync if your process requires it. +5. Watch the organism's SILO and LAPIS deployments until their new pods are ready. + +For a newly created organism, add the organism's deployment scaffolding as well as `configVersion: 1`. At minimum, the chart needs enough information to template the per-organism SILO/LAPIS deployments and any enabled preprocessing or ingest deployments. Reuse the shape already used by neighbouring organisms in that environment. New organisms stay hidden from the public organism list until an administrator marks them deployed after the rollout. + +## Direct Helm workflow + +For a manually managed Helm release, you can set the pin from the command line: + +```bash +helm upgrade \ + --reuse-values \ + --set organisms.cchf-multi-ref.configVersion=3 +``` + +Replace `` with your Helm release name and `` with the chart path or chart reference you actually deploy. In this repository's local preview workflow, the release is usually `preview` and the chart path is `kubernetes/loculus`, so the command looks like: + +```bash +helm upgrade preview kubernetes/loculus \ + --reuse-values \ + --set organisms.cchf-multi-ref.configVersion=3 +``` + +If your deployment relies on `defaultOrganisms` rather than `organisms`, set `defaultOrganisms..configVersion` instead. + +## Local previews in this repository + +For the k3d previews created from this repository, `deploy.py` installs the chart from `kubernetes/loculus` and normally uses the Helm release name `preview`. For an existing organism, either run a Helm upgrade with the new pin or edit the values used by your local deployment and redeploy. + +On a populated preview, avoid re-running fresh-only install hooks unless you intentionally want to reseed the database. If you are only changing a Helm value for running pods, a direct `helm upgrade preview kubernetes/loculus --reuse-values --set ...` is usually enough. + +For a newly created organism, publishing the config in the backend is not sufficient. The chart also needs an entry for the organism so Kubernetes creates SILO/LAPIS deployments for it. Add the organism to the values used by the preview, set `configVersion: 1`, and run a Helm upgrade. Until those deployments exist and become ready, the public organism list hides the organism. After the LAPIS endpoint is healthy, open **Admin → Organisms** and use **Mark deployed** for that organism. + +## What happens during rollout + +When the Helm value changes, Kubernetes rolls the organism's SILO and LAPIS deployments. Each new pod starts with the `config-adapter` init container, which fetches `/api/config/organisms/?version=` from the backend and renders the files SILO and LAPIS expect. SILO then imports data using the new rendered config, and LAPIS starts against the updated SILO service. + +If the new config is bad, the init container or SILO import should fail visibly and the new pod will not become ready. Revert by pinning the organism back to the previous published config version and rolling the deployment again. + +## Marking a new organism deployed + +Publishing v1 creates the config version that the deployment layer can pin, but it does not by itself prove that SILO and LAPIS exist. For this reason, new organisms start as **not deployed** in the admin organism list and are hidden from public organism navigation and submission organism lists. + +After the Helm/GitOps rollout has created the organism's SILO and LAPIS deployments, check the organism's LAPIS endpoint and the Kubernetes readiness state. Once the endpoint is healthy, click **Mark deployed** in **Admin → Organisms**. This changes only the backend deployment-readiness flag; it does not roll pods or edit Helm values. + +Existing organisms in upgraded installations are marked deployed by default during migration, so current previews and production organisms remain visible immediately. + +## When no rollout is needed + +No SILO/LAPIS rollout is needed for instance config changes such as branding, banners, feature flags, and data-use-terms text. The website reads those from the backend on request. + +Some organism changes are website-only, such as display names, descriptions, documentation text, and ordering of fields on the details page. These can be published without bumping the SILO/LAPIS pin, as long as they do not change what SILO indexes, what LAPIS exposes, or how preprocessing and ingest interpret data. diff --git a/docs/src/content/docs/for-administrators/sequence-reporting.md b/docs/src/content/docs/for-administrators/sequence-reporting.md index 1e6edd3427..5534daea6c 100644 --- a/docs/src/content/docs/for-administrators/sequence-reporting.md +++ b/docs/src/content/docs/for-administrators/sequence-reporting.md @@ -5,7 +5,11 @@ description: How to enable and configure sequence reporting By default, sequence reporting is disabled. -You can enable it in the `values.yaml` by configuring the `sequenceFlagging` section. For example: +:::note[Where this config now lives] +`sequenceFlagging` is part of Loculus's [database-backed instance config](../configuration-system/) — configure it through the admin dashboard (or the `kubernetes/loculus/fixtures/instance.yaml` seeded by the config loader), not in `values.yaml`. The YAML shape shown below is still correct; only its location has changed. +::: + +You enable it by configuring the `sequenceFlagging` section. For example: ```yaml sequenceFlagging: diff --git a/docs/src/content/docs/for-administrators/setup-with-k3d-and-nginx.mdx b/docs/src/content/docs/for-administrators/setup-with-k3d-and-nginx.mdx index 3403a81032..4659f9269e 100644 --- a/docs/src/content/docs/for-administrators/setup-with-k3d-and-nginx.mdx +++ b/docs/src/content/docs/for-administrators/setup-with-k3d-and-nginx.mdx @@ -113,12 +113,11 @@ helm upgrade --install kubernetes-secret-generator mittwald/kubernetes-secret-ge ## Step 3: Configure the instance -Create a file `my-values.yaml` with the following configuration for the instance. Please enter your domain name (e.g., `loculus.example.com`), define an initial admin password and give your instance a name. This instance uses a cluster-internal test database and a very simple organism. We will improve the configuration later. +Create a file `my-values.yaml` with the **deployment/infrastructure** configuration for your instance. Enter your domain name (e.g. `loculus.example.com`) and an initial admin password. This instance uses a cluster-internal test database. ```yaml -name: '' host: '' -environment: local # Do not change this: this setup uses a "local" environment as the public traffic is proxied through nginx. +environment: local # Do not change this: this setup proxies public traffic through nginx. runDevelopmentMainDatabase: true runDevelopmentKeycloakDatabase: true @@ -144,43 +143,18 @@ secrets: type: raw data: initialAdminPassword: '' - -organisms: - angelovirus: - schema: - organismName: 'Angelovirus' - metadata: - - name: country - type: string - initiallyVisible: true - - name: city - type: string - initiallyVisible: true - website: - tableColumns: - - country - - city - defaultOrder: descending - defaultOrderBy: country - preprocessing: - - version: 1 - image: ghcr.io/loculus-project/preprocessing-nextclade - args: - - 'prepro' - configFile: - log_level: DEBUG - batch_size: 100 - nextclade_sequence_and_datasets: - - name: 'main' - genes: [] - referenceGenomes: - singleReference: - nucleotideSequences: - - name: 'main' - sequence: 'NNN' # We are not performing alignment here, so this sequence doesn't matter - genes: [] ``` + + ## Step 4: Install and deploy Clone the [Loculus repository](https://github.com/loculus-project/loculus). The Loculus Helm chart is located in `kubernetes/loculus/` and you can install it with diff --git a/docs/src/content/docs/for-administrators/setup-with-kubernetes.md b/docs/src/content/docs/for-administrators/setup-with-kubernetes.md index 7b733da9fe..cba43271a7 100644 --- a/docs/src/content/docs/for-administrators/setup-with-kubernetes.md +++ b/docs/src/content/docs/for-administrators/setup-with-kubernetes.md @@ -65,9 +65,13 @@ The helm chart for deploying pathoplexus is within the Loculus repo, in the `kub ## Organism Configuration -Loculus supports multiple organisms, each with its own configuration. The organisms section in the values.yaml file allows you to define the specific settings for each organism. See [here](../../reference/helm-chart-config/) for a list of the available config fields. +:::caution[Domain config has moved to the database] +Organism **domain** config — the schema, metadata fields, reference genomes, link-outs, file categories, etc. — is now stored in Loculus's [database-backed configuration system](../configuration-system/), not in `values.yaml`. It is seeded from `kubernetes/loculus/fixtures/` by the config loader and edited through the admin dashboard; the backend, website, SILO, and LAPIS all read it from there. The `values.yaml` `organisms`/`defaultOrganisms` block is now used only for **deployment-level** per-organism settings: which pipelines run (preprocessing/ingest image, version, args), the pinned `configVersion`, and replicas. The schema-style fields shown in the example below are legacy and no longer drive the running instance (the ingest pipeline still reads some of them). See [Configuration (new)](../configuration-system/) for the current workflow. +::: -Here's an example of how the organism configuration might look: +Loculus supports multiple organisms, each with its own configuration. See [here](../../reference/helm-chart-config/) for a list of the available config fields. + +Here's an example of how the (legacy) organism configuration looked: ```yaml organisms: @@ -126,7 +130,7 @@ organisms: singleReference: nucleotideSequences: - name: 'main' - sequence: '[[URL:https://cov2tree.nyc3.cdn.digitaloceanspaces.com/reference.txt]]' + sequence: 'ACGT…' # the full reference sequence genes: [] ``` diff --git a/docs/src/content/docs/for-administrators/user-administration.md b/docs/src/content/docs/for-administrators/user-administration.md index 1802e7561d..35547f65f4 100644 --- a/docs/src/content/docs/for-administrators/user-administration.md +++ b/docs/src/content/docs/for-administrators/user-administration.md @@ -49,6 +49,16 @@ Superusers have the privilege to submit, revise, revoke and approve sequences on You can similarly unassign a role by selecting it from the current roles of the user, and clicking 'Unassign' (next to the 'Assign role' button) +## Loculus administrators + +Loculus administrators have access to the admin dashboard and can change database-backed instance, organism, and preprocessing-pipeline configuration. This is intentionally separate from `super_user`, which is intended for curation privileges. + +1. Click on 'Users' on the left-hand menu +2. Click on the user you want and go to ‘Role mapping’ +3. Click ‘Assign role’ button +4. Tick `loculus_administrator` +5. Click ‘Assign’ + ## Processing pipeline The processing pipeline requires a technical user to authenticate with the Loculus API. To create a new technical user, you can use the usual user registration form on the website. Afterwards, go to the Keycloak admin console, click on "Users" in the left navigation bar, select the user, click on "Role Mappings" and assign the `preprocessing_pipeline` role. diff --git a/docs/src/content/docs/reference/glossary.md b/docs/src/content/docs/reference/glossary.md index a7c1683924..f4a8f5e965 100644 --- a/docs/src/content/docs/reference/glossary.md +++ b/docs/src/content/docs/reference/glossary.md @@ -19,6 +19,22 @@ An aligned sequence is a sequence that has been aligned to a [reference sequence The "Loculus backend" is the central server service of Loculus and responsible for managing submissions and ensuring data persistence. Among other things, it offers APIs to submit and revise data. For querying and retrieving data, [LAPIS](#lapis) is usually used. The backend is written in Kotlin and uses the Spring framework. +### Configuration + +Loculus configuration is split into **domain config** — [organism](#organism) [schemas](#schema), [metadata](#metadata) fields, [reference sequences](#reference-sequences), link-outs, instance branding, etc. — and **deployment/runtime config**. Domain config is stored in the [backend](#backend) database (versioned, with an audit log) and edited through the [configuration dashboard](#configuration-dashboard) or the `/api/admin/config` API. Deployment config (service URLs, secrets, image tags, replica counts) lives in the Helm `values.yaml`. See the administrator guide [Configuration system](../../for-administrators/configuration-system/). + +### Configuration adapter + +An init container that runs in each [SILO](#silo) and [LAPIS](#lapis) pod. Before those services start, it fetches the pinned organism config version from the backend's public config API and renders the files SILO and LAPIS expect (`database_config.yaml`, `reference_genomes.json`, …). + +### Configuration dashboard + +The admin web interface at `/admin/config/` for viewing and editing the database-backed [configuration](#configuration). Access requires the `super_user` role in [Keycloak](#keycloak). Edits are made as drafts and published as new immutable versions, with every change recorded in an audit log. + +### Configuration loader + +A command-line tool (`loculus-config-loader`) that seeds the database-backed [configuration](#configuration) from fixture files, bringing a fresh instance to a known config state (e.g. in CI and local development). + ### Deletion A deletion is a type of [mutation](#mutation) where a nucleotide or amino acid is present in a reference sequence but not present in the sample sequence. The notation for a deletion in the case of a single-segmented nucleotide sequence is `-` (e.g., C100-). A mutation in the case of an amino acid sequence is further prefixed with the gene name by adding `:` (e.g., E:S100-). @@ -107,7 +123,7 @@ A Secret Key might look like: `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` ### Schema -A schema is a part of the configuration and describes the data structure of an instance. It includes the list of organisms and, for each [organism](#organism), the available [metadata](#metadata) fields and [segments](#segment). +A schema describes the data structure of an [organism](#organism): its available [metadata](#metadata) fields, [segments](#segment), and reference sequences. Each organism's schema is part of the database-backed [configuration](#configuration) and is edited through the [configuration dashboard](#configuration-dashboard). ### Secret key (S3) @@ -143,7 +159,7 @@ A substitution is a type of [mutation](#mutation) where at a given position in a ### Superuser -A superuser is a user role. Superusers have the privileges to act on behalf of any [submitting group](#submitting-group). This role is designed to be used by curators. +A superuser is a user role. Superusers have the privileges to act on behalf of any [submitting group](#submitting-group). This role is designed to be used by curators. The same `super_user` [Keycloak](#keycloak) role also grants access to the [configuration dashboard](#configuration-dashboard). ### Unaligned sequence diff --git a/docs/src/content/docs/reference/helm-chart-config.mdx b/docs/src/content/docs/reference/helm-chart-config.mdx index a7f0fb39f0..9f33a3cef9 100644 --- a/docs/src/content/docs/reference/helm-chart-config.mdx +++ b/docs/src/content/docs/reference/helm-chart-config.mdx @@ -62,6 +62,10 @@ windows. It defaults to `false`. ## Organism Configuration +:::caution[Organism domain config has moved to the database] +The organism **domain** config documented in this section — `schema`, metadata fields, reference genomes, link-outs, file categories, lineage system definitions, etc. — is now stored in Loculus's [database-backed configuration system](../../for-administrators/configuration-system/), seeded from `kubernetes/loculus/fixtures/` and edited through the admin dashboard. The backend, website, SILO, and LAPIS read it from there, **not** from `values.yaml`. The `organisms`/`defaultOrganisms` fields below remain in `values.yaml` for deployment-level settings (pipeline declarations, pinned `configVersion`, replicas) and are still partly read by the ingest pipeline, but the schema/metadata fields no longer drive the running instance. The dead `preprocessing[].configFile` and `metadata[].preprocessing` fields have been removed (pipelines fetch their config from the backend — see [Configuring pipelines in the admin panel](../../for-administrators/configure-pipeline-admin-panel/)). The remaining organism `schema`/`metadata`/`referenceGenomes` fields stay here only because the ingest pipeline still reads them from `values.yaml`. +::: + ### Lineage system definitions @@ -151,20 +155,13 @@ into a single row. ### Preprocessing (type) - - -The values for `args` and `configFile` depend on the used preprocessing pipeline. - -#### Nextclade Preprocessing Pipeline ConfigFile (type) +This declares the preprocessing pipeline deployment for an organism (which image and version to run, arguments, replicas). The values for `args` depend on the used preprocessing pipeline. - - -##### Nextclade Preprocessing Pipeline Sequence and Dataset Objects (type) - - + -For more details on the Nextclade preprocessing pipeline, please see -[here](../../for-administrators/existing-preprocessing-pipelines/#nextclade-based-pipeline). +:::note +A preprocessing pipeline's own config file is no longer set here. Pipelines fetch their config from the backend's public config API — see [Configuring pipelines in the admin panel](../../for-administrators/configure-pipeline-admin-panel/). For more details on the Nextclade pipeline, see [here](../../for-administrators/existing-preprocessing-pipelines/#nextclade-based-pipeline). +::: ### Ingest (type) diff --git a/documentation-experimental-features/README.md b/documentation-experimental-features/README.md new file mode 100644 index 0000000000..b269776c6a --- /dev/null +++ b/documentation-experimental-features/README.md @@ -0,0 +1,10 @@ +# Experimental Feature Architecture Documentation + +This folder contains arc42-style technical documentation for the main prototype ideas on this branch. + +| Feature area | Start here | +|---|---| +| Configuration management | [configuration-management/01_introductionAndGoals.md](configuration-management/01_introductionAndGoals.md) | +| API structure | [api-structure/01_introductionAndGoals.md](api-structure/01_introductionAndGoals.md) | +| SQL-backed views | [views/01_introductionAndGoals.md](views/01_introductionAndGoals.md) | + diff --git a/documentation-experimental-features/api-structure/01_introductionAndGoals.md b/documentation-experimental-features/api-structure/01_introductionAndGoals.md new file mode 100644 index 0000000000..c8a2874dbf --- /dev/null +++ b/documentation-experimental-features/api-structure/01_introductionAndGoals.md @@ -0,0 +1,28 @@ +# 1. Introduction and Goals + +## What this describes + +Loculus exposes sequence-query APIs through the backend under `/query/{organism}/{versionGroup}/...` instead of asking the website and users to call LAPIS directly. + +LAPIS remains the query engine. The backend owns the public API shape, resolves organism and view configuration, injects version-scope filters, proxies the request, and exposes OpenAPI documents for the resulting API surface. + +## Goals + +1. Provide one backend-owned API surface for submission, configuration, and sequence queries. +2. Keep query URLs stable even if internal LAPIS service names or deployment details change. +3. Document query APIs with the rest of the backend API in Swagger and Scalar. +4. Split large OpenAPI output into usable per-query and general specifications. +5. Preserve LAPIS streaming behavior and response formats. +6. Keep the backend proxy thin; LAPIS still performs filtering, aggregation, and sequence output. + +## Non-goals + +- Reimplementing LAPIS query semantics in the backend. +- Removing the temporary `/{organism}/lapis/**` proxy in this prototype. +- Adding production-grade per-user query authorization. +- Hiding public open metadata behind authentication. + +## Scope and status + +This is a working prototype. The website uses the new `/query` routes for the main search and documentation flows. A legacy LAPIS proxy remains for compatibility and for call paths that have not yet moved. + diff --git a/documentation-experimental-features/api-structure/02_constraints.md b/documentation-experimental-features/api-structure/02_constraints.md new file mode 100644 index 0000000000..cb33541530 --- /dev/null +++ b/documentation-experimental-features/api-structure/02_constraints.md @@ -0,0 +1,15 @@ +# 2. Constraints + +## Technical constraints + +1. LAPIS remains the source of query execution behavior. +2. Query endpoints must support both `GET` and `POST` because LAPIS clients use both forms. +3. Large responses must stream through the backend without buffering the full body. +4. Hop-by-hop HTTP headers must not be forwarded from LAPIS to clients. +5. Query APIs must work for normal organism databases and SQL-backed views. +6. Generated OpenAPI documents must stay valid after path rewriting from generic `/query/{organism}` paths to concrete organism/view keys. + +## Operational constraints + +The backend resolves LAPIS URLs from published config. If a database or view has no `lapisUrl`, the proxy returns `404` rather than guessing deployment names. + diff --git a/documentation-experimental-features/api-structure/03_contextAndScope.md b/documentation-experimental-features/api-structure/03_contextAndScope.md new file mode 100644 index 0000000000..b7fb8a14e2 --- /dev/null +++ b/documentation-experimental-features/api-structure/03_contextAndScope.md @@ -0,0 +1,27 @@ +# 3. Context and Scope + +## Context + +Before this prototype, Loculus exposed backend APIs and LAPIS APIs as separate surfaces. This made API documentation fragmented and leaked internal LAPIS routes into the website. + +The new shape is: + +- `/query/{key}/current/...` for latest-version query results. +- `/query/{key}/allVersions/...` for historical-version query results. +- `/api-docs.json` for the complete backend OpenAPI document. +- `/api-docs/general.json` for backend endpoints excluding query APIs. +- `/api-docs/query/{key}.json` for one database or view query API. + +## In scope + +- Backend query routing and proxying. +- Version-scope injection through `versionStatus`. +- OpenAPI splitting and per-spec documentation pages. +- Swagger and Scalar shells with a Loculus navigation bar. + +## Out of scope + +- LAPIS deployment internals. +- SILO import and preprocessing details. +- Query authorization beyond the current open endpoint model. + diff --git a/documentation-experimental-features/api-structure/04_solutionStrategy.md b/documentation-experimental-features/api-structure/04_solutionStrategy.md new file mode 100644 index 0000000000..3b70234a46 --- /dev/null +++ b/documentation-experimental-features/api-structure/04_solutionStrategy.md @@ -0,0 +1,10 @@ +# 4. Solution Strategy + +1. Add explicit backend routes under `/query/{organism}/{versionGroup}` for the LAPIS endpoints used by Loculus. +2. Resolve `{organism}` against both released organisms and configured views. +3. Translate `current` to a LAPIS `versionStatus=LATEST_VERSION` filter; leave `allVersions` unfiltered by version status. +4. Forward the request to the configured upstream LAPIS URL with streaming response bodies. +5. Keep a legacy `/{organism}/lapis/**` proxy for compatibility while callers migrate. +6. Generate one complete OpenAPI document and derive smaller documents by filtering paths. +7. Present the split documents on `/api-documentation`, Swagger, and Scalar. + diff --git a/documentation-experimental-features/api-structure/05_buildingBlockView.md b/documentation-experimental-features/api-structure/05_buildingBlockView.md new file mode 100644 index 0000000000..d9436f05aa --- /dev/null +++ b/documentation-experimental-features/api-structure/05_buildingBlockView.md @@ -0,0 +1,30 @@ +# 5. Building Block View + +## Level 1 + +``` +Client / Website + | + v +Loculus backend + - QueryController + - LapisAccessFilter + - LapisProxyService + - OpenApiSplitController + | + v +Configured LAPIS instance +``` + +## Components + +| Component | Responsibility | +|---|---| +| `QueryController` | Defines typed `/query/{key}/{versionGroup}/...` routes and resolves the configured LAPIS URL. | +| `LapisAccessFilter` | Adds the version-scope filter to JSON bodies or query strings. | +| `LapisProxyService` | Sends the upstream HTTP request and streams the LAPIS response back to the client. | +| `LapisProxyController` | Legacy `/{organism}/lapis/**` compatibility proxy. | +| `OpenApiSplitController` | Builds `/api-docs/general.json` and `/api-docs/query/{key}.json` from the generated OpenAPI document. | +| `InfoController` | Serves Swagger and Scalar shells with a small Loculus navigation bar. | +| Website docs page | Groups complete, general, database-query, and view-query API documents. | + diff --git a/documentation-experimental-features/api-structure/06_runtimeView.md b/documentation-experimental-features/api-structure/06_runtimeView.md new file mode 100644 index 0000000000..e26e9469fc --- /dev/null +++ b/documentation-experimental-features/api-structure/06_runtimeView.md @@ -0,0 +1,18 @@ +# 6. Runtime View + +## Query request + +1. A client sends `GET` or `POST` to `/query/{key}/{versionGroup}/metadata`, `/aggregated`, or another supported query endpoint. +2. The backend resolves `{key}` first as a configured view and then as an organism. +3. The backend maps the route to the corresponding LAPIS `/sample/...` path. +4. `current` adds `versionStatus=LATEST_VERSION`; `allVersions` does not. +5. The proxy forwards the request to the configured LAPIS URL. +6. LAPIS executes the query and streams the response through the backend. + +## API documentation request + +1. Springdoc generates the complete backend OpenAPI document at `/api-docs.json`. +2. `/api-docs/general.json` removes all `/query/` paths. +3. `/api-docs/query/{key}.json` keeps only paths for that one key. +4. Swagger and Scalar load whichever spec is selected in the top bar. + diff --git a/documentation-experimental-features/api-structure/07_deploymentView.md b/documentation-experimental-features/api-structure/07_deploymentView.md new file mode 100644 index 0000000000..1c3a354405 --- /dev/null +++ b/documentation-experimental-features/api-structure/07_deploymentView.md @@ -0,0 +1,20 @@ +# 7. Deployment View + +## Runtime deployment + +No new service is introduced. The existing backend pod proxies to existing LAPIS services. + +Each organism and view config carries a `lapisUrl`. In local previews these are in-cluster service URLs such as `http://loculus-lapis-service-west-nile:8080` or `http://loculus-lapis-service-overview:8080`. + +## Public entry points + +| Path | Purpose | +|---|---| +| `/query/{key}/current/...` | Latest-version query API. | +| `/query/{key}/allVersions/...` | Historical-version query API. | +| `/api-docs.json` | Complete OpenAPI document. | +| `/api-docs/general.json` | Backend endpoints without query APIs. | +| `/api-docs/query/{key}.json` | One database or view query API. | +| `/swagger-ui/loculus` | Swagger UI with Loculus navigation bar. | +| `/scalar-api-reference` | Scalar page with Loculus navigation bar. | + diff --git a/documentation-experimental-features/api-structure/08_crosscuttingConcepts.md b/documentation-experimental-features/api-structure/08_crosscuttingConcepts.md new file mode 100644 index 0000000000..de0d7b2813 --- /dev/null +++ b/documentation-experimental-features/api-structure/08_crosscuttingConcepts.md @@ -0,0 +1,22 @@ +# 8. Crosscutting Concepts + +## Version groups + +`current` is a backend-level concept that injects `versionStatus=LATEST_VERSION`. `allVersions` leaves version history visible to LAPIS. + +## Streaming + +The backend forwards LAPIS responses as `StreamingResponseBody`, removes hop-by-hop headers, and removes `Content-Length` because the response is streamed. + +## OpenAPI size management + +The complete spec remains available, but day-to-day browsing should use the split specs. This keeps Swagger and Scalar usable when many organisms and views are configured. + +## Documentation navigation + +Swagger and Scalar pages include a small Loculus bar with a home link, a spec selector, and a Data Use Terms notice when configured. + +## Compatibility proxy + +The `/{organism}/lapis/**` proxy remains temporary. New website and API documentation work should prefer `/query`. + diff --git a/documentation-experimental-features/api-structure/09_architecturalDecisions.md b/documentation-experimental-features/api-structure/09_architecturalDecisions.md new file mode 100644 index 0000000000..370fe0bd50 --- /dev/null +++ b/documentation-experimental-features/api-structure/09_architecturalDecisions.md @@ -0,0 +1,38 @@ +# 9. Architectural Decisions + +## ADR-001: Backend owns the public query API path + +**Decision.** Public query calls use `/query/{key}/{versionGroup}/...`. + +**Consequences.** Clients do not need LAPIS service URLs. The backend can document and evolve the public query surface. + +## ADR-002: LAPIS remains the query engine + +**Decision.** The backend forwards to LAPIS instead of evaluating metadata or sequence queries. + +**Consequences.** Query behavior stays aligned with LAPIS. Backend work is limited to routing, filtering, and streaming. + +## ADR-003: Version scope is explicit in the path + +**Decision.** `current` and `allVersions` are route segments. + +**Consequences.** Callers choose latest-only or historical behavior explicitly, and OpenAPI documents show both modes. + +## ADR-004: OpenAPI is split by path filtering + +**Decision.** Springdoc still generates a complete document; custom endpoints filter it into general and per-query specs. + +**Consequences.** There is one source OpenAPI model, but users can load smaller specs. + +## ADR-005: Views use the same query API as organisms + +**Decision.** `QueryController` resolves keys against configured views and organism configs. + +**Consequences.** SQL-backed metadata views get Swagger, Scalar, and `/query` access without a separate API family. + +## ADR-006: Keep the legacy LAPIS proxy temporarily + +**Decision.** `/{organism}/lapis/**` remains available while callers migrate. + +**Consequences.** Compatibility is preserved, but the API documentation page no longer promotes the legacy proxy. + diff --git a/documentation-experimental-features/api-structure/10_qualityRequirements.md b/documentation-experimental-features/api-structure/10_qualityRequirements.md new file mode 100644 index 0000000000..ec496281a8 --- /dev/null +++ b/documentation-experimental-features/api-structure/10_qualityRequirements.md @@ -0,0 +1,11 @@ +# 10. Quality Requirements + +| Quality | Requirement | +|---|---| +| Usability | Swagger and Scalar must load a focused spec for one database or view. | +| Compatibility | Query endpoints should preserve LAPIS request/response formats. | +| Performance | Large LAPIS responses must stream through the backend. | +| Maintainability | Adding a new configured view should automatically produce a query spec. | +| Observability | Upstream LAPIS failures should surface as clear backend proxy errors. | +| Consistency | Website query paths and documented API paths should use the same backend route family. | + diff --git a/documentation-experimental-features/api-structure/11_risksAndTechnicalDebt.md b/documentation-experimental-features/api-structure/11_risksAndTechnicalDebt.md new file mode 100644 index 0000000000..ac54d2bec9 --- /dev/null +++ b/documentation-experimental-features/api-structure/11_risksAndTechnicalDebt.md @@ -0,0 +1,8 @@ +# 11. Risks and Technical Debt + +1. The legacy `/{organism}/lapis/**` proxy still exists and should be retired after remaining callers move. +2. The backend currently injects only version-scope filters; future access-control filters need careful composition with user queries. +3. Query endpoint coverage follows current Loculus usage, so new LAPIS endpoints need explicit backend route additions. +4. The split OpenAPI endpoints filter paths after generation; future customizers must keep concrete query paths stable. +5. Scalar is loaded from a CDN in the current backend HTML shell. + diff --git a/documentation-experimental-features/api-structure/12_glossary.md b/documentation-experimental-features/api-structure/12_glossary.md new file mode 100644 index 0000000000..97aa54d389 --- /dev/null +++ b/documentation-experimental-features/api-structure/12_glossary.md @@ -0,0 +1,10 @@ +# 12. Glossary + +| Term | Meaning | +|---|---| +| Query API | Backend-owned `/query/{key}/{versionGroup}/...` API that proxies LAPIS. | +| Version group | Route segment selecting latest-only (`current`) or historical (`allVersions`) records. | +| Split spec | A filtered OpenAPI document for either general backend endpoints or one query key. | +| Query key | Public key for an organism database or SQL-backed view. | +| Legacy LAPIS proxy | Temporary `/{organism}/lapis/**` backend proxy retained for compatibility. | + diff --git a/documentation-experimental-features/api-structure/13_databaseSchema.md b/documentation-experimental-features/api-structure/13_databaseSchema.md new file mode 100644 index 0000000000..4eb16381b7 --- /dev/null +++ b/documentation-experimental-features/api-structure/13_databaseSchema.md @@ -0,0 +1,12 @@ +# 13. Database Schema + +The API proxy introduces no dedicated database tables. + +It reads published configuration through `ConfigService`: + +- organism configs provide organism `lapisUrl` values; +- instance config `views` provides view `lapisUrl` values; +- released organism listings drive the Swagger/Scalar spec selector. + +The relevant persisted structures are documented in `../configuration-management/13_databaseSchema.md`. + diff --git a/documentation-experimental-features/api-structure/14_configSchema.md b/documentation-experimental-features/api-structure/14_configSchema.md new file mode 100644 index 0000000000..fae07a9acd --- /dev/null +++ b/documentation-experimental-features/api-structure/14_configSchema.md @@ -0,0 +1,25 @@ +# 14. Config Schema + +## Organism config + +Each organism config may contain: + +```yaml +lapisUrl: "http://loculus-lapis-service-west-nile:8080" +``` + +The backend query proxy returns `404` if a query key resolves to an organism without a `lapisUrl`. + +## View config + +Each view config may contain: + +```yaml +displayName: "Overview" +lapisUrl: "http://loculus-lapis-service-overview:8080" +query: "..." +schema: "..." +``` + +The query proxy uses only `lapisUrl` and `displayName`; the view materialization pipeline uses `query` and `schema`. + diff --git a/documentation-experimental-features/configuration-management/01_introductionAndGoals.md b/documentation-experimental-features/configuration-management/01_introductionAndGoals.md new file mode 100644 index 0000000000..0fc29a2e32 --- /dev/null +++ b/documentation-experimental-features/configuration-management/01_introductionAndGoals.md @@ -0,0 +1,39 @@ +# 1. Introduction and Goals + +## What this describes + +Loculus's **domain-specific configuration** (organisms, schemas, branding, link-outs, reference genomes, lineage systems, …) lives in the backend **database** and is edited through a Keycloak-gated **admin panel**, instead of being baked into Helm's `values.yaml`. + +Only domain configuration moved. Technical/infrastructure config (database connections, ingress, ports, image tags, replica counts, credentials) stays in Helm. The work spans the **core software** — website, backend, and the LAPIS/SILO integration. The **preprocessing** pipeline has since been decoupled too: it is treated as external and fetches what it needs from the backend's public config API, including an optional opaque per-organism config file ([ADR-019/020](09_architecturalDecisions.md)). The **ingest** pipeline remains `values.yaml`-configured for now. + +## Goals + +1. **Editable without redeploy** — admins change domain config from a UI; common edits no longer require editing `values.yaml`. +2. **Safe by construction** — the write API exposes only operations whose effect on existing data, queries, and pipelines is well understood. Destructive operations are not in the API surface at all. +3. **Zero/minimal downtime** — when a change requires SILO/LAPIS reindexing, the old instance stays live until the new one is ready. +4. **Predictable, observable rollouts** — changes that need pod restarts propagate through standard Kubernetes rolling updates, not custom kill-and-restart logic. +5. **Decoupled responsibilities** — the backend knows nothing about Kubernetes, LAPIS, or SILO; LAPIS/SILO pods know nothing about the Loculus database. The contract between them is an HTTP API. +6. **Auditable** — every config change is attributed and replayable; short-window rollback is trivial. + +## Non-goals + +- Making the core (backend, config DB, `config-tools`) aware of any specific pipeline, or letting it deploy/run pipelines. Pipelines stay external and customizable; the optional preprocessing config-file feature ([ADR-020](09_architecturalDecisions.md)) is a store-and-serve text channel only. +- Changing how the **ingest** pipeline is configured (still `values.yaml`). +- Removing Helm or `values.yaml` — they remain the source of infrastructure config. +- Letting Loculus dynamically provision Kubernetes pods. Provisioning stays a Helm/admin responsibility. +- Hot-reloading LAPIS or SILO in-process (their architecture does not support it; we work with restarts). + +## Scope and status + +This is a **working prototype**, not a production migration. Loculus has no stable release yet, so breaking changes are acceptable and a production migration tool can be designed later. + +What must keep working from day one is **integration tests and preview instances**. The per-organism content in `kubernetes/loculus/values.yaml` exists for CI and previews, not for real instances. A small **config loader** populates the DB-backed config from fixture YAML so tests and previews stay functional without manual entry (see [section 7](07_deploymentView.md)). + +## Stakeholders + +| Role | Goal | +|---|---| +| Instance admin (domain expert) | Change branding, add organisms, evolve schemas without writing YAML or rolling Helm for every tweak. | +| Server operator | Owns the technical setup (PostgreSQL provisioning, infrastructure, Loculus version upgrades). | +| End user / submitter | Stable URLs and stable LAPIS queries; no surprise breakage. | +| Loculus developer | Add new config options without invasive schema/code changes. | diff --git a/documentation-experimental-features/configuration-management/02_constraints.md b/documentation-experimental-features/configuration-management/02_constraints.md new file mode 100644 index 0000000000..10d55b36cc --- /dev/null +++ b/documentation-experimental-features/configuration-management/02_constraints.md @@ -0,0 +1,33 @@ +# 2. Constraints + +## Architectural constraints + +| # | Constraint | Origin | +|---|---|---| +| C1 | The backend must not depend on Kubernetes APIs. It runs identically in any container environment. | Project policy — keep core software portable | +| C2 | The backend must not embed knowledge of LAPIS or SILO config formats. | Decoupling principle | +| C3 | Existing data (sequences, submissions, accessions) must remain valid through any change. | Migration safety | +| C4 | LAPIS and SILO do not support hot reload — schema changes require restart + reimport. | Upstream limitation | + +## Technical constraints + +| # | Constraint | Origin | +|---|---|---| +| T1 | Backend is Kotlin + Spring Boot; persistence is PostgreSQL via Flyway migrations. | Existing stack | +| T2 | Website is Astro + React + TypeScript; Zod for schema validation. | Existing stack | +| T3 | Domain config types are defined twice (Kotlin and Zod) and must stay in sync. | Existing stack | +| T4 | Deployment is via Helm; one SILO pod and one LAPIS pod per organism. | Existing topology | +| T5 | Field names (`metadata[].name`, `inputFields[].name`, …) are part of the public LAPIS query surface and must remain stable. | External LAPIS clients | + +## Organisational constraints + +| # | Constraint | Origin | +|---|---|---| +| O1 | Start with provably safe operations; expand the API surface deliberately. | Stakeholder requirement | +| O2 | Admins are technically capable (Helm, kubectl) but should not have to be for routine config edits. | Stakeholder model | + +## Scope constraints + +- **In scope:** website, backend, and the LAPIS/SILO integration. +- **Preprocessing pipeline:** decoupled from the core ([ADR-019/020](09_architecturalDecisions.md)). It is treated as external and fetches the generic domain config plus an optional opaque per-organism config file from the backend; its deployment stays in Helm. The core stays pipeline-agnostic. +- **Out of scope:** ingest pipeline and ENA deposition pipeline. These continue to be configured through `values.yaml`. (Migrating them to the DB-backed config is a plausible future step.) diff --git a/documentation-experimental-features/configuration-management/03_contextAndScope.md b/documentation-experimental-features/configuration-management/03_contextAndScope.md new file mode 100644 index 0000000000..5873db78b9 --- /dev/null +++ b/documentation-experimental-features/configuration-management/03_contextAndScope.md @@ -0,0 +1,93 @@ +# 3. Context and Scope + +## System context + +``` + ┌──────────────────────┐ + │ Instance admin │ + │ (browser) │ + └──────────┬───────────┘ + │ HTTPS, Keycloak-authenticated + ┌──────────▼───────────┐ + │ Loculus admin │ + │ panel (Astro app) │ + └──────────┬───────────┘ + │ + │ /api/admin/config/... + │ + ┌──────────────┐ ┌──────▼──────────┐ ┌────────────────────────┐ + │ End users │ │ Loculus backend │───│ Loculus PostgreSQL │ + │ (browser) │ │ (Kotlin/Spring) │ │ (config_* tables) │ + └──────┬───────┘ └─┬──────────┬────┘ └────────────────────────┘ + │ │ │ + ▼ ▲ │ /api/config/... + ┌───────────┐ │ ▼ + │ Website │────────┘ ┌──────────────────────────────┐ + │ (Astro) │ │ Per-organism pod (admin- │ + └───────────┘ │ managed via Helm) │ + │ ┌─────────────────────────┐ │ + │ │ loculus-config-adapter │ │ fetches config at startup, + │ │ (init container) │ │ renders SILO/LAPIS files + │ └────────────┬────────────┘ │ + │ │ shared volume│ + │ ┌────────────▼────────────┐ │ + │ │ SILO (upstream) │ │ + │ │ LAPIS (upstream) │ │ + │ │ silo-importer │ │ + │ └─────────────────────────┘ │ + └──────────────────────────────┘ +``` + +## External actors + +| Actor | Interaction | +|---|---| +| Instance admin | Edits config through the admin panel; runs `helm upgrade` to apply changes that affect deployed SILO/LAPIS pods. | +| End user (incl. submitter) | Uses the website to browse, search, and submit; consumes published config indirectly via the website and LAPIS. | + +## External technical systems + +| System | Role | Loculus's coupling | +|---|---|---| +| PostgreSQL | Stores config and everything else | Direct (JDBC, Flyway) | +| Keycloak | Authenticates admins and submitters | OAuth/OIDC | +| LAPIS / SILO | Per-organism query engine and storage | HTTP (from the adapter only); no direct DB coupling | +| Kubernetes | Container orchestration | Backend: zero coupling. Admins use Helm/kubectl. | + +## Interfaces + +The backend exposes two HTTP surfaces. Full reference: [section 13](13_databaseSchema.md) (storage) and [section 14](14_configSchema.md) (payload shapes); the operation registry is in [section 8](08_crosscuttingConcepts.md). + +### Public read API (consumed by website, config adapter, external tools) + +``` +GET /api/config/instance → { version, publishedAt, config, readOnlyMode } +GET /api/config/instance?version={n} → instance config at version n +GET /api/config/organisms → released organisms + displayName + current version +GET /api/config/organisms/{key} → { key, version, publishedAt, config } +GET /api/config/organisms/{key}?version={n} → organism config at version n +``` + +The read API is **open** — Loculus config holds only non-sensitive data; sensitive values stay in Helm (see [ADR-011](09_architecturalDecisions.md)). + +### Admin write API (admin panel only; Keycloak `loculus_administrator` role) + +``` +GET /api/admin/config/organisms list all organisms (incl. unreleased) +POST /api/admin/config/organisms create unreleased organism (key only) +GET /api/admin/config/organisms/{key}/draft read draft (+ pending ops); 204 if none +PUT /api/admin/config/organisms/{key}/draft replace draft (unreleased only) +POST /api/admin/config/organisms/{key}/draft/operations append operation(s) (released only) +DELETE /api/admin/config/organisms/{key}/draft discard draft +POST /api/admin/config/organisms/{key}/publish publish → new version +GET /api/admin/config/organisms/{key}/versions list kept versions +GET /api/admin/config/instance/draft instance draft; 204 if none +PUT /api/admin/config/instance/draft replace instance draft (full document) +POST /api/admin/config/instance/draft/operations append instance operation(s) +DELETE /api/admin/config/instance/draft discard instance draft +POST /api/admin/config/instance/publish publish instance → new version +GET /api/admin/config/instance/versions list instance versions +GET /api/admin/config/audit[?organism={key}] audit-log entries (most recent first) +``` + +Concurrency on drafts is optimistic: mutating endpoints accept `If-Match: ` and return `409` on mismatch with the current state. diff --git a/documentation-experimental-features/configuration-management/04_solutionStrategy.md b/documentation-experimental-features/configuration-management/04_solutionStrategy.md new file mode 100644 index 0000000000..c454fcee6f --- /dev/null +++ b/documentation-experimental-features/configuration-management/04_solutionStrategy.md @@ -0,0 +1,14 @@ +# 4. Solution Strategy + +| # | Decision | Reasoning | +|---|---|---| +| S1 | PostgreSQL is the source of truth for domain config. | Centralised, transactional, easy to attribute changes. | +| S2 | The backend exposes config over HTTP; every other component consumes it as a client. | Decoupled consumers; one API to evolve. | +| S3 | SILO and LAPIS pods include a per-pod **config adapter** that translates Loculus config into their file formats. | Keeps the backend tool-agnostic. | +| S4 | Pod specs **pin a specific config version**; the admin updates the pin to apply changes; Kubernetes performs the rolling update. | Native k8s semantics; no custom restart machinery. | +| S5 | Two editing paths: **free-form document replacement** for never-published organisms, **restricted named operations** for released ones. | Maximises flexibility before release, safety after. | +| S6 | The write API exposes only operations that are safe by construction; unsafe edits are not in the API at all. | Predictable safety story; small surface to review. | +| S7 | Published versions are **immutable while kept**; they exist to support smooth rollouts and short-window rollback. | Avoids long-term commitments that would constrain future schema evolution. | +| S8 | Shared TypeScript package (`config-tools/`) owns the canonical schemas and both edge programs (loader, adapter). | One place to keep Zod schemas; the website re-exports them. | + +These choices appear as the building blocks in [section 5](05_buildingBlockView.md) and are justified in detail as ADRs in [section 9](09_architecturalDecisions.md). diff --git a/documentation-experimental-features/configuration-management/05_buildingBlockView.md b/documentation-experimental-features/configuration-management/05_buildingBlockView.md new file mode 100644 index 0000000000..d19194b455 --- /dev/null +++ b/documentation-experimental-features/configuration-management/05_buildingBlockView.md @@ -0,0 +1,114 @@ +# 5. Building Block View + +## Level 1 — system overview + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Loculus system │ +│ │ +│ ┌────────────┐ ┌────────────┐ ┌──────────────────┐ │ +│ │ Website │ │ Backend │ │ Postgres │ │ +│ │ + Admin │────│ │────│ (config_*) │ │ +│ │ panel │ │ │ │ │ │ +│ └────────────┘ └─────┬──────┘ └──────────────────┘ │ +│ │ │ +│ │ HTTP │ +│ ▼ │ +│ ┌──────────────────────────┐ │ +│ │ Per-organism pod │ │ +│ │ ├ loculus-config-adapter │ (init container) │ +│ │ ├ SILO │ │ +│ │ ├ LAPIS │ │ +│ │ └ silo-importer │ │ +│ └──────────────────────────┘ │ +└──────────────────────────────────────────────────────────────┘ +``` + +## Components + +| Component | Where | Responsibility | +|---|---|---| +| **Backend config module** | `backend/.../config/` (Kotlin) | Stores/serves config; owns the operation registry; the only writer of the `config_*` tables. | +| **Admin panel** | `website/src/pages/admin/config/`, `website/src/components/admin/` | Keycloak-gated UI for instance + organism config editing, drafts, publish, history, audit. | +| **Website config access** | `website/src/middleware/configMiddleware.ts`, `services/configTransform.ts` | Fetches published config per request and derives the website's view model. | +| **`config-tools/`** | top-level TS package | Canonical Zod schemas (re-exported by the website) + two CLIs: the **loader** and the **adapter**. | +| **loculus-config-loader** | `config-tools/src/loader/` | Reads fixture YAML and brings the backend into that state via the admin API. Used by CI, previews, local dev. | +| **loculus-config-adapter** | `config-tools/src/adapter/` | Per-pod init container; fetches one pinned organism version and renders SILO + LAPIS files (it does not render any Loculus preprocessing-pipeline config). | + +## Level 2 — backend config module + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ Loculus backend │ +│ ┌──────────────┐ ┌──────────────────┐ │ +│ │ Public read │ │ Admin write API │ (Keycloak loculus-admin.) │ +│ │ API │ │ /api/admin/ │ │ +│ │ /api/config/ │ │ config/... │ │ +│ └──────┬───────┘ └─────┬────────────┘ │ +│ ▼ ▼ │ +│ ┌────────────────────┐ ┌──────────────────────────┐ │ +│ │ ConfigService │ │ DraftService │ │ +│ │ read versions, │ │ draft CRUD, publish, │ │ +│ │ cache (invalidated│ │ optimistic concurrency │ │ +│ │ on publish) │ └────────────┬─────────────┘ │ +│ └─────────┬──────────┘ │ │ +│ │ ┌────────────▼─────────────┐ │ +│ │ │ OperationRegistry + │ │ +│ │ │ OperationDispatcher │ │ +│ │ │ (typed handlers) │ │ +│ │ └────────────┬─────────────┘ │ +│ │ ┌──────────────────┘ │ +│ │ │ AuditLogService (append-only; source of │ +│ │ │ the "pending ops" list) │ +│ ▼ ▼ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Postgres config_* tables (see section 13) │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────────────────────────┘ +``` + +| Service | Responsibility | +|---|---| +| `ConfigService` | Read published instance/organism config and version lists; naive in-process cache invalidated on publish. | +| `DraftService` | Draft CRUD and publish for both scopes; lazy-creates a draft from the current published config on first edit; `revision`-based optimistic concurrency. | +| `OrganismAdminService` | Creates an organism row + its `current_processing_pipeline` row in one transaction. | +| `AuditLogService` | Appends audit rows; computes the pending-operations list from entries since the last publish/discard. | +| `OperationRegistry` / `OperationDispatcher` | Maps `opType → handler`; handlers are auto-discovered Spring components. | + +### Operation registry (implemented handlers) + +The registry IS the safety policy: if there is no handler, an admin cannot perform the edit. Currently implemented (all classified safe — see [section 8](08_crosscuttingConcepts.md) for the T1/T2/T3 model): + +| `opType` | Scope | Effect | +|---|---|---| +| `setInstanceBranding` | instance | name / description / logo / supportContact | +| `setOrganismDisplay` | organism | displayName / description / image | +| `setMetadataFieldDisplay` | organism | displayName / header / description / hidden / customDisplay on an existing field | +| `reorderMetadataFields` | organism | set field display order | +| `addLinkOut` / `updateLinkOut` / `removeLinkOut` | organism | manage `schema.linkOuts` (keyed by `name`) | +| `addOptionalMetadataField` | organism (T2) | append a `required: false` metadata field | + +Instance fields not covered by `setInstanceBranding` (banners, GitHub links, feature toggles, data-use-terms, display defaults) are edited by replacing the whole instance draft via `PUT /api/admin/config/instance/draft`; the admin panel builds that document from typed forms. Adding a granular operation later is one new handler file. + +## Level 2 — config adapter + +``` +loculus-config-adapter (TypeScript CLI, init container in SILO and LAPIS pods) + 1. Read env: LOCULUS_BACKEND_URL, LOCULUS_ORGANISM_KEY, LOCULUS_ORGANISM_CONFIG_VERSION + 2. GET /api/config/organisms/{key}?version={v} (and the instance config for common metadata) + 3. Render files into the shared output dir (atomic stage + rename) + 4. Exit 0; the main container starts and reads the files +``` + +One image, one-shot, no sidecar, no watch loop, no auth token. It renders the full file set; each pod mounts only what it consumes: + +| File | Consumed by | +|---|---| +| `database_config.yaml` | SILO, LAPIS, silo-importer | +| `reference_genomes.json` | SILO, LAPIS, silo-importer | +| `preprocessing_config.yaml` (SILO's own input/lineage config — not the Loculus pipeline's) | silo-importer | +| `lineage_definitions.json` (the `lineageSystemDefinitions` URL map from the DB instance config, filtered to this organism's systems) | silo-importer — it downloads each `.yaml` itself at import time and writes them where SILO reads them via `preprocessing_config.yaml` | + +The adapter does **not** render any config for the Loculus *preprocessing* pipeline — that pipeline is external and fetches what it needs from the backend itself ([ADR-019/020](09_architecturalDecisions.md)). + +Adapters pin only the **organism** config version. Anything instance-level they need (e.g. `accessionPrefix`, the common system metadata, lineage definition URLs) is read from the instance config at render time; common metadata is composed onto the organism's fields in the same way the website does. diff --git a/documentation-experimental-features/configuration-management/06_runtimeView.md b/documentation-experimental-features/configuration-management/06_runtimeView.md new file mode 100644 index 0000000000..ea62709917 --- /dev/null +++ b/documentation-experimental-features/configuration-management/06_runtimeView.md @@ -0,0 +1,80 @@ +# 6. Runtime View + +## Scenario 1 — Admin edits a released organism and publishes + +``` +Admin Admin panel Backend Postgres + │ open organism │ │ │ + ├─────────────────>│ GET .../draft │ │ + │ ├─────────────────────>│ read draft or seed │ + │ │<─────────────────────┤ from current published │ + │ edit a field │ │ │ + ├─────────────────>│ POST .../operations │ │ + │ │ { operations:[op] } │ validate (registry) │ + │ ├─────────────────────>│ apply to draft, bump │ + │ │ │ revision, audit row │ + │ │<─────────────────────┤ { revision } │ + │ click Publish │ │ │ + ├─────────────────>│ POST .../publish │ INSERT version, │ + │ ├─────────────────────>│ UPDATE current_version,│ + │ │ │ DELETE draft, audit │ + │ │<─────────────────────┤ { version: 44, ... } │ + │ modal: "Published v44 — update organisms..configVersion │ + │ to 44 in GitOps values or direct Helm, then roll SILO+LAPIS." │ +``` + +The backend and website need no rollout: the backend reads the DB directly, the website fetches the latest config per request. The pin bump is only for the organism's **SILO and LAPIS pods**, whose adapter init containers refetch at the new version when the rolling update brings them up. The admin UI should point to the rollout documentation instead of implying that every deployment can or should run a direct `helm upgrade`; GitOps/ArgoCD deployments update the watched values file instead. + +The admin can make edits across several sub-pages (display, metadata, link-outs, …) before publishing; they all accumulate in one draft and publish together. A status banner shows the pending-change count. + +## Scenario 2 — Admin rolls out the new version + +``` +values.yaml / GitOps: organisms..configVersion: 44 +direct Helm: helm upgrade --set organisms..configVersion=44 + → Kubernetes detects the spec diff on the SILO and LAPIS Deployments + → rolling update: new pod's adapter init container GETs ?version=44, renders files, exits 0 + → SILO starts; silo-importer reimports; pod becomes ready; old pod drained + (old pod serves v43 throughout, until the new pod is ready) +``` + +A single Helm value drives both the SILO and LAPIS pod for an organism, so they always roll in lockstep and can never run mismatched schemas. + +## Scenario 3 — Admin creates a new organism + +``` +POST /api/admin/config/organisms { key } → row with status='unreleased', deployed=false + (+ current_processing_pipeline row, same txn) +PUT /api/admin/config/organisms/{key}/draft → full OrganismConfig; base_version=NULL + (admin iterates with more PUTs, or pastes/edits JSON in the document editor) +POST /api/admin/config/organisms/{key}/publish→ version=1; status='released'; deployed remains false; draft deleted +then: add the key to values.yaml with configVersion=1 and apply it through GitOps/ArgoCD or direct Helm +then: after LAPIS is healthy, POST /api/admin/config/organisms/{key}/mark-deployed +``` + +Before the first publish, the whole document is replaceable (nothing depends on it yet). After release, only registry operations are allowed. Until the administrator marks the organism deployed, the public organism list and generated public organism-key enum hide it; pinned reads such as `/api/config/organisms/{key}?version=1` still work so the deployment adapter can fetch the config during rollout. + +## Scenario 4 — Adapter pinned to a missing version + +``` +adapter init: GET /api/config/organisms/{key}?version=99 → 404 +adapter: log error, exit 1 +Kubernetes: pod stays in Init:Error; admin re-pins a kept version via helm upgrade. +``` + +## Scenario 5 — Concurrent draft edits + +``` +Admin A and Admin B both hold revision 7. +A posts an op → accepted, revision becomes 8. +B posts an op → 409 Conflict. +B's panel: toast "someone else edited this draft", refetches, admin redoes the change. +``` + +## Scenario 6 — Cosmetic instance edit + +``` +Admin edits the instance banner / a link-out label → PUT instance draft → publish. +No rollout. The website picks up the new instance/organism config within the +config cache window (~30s) at the next SSR request. +``` diff --git a/documentation-experimental-features/configuration-management/07_deploymentView.md b/documentation-experimental-features/configuration-management/07_deploymentView.md new file mode 100644 index 0000000000..091e731b6c --- /dev/null +++ b/documentation-experimental-features/configuration-management/07_deploymentView.md @@ -0,0 +1,101 @@ +# 7. Deployment View + +## Cluster layout + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ Kubernetes namespace │ +│ │ +│ ┌─────────────┐ ┌────────────┐ ┌─────────────┐ ┌────────────┐ │ +│ │ Backend │ │ Website │ │ Keycloak │ │ PostgreSQL │ │ +│ │ Deployment │ │ Deployment │ │ Deployment │ │ StatefulSet│ │ +│ └──────┬──────┘ └─────┬──────┘ └─────────────┘ └──────┬─────┘ │ +│ └───────────────┴──── connect ─────────────────────┘ │ +│ │ +│ Per organism: │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Organism SILO pod (one Deployment per organism) │ │ +│ │ initContainer: loculus-config-adapter │ │ +│ │ containers: SILO, silo-importer │ │ +│ │ volume: emptyDir (shared, rendered config) │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Organism LAPIS pod (one Deployment per organism) │ │ +│ │ initContainer: loculus-config-adapter │ │ +│ │ container: LAPIS │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +## SILO pod spec sketch + +```yaml +spec: + initContainers: + - name: loculus-config + image: ghcr.io/loculus-project/config-adapter:X.Y.Z + env: + - { name: LOCULUS_BACKEND_URL, value: "http://loculus-backend:8080" } + - { name: LOCULUS_ORGANISM_KEY, value: "ebola-sudan" } + - { name: LOCULUS_ORGANISM_CONFIG_VERSION, value: "44" } + volumeMounts: + - { name: loculus-config, mountPath: /loculus-config } + containers: + - name: silo # upstream, unmodified + volumeMounts: + - { name: loculus-config, mountPath: /app/database_config.yaml, subPath: database_config.yaml } + - { name: loculus-config, mountPath: /app/reference_genomes.json, subPath: reference_genomes.json } + - name: silo-importer + volumeMounts: + - { name: loculus-config, mountPath: /config } + volumes: + - { name: loculus-config, emptyDir: {} } +``` + +## What Helm owns / no longer owns + +| Helm still owns | Helm no longer owns | +|---|---| +| Deployments, Services, Ingress, PVCs | Organism schemas | +| Image tags, replica counts, resource limits | Metadata field definitions | +| DB connection strings, JDBC URLs, credentials | Reference genome content | +| Keycloak/LAPIS URLs, cross-pod networking | Lineage system definitions | +| The list of organism **keys** and their **pinned config versions** | Instance branding, dataUseTerms, fileSharing, link-outs | + +## `values.yaml` after migration + +```yaml +organisms: + - key: ebola-sudan + configVersion: 44 # one value, templated into both the SILO and LAPIS pod spec + - key: west-nile + configVersion: 12 + +backend: { image: ghcr.io/loculus-project/backend:1.0.0, databaseUrl: … } +website: { image: ghcr.io/loculus-project/website:1.0.0 } # no pin; fetches latest per request +# (no metadata, no schemas, no reference genomes) +``` + +Bumping a single per-organism value rolls both pods in step. The **preprocessing** pipeline no longer reads organism content from a Helm ConfigMap: it fetches its opaque config file plus the organism metadata directly from the backend's public config API (see [ADR-019/020](09_architecturalDecisions.md)); Helm passes only operational args (`--backend-host`, `--organism`, `--pipeline-version`, the Keycloak secret) and the deployment topology (image/version/replicas). **Ingest** still reads organism content from `values.yaml`, so `defaultOrganismConfig`/`defaultOrganisms` remain there for it. + +## Images + +| Image | Built by | Use | +|---|---|---| +| `ghcr.io/loculus-project/config-adapter` | Loculus | Init container in SILO + LAPIS pods (one binary; renders the same files in both). | +| `ghcr.io/loculus-project/config-loader` | Loculus | Job that seeds the DB for CI/preview (below). | +| `ghcr.io/genspectrum/lapis-silo`, `.../lapis` | GenSpectrum (upstream) | SILO / LAPIS containers, unmodified. | + +The adapter and loader are TypeScript CLIs in the shared `config-tools/` package, run with `tsx` (no separate compile step). + +## Seeding the DB for tests and previews + +Real instances are populated by admins through the panel. CI and preview deployments instead run the **config loader** to make the DB deterministic and scriptable: + +- **Fixtures.** `kubernetes/loculus/fixtures/instance.yaml` + `fixtures/organisms/.yaml`, one file per organism (the content formerly in `values.yaml.defaultOrganisms`, converted to the canonical schema). Backend tests use a parallel fixture set under `backend/src/test/resources/fixtures//`. +- **Loader.** `loculus-config-loader` reads the fixtures and drives the **public admin API** (`POST organisms` → `PUT draft` → `publish`). Using the real API everywhere — no test-only backdoor — means every test run also exercises the write path. On a fresh DB each organism lands at v1, so Helm can pin `configVersion: 1`. +- **Helm.** A `pre-install` ConfigMap bundles the fixtures; a `post-install/post-upgrade` Job exchanges the `config_loader_user` Keycloak password for a token carrying the `loculus_administrator` role and runs the loader in `fresh-only` mode (fails loudly rather than publishing surprise new versions if a fixture changed against a non-fresh DB). CI waits for the Job to complete before Playwright. +- **Backend tests.** A `ConfigFixtures` `@TestConfiguration` loads the fixture YAML into the `config_*` tables in `@BeforeEach`, after the DB is wiped, so every `@EndpointTest` sees the expected organisms via `ConfigService`. +- **Local dev.** Same loader from a shell, against `docker-compose`. + +During the brief window between pod startup and Job completion, SILO/LAPIS pods sit in `Init:Error` (their organism isn't in the DB yet) and Kubernetes retries them. Acceptable for previews. diff --git a/documentation-experimental-features/configuration-management/08_crosscuttingConcepts.md b/documentation-experimental-features/configuration-management/08_crosscuttingConcepts.md new file mode 100644 index 0000000000..1be8bea740 --- /dev/null +++ b/documentation-experimental-features/configuration-management/08_crosscuttingConcepts.md @@ -0,0 +1,68 @@ +# 8. Crosscutting Concepts + +## Versioning and snapshots + +- Each scope (the instance, each organism) has its own monotonic integer version counter starting at 1. No content hashes. +- Every publish writes a new immutable row in `config_*_versions`. A version is never modified once written. +- A snapshot **inlines everything it needs** at publish time (reference genomes, and the lineage-definition URLs the adapter resolves), so the version a pod fetched is the version it runs, with no further lookups. +- Versions are kept long enough to cover rollouts and short-window rollback. There is **no commitment that old versions stay valid forever** — future config-structure evolution may make very old versions unreadable, and that is acceptable. Garbage collection of unreferenced versions is allowed but not yet implemented. + +## Drafts and optimistic concurrency + +- One draft per scope (a singleton row for the instance, one row per organism). The draft `config` column is the **materialized** current draft state. +- Each draft has a `revision` counter, bumped on every mutation. Mutating endpoints accept `If-Match: `; a mismatch returns `409` with the current state. +- For a released organism the draft is **lazily created** from the current published config on the first edit; `base_version` records the predecessor. +- Drafts are deleted on publish or explicit discard. There is no per-operation undo — discard is the only revert. + +## Editing model + +Two API paths, gated by status: + +- **Unreleased organism** → `PUT /draft` replaces the whole document. No safety checks against existing data, because nothing has been released. +- **Released organism** → `POST /draft/operations` appends named operations from the registry. + +The **instance config** always has a published version (a Flyway migration seeds v1 with defaults — name `"Loculus"`, `accessionPrefix "LOC_"`, dataUseTerms/fileSharing disabled), so it is "released" from the moment the DB exists. It supports both a `setInstanceBranding` operation and a full-document `PUT /draft`; the admin panel uses typed forms that build the whole document and PUT it, keeping a raw-JSON editor as an advanced escape hatch. There is no `mode` column anywhere — status-gating happens at the API layer (`403` for the wrong path). + +## Operation registry + +- One in-code map `opType → handler`, owned entirely by the backend (Kotlin). Each handler is a typed component with a payload data class, a pure-function validator (preconditions against the current draft), a pure-function applier (produces the next draft), and a human-readable summary used in the pending-ops/audit views. +- Handlers are auto-discovered at startup via Spring component scanning. Adding an operation is one new handler file — no DB migration, no API version bump. +- **The registry IS the safety policy.** If there is no handler, an admin cannot do it. Operations are classified by rollout impact: + - **T1 — cosmetic / no-rollout**: display labels, descriptions, branding, link-out text, field ordering. No effect on backend validation or SILO/LAPIS schema. + - **T2 — non-breaking schema**: needs a config publish + SILO/LAPIS rollout but must not break existing data, queries, or services. Adding an optional metadata field is the example. Allowed only when the validator can prove non-breakage. + - **T3 — breaking / high-risk**: field renames/removals, type changes, required-field changes, reference-genome changes, lineage-index changes. Deliberately **not** in the registry; to be designed later with their own safety reasoning. +- The publish response tells the admin when a published change requires bumping the pinned organism version and rolling SILO/LAPIS. + +## Consumer models + +Different consumers reach config differently — intentionally asymmetric: + +- **SILO and LAPIS pods**: pinned via env var at pod startup; the adapter init container fetches that exact version. A change applies only when the admin bumps the pin and runs `helm upgrade`. SILO and LAPIS for one organism share a single Helm value, so they roll together. This matches their fragility: they hold large indexed datasets whose schema must match their config. +- **Website**: fetches the latest published config per SSR request (no pin), behind a short (~30s) in-process cache with in-flight de-duplication. Cosmetic edits appear within the cache window without any rollout. The cache exists purely as a performance guard (an uncached version OOM-killed the pod under load); the trade-off is that publishes appear after up to ~30s rather than instantly. +- **Backend**: reads domain config from the DB through `ConfigService`, with a naive in-process cache invalidated on every publish. + +The prototype assumes a **single backend replica**, so the naive cache is sufficient. Running multiple replicas later would require revisiting invalidation (short TTL, Postgres notifications, or version polling). + +## Domain vs technical configuration + +Only the **domain** portion of the old mixed `BackendConfig` moved to the DB. The split: + +- **DB-backed domain config**: instance branding, public links, accession prefix, data-use terms, file-sharing policy, domain feature flags, organism schemas, reference genomes, lineage systems, display settings, link-outs. +- **Helm/Spring technical config**: backend/website/Keycloak/LAPIS URLs, DB/S3/SMTP credentials, image tags, ports, resource limits, compression level, pipeline polling intervals, read-only operational mode. + +The backend now carries technical settings in a `@ConfigurationProperties` bean (populated from Spring/Helm) and reads all domain config through `ConfigService`. No backend business logic reads domain config from a file. + +## Sensitivity and authentication + +- The config model — instance and organism — stores **only non-sensitive data**: schemas, labels, public URLs, reference genomes, lineage definitions, link-outs. **Credentials, API keys, SMTP passwords, and certificates stay in Helm `values.yaml`** as Kubernetes Secrets. This invariant is what lets the read API be open and audit logs be retained freely. +- **Read API**: open, no token (website, adapter, external tools). +- **Admin API**: Keycloak `loculus_administrator` realm role; bearer token via OAuth2. This is separate from `super_user`, which remains a curation role for cross-group sequence-entry actions. The website decodes `realm_access.roles` during session establishment and gates `/admin/*` accordingly. + +## Audit log + +- `config_audit_log` is append-only. Every state-changing action writes a row: `organism_create`, `document_replace`, `op_append`, `publish`, `discard_draft`. +- It doubles as the source of the admin panel's **pending-operations** list: the `op_append` rows since the organism's last `publish`/`discard_draft` are exactly the draft's queued edits. + +## Domain vocabulary + +Config uses domain words (`lineageSearch`, `substringSearch`, `generateIndex`-intent), not tool words. Translation into SILO/LAPIS vocabulary happens in the adapter. diff --git a/documentation-experimental-features/configuration-management/09_architecturalDecisions.md b/documentation-experimental-features/configuration-management/09_architecturalDecisions.md new file mode 100644 index 0000000000..f0dfa19f38 --- /dev/null +++ b/documentation-experimental-features/configuration-management/09_architecturalDecisions.md @@ -0,0 +1,140 @@ +# 9. Architectural Decisions + +## ADR-001: PostgreSQL is the source of truth for domain config + +**Context.** Domain config lived in `values.yaml`; changing it meant editing files and rolling Helm, even for cosmetic edits. + +**Decision.** Domain config (organisms, schemas, branding, link-outs, reference genomes, lineage definitions) lives in PostgreSQL `config_*` tables. `values.yaml` keeps only infrastructure config. Technical runtime config stays in Helm/Spring (URLs, credentials, image tags, ports, limits, compression level, polling intervals, read-only mode). + +**Consequences.** Edits are possible from a UI. The old mixed file-loaded `BackendConfig` is split into DB-backed domain config and a Spring `@ConfigurationProperties` technical-config bean. + +## ADR-002: The backend is ignorant of Kubernetes and of LAPIS/SILO formats + +**Decision.** The backend has no Kubernetes dependency and no LAPIS/SILO config code. It serves a tool-agnostic config document; consumers translate. + +**Consequences.** Each consumer embeds its own translation. The system grows by adding adapters, not backend integrations. The backend stays testable as a pure HTTP service. + +## ADR-003: A single per-pod config adapter + +**Context.** Something must translate Loculus config into SILO/LAPIS files. Both read `database_config.yaml`; SILO additionally reads `reference_genomes.json` and the preprocessing config. + +**Decision.** One image, `loculus-config-adapter`, runs as an init container in both SILO and LAPIS pods. It always renders the full file set; each pod mounts only what it needs. It is a TypeScript CLI in the shared `config-tools/` package (a port of the former Helm `_*.tpl` templates). + +**Consequences.** One image to maintain. Upstream SILO/LAPIS images are unmodified. The adapter's output is identical regardless of which pod it runs in. + +## ADR-004: Pinned-version model with Kubernetes rolling updates + +**Context.** LAPIS and SILO do not support hot reload. Sidecar restarts, controller k8s access, and signal reloads all add coupling or fragility. + +**Decision.** Each pod spec pins a config version via env var. The admin bumps the pin to apply a change; Kubernetes performs a standard rolling update. The adapter is init-only — no sidecar, no reload loop. + +**Consequences.** Minimal-downtime updates via native k8s semantics. The cost is a two-step workflow (publish + `helm upgrade`). Rollback is "re-pin a previous, still-kept version". + +## ADR-005: Two editing paths gated by organism status + +**Decision.** `PUT /draft` replaces the whole config for **unreleased** organisms; `POST /draft/operations` appends registry operations for **released** ones. The gate is the organism's status, not a column. Wrong path → `403`. + +**Consequences.** Onboarding is unrestricted; maintaining a released organism is registry-restricted. The registry stays focused on small parameterised edits. + +## ADR-006: The operation registry is the safety policy + +**Decision.** Admin operations on released organisms (and the instance) are an in-code registry of typed handlers, auto-discovered at startup. If an operation is not in the registry, an admin cannot perform it. The registry currently exposes cosmetic/display edits, link-out edits, and one non-breaking schema edit (add optional field). Breaking/high-risk edits are absent. + +**Consequences.** Safety is a code-review concern at the registry level; the action set is enumerable; growing the surface is deliberate. + +## ADR-007: Inline lineage and reference data in organism snapshots + +**Decision.** Reference genomes and lineage-definition references are inlined into the organism snapshot at publish time, so a published version contains everything the adapter needs without further lookups. + +**Consequences.** Snapshots grow (a few hundred KB). Adapters never resolve cross-snapshot references. This is for correctness of currently-kept versions, not a guarantee that arbitrarily old versions stay valid (see [section 8](08_crosscuttingConcepts.md)). + +## ADR-008: Per-scope monotonic version numbers; no content hashes + +**Decision.** Each organism and the instance has its own integer counter from 1. No content hash. + +**Consequences.** Version numbers stay small and meaningful. Concurrent publishes of two organisms don't share a counter. Identical-content publishes still create distinct versions (no dedup); acceptable at expected scale. + +## ADR-009: Adapter fetches once at init; no polling, no push + +**Decision.** Adapters fetch once at init-container startup. Config changes propagate only through pod restarts triggered by spec changes. + +**Consequences.** No sidecar. The pod spec is the single source of truth for what version a pod runs. + +## ADR-010: Adapters pin only the organism config version + +**Decision.** Adapters take only the organism version. Instance-level fields they need (accession prefix, common system metadata, lineage URLs) are read from the instance config at render time, not pinned. + +**Consequences.** One pin per pod. Instance-config changes don't trigger rollouts for every organism's SILO/LAPIS. + +## ADR-011: Config holds only non-sensitive data; the read API is open + +**Decision.** The config domain is non-sensitive only. Sensitive values (SMTP/JWT/API credentials, TLS) live in Helm as Secrets. The read API needs no token. + +**Consequences.** No adapter token to provision or rotate; the website fetches without auth. The schema and registry must never admit credential fields. + +## ADR-012: Website fetches latest per request; SILO+LAPIS share one Helm pin + +**Decision.** The website does not pin — it fetches `/api/config/...` at request time (behind a short cache, ADR-018). SILO and LAPIS for an organism share one `organisms[i].configVersion` value templated into both pod specs. + +**Consequences.** Instance/cosmetic edits apply with no `helm upgrade`; organism edits stay explicit and gated. The shared pin removes a class of admin error (rolling SILO without LAPIS). + +## ADR-013: LAPIS-relevant adapter outputs are `database_config.yaml` and `reference_genomes.json` + +**Context.** Verified against `lapis-deployment.yaml`: the LAPIS container mounts only those two files; the SILO URL is a CLI argument; everything else is Helm-set, not domain config. + +**Decision.** The adapter renders those two files for LAPIS pods; no LAPIS-specific files are needed. + +## ADR-014: Canonical schemas and edge tooling live in a shared TS package + +**Decision.** `config-tools/` owns the canonical Zod schemas plus the loader and adapter CLIs. The website re-exports the schemas (`website/src/types/loculusConfig.ts` is a one-line re-export); the Kotlin types in the backend are the parallel definition. + +**Consequences.** One TS home for the schema; loader, adapter, and website cannot drift from each other. Kotlin ↔ Zod sync remains manual (see [section 11](11_risksAndTechnicalDebt.md)). + +## ADR-015: Instance config edited via full-document PUT plus a branding operation + +**Context.** Building granular operations for every instance field (banners, GitHub links, feature toggles, data-use-terms, display defaults) is a lot of surface for cosmetic, non-schema fields. + +**Decision.** The instance supports a `setInstanceBranding` operation and a full-document `PUT /draft`. The admin panel holds the whole instance config in state, edits it through typed forms, and PUTs it; a raw-JSON editor is the advanced escape hatch. Granular instance operations can be added later if needed. + +**Consequences.** Fast coverage of all instance fields without a handler per field. Instance edits are `document_replace`, which is acceptable because the instance has no SILO/LAPIS schema to protect. + +## ADR-016: `OrganismConfig.displayName` is the canonical organism name + +**Context.** The schema historically carried the organism name in three places: top-level `displayName`, an unused top-level scientific-name field, and the legacy required `schema.organismName`. + +**Decision.** `OrganismConfig.displayName` is the canonical display name. The unused scientific-name field was removed. `schema.organismName` is retained as a backward-compatibility fallback (the website's transform and SILO's `instanceName` still read it) and is documented as legacy; consumers prefer `displayName` and fall back to `schema.organismName`. + +**Consequences.** One source of truth going forward; a future cleanup can drop `schema.organismName` once the website transform and the SILO adapter read `displayName` directly. + +## ADR-017: One loader, driving the public admin API, seeds tests and previews + +**Context.** Real instances are populated by admins, but CI, preview deployments, and local dev need a deterministic, scriptable way to fill the DB. + +**Decision.** A single `loculus-config-loader` reads fixture YAML and brings the backend into that state through the **public admin API** (create → PUT draft → publish). The same code path is used by the Helm seeding Job, CI, and local dev. Backend tests use a sibling `ConfigFixtures` helper that inserts the same fixture content into the `config_*` tables before each `@EndpointTest`. + +**Consequences.** No test-only backdoor; every seeded run exercises the write path. The loader is a bootstrap, not a production migrator. It runs in `fresh-only` mode for Helm so a fixture change against a non-fresh DB fails loudly instead of publishing surprise versions. + +## ADR-018: Short in-process cache on the website's config fetch + +**Context.** Fetching every organism's config on every SSR request (no cache) OOM-killed the website pod under load. + +**Decision.** The website caches the assembled config for ~30s with in-flight de-duplication, keyed per process. + +**Consequences.** A publish appears on the website after up to ~30s rather than instantly — an acceptable trade-off for the prototype, and consistent with the "cosmetic edits within seconds" intent. A finer-grained invalidation (content-hash `If-None-Match`, push, or per-page cache) can replace it later behind the same interface. + +## ADR-019: Preprocessing pipelines are external; pipeline config leaves the core + +**Context.** Preprocessing pipelines are customizable — the in-repo `dummy` and `nextclade` pipelines are just *our* pipelines; other instances run entirely different ones. Yet pipeline knowledge had leaked into core: `metadata[].preprocessing` directives lived in the canonical schema, and `config-tools` rendered a Loculus-pipeline config file (`pipeline_preprocessing_config.yaml`). The backend must also remain ignorant of Kubernetes ([ADR-002](#adr-002-the-backend-is-ignorant-of-kubernetes-and-of-lapissilo-formats)) and so manages no pipeline pods. + +**Decision.** Core (backend, config DB, `config-tools`) is pipeline-agnostic. `metadata[].preprocessing` was removed from the canonical Zod + Kotlin schemas, `values.yaml`, `values.schema.json`, and the fixtures. `config-tools` no longer renders any Loculus-pipeline file (the per-field renderer was deleted; the adapter still renders SILO's own `preprocessing_config.yaml`, which is SILO infrastructure). A preprocessing pipeline fetches the generic domain config (metadata, reference genomes) from the public API and derives its own processing spec; its deployment (image, version, args, replicas) stays in Helm, and the chart passes `--organism` so the pipeline knows what to fetch. + +**Consequences.** Pipeline-specific logic lives with the pipeline. The backend never interprets processing directives and never deploys a pipeline. Ingest still reads some organism fields from `values.yaml` (accepted debt — [section 11](11_risksAndTechnicalDebt.md)). + +## ADR-020: Opaque, unversioned per-organism preprocessing config files + +**Context.** A pipeline often needs per-organism configuration (e.g. nextclade datasets) that an admin should be able to edit without redeploying — but that the core should neither understand nor validate. + +**Decision.** Loculus optionally stores one **opaque text** config file per `(organism, pipeline version)` in a dedicated, **unversioned** table (`config_preprocessing_files`, [section 13](13_databaseSchema.md)), separate from the versioned config documents. It is served raw from a dedicated public endpoint (`GET /api/config/organisms/{key}/preprocessing/{version}`) and written via admin `PUT`/`DELETE` (direct save — no draft/publish). The backend stores and serves it verbatim and never parses it. Using it is entirely optional. + +**Consequences.** A pipeline can fetch its config from the backend if it wants, while the core stays pipeline-agnostic; storing a config file is *not* the same as running a pipeline (the admin still deploys one). No secrets may go in it (the endpoint is open — [ADR-011](#adr-011-config-holds-only-non-sensitive-data-the-read-api-is-open)). Being unversioned, it is decoupled from the SILO/LAPIS config-version rollout. + diff --git a/documentation-experimental-features/configuration-management/10_qualityRequirements.md b/documentation-experimental-features/configuration-management/10_qualityRequirements.md new file mode 100644 index 0000000000..d1e2276dcc --- /dev/null +++ b/documentation-experimental-features/configuration-management/10_qualityRequirements.md @@ -0,0 +1,11 @@ +# 10. Quality Requirements + +| ID | Quality | Scenario | Target / how it's met | +|---|---|---|---| +| Q1 | Safety | Admin attempts to rename a metadata field on a released organism via the API. | Rejected — no such operation exists in the registry ([ADR-006](09_architecturalDecisions.md)). | +| Q2 | Availability | Admin publishes a new organism config version. | LAPIS for that organism stays available throughout the rolling update; the old pod serves until the new one is ready ([section 6](06_runtimeView.md)). | +| Q3 | Auditability | "Who changed the country display name two weeks ago?" | A `config_audit_log` query returns actor, timestamp, action, and summary. | +| Q4 | Reversibility | A published version turns out wrong. | Re-pin the previous version and roll out; back in minutes, as long as that version is still kept. | +| Q5 | Stability | An external LAPIS client queries by field name `country`. | Field names cannot change via the API; existing queries keep working across publishes ([T5](02_constraints.md)). | +| Q6 | Modifiability | Adding a new safe operation. | One PR adding a single registry handler; no DB migration ([ADR-006](09_architecturalDecisions.md)). | +| Q7 | Determinism | CI / preview must reproduce a known config from scratch. | The loader seeds a fresh DB from fixture YAML through the admin API; each organism lands at v1 ([section 7](07_deploymentView.md), [ADR-017](09_architecturalDecisions.md)). | diff --git a/documentation-experimental-features/configuration-management/11_risksAndTechnicalDebt.md b/documentation-experimental-features/configuration-management/11_risksAndTechnicalDebt.md new file mode 100644 index 0000000000..6ccdd49c28 --- /dev/null +++ b/documentation-experimental-features/configuration-management/11_risksAndTechnicalDebt.md @@ -0,0 +1,30 @@ +# 11. Risks and Technical Debt + +## Risks + +| ID | Risk | Mitigation | +|---|---|---| +| R1 | Schema drift between the Kotlin and Zod definitions. | Manual sync via code review; widen the backend side first. Codegen is a possible future option. | +| R2 | Two-step admin workflow (publish + deployment rollout) is friction. | The publish modal surfaces the new version, the exact `configVersion` pin, a GitOps values example, a direct Helm example, and links to administrator rollout docs. | +| R3 | Concurrent edits to the same draft. | Optimistic concurrency on `revision`; `409` returns the current state; the panel refetches and prompts a redo. | +| R4 | Admin pins a non-existent / garbage-collected version. | Adapter init fails clearly; the pod stays `Init:Error`; admin re-pins a kept version. | +| R5 | Adapter cannot reach the backend at startup. | Init container retries with backoff, then fails; the pod won't start with stale config. | +| R6 | A published snapshot has malformed lineage content. | The adapter surfaces parse errors; init fails clearly; admin pins the previous version. | +| R7 | Bad config published and rolled out before anyone notices. | Same risk class as `values.yaml` today; mitigated by re-pinning an earlier kept version. | +| R8 | A newly published organism becomes visible before its SILO/LAPIS deployments exist. | Needs a deployment-readiness gate; see [section 62](62_organismCreationDeployment.md). | +| R9 | The DB-backed schema silently drops behaviour Helm templates used to generate. | Fixtures were converted 1:1 from `values.yaml`; renderer tests cover the adapter output. A full field-level parity inventory is still worth writing (see debt). | +| R10 | The naive backend cache becomes stale with multiple backend replicas. | Prototype assumes one replica. Revisit invalidation before scaling out. | +| R11 | Website requires a reachable backend on every SSR request. | A backend outage stalls page renders; the config fetch returns `503`. Acceptable for the prototype; a stale-cache fallback can sit behind the same interface. | + +## Technical debt (accepted) + +- **Kotlin ↔ Zod schema duplication** — kept in sync manually, no codegen. +- **Ingest still reads organism content from `values.yaml`** — `defaultOrganismConfig` / `defaultOrganisms` remain for the ingest (and ENA-submission) pipelines, but only the schema/metadata and the reference-genome **segment names**: the reference-genome **sequences** (formerly `[[URL:…]]` placeholders) have been removed from `values.yaml`, and the backend/website ConfigMaps no longer carry any domain config — both read it from the database. Preprocessing has been fully migrated ([ADR-019/020](09_architecturalDecisions.md)): the nextclade pipeline fetches its opaque config file (and the organism metadata) from the backend, the Helm `processing_spec`/ConfigMap generation was removed, `metadata[].preprocessing` was dropped from the schema/values/fixtures, and the loader seeds the per-organism config files from `kubernetes/loculus/fixtures/preprocessing/`. An end-to-end validation of the nextclade pipeline against a live preview is still advisable. +- **Backend draft validation is structure-only.** A `PUT /draft` is validated by deserialization, not by the cross-field invariants documented in [section 14](14_configSchema.md) (`tableColumns ⊆ metadata names`, `defaultOrderBy` exists, `metadataTemplate ⊆ inputFields`, multi-field-search references, etc.). Individual operation handlers do check their own preconditions. A canonical validator that runs on every draft mutation and returns `422` is the intended fix. +- **Instance operation coverage is partial.** Only `setInstanceBranding` exists as a granular instance operation; all other instance fields go through full-document `PUT` (see [ADR-015](09_architecturalDecisions.md)). Fine functionally; granular ops + audit detail can be added later. +- **Lineage definitions flow from the DB through the adapter (resolved).** The canonical `InstanceConfig.lineageSystemDefinitions` (a `{ system: { pipelineVersion: URL } }` map) is the source of truth. The config-adapter reads it from the backend at render time, filters it to the organism's referenced systems, and writes `lineage_definitions.json` into the shared volume; the silo-importer mounts that file (`LINEAGE_DEFINITIONS_FILE`) and downloads the definitions itself at import time. The old adapter download path (`LOCULUS_LINEAGE_*` env vars, `lineage_.yaml`) and the Helm `LINEAGE_DEFINITIONS` env var (sourced from `fixtures/instance.yaml`) have been removed. +- **`schema.organismName` is still required** even though `OrganismConfig.displayName` is canonical ([ADR-016](09_architecturalDecisions.md)). Dropping it needs the website transform and the SILO `instanceName` to read `displayName` directly. +- **`accessionPrefix` is immutable by registry omission**, not by schema. Review any future instance operation that could touch it. +- **No production migration tool.** The loader bootstraps from fixtures; migrating an existing `values.yaml`-driven production instance to DB-backed config still needs a separate one-time tool. +- **Admin-panel end-to-end (Playwright) coverage** is not yet authored. Component-level Vitest specs and manual click-through cover the flows for now. +- **Single-backend-replica cache assumption** and the **~30s website config cache** ([ADR-018](09_architecturalDecisions.md)) are prototype trade-offs. diff --git a/documentation-experimental-features/configuration-management/12_glossary.md b/documentation-experimental-features/configuration-management/12_glossary.md new file mode 100644 index 0000000000..f6cdc0ca37 --- /dev/null +++ b/documentation-experimental-features/configuration-management/12_glossary.md @@ -0,0 +1,26 @@ +# 12. Glossary + +| Term | Meaning | +|---|---| +| **Adapter** (`loculus-config-adapter`) | TypeScript CLI in `config-tools/` that fetches a pinned organism config from the backend and renders the SILO + LAPIS files. Runs as an init container in both SILO and LAPIS pods. Does **not** render any Loculus preprocessing-pipeline config (that pipeline is external — see Preprocessing config file). | +| **Common (system) metadata** | The built-in fields (`accessionVersion`, `accession`, `version`, `submissionId`, `submitter`, `groupName`, `submittedAtTimestamp`, data-use-terms fields, …) composed onto every organism's metadata at render time, by both the website and the adapter. Defined in code, not stored per organism. | +| **Config loader** (`loculus-config-loader`) | TypeScript CLI in `config-tools/` that reads fixture YAML and brings the backend to that state through the admin API. Seeds CI, previews, and local dev. | +| **Config version** | A monotonically-numbered snapshot of instance or organism config, identified by `(scope, version)`. Immutable while kept; not guaranteed to exist forever. | +| **`config-tools/`** | Top-level npm package owning the canonical Zod schemas (re-exported by the website) and the loader + adapter CLIs. | +| **Draft** | A mutable working copy of instance or organism config — one per scope, materialized in a `config_*_draft(s)` row, with a `revision` counter. | +| **Fixtures** | Canonical-schema YAML files (`instance.yaml` + `organisms/.yaml`) used to seed the DB for tests and previews. | +| **Instance config** | Singleton config: branding, dataUseTerms, fileSharing, feature toggles, accession prefix, lineage system definitions, display defaults. | +| **LAPIS** | Upstream HTTP query API in front of SILO. One deployment per organism. | +| **Operation** | A named, parameterized edit appended to a draft; defined by a handler in the in-code operation registry. | +| **Operation registry** | The in-code set of admin operations for released organisms and the instance. It *is* the safety policy — operations are classified cosmetic / non-breaking-schema / (future) breaking; absent operations cannot be performed. | +| **Organism config** | Per-organism config: schema (metadata, input fields, table columns, link-outs, submission types, files), reference genome(s), display fields. | +| **Pinned version** | The config version set on a SILO/LAPIS pod spec via env var. Determines what config that pod runs. | +| **Preprocessing config file** | An optional, opaque text file stored per `(organism, pipeline version)` in `config_preprocessing_files` and served raw from a dedicated public endpoint. Unversioned (direct admin save). The backend never interprets it; an external preprocessing pipeline fetches it if it wants. See [ADR-020](09_architecturalDecisions.md). | +| **Published version** | A `config_*_versions` row; immutable while kept. | +| **Released (organism)** | An organism with at least one published version (`config_organisms.status = 'released'`). | +| **Rollout** | The `helm upgrade` action that applies an updated organism config version to running pods, implemented by Kubernetes as a rolling update. | +| **Schema (organism)** | The `schema` field of an organism config. | +| **SILO** | Upstream columnar genomic database. One deployment per organism. | +| **silo-importer** | Loculus component that pulls processed sequences from the backend into SILO. Runs in the SILO pod. | +| **Unreleased (organism)** | An organism with no published version yet (`config_organisms.status = 'unreleased'`). | +| **`values.yaml`** | Helm values file. After the migration: infrastructure config plus organism keys and pinned versions. | diff --git a/documentation-experimental-features/configuration-management/13_databaseSchema.md b/documentation-experimental-features/configuration-management/13_databaseSchema.md new file mode 100644 index 0000000000..4aa35fe511 --- /dev/null +++ b/documentation-experimental-features/configuration-management/13_databaseSchema.md @@ -0,0 +1,190 @@ +# 13. Database Schema + +Detail companion to [section 5](05_buildingBlockView.md) (Building Block View) and [section 8](08_crosscuttingConcepts.md) (Crosscutting Concepts). The full DDL below is implemented in a single migration, `backend/src/main/resources/db/migration/V2.0__add_config_tables.sql`, which also creates `config_preprocessing_files` and the per-organism `deployed` readiness flag. + +## Overview + +Eight tables, all prefixed `config_`, all in the same Postgres database as the rest of the Loculus backend. The first seven hold the versioned, draftable config documents; `config_preprocessing_files` is a separate, deliberately unversioned store of opaque pipeline config files. + +| Table | Role | Cardinality | +|---|---|---| +| `config_organisms` | Organism registry + lifecycle status + pointer to current version | One per organism | +| `config_instance_versions` | Published instance-config snapshots | One per publish | +| `config_instance_state` | Pointer to the current published instance version | Singleton | +| `config_organism_versions` | Published organism-config snapshots | One per (organism, publish) | +| `config_instance_draft` | Active instance draft (if any) | 0 or 1 | +| `config_organism_drafts` | Active organism draft (per organism) | 0 or 1 per organism | +| `config_audit_log` | Permanent action history; doubles as the source for "pending ops" listings | Append-only | +| `config_preprocessing_files` | Opaque, unversioned preprocessing config files (one per organism + pipeline version) | 0..n per organism | + +## DDL + +```sql +-- Organism registry + lifecycle state +CREATE TABLE config_organisms ( + key TEXT PRIMARY KEY, + status TEXT NOT NULL, -- 'unreleased' | 'released' + current_version BIGINT, -- FK to config_organism_versions (deferred) + deployed BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMP NOT NULL DEFAULT now(), + created_by TEXT NOT NULL, + first_published_at TIMESTAMP, + last_published_at TIMESTAMP, + CHECK (status IN ('unreleased', 'released')), + CHECK ((status = 'unreleased') = (current_version IS NULL)) +); + +-- Instance config: published snapshots +CREATE TABLE config_instance_versions ( + version BIGSERIAL PRIMARY KEY, + config JSONB NOT NULL, + published_at TIMESTAMP NOT NULL DEFAULT now(), + published_by TEXT NOT NULL +); + +-- Instance config: singleton pointer to "current" +CREATE TABLE config_instance_state ( + singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton), + current_version BIGINT REFERENCES config_instance_versions(version) +); + +-- Organism config: published snapshots +CREATE TABLE config_organism_versions ( + organism_key TEXT NOT NULL REFERENCES config_organisms(key), + version BIGINT NOT NULL, -- per-organism, monotonic from 1 + config JSONB NOT NULL, -- self-contained (includes resolved lineage defs) + published_at TIMESTAMP NOT NULL DEFAULT now(), + published_by TEXT NOT NULL, + PRIMARY KEY (organism_key, version) +); + +ALTER TABLE config_organisms + ADD CONSTRAINT config_organisms_current_version_fk + FOREIGN KEY (key, current_version) + REFERENCES config_organism_versions(organism_key, version) + DEFERRABLE INITIALLY DEFERRED; + +-- Instance config: optional active draft (singleton) +CREATE TABLE config_instance_draft ( + singleton BOOLEAN PRIMARY KEY DEFAULT TRUE CHECK (singleton), + config JSONB NOT NULL, -- materialized current draft state + base_version BIGINT REFERENCES config_instance_versions(version), + revision BIGINT NOT NULL DEFAULT 0, -- optimistic-CC token + created_at TIMESTAMP NOT NULL DEFAULT now(), + updated_at TIMESTAMP NOT NULL DEFAULT now(), + created_by TEXT NOT NULL, + updated_by TEXT NOT NULL +); + +-- Organism config: optional active draft (one per organism) +CREATE TABLE config_organism_drafts ( + organism_key TEXT PRIMARY KEY REFERENCES config_organisms(key) ON DELETE CASCADE, + config JSONB NOT NULL, -- materialized current draft state + base_version BIGINT, -- null when organism has no published version + revision BIGINT NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT now(), + updated_at TIMESTAMP NOT NULL DEFAULT now(), + created_by TEXT NOT NULL, + updated_by TEXT NOT NULL, + FOREIGN KEY (organism_key, base_version) + REFERENCES config_organism_versions(organism_key, version) +); + +-- Permanent audit trail of every config-changing action. +-- Also the source for "pending operations" listings (audit entries since +-- the organism's last publish are the contents of the current draft). +CREATE TABLE config_audit_log ( + id BIGSERIAL PRIMARY KEY, + occurred_at TIMESTAMP NOT NULL DEFAULT now(), + actor TEXT NOT NULL, + scope TEXT NOT NULL, -- 'instance' | 'organism' + organism_key TEXT, -- null for instance scope + action TEXT NOT NULL, + -- 'organism_create' | 'document_replace' | 'op_append' + -- | 'publish' | 'mark_deployed' | 'discard_draft' + details JSONB, -- op type + payload for op_append; diff summary for publish + result_version BIGINT +); + +CREATE INDEX ix_config_audit_log_organism_time ON config_audit_log (organism_key, occurred_at DESC); +CREATE INDEX ix_config_audit_log_actor_time ON config_audit_log (actor, occurred_at DESC); + +-- Opaque, unversioned preprocessing config files. One row per +-- (organism, pipeline version); stored and served verbatim, never interpreted. +CREATE TABLE config_preprocessing_files ( + organism_key TEXT NOT NULL REFERENCES config_organisms(key) ON DELETE CASCADE, + pipeline_version BIGINT NOT NULL, + config_file TEXT NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT now(), + updated_by TEXT NOT NULL, + PRIMARY KEY (organism_key, pipeline_version) +); +``` + +`config_preprocessing_files` is intentionally outside the version/draft/publish machinery: admins edit these files directly (a `PUT` replaces the current value, a `DELETE` removes it) and the current value is what the public endpoint serves. This keeps pipeline-specific configuration — which the core neither understands nor needs — decoupled from the versioned organism config that SILO/LAPIS pin. See [section 14](14_configSchema.md) for the endpoints and [ADR-020](09_architecturalDecisions.md) for the rationale. + +## Key invariants + +- **Versions are per-scope and monotonic.** Each organism has its own counter; the instance has its own. +- **Versions are immutable while kept** — never modified after creation. Old versions may be garbage-collected once no pod pins them; the concrete retention policy is an implementation detail. +- **A `released` organism always has `current_version IS NOT NULL`** and vice versa (CHECK constraint). +- **`deployed` is a public-readiness gate, not a config lifecycle state.** Existing rows default to `TRUE` during migration so current organisms remain visible; newly created organisms explicitly set it to `FALSE` until an administrator confirms SILO/LAPIS are deployed. +- **Drafts are materialized.** `config_organism_drafts.config` always reflects the current draft state. Each mutation updates the materialized state in place and writes an audit row. +- **Snapshots are self-contained.** No FK between `config_organism_versions` and `config_instance_versions`. Lineage definitions referenced by an organism are inlined at publish time. +- **Unreleased organism drafts have `base_version IS NULL`.** Released organism drafts have `base_version` pointing at the predecessor. +- **Only the current draft is kept.** Drafts disappear on publish/discard. The audit log preserves history. +- **Deferrable FK between organisms and organism_config_versions** lets us insert both rows in a single transaction during initial publish. + +## Row-flow scenarios + +### New organism → first publish + +1. `INSERT INTO config_organisms (key, status='unreleased', current_version=NULL, deployed=FALSE, …)`. Audit row `organism_create`. +2. First `PUT /draft`: `INSERT INTO config_organism_drafts (organism_key, config=…, base_version=NULL)`. Audit row `document_replace`. +3. Subsequent `PUT /draft` calls: `UPDATE config_organism_drafts SET config=…, revision=revision+1`. Audit row `document_replace`. +4. On publish (single transaction, deferred FK): + - `INSERT INTO config_organism_versions (organism_key, version=1, config)`. + - `UPDATE config_organisms SET status='released', current_version=1, first_published_at=…, last_published_at=…` while leaving `deployed=FALSE`. + - `DELETE FROM config_organism_drafts WHERE organism_key=…`. + - Audit row `publish` with `result_version=1`. +5. After SILO/LAPIS are deployed and healthy, `POST /api/admin/config/organisms/{key}/mark-deployed` sets `deployed=TRUE` and writes audit row `mark_deployed`. Only then does `GET /api/config/organisms` include the new organism. + +### Edit released organism → publish + +1. First op append: lazily create `config_organism_drafts` row with `config = (copy of current published config)`, `base_version = current_version`. +2. Apply op to draft `config` (UPDATE in place); bump `revision`. Audit row `op_append`. +3. Publish (single transaction): + - `INSERT INTO config_organism_versions` with `version = previous + 1`. + - `UPDATE config_organisms SET current_version=N, last_published_at=…`. + - `DELETE FROM config_organism_drafts`. + - Audit row `publish`. + +### Discard draft + +`DELETE FROM config_organism_drafts WHERE organism_key=…`. Audit row `discard_draft`. + +### Initial DB setup + +The `V2.0` migration that creates the tables also inserts: +- An initial `config_instance_versions` row with `version=1` and a hardcoded default `config` JSON (`name="Loculus"`, `accessionPrefix="LOC_"`, `dataUseTerms`/`fileSharing` disabled, all optional fields null). This JSON literal mirrors `DEFAULT_INSTANCE_CONFIG_JSON` in `InstanceConfig.kt` and must be kept in sync with it. +- A `config_instance_state` row with `current_version=1`. + +So the instance config is always "released" from the moment the DB exists; admins customise it via operations or a full-document PUT. + +### Listing pending operations for the admin panel + +`SELECT details FROM config_audit_log WHERE organism_key=$1 AND action='op_append' AND occurred_at > (SELECT COALESCE(MAX(occurred_at), 'epoch') FROM config_audit_log WHERE organism_key=$1 AND action IN ('publish','discard_draft','organism_create')) ORDER BY occurred_at;` + +## Indexes + +Required: +- `config_audit_log (organism_key, occurred_at DESC)` — recent history for an organism. +- `config_audit_log (actor, occurred_at DESC)` — "what has this admin done lately". + +Already covered by PKs / unique constraints: +- `config_organisms.key`, `config_organism_versions (organism_key, version)`, `config_instance_versions.version`. + +## Relationship to existing Loculus tables + +- `sequence_entries.organism` continues to be a free-form string; it is **not** FK'd to `config_organisms.key`. Adding that FK is a separate, optional future migration. +- No other existing tables are touched. diff --git a/documentation-experimental-features/configuration-management/14_configSchema.md b/documentation-experimental-features/configuration-management/14_configSchema.md new file mode 100644 index 0000000000..5185fc29e9 --- /dev/null +++ b/documentation-experimental-features/configuration-management/14_configSchema.md @@ -0,0 +1,201 @@ +# 14. Config Schema (JSONB shapes) + +Detail companion to [section 8](08_crosscuttingConcepts.md). Describes the JSONB stored in `config_instance_versions.config`, `config_organism_versions.config`, and the draft tables. + +The **source of truth** is the canonical Zod schema in `config-tools/src/schema/canonicalConfig.ts` (re-exported by the website) and its Kotlin counterpart in `backend/.../config/Config.kt` + `InstanceConfig.kt`. The two are kept in sync by hand ([ADR-014](09_architecturalDecisions.md)); widen the Kotlin side first. This document summarises the shapes — when in doubt, the code wins. + +Design principles: + +- **Domain vocabulary**, not tool vocabulary. Translation to SILO/LAPIS happens in the adapter. +- **Self-contained snapshots.** Reference genomes are inlined; lineage definitions are referenced by URL from the instance config and resolved by the adapter at render time. +- **Stable identifiers.** `metadata[].name`, `inputFields[].name`, `linkOuts[].name` never change; display strings are freely editable. + +Notation: TypeScript-ish; `?` = optional/nullable. Wire format is JSON. + +## Instance config + +```ts +type InstanceConfig = { + name: string; // "Loculus" + accessionPrefix: string; // "LOC_" — effectively immutable (no operation edits it) + dataUseTerms: { + enabled: boolean; + urls: { open: string; restricted: string } | null; + }; + fileSharing: { outputFileUrlType: 'website' | 'backend' | 's3' }; // default 'website' + + // Branding / public text (all optional) + description?: string | null; + logo?: { url: string; alt?: string; width?: number; height?: number } | null; + supportContact?: { email?: string; url?: string } | null; + bannerMessage?: string | null; bannerMessageURL?: string | null; + submissionBannerMessage?: string | null; submissionBannerMessageURL?: string | null; + welcomeMessageHTML?: string | null; additionalHeadHTML?: string | null; + gitHubMainUrl?: string | null; gitHubEditLink?: string | null; + gitHubIssuesUrl?: string | null; issuesEmail?: string | null; + dataUseTermsAgreementHTML?: string | null; + + // Feature toggles (flat booleans — there is no `featureFlags` object) + enableSeqSets: boolean; // default false + enableLoginNavigationItem: boolean; // default true + enableSubmissionNavigationItem: boolean; // default true + enableSubmissionPages: boolean; // default true + + // Display defaults + dateFieldForGroupGraph?: string | null; // metadata field for the group-activity chart + seqSetsFieldsToDisplay?: Array<{ field: string; displayName: string }> | null; + seqSetsGraphs?: Array<{ name: string; displayName: string; type: 'date' | 'category'; fields: string[] }> | null; + sequenceFlagging?: { github: { organization: string; repository: string; issueTemplate?: string } } | null; + + // Lineage system → pipeline version → definition-file URL. The adapter/silo-importer resolve these. + lineageSystemDefinitions?: Record> | null; +}; +``` + +The public read endpoint wraps it: `{ version, publishedAt, config: InstanceConfig, readOnlyMode }`. `readOnlyMode` comes from technical config, not the stored document. + +## Preprocessing config files (separate, opaque, unversioned) + +Distinct from the versioned config documents above: an admin may attach an opaque **config file** (free-form text) per `(organism, pipeline version)`. It is stored verbatim in `config_preprocessing_files` ([section 13](13_databaseSchema.md)), is *not* versioned, and the backend never parses it. External preprocessing pipelines fetch it themselves; the core stays pipeline-agnostic. + +- Public read: `GET /api/config/organisms/{key}/preprocessing/{pipelineVersion}` → `text/plain` (404 if none); `GET /api/config/organisms/{key}/preprocessing` → list of configured versions. +- Admin write (`loculus_administrator`): `PUT`/`DELETE /api/admin/config/organisms/{key}/preprocessing/{pipelineVersion}` (direct save — no draft/publish). + +Rationale is in [ADR-019/020](09_architecturalDecisions.md). + +## Organism config + +```ts +type OrganismConfig = { + schema: Schema; + referenceGenome: ReferenceGenome; // simple, always present + displayName?: string | null; // canonical organism display name (ADR-016) + description?: string | null; + image?: { url: string } | null; + referenceGenomes?: ReferenceGenomeSegment[] | null; // rich multi-segment / multi-reference shape +}; +``` + +There is **no** top-level `organismName` (removed — see [ADR-016](09_architecturalDecisions.md)) and **no** `key` inside the JSON (the key is the `config_organisms.key` row identifier; the organism read response carries it as a sibling of `config`). + +```ts +type Schema = { + organismName: string; // LEGACY required name; consumers prefer OrganismConfig.displayName (ADR-016) + image?: string | null; + metadata: MetadataField[]; + externalMetadata: ExternalMetadata[]; // default [] + inputFields: InputField[]; // default [] + tableColumns: string[]; // default []; names must exist in metadata + primaryKey?: string | null; // typically accessionVersion + defaultOrderBy?: string | null; + defaultOrder?: 'ascending' | 'descending' | null; + metadataTemplate?: string[] | null; // references inputFields[].name + earliestReleaseDate?: { enabled: boolean; externalFields: string[] }; // default { false, [] } + submissionDataTypes?: { + consensusSequences: boolean; // default true + maxSequencesPerEntry?: number | null; + files?: { enabled: boolean; categories: FileCategory[] } | null; + }; + files: FileCategory[]; // default []; allowed output-file categories + loadSequencesAutomatically?: boolean | null; + richFastaHeaderFields?: string[] | null; + linkOuts: LinkOut[]; // default [] + referenceIdentifierField?: string | null; + multiFieldSearches?: MultiFieldSearch[] | null; +}; +``` + +## Sub-types + +```ts +type MetadataField = { + name: string; // canonical identifier; never renamed via the API + type: 'string' | 'date' | 'int' | 'float' | 'number' | 'timestamp' | 'boolean' | 'authors'; + required?: boolean; + + // Display + displayName?: string; description?: string; definition?: string; header?: string; + hidden?: boolean; initiallyVisible?: boolean; hideInSearchResultsTable?: boolean; + hideOnSequenceDetailsPage?: boolean; includeInDownloadsByDefault?: boolean; + columnWidth?: number; order?: number; orderOnDetailsPage?: number; orderInSearchDisplay?: number; + percentage?: boolean; customDisplay?: Record; + + // Search / query hints + autocomplete?: boolean; notSearchable?: boolean; substringSearch?: boolean; + rangeSearch?: boolean; + rangeOverlapSearch?: { rangeName: string; rangeDisplayName: string; bound: 'lower' | 'upper' }; + lineageSearch?: boolean; isSequenceFilter?: boolean; noInput?: boolean; + onlyForReference?: string; relatesToSegment?: string; + + // Adapter-facing (drive SILO rendering) + perSegment?: boolean; // expanded per segment for multi-segment organisms + lineageSystem?: string; // references a key in InstanceConfig.lineageSystemDefinitions + generateIndex?: boolean; oneHeader?: boolean; + options?: Array<{ name: string }>; ingest?: string; ontology_id?: string; +}; + +type ExternalMetadata = { externalMetadataUpdater: string; name: string; type: MetadataField['type']; required?: boolean }; + +type InputField = { + name: string; + displayName?: string; definition?: string; guidance?: string; + example?: string | number; required?: boolean; desired?: boolean; noEdit?: boolean; + options?: Array<{ name: string }>; +}; + +type MultiFieldSearch = { name: string; displayName: string; fields: string[]; orderInSearchDisplay?: number }; + +type LinkOut = { + name: string; // stable identifier; operations target by name + url: string; // template, e.g. "https://example.com/?acc={accession}" + category?: string; + maxNumberOfRecommendedEntries?: number; // positive int + onlyForReferences?: Record; +}; + +type FileCategory = { name: string; displayName?: string }; + +type ReferenceGenome = { + nucleotideSequences: Array<{ name: string; sequence: string }>; // 'main', or 'L'/'M'/'S' + genes: Array<{ name: string; sequence: string }>; +}; + +type ReferenceGenomeSegment = { + name: string; displayName?: string; + references: Array<{ + name: string; displayName?: string; sequence: string; + insdcAccessionFull?: string; genes?: Array<{ name: string; sequence: string }>; + }>; +}; +``` + +## Common (system) metadata + +The fields every Loculus organism needs — `accessionVersion`, `accession`, `version`, `submissionId`, `isRevocation`, `submitter`, `groupName`, `groupId`, `submittedAtTimestamp`, `submittedDate`, `releasedAtTimestamp`, `releasedDate`, `versionStatus`, `versionComment`, `pipelineVersion`, and the data-use-terms fields when enabled — are **not** stored per organism. They are defined once in code (`config-tools/src/adapter/commonMetadata.ts`, driven by the instance config) and **composed onto** each organism's `metadata` at render time, by both the website transform and the adapter. Fixtures and admin forms therefore contain only organism-specific fields. + +## What is intentionally not here + +- Backend/Keycloak/LAPIS URLs, DB connection strings, ports, ingress, image tags, replica counts, resource limits — Helm/Spring only. +- Backend operational tuning (compression level, pipeline polling, read-only mode) — Spring/Helm technical config. +- The list of organisms — derived from `config_organisms` rows, not part of any config JSON. +- **Preprocessing pipeline config** — pipelines are external/customizable, so their config is *not* in the core config document. Per-field processing directives (the old `metadata[].preprocessing`) were removed. Instead an admin may attach an opaque, unversioned **config file** per `(organism, pipeline version)`, stored in `config_preprocessing_files` and served raw from a dedicated endpoint (below). The backend never interprets it. See [ADR-019/020](09_architecturalDecisions.md) and [section 13](13_databaseSchema.md). +- Ingest and ENA-deposition pipeline config — out of scope. +- Any credential or secret — those live in Helm as Kubernetes Secrets ([ADR-011](09_architecturalDecisions.md)). + +## Adapter consumption (SILO, illustrative) + +| Loculus field | SILO output | +|---|---| +| `metadata[].name` / `type` | `database_config.yaml: metadata[]` (with `timestamp→int`, `authors→string` translation) | +| `metadata[].generateIndex: true` | SILO `generateIndex: true` | +| `metadata[].lineageSystem` | SILO `generateIndex: true` + `generateLineageIndex: `; lineage file referenced | +| `metadata[].perSegment: true` | expanded to `name_` per sorted segment | +| `referenceGenome` / `referenceGenomes` | flattened into `reference_genomes.json` | +| `schema.organismName` | `database_config.yaml: schema.instanceName` (legacy — would move to `displayName`) | +| `lineageSystemDefinitions` (instance) | downloaded and written as `lineage_.yaml`, referenced by SILO | + +These mappings live in `config-tools/src/adapter/` and evolve with SILO; no backend change is needed when they do. + +## Validation status + +The Kotlin and Zod representations validate **structure** on every draft mutation (deserialization + Zod parse). Cross-field invariants — `tableColumns` ⊆ `metadata` names, `defaultOrderBy`/`primaryKey` exist, `metadataTemplate` ⊆ `inputFields`, `multiFieldSearches[].fields` exist, file categories reference `schema.files` — are **not yet enforced server-side** on a full-document PUT (individual operation handlers do check their own preconditions). A canonical validator that runs on every mutation and returns `422` is the intended fix; see [section 11](11_risksAndTechnicalDebt.md). diff --git a/documentation-experimental-features/views/01_introductionAndGoals.md b/documentation-experimental-features/views/01_introductionAndGoals.md new file mode 100644 index 0000000000..406722749f --- /dev/null +++ b/documentation-experimental-features/views/01_introductionAndGoals.md @@ -0,0 +1,24 @@ +# 1. Introduction and Goals + +## What this describes + +Loculus can define cross-organism views. A view is configured with an SQL query, a manual SILO/LAPIS schema, and optional unaligned nucleotide sequence settings. The view importer downloads released data from source organisms, transforms it to NDJSON, evaluates the SQL with DuckDB, attaches configured unaligned sequences by `accessionVersion`, and feeds the result into a dedicated SILO/LAPIS instance. + +The first views are `overview`, `real-organisms`, `test-organisms`, and `co-infections`. + +## Goals + +1. Let admins define metadata-first views without changing Python code for each view. +2. Support simple SQL over per-organism source tables. +3. Normalize common fields across organisms with different metadata names. +4. Serve each view through the normal search UI and backend query API. +5. Keep SILO/LAPIS input explicit through a manual schema. +6. Make preview fixtures include useful default views. +7. Support opt-in unaligned nucleotide sequence retrieval for views. + +## Non-goals + +- Inferring the SILO schema from SQL output. +- Aligned nucleotide sequence, mutation, insertion, or amino acid views. +- Providing a full admin-panel editor for views. +- Making views a replacement for organism databases. diff --git a/documentation-experimental-features/views/02_constraints.md b/documentation-experimental-features/views/02_constraints.md new file mode 100644 index 0000000000..cb704edbbf --- /dev/null +++ b/documentation-experimental-features/views/02_constraints.md @@ -0,0 +1,10 @@ +# 2. Constraints + +1. Views are metadata-first; sequence output is limited to configured unaligned nucleotide segments. +2. The source data comes from each organism's `/get-released-data` endpoint. +3. The legacy NDJSON transformer remains the normalization step before DuckDB reads source files. +4. DuckDB infers JSON types per source file, so generated source views cast schema-known fields to stable types. +5. The output schema is written manually in the view config. +6. Every deployed view needs its own SILO and LAPIS instance. +7. Empty source organisms must still be usable in `UNION ALL` queries. +8. Aligned sequence, mutation, insertion, and amino acid endpoints are not generated for views. diff --git a/documentation-experimental-features/views/03_contextAndScope.md b/documentation-experimental-features/views/03_contextAndScope.md new file mode 100644 index 0000000000..99662b8502 --- /dev/null +++ b/documentation-experimental-features/views/03_contextAndScope.md @@ -0,0 +1,28 @@ +# 3. Context and Scope + +## Context + +The original overview table was a dedicated special case with a fixed preprocessing script. That made it hard to add another cross-organism view or adjust fields without code changes. + +The new model moves the core view definition into instance config: + +- `query`: SQL run by DuckDB; +- `schema`: manual SILO database config for the query output; +- `tableColumns`: default website table columns; +- `sequenceData`: optional unaligned nucleotide segment configuration; +- `lapisUrl`: backend proxy target for the deployed view. + +## In scope + +- Downloading released metadata from configured source organisms. +- Running SQL over transformed source data. +- Attaching configured unaligned nucleotide sequences to selected rows by `accessionVersion`. +- Producing `data.ndjson.zst` for SILO. +- Exposing the resulting LAPIS instance as a normal query key. + +## Out of scope + +- View authoring UI. +- Automatic schema inference. +- Incremental SQL execution. +- Secret or private-data views. diff --git a/documentation-experimental-features/views/04_solutionStrategy.md b/documentation-experimental-features/views/04_solutionStrategy.md new file mode 100644 index 0000000000..571d086d62 --- /dev/null +++ b/documentation-experimental-features/views/04_solutionStrategy.md @@ -0,0 +1,10 @@ +# 4. Solution Strategy + +1. Deploy one SILO/LAPIS pair per configured view key. +2. Render each view's SQL query and schema with `loculus-config-adapter`. +3. In the view importer, download `/get-released-data` for every configured source organism. +4. Run `legacy-ndjson-transformer` once per source organism. +5. Register one DuckDB table-like view per organism. +6. Cast schema-known source columns inside generated DuckDB views. +7. Execute the admin-provided SQL, optionally attach unaligned sequence fields for returned `accessionVersion` values, and write `data.ndjson.zst`. +8. Let SILO import that file and LAPIS serve it through the backend query API. diff --git a/documentation-experimental-features/views/05_buildingBlockView.md b/documentation-experimental-features/views/05_buildingBlockView.md new file mode 100644 index 0000000000..4a2ea51cb4 --- /dev/null +++ b/documentation-experimental-features/views/05_buildingBlockView.md @@ -0,0 +1,36 @@ +# 5. Building Block View + +## Level 1 + +``` +Source organism backends + | + v +view silo-importer + - download released data + - legacy NDJSON transform + - DuckDB SQL execution + - optional sequence attachment + | + v +data.ndjson.zst + | + v +SILO + LAPIS for the view + | + v +Website and /query/{view}/... +``` + +## Components + +| Component | Responsibility | +|---|---| +| Instance `views` config | Stores SQL, manual schema, default table columns, display name, and optional sequence settings. | +| `loculus-config-adapter` | Renders `overview_query.sql`, `database_config.yaml`, `reference_genomes.json`, `view_sequence_config.json`, and SILO preprocessing config for a view. | +| `OverviewImporterRunner` | Downloads all source releases and builds the view input file. | +| `legacy-ndjson-transformer` | Converts released-data records into flat NDJSON records with metadata and sequence fields. | +| DuckDB | Executes generated source views and the configured SQL query. | +| SILO/LAPIS view pods | Index and serve the materialized view. | +| Website config transform | Treats views like organisms for browse pages and navigation. | +| Backend query proxy | Serves the view under `/query/{view}/{versionGroup}/...`. | diff --git a/documentation-experimental-features/views/06_runtimeView.md b/documentation-experimental-features/views/06_runtimeView.md new file mode 100644 index 0000000000..148c716ba4 --- /dev/null +++ b/documentation-experimental-features/views/06_runtimeView.md @@ -0,0 +1,16 @@ +# 6. Runtime View + +## Materialization cycle + +1. The importer starts with a rendered SQL query and rendered database config. +2. It downloads released data from each source organism, using ETags to skip unchanged full refreshes. +3. It writes one transformed `.ndjson.zst` file per source organism. +4. It creates generated DuckDB source views named after organism keys. +5. It executes the configured SQL query. +6. If unaligned sequences are enabled, it copies configured segment fields from transformed source records to matching SQL result rows by `accessionVersion`. +7. It writes `data.ndjson.zst`. +8. SILO preprocessing imports the file and LAPIS serves the resulting view until the next successful materialization. + +## Empty and changing sources + +If all sources are unchanged, the importer skips the cycle. If one source changed while others returned `304`, the importer refetches all sources so the SQL query sees a consistent snapshot. diff --git a/documentation-experimental-features/views/07_deploymentView.md b/documentation-experimental-features/views/07_deploymentView.md new file mode 100644 index 0000000000..4f33134427 --- /dev/null +++ b/documentation-experimental-features/views/07_deploymentView.md @@ -0,0 +1,17 @@ +# 7. Deployment View + +## Kubernetes shape + +Each configured view key produces: + +- one `loculus-silo-{view}` deployment; +- one `loculus-lapis-{view}` deployment; +- one config-adapter init container in each deployment; +- one importer container alongside SILO for materialization. + +## Helm values + +`values.yaml` controls which view deployments exist. The SQL and schema come from the DB-backed instance config seeded by fixtures in previews. + +The legacy `overview` deployment setting is still supported and merged into the `views` map under key `overview`. + diff --git a/documentation-experimental-features/views/08_crosscuttingConcepts.md b/documentation-experimental-features/views/08_crosscuttingConcepts.md new file mode 100644 index 0000000000..5201e3b5ad --- /dev/null +++ b/documentation-experimental-features/views/08_crosscuttingConcepts.md @@ -0,0 +1,25 @@ +# 8. Crosscutting Concepts + +## Typed generated source views + +Admins should not have to write casts for every field. The importer reads the manual output schema and casts source columns with known names inside generated DuckDB views. This avoids type conflicts when different JSON files infer different types. + +## Manual output schema + +The schema remains explicit because SILO needs field types, indexes, display names, default ordering, and feature flags. SQL output and schema must stay aligned. + +## Naming + +Organism keys become quoted DuckDB view names, so queries can use source names like `"ebola-sudan"` safely. + +## Sequence namespace + +Views can opt into unaligned nucleotide sequences. Non-segmented organisms use the `main` segment, segmented organisms use biological segment names such as `L`, `M`, and `S`, and sparse rows are allowed when a row has no sequence for a view segment. + +## Synthetic references + +Sequence-enabled views render placeholder reference genomes with sequence `N`. They exist to declare segment names for unaligned LAPIS endpoints; aligned sequence, mutation, insertion, and amino acid endpoints remain disabled. + +## Normalized fields + +View SQL can normalize source-specific fields, for example `geoLocCountry` and `country` into `country`, or `sampleCollectionDate` and `date` into `date`. diff --git a/documentation-experimental-features/views/09_architecturalDecisions.md b/documentation-experimental-features/views/09_architecturalDecisions.md new file mode 100644 index 0000000000..639a3954f6 --- /dev/null +++ b/documentation-experimental-features/views/09_architecturalDecisions.md @@ -0,0 +1,43 @@ +# 9. Architectural Decisions + +## ADR-001: Views are configured in instance config + +**Decision.** SQL, schema, display name, table columns, and LAPIS URL live in the DB-backed instance config. + +**Consequences.** Fixtures and future admin tooling can define views without changing importer code. + +## ADR-002: Use DuckDB for SQL execution + +**Decision.** DuckDB reads transformed NDJSON and executes the configured SQL query. + +**Consequences.** Admins can express unions, aliases, filters, and joins in SQL while the importer stays generic. + +## ADR-003: Keep manual SILO schemas + +**Decision.** The view config includes an explicit schema instead of inferring it from SQL output. + +**Consequences.** LAPIS/SILO behavior is predictable, but admins must keep SQL output and schema aligned. + +## ADR-004: Cast schema-known fields in generated source views + +**Decision.** The importer casts fields named in the manual schema before admin SQL runs. + +**Consequences.** Admin SQL stays readable. Arbitrary extra fields still need explicit handling if they are not in the schema. + +## ADR-005: One SILO/LAPIS pair per view + +**Decision.** Each view is materialized into its own query database. + +**Consequences.** Views are isolated and can have separate schemas, but each view adds runtime pods and import work. + +## ADR-006: Download released data through backend APIs + +**Decision.** The importer reads `/get-released-data` for each source organism. + +**Consequences.** Views see the same released-data surface as external consumers and do not read the Loculus database directly. + +## ADR-007: Support only unaligned nucleotide sequences in views + +**Decision.** Views may opt into unaligned nucleotide sequence fields, using `main` for non-segmented organisms and biological segment names for segmented organisms. + +**Consequences.** The first sequence-capable views avoid reference-choice, alignment, mutation, and amino acid semantics while still supporting FASTA/JSON sequence retrieval for materialized rows. diff --git a/documentation-experimental-features/views/10_qualityRequirements.md b/documentation-experimental-features/views/10_qualityRequirements.md new file mode 100644 index 0000000000..14465eba6a --- /dev/null +++ b/documentation-experimental-features/views/10_qualityRequirements.md @@ -0,0 +1,11 @@ +# 10. Quality Requirements + +| Quality | Requirement | +|---|---| +| Flexibility | Adding a new metadata view should be mostly configuration work. | +| Correctness | A view materialization must use a consistent snapshot across source organisms. | +| Usability | SQL should not require repetitive casts for schema-known fields. | +| Searchability | View schemas must support normal LAPIS search, aggregation, and autocomplete. | +| Operability | Failed materializations must leave the previous SILO input in place until a successful rebuild. | +| Compatibility | Views should appear in the website and query API like organisms where possible. | + diff --git a/documentation-experimental-features/views/11_risksAndTechnicalDebt.md b/documentation-experimental-features/views/11_risksAndTechnicalDebt.md new file mode 100644 index 0000000000..b604a21686 --- /dev/null +++ b/documentation-experimental-features/views/11_risksAndTechnicalDebt.md @@ -0,0 +1,10 @@ +# 11. Risks and Technical Debt + +1. There is no admin UI for creating or editing views yet. +2. Schema and SQL output can drift because schema inference is not implemented. +3. Each view downloads all configured source organisms, which may become expensive. +4. DuckDB SQL is powerful enough to create slow or invalid queries. +5. View materialization currently works at whole-view granularity, not incremental row updates. +6. Sequence-enabled views can confuse admins if segment names are not documented in the config. +7. Views do not support aligned sequences, mutations, insertions, or amino acid data. +8. The `overview` name still exists as a legacy compatibility concept. diff --git a/documentation-experimental-features/views/12_glossary.md b/documentation-experimental-features/views/12_glossary.md new file mode 100644 index 0000000000..5affc4899c --- /dev/null +++ b/documentation-experimental-features/views/12_glossary.md @@ -0,0 +1,12 @@ +# 12. Glossary + +| Term | Meaning | +|---|---| +| View | SQL-backed dataset materialized into its own SILO/LAPIS instance. | +| Source organism | Organism whose released data is downloaded and exposed as a DuckDB source view. | +| Admin SQL | The configured SQL query that defines the view output. | +| Generated source view | DuckDB view created by the importer for one source organism. | +| Manual schema | YAML schema supplied with the view and rendered as SILO database config. | +| Materialization | Rebuilding `data.ndjson.zst` from source releases and SQL output. | +| Co-infection view | View that selects records linked by group and isolate name across real organisms. | +| Sequence namespace | View-level segment names used for unaligned nucleotide sequence fields. | diff --git a/documentation-experimental-features/views/13_databaseSchema.md b/documentation-experimental-features/views/13_databaseSchema.md new file mode 100644 index 0000000000..65a6c54f2e --- /dev/null +++ b/documentation-experimental-features/views/13_databaseSchema.md @@ -0,0 +1,18 @@ +# 13. Database Schema + +No new dedicated view tables are introduced. + +Views are stored inside the DB-backed instance config document: + +```yaml +views: + overview: + displayName: Overview + query: "..." + schema: "..." + tableColumns: [...] + lapisUrl: "..." +``` + +The same config versioning, publication, and fixture-loading mechanics described in `../configuration-management/13_databaseSchema.md` apply to views because they are part of instance config. + diff --git a/documentation-experimental-features/views/14_configSchema.md b/documentation-experimental-features/views/14_configSchema.md new file mode 100644 index 0000000000..8cc657d585 --- /dev/null +++ b/documentation-experimental-features/views/14_configSchema.md @@ -0,0 +1,48 @@ +# 14. Config Schema + +## View config + +| Field | Meaning | +|---|---| +| `displayName` | Human-facing name shown in navigation, pages, and API docs. | +| `query` | DuckDB SQL query executed against generated source views. | +| `schema` | Manual SILO/LAPIS schema for the query output. | +| `tableColumns` | Default visible columns in the website browse table. | +| `sequenceData.unalignedNucleotideSequences.enabled` | Enables unaligned nucleotide sequence output for this view. | +| `sequenceData.unalignedNucleotideSequences.segments` | View-level segment names, usually `main` for non-segmented organisms and biological segment names for segmented organisms. | +| `sequenceData.unalignedNucleotideSequences.sourceSegments` | Optional mapping from view segment to organism-specific source segment name. | +| `lapisUrl` | Upstream LAPIS URL used by the backend query proxy. | + +## Example + +```yaml +views: + overview: + displayName: Overview + query: | + select accessionVersion, accession, geoLocCountry as country + from "enteroviruses" + union all + select accessionVersion, accession, country as country + from "dummy-organism" + sequenceData: + unalignedNucleotideSequences: + enabled: true + segments: + - main + schema: | + schema: + instanceName: Overview + opennessLevel: OPEN + metadata: + - name: accessionVersion + type: string + - name: accession + type: string + - name: country + type: string + primaryKey: accessionVersion + tableColumns: + - country + lapisUrl: "http://loculus-lapis-service-overview:8080" +``` diff --git a/integration-tests/tests/specs/features/annotations.dependent.spec.ts b/integration-tests/tests/specs/features/annotations.dependent.spec.ts index 0eb785c2e1..b3a44fe1f6 100644 --- a/integration-tests/tests/specs/features/annotations.dependent.spec.ts +++ b/integration-tests/tests/specs/features/annotations.dependent.spec.ts @@ -1,10 +1,9 @@ import { expect } from '@playwright/test'; import { SearchPage } from '../../pages/search.page'; import { test } from '../../fixtures/group.fixture'; -import { getFromLinkTargetAndAssertContent } from '../../utils/link-helpers'; test.describe('Sequence Preview Annotations', () => { - test('should have an embl file in the Files section', async ({ page }) => { + test('shows generated EMBL files', async ({ page }) => { const searchPage = new SearchPage(page); await searchPage.ebolaSudan(); @@ -19,74 +18,12 @@ test.describe('Sequence Preview Annotations', () => { await searchPage.fill('Author affiliations', 'Patho Institute, Paris'); const accessionVersion = await searchPage.clickOnSequenceAndGetAccession(0); - const accession = accessionVersion.split('.')[0]; - await expect(page.getByTestId('sequence-preview-modal')).toBeVisible(); + const modal = page.getByTestId('sequence-preview-modal'); + await expect(modal).toBeVisible(); + await expect(modal.getByText(accessionVersion)).toBeVisible(); await expect(page.getByRole('heading', { name: 'Files' })).toBeVisible(); - await expect( - page.getByTestId('sequence-preview-modal').getByText('Annotations'), - ).toBeVisible(); - - const emblLink = page.getByRole('link', { name: `${accessionVersion}.embl` }); - await expect(emblLink).toBeVisible(); - - const expected_content = EMBL_CONTENT.replace(/LOC_\w{6,10}/g, accession); - await getFromLinkTargetAndAssertContent(emblLink, expected_content); + await expect(page.getByRole('link', { name: `${accessionVersion}.embl` })).toBeVisible(); }); }); - -const EMBL_CONTENT = ` -ID LOC_000002W; ; linear; RNA; ; UNC; 910 BP. -XX -AC LOC_000002W; -XX -DE Loculus accession: LOC_000002W.1 -XX -OS Sudan ebolavirus -OC . -XX -RN [1] -XX -FH Key Location/Qualifiers -FH -FT source 1..910 -FT /molecule_type="genomic RNA" -FT /organism="Sudan ebolavirus" -FT /country="France" -FT /collection_date="2021-05-12" -FT gene 1..910 -FT /gene="NP" -FT /product="NP" -FT CDS 1..910 -FT /gene="NP" -FT /product="nucleoprotein" -FT /protein_id="YP_138520.1" -FT /note="predominant component of nucleocapsid" -FT /codon_start=1 -FT /translation="MDKRVRGSWALGGQSEVDLDYHKILTAGLSVQQGIVRQRVIPVYV -FT VSDLEGICQHIIQAFEAGVDFQDNADSFLLLLCLHHAYQGDHRLFLKSDAVQYLEGHGF -FT RFEVREKENVHRLDELLPNVTGGKNLRRTLAAMPEEETTEANAGQFLSFASLFLPKLVV -FT GEKACLEKVQRQIQVHAEQGLIQYPTSWQSVGHMMVIFRLMRTNFLIKFLLIHQGMHMV -FT AGHDANDTVISNSVAQARFSGLLIVKTVLDHILQKTDLGVRLHPLARTAKVKNEVSSFK -FT AALGSLAKHGEYAPFARLLNLS" -XX -SQ - atggataaac gggtgagagg ttcatgggcc ctgggaggac aatctgaagt tgatcttgac 60 - taccacaaaa tattaacagc cgggctttcg gtccaacaag ggattgtgcg acaaagagtc 120 - atcccggtat atgttgtgag tgatcttgag ggtatttgtc aacatatcat tcaggccttt 180 - gaagcaggcg tagatttcca agataatgct gacagcttcc ttttactttt atgtttacat 240 - catgcttacc aaggagatca taggctcttc ctcaaaagtg atgcagttca atacttagag 300 - ggccatggtt tcaggtttga ggtccgagaa aaggagaatg tgcaccgtct ggatgaattg 360 - ttgcccaatg tcaccggtgg aaaaaatctt aggagaacat tggctgcaat gcctgaagag 420 - gagacaacag aagctaatgc tggtcagttt ttatcctttg ccagtttgtt tctacccaaa 480 - cttgtcgttg gggagaaagc gtgtctggaa aaagtacaaa ggcagattca ggtccatgca 540 - gaacaagggc tcattcaata tccaacttcc tggcaatcag ttggacacat gatggtgatc 600 - ttccgtttga tgagaacaaa ctttttaatc aagttcctac taatacatca ggggatgcac 660 - atggtcgcag gccatgatgc gaatgacaca gtaatatcta attctgttgc ccaagcaagg 720 - ttctctggtc ttctgattgt aaagactgtt ctggaccaca tcctacaaaa aacagatctt 780 - ggagtacgac ttcatccact ggccaggaca gcaaaagtca agaatgaggt cagttcattc 840 - aaggcagctc ttggctcact tgccaagcat ggagaatatg ctccatttgc acgtctcctc 900 - aatctttctg 910 -// -`.trimStart(); diff --git a/integration-tests/tests/specs/features/api-documentation.spec.ts b/integration-tests/tests/specs/features/api-documentation.spec.ts new file mode 100644 index 0000000000..0b4f25987d --- /dev/null +++ b/integration-tests/tests/specs/features/api-documentation.spec.ts @@ -0,0 +1,140 @@ +import { expect } from '@playwright/test'; +import { test } from '../../fixtures/console-warnings.fixture'; + +type OpenApiDocument = { + paths: Record; +}; + +function getBackendBaseUrl(): URL { + const configuredBackendUrl = process.env.PLAYWRIGHT_TEST_BACKEND_URL; + if (configuredBackendUrl !== undefined && configuredBackendUrl !== '') { + return new URL(configuredBackendUrl); + } + + const baseUrl = new URL(process.env.PLAYWRIGHT_TEST_BASE_URL ?? 'http://localhost:3000'); + if (baseUrl.hostname === 'localhost' || baseUrl.hostname === '127.0.0.1') { + return new URL(`${baseUrl.protocol === 'https:' ? 'https:' : 'http:'}//localhost:8079`); + } + + const backendHostname = baseUrl.hostname.startsWith('backend') + ? baseUrl.hostname + : `backend-${baseUrl.hostname}`; + return new URL(`${baseUrl.protocol}//${backendHostname}`); +} + +test.describe('API documentation', () => { + test.describe.configure({ mode: 'serial' }); + + test('links to split OpenAPI specifications', async ({ page }) => { + await page.goto('/api-documentation'); + + await expect( + page.getByRole('heading', { name: 'Backend API specifications' }), + ).toBeVisible(); + await expect( + page.getByRole('heading', { name: 'Query API specifications - databases' }), + ).toBeVisible(); + await expect( + page.getByRole('heading', { name: 'Query API specifications - views' }), + ).toBeVisible(); + await expect(page.getByRole('heading', { name: 'Overview' })).toBeVisible(); + await expect(page.locator('body')).not.toContainText('LAPIS'); + await expect(page.locator('body')).not.toContainText('Also available at'); + + await expect(page.locator('a[href$="/api-docs.json"]')).toBeVisible(); + await expect(page.locator('a[href$="/api-docs/general.json"]')).toBeVisible(); + await expect( + page.locator('a[href*="/swagger-ui/loculus?url=%2Fapi-docs%2Fgeneral.json"]'), + ).toBeVisible(); + await expect( + page.locator( + 'a[href*="/scalar-api-reference?url=%2Fapi-docs%2Fquery%2Foverview.json"]', + ), + ).toBeVisible(); + }); + + test('serves complete, general, and per-query OpenAPI documents', async ({ page }) => { + const backendUrl = getBackendBaseUrl().origin; + + const completeResponse = await page.request.get(`${backendUrl}/api-docs.json`); + expect(completeResponse.ok()).toBe(true); + const complete = (await completeResponse.json()) as OpenApiDocument; + expect(Object.keys(complete.paths).some((path) => path.startsWith('/query/'))).toBe(true); + expect(Object.keys(complete.paths).some((path) => !path.startsWith('/query/'))).toBe(true); + + const legacyResponse = await page.request.get(`${backendUrl}/api-docs`); + expect(legacyResponse.status()).toBe(404); + + const generalResponse = await page.request.get(`${backendUrl}/api-docs/general.json`); + expect(generalResponse.ok()).toBe(true); + const general = (await generalResponse.json()) as OpenApiDocument; + expect(Object.keys(general.paths).some((path) => path.startsWith('/query/'))).toBe(false); + + const overviewResponse = await page.request.get( + `${backendUrl}/api-docs/query/overview.json`, + ); + expect(overviewResponse.ok()).toBe(true); + const overview = (await overviewResponse.json()) as OpenApiDocument; + const overviewPaths = Object.keys(overview.paths); + expect(overviewPaths.length).toBeGreaterThan(0); + expect(overviewPaths.every((path) => path.startsWith('/query/overview/'))).toBe(true); + }); + + test('Swagger and Scalar load the selected query specification', async ({ page, context }) => { + const backendUrl = getBackendBaseUrl().origin; + const swaggerRequests: string[] = []; + page.on('request', (request) => { + const url = request.url(); + if (url.includes('/api-docs')) swaggerRequests.push(url); + }); + + await page.goto(`${backendUrl}/swagger-ui/loculus?url=/api-docs/query/overview.json`); + await page.waitForLoadState('networkidle'); + await expect(page.getByRole('link', { name: 'Loculus home' })).toBeVisible(); + await expect(page.locator('#loculus-docs-spec')).toHaveValue( + '/api-docs/query/overview.json', + ); + await expect(page.locator('body')).toContainText('/query/overview/{versionGroup}/metadata'); + await expect(page.locator('body')).not.toContainText('/{organism}/submit'); + expect(swaggerRequests).toContain(`${backendUrl}/api-docs/query/overview.json`); + + const scalarPage = await context.newPage(); + const scalarRequests: string[] = []; + scalarPage.on('request', (request) => { + const url = request.url(); + if (url.includes('/api-docs')) scalarRequests.push(url); + }); + + await scalarPage.goto( + `${backendUrl}/scalar-api-reference?url=/api-docs/query/overview.json`, + ); + await scalarPage.waitForLoadState('networkidle'); + await expect(scalarPage.getByRole('link', { name: 'Loculus home' })).toBeVisible(); + await expect(scalarPage.locator('#loculus-docs-spec')).toHaveValue( + '/api-docs/query/overview.json', + ); + await expect(scalarPage.locator('body')).toContainText( + '/query/overview/{versionGroup}/metadata', + ); + await expect(scalarPage.locator('body')).not.toContainText('/{organism}/submit'); + expect(scalarRequests).toContain(`${backendUrl}/api-docs/query/overview.json`); + }); + + test('API reference bar can switch specifications', async ({ page }) => { + const backendUrl = getBackendBaseUrl().origin; + + await page.goto(`${backendUrl}/swagger-ui/loculus?url=/api-docs/query/overview.json`); + await expect(page.locator('body')).toContainText('/query/overview/{versionGroup}/metadata'); + await page.locator('#loculus-docs-spec').selectOption('/api-docs/general.json'); + await expect(page).toHaveURL( + `${backendUrl}/swagger-ui/loculus?url=%2Fapi-docs%2Fgeneral.json`, + ); + + await page.goto(`${backendUrl}/scalar-api-reference?url=/api-docs/query/overview.json`); + await expect(page.locator('body')).toContainText('/query/overview/{versionGroup}/metadata'); + await page.locator('#loculus-docs-spec').selectOption('/api-docs/general.json'); + await expect(page).toHaveURL( + `${backendUrl}/scalar-api-reference?url=%2Fapi-docs%2Fgeneral.json`, + ); + }); +}); diff --git a/integration-tests/tests/specs/features/views.spec.ts b/integration-tests/tests/specs/features/views.spec.ts new file mode 100644 index 0000000000..e657a15310 --- /dev/null +++ b/integration-tests/tests/specs/features/views.spec.ts @@ -0,0 +1,120 @@ +import { expect, type Page } from '@playwright/test'; +import { test } from '../../fixtures/console-warnings.fixture'; + +const sequenceRows = '[data-testid="sequence-row"]'; + +type InstanceConfigResponse = { + config: { + views: Record; + }; +}; + +test.describe('SQL-backed views', () => { + test('landing page links to the overview and the overview shows released data', async ({ + page, + }) => { + await page.goto('/'); + + const seeAllSamples = page.getByRole('link', { name: 'See all samples' }); + await expect(seeAllSamples).toBeVisible(); + await expect(seeAllSamples).toHaveAttribute('href', '/overview'); + await expect(page.getByRole('link', { name: 'Real organisms' })).toHaveAttribute( + 'href', + '/views/real-organisms', + ); + await expect(page.getByRole('link', { name: 'Test organisms' })).toHaveAttribute( + 'href', + '/views/test-organisms', + ); + await expect(page.getByRole('link', { name: 'Co-infections' })).toHaveAttribute( + 'href', + '/views/co-infections', + ); + await expect( + page.getByText('Browse released records from the NCBI-ingested organism databases'), + ).toBeVisible(); + await expect(page.getByText('Explore the configured test organisms')).toBeVisible(); + await expect( + page.getByText( + 'Find real-organism records linked by submitting group and isolate name', + ), + ).toBeVisible(); + await expect(page.getByRole('link', { name: 'Overview' })).toBeVisible(); + + await seeAllSamples.click(); + await expect(page).toHaveTitle(/Overview - Browse/); + await expect(page.locator(sequenceRows).first()).toBeVisible({ timeout: 60_000 }); + await expect(page.getByRole('columnheader', { name: 'Organism' })).toBeVisible(); + await expect(page.getByRole('columnheader', { name: 'Country' })).toBeVisible(); + }); + + test('additional configured views load with their own schemas', async ({ page }) => { + await page.goto('/views/real-organisms'); + await expect(page).toHaveTitle(/Real organisms - Browse/); + await expect(page.locator(sequenceRows).first()).toBeVisible({ timeout: 60_000 }); + await expect(page.getByRole('columnheader', { name: 'Collection country' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'Add search fields' })).toBeVisible(); + + await page.goto('/views/test-organisms'); + await expect(page).toHaveTitle(/Test organisms - Browse/); + await page.getByRole('button', { name: 'Add search fields' }).click(); + await expect(page.getByRole('checkbox', { name: 'Country', exact: true })).toBeVisible(); + await expect(page.getByRole('checkbox', { name: 'Lineage', exact: true })).toBeVisible(); + + await page.goto('/views/co-infections'); + await expect(page).toHaveTitle(/Co-infections - Browse/); + await expect(page.locator(sequenceRows).first()).toBeVisible({ timeout: 60_000 }); + await expect(page.getByRole('columnheader', { name: 'Isolate name' })).toBeVisible(); + await expect(page.getByRole('columnheader', { name: 'Organism' })).toBeVisible(); + await expect(page.getByLabel('Isolate name')).toBeVisible(); + + await page.goto('/views/co-infections?specimenCollectorSampleId=coinfection-preview-001'); + await expect(page.locator(sequenceRows)).toHaveCount(2, { timeout: 60_000 }); + await expect( + page.locator(sequenceRows).filter({ hasText: 'coinfection-preview-001' }), + ).toHaveCount(2); + await expect(page.locator(sequenceRows).filter({ hasText: 'Ebola Sudan' })).toBeVisible(); + await expect( + page.locator(sequenceRows).filter({ hasText: 'West Nile Virus' }), + ).toBeVisible(); + }); + + test('views sort by collection date descending by default', async ({ page }) => { + await page.goto('/overview'); + await expectRowsSortedByDateDescending(page); + + await page.goto('/views/real-organisms'); + await expectRowsSortedByDateDescending(page); + + await page.goto('/views/co-infections'); + await expectRowsSortedByDateDescending(page); + + const backendUrl = process.env.PLAYWRIGHT_TEST_BACKEND_URL ?? 'http://localhost:8079'; + const response = await page.request.get(`${backendUrl}/api/config/instance`); + expect(response.ok()).toBe(true); + const instance = (await response.json()) as InstanceConfigResponse; + expect(instance.config.views.overview.schema).toContain('defaultOrderBy: date'); + expect(instance.config.views['real-organisms'].schema).toContain( + 'defaultOrderBy: sampleCollectionDate', + ); + expect(instance.config.views['co-infections'].schema).toContain( + 'defaultOrderBy: sampleCollectionDate', + ); + expect(instance.config.views['test-organisms'].schema).toContain('defaultOrderBy: date'); + }); +}); + +async function expectRowsSortedByDateDescending(page: Page) { + await expect(page.locator(sequenceRows).first()).toBeVisible({ timeout: 60_000 }); + const rows = await page + .locator(sequenceRows) + .evaluateAll((elements) => + elements.slice(0, 5).map((element) => element.textContent ?? ''), + ); + const dates = rows + .map((row) => row.match(/\d{4}-\d{2}-\d{2}/)?.[0]) + .filter((date): date is string => date !== undefined); + + expect(dates.length).toBeGreaterThan(1); + expect(dates).toEqual([...dates].sort().reverse()); +} diff --git a/kubernetes/config-processor/README.md b/kubernetes/config-processor/README.md index 74511e8f27..7f215f3839 100644 --- a/kubernetes/config-processor/README.md +++ b/kubernetes/config-processor/README.md @@ -2,20 +2,16 @@ ## Overview -The Loculus Config Processor is a utility container designed to dynamically process configuration files in Kubernetes deployments. It serves as an init container that transforms configuration files by: +The Loculus Config Processor is a utility container designed to dynamically process configuration files in Kubernetes deployments. It serves as an init container that substitutes environment variables for sensitive information (passwords, secrets) into configuration files, so secrets are not stored directly in ConfigMaps. -1. Fetching and inserting external content from URLs -2. Substituting environment variables for sensitive information (passwords, secrets) - -This allows configuration files to reference large external assets (like reference genomes) and inject sensitive data without storing them directly in ConfigMaps. +> Note: the processor previously also inlined external content via `[[URL:…]]` placeholders. That feature has been removed — reference genomes and other domain config now live in the backend database, not in Helm-rendered config. Only secret substitution remains. ## How It Works -The Config Processor operates in three main steps: +The Config Processor operates in two main steps: 1. **Copy**: Duplicates the entire directory structure from input to output -2. **Fetch URLs**: Finds and replaces `[[URL:https://example.com/file.txt]]` patterns with the actual content from those URLs (just as simple string replacement.) -3. **Substitute Secrets**: Replaces `[[KEY]]` placeholders with values from environment variables prefixed with `LOCULUSSUB_` +2. **Substitute Secrets**: Replaces `[[KEY]]` placeholders with values from environment variables prefixed with `LOCULUSSUB_` ## Usage in Kubernetes @@ -64,7 +60,6 @@ metadata: data: config.json: | { - "referenceGenome": "[[URL:https://example.com/genome.fasta]]", "smtpPassword": "[[smtpPassword]]" } ``` diff --git a/kubernetes/config-processor/config-processor.py b/kubernetes/config-processor/config-processor.py index 8661766699..4ac903617e 100644 --- a/kubernetes/config-processor/config-processor.py +++ b/kubernetes/config-processor/config-processor.py @@ -1,13 +1,5 @@ import os -import re import shutil -import threading -from concurrent.futures import ThreadPoolExecutor, as_completed - -import requests - -DEFAULT_MAX_WORKERS = 16 -thread_local = threading.local() def copy_structure(input_dir, output_dir): @@ -21,75 +13,19 @@ def copy_structure(input_dir, output_dir): os.makedirs(os.path.dirname(file_path), exist_ok=True) shutil.copy(os.path.join(root, file), file_path) - -def download_urls(urls): - if not urls: - return {} - - max_workers = DEFAULT_MAX_WORKERS - while True: - try: - return download_urls_with_workers(urls, max_workers) - except requests.exceptions.RequestException as error: - if "Too many open files" not in str(error) or max_workers == 1: - raise - max_workers = max(max_workers // 2, 1) - print(f"Too many open files while downloading URLs, retrying with {max_workers} worker(s)") - - -def download_urls_with_workers(urls, max_workers): - print(f"Downloading {len(urls)} unique URL(s) with {max_workers} worker(s)") - - downloaded_content = {} - with ThreadPoolExecutor(max_workers=max_workers) as executor: - future_to_url = {executor.submit(download_url, url): url for url in urls} - for future in as_completed(future_to_url): - url = future_to_url[future] - response = future.result() - if response.status_code == 200: - downloaded_content[url] = response.text.strip() - else: - error_details = f"URL: {url}, Status Code: {response.status_code}, Reason: {response.reason}" - raise ValueError(f"Problem downloading {error_details}") - return downloaded_content - - -def download_url(url): - if not hasattr(thread_local, "session"): - thread_local.session = requests.Session() - return thread_local.session.get(url) - - -def replace_url_with_content(file_content, downloaded_content): - urls = re.findall(r'\[\[URL:([^\]]*)\]\]', file_content) - for url in set(urls): - file_content = file_content.replace(f"[[URL:{url}]]", downloaded_content[url]) - return file_content - def make_substitutions(file_content, substitutions): for key, value in substitutions.items(): file_content = file_content.replace(f"[[{key}]]", value) return file_content -def collect_urls(output_dir): - urls = set() - for root, dirs, files in os.walk(output_dir): - for file in files: - file_path = os.path.join(root, file) - with open(file_path) as f: - urls.update(re.findall(r'\[\[URL:([^\]]*)\]\]', f.read())) - return urls - def process_files(output_dir, substitutions): - downloaded_content = download_urls(collect_urls(output_dir)) for root, dirs, files in os.walk(output_dir): for file in files: file_path = os.path.join(root, file) with open(file_path, 'r+') as f: print(f"Processing {file_path}") content = f.read() - new_content = replace_url_with_content(content, downloaded_content) - new_content = make_substitutions(new_content, substitutions) + new_content = make_substitutions(content, substitutions) if new_content != content: f.seek(0) f.write(new_content) diff --git a/kubernetes/config-processor/requirements.txt b/kubernetes/config-processor/requirements.txt index 663bd1f6a2..3b3d5599d4 100644 --- a/kubernetes/config-processor/requirements.txt +++ b/kubernetes/config-processor/requirements.txt @@ -1 +1 @@ -requests \ No newline at end of file +# No third-party dependencies — config-processor uses only the Python standard library. diff --git a/kubernetes/loculus/fixtures/README.md b/kubernetes/loculus/fixtures/README.md new file mode 100644 index 0000000000..6f351db393 --- /dev/null +++ b/kubernetes/loculus/fixtures/README.md @@ -0,0 +1,54 @@ +# `kubernetes/loculus/fixtures/` + +Fixture YAMLs consumed by the `loculus-config-loader` CLI (see +`config-tools/Dockerfile.loader`) to seed a fresh Loculus deployment. + +## Provenance + +These fixtures are the **source of truth** for the organism/instance domain +config used by CI, previews, and local dev (the backend DB is seeded from them +by `loculus-config-loader`). They are hand-maintained — edit them directly. + +Reference-genome sequences are **inlined** here (not `[[URL:...]]` placeholders), +so the loader needs no network access at load time. The sequences no longer live +in `values.yaml` at all: domain config (organisms, schemas, reference genomes, +lineage definitions) is owned by these fixtures / the database, while +`values.yaml` keeps only deployment/technical config plus the legacy per-organism +scaffolding that `ingest`/`ena-submission` still read (segment names, no sequences). + +(These fixtures were originally bootstrapped from `values.yaml` by a one-shot +`migrate-values-to-fixtures.py` script, since deleted.) + +## Layout + +``` +fixtures/ +├── instance.yaml # validates against canonicalInstanceConfig +└── organisms/ + └── .yaml # one file per organism; key = filename stem +``` + +Current organisms (matches `defaultOrganisms` in `values.yaml`): +`cchf`, `cchf-multi-ref`, `dummy-organism`, `dummy-organism-with-files`, +`ebola-sudan`, `enteroviruses`, `not-aligned-organism`, `west-nile`. + +## Local-dev loading + +```bash +cd config-tools +npm install # one-time +npm run loader -- \ + --backend-url http://localhost:8079 \ + --fixtures ../kubernetes/loculus/fixtures \ + --admin-token "$LOCULUS_ADMIN_TOKEN" +``` + +`LOCULUS_ADMIN_TOKEN` must be a Keycloak token for a user with the `loculus_administrator` realm role. + +Use `--dry-run` to validate fixtures against the canonical Zod schema without +making any HTTP calls. + +## Helm Job usage (Phase 3.4) + +Helm mounts these YAMLs as a ConfigMap (`loculus-config-loader-fixtures`) and +runs the loader as a `post-install` Job with `--mode fresh-only`. diff --git a/kubernetes/loculus/fixtures/instance.yaml b/kubernetes/loculus/fixtures/instance.yaml new file mode 100644 index 0000000000..4ed30f7217 --- /dev/null +++ b/kubernetes/loculus/fixtures/instance.yaml @@ -0,0 +1,1972 @@ +gitHubMainUrl: https://github.com/loculus-project/loculus +bannerMessage: This is a demonstration environment. It may contain non-accurate test data and should not be used for real-world applications. Data will be deleted regularly. +logo: + url: /favicon.svg + width: 100 + height: 100 +issuesEmail: noreply@loculus.org +gitHubEditLink: https://github.com/loculus-project/loculus/edit/main/monorepo/website/ +additionalHeadHTML: '' +accessionPrefix: LOC_ +gitHubIssuesUrl: https://github.com/loculus-project/loculus/issues +name: Loculus +dataUseTerms: + enabled: true + urls: + open: https://#TODO-MVP/open + restricted: https://#TODO-MVP/restricted +fileSharing: + outputFileUrlType: backend +enableSeqSets: true +seqSetsFieldsToDisplay: + - field: geoLocCountry + displayName: Country + - field: sampleCollectionDate + displayName: Collection Date + - field: authors + displayName: Authors +seqSetsGraphs: + - name: sampleCollectionDates + displayName: Sample collection dates + type: date + fields: + - sampleCollectionDate + - date + - name: sampleCollectionCountries + displayName: Sample collection countries + type: category + fields: + - geoLocCountry + - country + - name: dataUseTerms + displayName: Data use terms + type: category + fields: + - dataUseTerms +enableLoginNavigationItem: true +enableSubmissionNavigationItem: true +enableSubmissionPages: true +lineageSystemDefinitions: + pangoLineage: + '1': https://raw.githubusercontent.com/loculus-project/loculus/c400348ea0ba0b8178aa43475d5c7539fc097997/preprocessing/dummy/lineage.yaml + '2': https://raw.githubusercontent.com/loculus-project/loculus/c400348ea0ba0b8178aa43475d5c7539fc097997/preprocessing/dummy/lineage.yaml + alternativeLineage: + '4': https://raw.githubusercontent.com/loculus-project/loculus/fe3b2974071acc9a25856d650350f8a952040a7c/preprocessing/dummy/lineage-alternative.yaml + cchfS: + '1': https://pathoplexus.github.io/silo-lineage-hierarchy-definitions/definitions/cchf/S/2025-09-24--07-29-03Z/lineages.yaml + +# Cross-organism overview table / LAPIS instance (admin-configurable fields). +overview: + displayName: Overview + lapisUrl: "http://loculus-lapis-service-overview:8080" + sequenceData: + unalignedNucleotideSequences: + enabled: true + segments: + - main + - L + - M + - S + query: | + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'CCHF (Multi-Ref)' as organism + from "cchf-multi-ref" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'Crimean-Congo Hemorrhagic Fever Virus' as organism + from "cchf" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, country as country, + date as date, 'Test organism (with files)' as organism + from "dummy-organism-with-files" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, country as country, + date as date, 'Test Dummy Organism' as organism + from "dummy-organism" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'Ebola Sudan' as organism + from "ebola-sudan" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'Enterovirus' as organism + from "enteroviruses" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, country as country, + date as date, 'Test organism (without alignment)' as organism + from "not-aligned-organism" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'West Nile Virus' as organism + from "west-nile" + schema: | + schema: + instanceName: Overview + opennessLevel: OPEN + metadata: + - name: accessionVersion + displayName: Accession version + type: string + notSearchable: true + hideOnSequenceDetailsPage: true + includeInDownloadsByDefault: true + - name: accession + displayName: Accession + type: string + notSearchable: true + hideOnSequenceDetailsPage: true + - name: version + displayName: Version + type: int + hideOnSequenceDetailsPage: true + - name: submissionId + displayName: Submission ID + type: string + header: Submission details + substringSearch: true + includeInDownloadsByDefault: true + - name: isRevocation + displayName: Is revocation + type: boolean + autocomplete: true + hideOnSequenceDetailsPage: true + - name: submitter + displayName: Submitter + type: string + generateIndex: true + autocomplete: true + header: Submission details + hideOnSequenceDetailsPage: true + - name: groupName + displayName: Submitting group + type: string + generateIndex: true + autocomplete: true + header: Submission details + includeInDownloadsByDefault: true + - name: groupId + displayName: Submitting group (numeric ID) + type: int + autocomplete: true + header: Submission details + - name: submittedAtTimestamp + displayName: Date submitted + type: timestamp + header: Submission details + - name: submittedDate + displayName: Date submitted (exact) + type: string + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + - name: releasedAtTimestamp + displayName: Date released + type: timestamp + header: Submission details + columnWidth: 100 + - name: releasedDate + displayName: Date released (exact) + type: string + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + columnWidth: 100 + - name: versionStatus + displayName: Version status + type: string + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + - name: versionComment + displayName: Version comment + type: string + header: Submission details + - name: pipelineVersion + displayName: Pipeline version + type: int + notSearchable: true + hideOnSequenceDetailsPage: true + - name: dataUseTerms + displayName: Data use terms + type: string + generateIndex: true + autocomplete: true + header: Data use terms + initiallyVisible: true + includeInDownloadsByDefault: true + - name: dataUseTermsRestrictedUntil + displayName: Data use terms restricted until + type: date + header: Data use terms + hideOnSequenceDetailsPage: true + - name: dataBecameOpenAt + displayName: Date data became open + type: date + header: Data use terms + hideOnSequenceDetailsPage: true + - name: dataUseTermsUrl + displayName: Data use terms URL + type: string + header: Data use terms + notSearchable: true + includeInDownloadsByDefault: true + - name: country + displayName: Country + type: string + generateIndex: true + autocomplete: true + header: Sample details + initiallyVisible: true + includeInDownloadsByDefault: true + - name: date + displayName: Collection date + type: string + header: Sample details + initiallyVisible: true + generateIndex: true + autocomplete: true + columnWidth: 100 + - name: organism + displayName: Organism + type: string + generateIndex: true + autocomplete: true + header: Sample details + initiallyVisible: true + includeInDownloadsByDefault: true + primaryKey: accessionVersion + defaultOrderBy: date + defaultOrder: descending + features: + - name: generalizedAdvancedQuery + tableColumns: + - organism + - country + - date + +views: + overview: + displayName: Overview + lapisUrl: "http://loculus-lapis-service-overview:8080" + sequenceData: + unalignedNucleotideSequences: + enabled: true + segments: + - main + - L + - M + - S + query: | + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'CCHF (Multi-Ref)' as organism + from "cchf-multi-ref" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'Crimean-Congo Hemorrhagic Fever Virus' as organism + from "cchf" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, country as country, + date as date, 'Test organism (with files)' as organism + from "dummy-organism-with-files" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, country as country, + date as date, 'Test Dummy Organism' as organism + from "dummy-organism" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'Ebola Sudan' as organism + from "ebola-sudan" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'Enterovirus' as organism + from "enteroviruses" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, country as country, + date as date, 'Test organism (without alignment)' as organism + from "not-aligned-organism" + union all + select accessionVersion, accession, version, submissionId, isRevocation, + submitter, groupName, groupId, submittedAtTimestamp, submittedDate, + releasedAtTimestamp, releasedDate, versionStatus, versionComment, + pipelineVersion, dataUseTerms, dataUseTermsRestrictedUntil, + dataBecameOpenAt, dataUseTermsUrl, geoLocCountry as country, + sampleCollectionDate as date, 'West Nile Virus' as organism + from "west-nile" + schema: | + schema: + instanceName: Overview + opennessLevel: OPEN + metadata: + - name: accessionVersion + displayName: Accession version + type: string + notSearchable: true + hideOnSequenceDetailsPage: true + includeInDownloadsByDefault: true + - name: accession + displayName: Accession + type: string + notSearchable: true + hideOnSequenceDetailsPage: true + - name: version + displayName: Version + type: int + hideOnSequenceDetailsPage: true + - name: submissionId + displayName: Submission ID + type: string + header: Submission details + substringSearch: true + includeInDownloadsByDefault: true + - name: isRevocation + displayName: Is revocation + type: boolean + autocomplete: true + hideOnSequenceDetailsPage: true + - name: submitter + displayName: Submitter + type: string + generateIndex: true + autocomplete: true + header: Submission details + hideOnSequenceDetailsPage: true + - name: groupName + displayName: Submitting group + type: string + generateIndex: true + autocomplete: true + header: Submission details + includeInDownloadsByDefault: true + - name: groupId + displayName: Submitting group (numeric ID) + type: int + autocomplete: true + header: Submission details + - name: submittedAtTimestamp + displayName: Date submitted + type: timestamp + header: Submission details + - name: submittedDate + displayName: Date submitted (exact) + type: string + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + - name: releasedAtTimestamp + displayName: Date released + type: timestamp + header: Submission details + columnWidth: 100 + - name: releasedDate + displayName: Date released (exact) + type: string + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + columnWidth: 100 + - name: versionStatus + displayName: Version status + type: string + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + - name: versionComment + displayName: Version comment + type: string + header: Submission details + - name: pipelineVersion + displayName: Pipeline version + type: int + notSearchable: true + hideOnSequenceDetailsPage: true + - name: dataUseTerms + displayName: Data use terms + type: string + generateIndex: true + autocomplete: true + header: Data use terms + initiallyVisible: true + includeInDownloadsByDefault: true + - name: dataUseTermsRestrictedUntil + displayName: Data use terms restricted until + type: date + header: Data use terms + hideOnSequenceDetailsPage: true + - name: dataBecameOpenAt + displayName: Date data became open + type: date + header: Data use terms + hideOnSequenceDetailsPage: true + - name: dataUseTermsUrl + displayName: Data use terms URL + type: string + header: Data use terms + notSearchable: true + includeInDownloadsByDefault: true + - name: country + displayName: Country + type: string + generateIndex: true + autocomplete: true + header: Sample details + initiallyVisible: true + includeInDownloadsByDefault: true + - name: date + displayName: Collection date + type: string + header: Sample details + initiallyVisible: true + generateIndex: true + autocomplete: true + columnWidth: 100 + - name: organism + displayName: Organism + type: string + generateIndex: true + autocomplete: true + header: Sample details + initiallyVisible: true + includeInDownloadsByDefault: true + primaryKey: accessionVersion + defaultOrderBy: date + defaultOrder: descending + features: + - name: generalizedAdvancedQuery + tableColumns: + - organism + - country + - date + real-organisms: + displayName: Real organisms + lapisUrl: "http://loculus-lapis-service-real-organisms:8080" + sequenceData: + unalignedNucleotideSequences: + enabled: true + segments: + - main + - L + - M + - S + query: | + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, authors, + authorAffiliations, ncbiSubmitterCountry, bioprojectAccession, + gcaAccession, biosampleAccession, cultureId, + sampleReceivedDate, sampleType, purposeOfSampling, + presamplingActivity, anatomicalMaterial, anatomicalPart, + bodyProduct, environmentalMaterial, environmentalSite, + collectionDevice, collectionMethod, foodProduct, + foodProductProperties, specimenProcessing, specimenProcessingDetails, + experimentalSpecimenRoleType, hostAge, hostAgeBin, + hostGender, hostOriginCountry, hostDisease, + signsAndSymptoms, hostHealthState, hostHealthOutcome, + travelHistory, exposureEvent, hostRole, + exposureSetting, exposureDetails, previousInfectionDisease, + previousInfectionOrganism, hostVaccinationStatus, purposeOfSequencing, + sequencingDate, ampliconPcrPrimerScheme, ampliconSize, + sequencingInstrument, sequencingProtocol, sequencingAssayType, + sequencedByOrganization, sequencedByContactName, sequencedByContactEmail, + rawSequenceDataProcessingMethod, dehostingMethod, referenceGenomeAccession, + consensusSequenceSoftwareName, consensusSequenceSoftwareVersion, depthOfCoverage, + breadthOfCoverage, qualityControlMethodName, qualityControlMethodVersion, + qualityControlDetermination, qualityControlIssues, qualityControlDetails, + diagnosticMeasurementMethod, diagnosticTargetPresence, diagnosticTargetGeneName, + diagnosticMeasurementValue, diagnosticMeasurementUnit, hostTaxonId, + hostNameScientific, hostNameCommon, isLabHost, + cellLine, passageNumber, passageMethod, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + insdcRawReadsAccession, versionComment, + 'CCHF (Multi-Ref)' as organism + from "cchf-multi-ref" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, authors, + authorAffiliations, ncbiSubmitterCountry, bioprojectAccession, + gcaAccession, biosampleAccession, cultureId, + sampleReceivedDate, sampleType, purposeOfSampling, + presamplingActivity, anatomicalMaterial, anatomicalPart, + bodyProduct, environmentalMaterial, environmentalSite, + collectionDevice, collectionMethod, foodProduct, + foodProductProperties, specimenProcessing, specimenProcessingDetails, + experimentalSpecimenRoleType, hostAge, hostAgeBin, + hostGender, hostOriginCountry, hostDisease, + signsAndSymptoms, hostHealthState, hostHealthOutcome, + travelHistory, exposureEvent, hostRole, + exposureSetting, exposureDetails, previousInfectionDisease, + previousInfectionOrganism, hostVaccinationStatus, purposeOfSequencing, + sequencingDate, ampliconPcrPrimerScheme, ampliconSize, + sequencingInstrument, sequencingProtocol, sequencingAssayType, + sequencedByOrganization, sequencedByContactName, sequencedByContactEmail, + rawSequenceDataProcessingMethod, dehostingMethod, referenceGenomeAccession, + consensusSequenceSoftwareName, consensusSequenceSoftwareVersion, depthOfCoverage, + breadthOfCoverage, qualityControlMethodName, qualityControlMethodVersion, + qualityControlDetermination, qualityControlIssues, qualityControlDetails, + diagnosticMeasurementMethod, diagnosticTargetPresence, diagnosticTargetGeneName, + diagnosticMeasurementValue, diagnosticMeasurementUnit, hostTaxonId, + hostNameScientific, hostNameCommon, isLabHost, + cellLine, passageNumber, passageMethod, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + insdcRawReadsAccession, versionComment, + 'Crimean-Congo Hemorrhagic Fever Virus' as organism + from "cchf" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, authors, + authorAffiliations, ncbiSubmitterCountry, bioprojectAccession, + gcaAccession, biosampleAccession, cultureId, + sampleReceivedDate, sampleType, purposeOfSampling, + presamplingActivity, anatomicalMaterial, anatomicalPart, + bodyProduct, environmentalMaterial, environmentalSite, + collectionDevice, collectionMethod, foodProduct, + foodProductProperties, specimenProcessing, specimenProcessingDetails, + experimentalSpecimenRoleType, hostAge, hostAgeBin, + hostGender, hostOriginCountry, hostDisease, + signsAndSymptoms, hostHealthState, hostHealthOutcome, + travelHistory, exposureEvent, hostRole, + exposureSetting, exposureDetails, previousInfectionDisease, + previousInfectionOrganism, hostVaccinationStatus, purposeOfSequencing, + sequencingDate, ampliconPcrPrimerScheme, ampliconSize, + sequencingInstrument, sequencingProtocol, sequencingAssayType, + sequencedByOrganization, sequencedByContactName, sequencedByContactEmail, + rawSequenceDataProcessingMethod, dehostingMethod, referenceGenomeAccession, + consensusSequenceSoftwareName, consensusSequenceSoftwareVersion, depthOfCoverage, + breadthOfCoverage, qualityControlMethodName, qualityControlMethodVersion, + qualityControlDetermination, qualityControlIssues, qualityControlDetails, + diagnosticMeasurementMethod, diagnosticTargetPresence, diagnosticTargetGeneName, + diagnosticMeasurementValue, diagnosticMeasurementUnit, hostTaxonId, + hostNameScientific, hostNameCommon, isLabHost, + cellLine, passageNumber, passageMethod, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + insdcRawReadsAccession, versionComment, + 'Ebola Sudan' as organism + from "ebola-sudan" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, authors, + authorAffiliations, ncbiSubmitterCountry, bioprojectAccession, + gcaAccession, biosampleAccession, cultureId, + sampleReceivedDate, sampleType, purposeOfSampling, + presamplingActivity, anatomicalMaterial, anatomicalPart, + bodyProduct, environmentalMaterial, environmentalSite, + collectionDevice, collectionMethod, foodProduct, + foodProductProperties, specimenProcessing, specimenProcessingDetails, + experimentalSpecimenRoleType, hostAge, hostAgeBin, + hostGender, hostOriginCountry, hostDisease, + signsAndSymptoms, hostHealthState, hostHealthOutcome, + travelHistory, exposureEvent, hostRole, + exposureSetting, exposureDetails, previousInfectionDisease, + previousInfectionOrganism, hostVaccinationStatus, purposeOfSequencing, + sequencingDate, ampliconPcrPrimerScheme, ampliconSize, + sequencingInstrument, sequencingProtocol, sequencingAssayType, + sequencedByOrganization, sequencedByContactName, sequencedByContactEmail, + rawSequenceDataProcessingMethod, dehostingMethod, referenceGenomeAccession, + consensusSequenceSoftwareName, consensusSequenceSoftwareVersion, depthOfCoverage, + breadthOfCoverage, qualityControlMethodName, qualityControlMethodVersion, + qualityControlDetermination, qualityControlIssues, qualityControlDetails, + diagnosticMeasurementMethod, diagnosticTargetPresence, diagnosticTargetGeneName, + diagnosticMeasurementValue, diagnosticMeasurementUnit, hostTaxonId, + hostNameScientific, hostNameCommon, isLabHost, + cellLine, passageNumber, passageMethod, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + insdcRawReadsAccession, versionComment, + 'Enterovirus' as organism + from "enteroviruses" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, authors, + authorAffiliations, ncbiSubmitterCountry, bioprojectAccession, + gcaAccession, biosampleAccession, cultureId, + sampleReceivedDate, sampleType, purposeOfSampling, + presamplingActivity, anatomicalMaterial, anatomicalPart, + bodyProduct, environmentalMaterial, environmentalSite, + collectionDevice, collectionMethod, foodProduct, + foodProductProperties, specimenProcessing, specimenProcessingDetails, + experimentalSpecimenRoleType, hostAge, hostAgeBin, + hostGender, hostOriginCountry, hostDisease, + signsAndSymptoms, hostHealthState, hostHealthOutcome, + travelHistory, exposureEvent, hostRole, + exposureSetting, exposureDetails, previousInfectionDisease, + previousInfectionOrganism, hostVaccinationStatus, purposeOfSequencing, + sequencingDate, ampliconPcrPrimerScheme, ampliconSize, + sequencingInstrument, sequencingProtocol, sequencingAssayType, + sequencedByOrganization, sequencedByContactName, sequencedByContactEmail, + rawSequenceDataProcessingMethod, dehostingMethod, referenceGenomeAccession, + consensusSequenceSoftwareName, consensusSequenceSoftwareVersion, depthOfCoverage, + breadthOfCoverage, qualityControlMethodName, qualityControlMethodVersion, + qualityControlDetermination, qualityControlIssues, qualityControlDetails, + diagnosticMeasurementMethod, diagnosticTargetPresence, diagnosticTargetGeneName, + diagnosticMeasurementValue, diagnosticMeasurementUnit, hostTaxonId, + hostNameScientific, hostNameCommon, isLabHost, + cellLine, passageNumber, passageMethod, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + insdcRawReadsAccession, versionComment, + 'West Nile Virus' as organism + from "west-nile" + schema: | + schema: + instanceName: Real organisms + opennessLevel: OPEN + metadata: + - name: accessionVersion + displayName: Accession version + type: string + header: Loculus + - name: accession + displayName: Accession + type: string + header: Loculus + - name: version + displayName: Version + type: int + header: Loculus + - name: submissionId + displayName: Submission ID + type: string + header: Submission details + substringSearch: true + - name: isRevocation + displayName: Is revocation + type: boolean + header: Loculus + autocomplete: true + - name: submitter + displayName: Submitter + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: groupName + displayName: Submitting group + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: groupId + displayName: Submitting group (numeric ID) + type: int + header: Submission details + - name: submittedAtTimestamp + displayName: Date submitted + type: timestamp + header: Submission details + - name: submittedDate + displayName: Date submitted (exact) + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: releasedAtTimestamp + displayName: Date released + type: timestamp + header: Submission details + - name: releasedDate + displayName: Date released (exact) + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: versionStatus + displayName: Version status + type: string + header: Loculus + generateIndex: true + autocomplete: true + - name: pipelineVersion + displayName: Pipeline version + type: int + header: Loculus + - name: dataUseTerms + displayName: Data use terms + type: string + header: Data use terms + generateIndex: true + autocomplete: true + - name: dataUseTermsRestrictedUntil + displayName: Data use terms restricted until + type: date + header: Data use terms + - name: dataBecameOpenAt + displayName: Date data became open + type: date + header: Data use terms + - name: dataUseTermsUrl + displayName: Data use terms URL + type: string + header: Data use terms + - name: sampleCollectionDate + displayName: Collection date + type: string + header: Sample details + columnWidth: 100 + order: 10 + orderOnDetailsPage: 200 + includeInDownloadsByDefault: true + initiallyVisible: true + - name: sampleCollectionDateRangeLower + displayName: Collection date (lower bound) + type: date + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: lower + - name: sampleCollectionDateRangeUpper + displayName: Collection date (upper bound) + type: date + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: upper + - name: displayName + displayName: Display name + type: string + header: Sample details + initiallyVisible: true + - name: ncbiReleaseDate + displayName: NCBI release date + type: date + header: INSDC + columnWidth: 100 + orderOnDetailsPage: 1000 + initiallyVisible: true + - name: earliestReleaseDate + displayName: Earliest release date + type: date + header: Sample details + rangeSearch: true + orderOnDetailsPage: 300 + includeInDownloadsByDefault: true + - name: geoLocLatitude + displayName: Latitude + type: float + header: Sample details + orderOnDetailsPage: 420 + - name: geoLocLongitude + displayName: Longitude + type: float + header: Sample details + orderOnDetailsPage: 440 + - name: geoLocCountry + displayName: Collection country + type: string + header: Sample details + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + order: 20 + orderOnDetailsPage: 400 + includeInDownloadsByDefault: true + initiallyVisible: true + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + type: string + header: Sample details + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + order: 30 + orderOnDetailsPage: 460 + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + type: string + header: Sample details + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + orderOnDetailsPage: 480 + - name: geoLocCity + displayName: Collection city + type: string + header: Sample details + generateIndex: true + autocomplete: true + orderOnDetailsPage: 500 + - name: geoLocSite + displayName: Collection site + type: string + header: Sample details + orderOnDetailsPage: 520 + - name: specimenCollectorSampleId + displayName: Isolate name + type: string + header: Sample details + substringSearch: true + orderOnDetailsPage: 600 + - name: authors + displayName: Authors + type: authors + header: Authors + substringSearch: true + columnWidth: 140 + order: 40 + orderOnDetailsPage: 800 + includeInDownloadsByDefault: true + initiallyVisible: true + - name: authorAffiliations + displayName: Author affiliations + type: string + header: Authors + substringSearch: true + orderOnDetailsPage: 820 + includeInDownloadsByDefault: true + - name: ncbiSubmitterCountry + displayName: NCBI submitter country + type: string + header: INSDC + generateIndex: true + autocomplete: true + - name: bioprojectAccession + displayName: BioProject accession + type: string + header: INSDC + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/bioproject/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/bioproject/__value__ + orderOnDetailsPage: 1060 + - name: gcaAccession + displayName: GCA accession + type: string + header: INSDC + customDisplay: + type: link + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + orderOnDetailsPage: 1100 + - name: biosampleAccession + displayName: BioSample accession + type: string + header: INSDC + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/biosample/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/biosample/__value__ + orderOnDetailsPage: 1080 + - name: cultureId + displayName: Culture ID + type: string + header: Sample details + orderOnDetailsPage: 620 + - name: sampleReceivedDate + displayName: Sample received date + type: date + header: Sample details + orderOnDetailsPage: 260 + - name: sampleType + displayName: Sample type + type: string + header: Sampling + orderOnDetailsPage: 1200 + - name: purposeOfSampling + displayName: Purpose of sampling + type: string + header: Sampling + orderOnDetailsPage: 1220 + - name: presamplingActivity + displayName: Presampling activity + type: string + header: Sampling + orderOnDetailsPage: 1240 + - name: anatomicalMaterial + displayName: Anatomical material + type: string + header: Sampling + orderOnDetailsPage: 1260 + - name: anatomicalPart + displayName: Anatomical part + type: string + header: Sampling + orderOnDetailsPage: 1280 + - name: bodyProduct + displayName: Body product + type: string + header: Sampling + orderOnDetailsPage: 1300 + - name: environmentalMaterial + displayName: Environmental material + type: string + header: Sampling + orderOnDetailsPage: 1320 + - name: environmentalSite + displayName: Environmental site + type: string + header: Sampling + orderOnDetailsPage: 1340 + - name: collectionDevice + displayName: Collection device + type: string + header: Sampling + orderOnDetailsPage: 1360 + - name: collectionMethod + displayName: Collection method + type: string + header: Sampling + orderOnDetailsPage: 1380 + - name: foodProduct + displayName: Food product + type: string + header: Sampling + orderOnDetailsPage: 1400 + - name: foodProductProperties + displayName: Food product properties + type: string + header: Sampling + orderOnDetailsPage: 1420 + - name: specimenProcessing + displayName: Specimen processing + type: string + header: Specimen processing + orderOnDetailsPage: 1500 + - name: specimenProcessingDetails + displayName: Specimen processing details + type: string + header: Specimen processing + orderOnDetailsPage: 1520 + - name: experimentalSpecimenRoleType + displayName: Experimental specimen role type + type: string + header: Specimen processing + - name: hostAge + displayName: Host age + type: int + header: Host + rangeSearch: true + - name: hostAgeBin + displayName: Host age bin + type: string + header: Host + - name: hostGender + displayName: Host gender + type: string + header: Host + orderOnDetailsPage: 1640 + - name: hostOriginCountry + displayName: Host origin country + type: string + header: Host + - name: hostDisease + displayName: Host disease + type: string + header: Host + - name: signsAndSymptoms + displayName: Signs and symptoms + type: string + header: Host + - name: hostHealthState + displayName: Host health state + type: string + header: Host + - name: hostHealthOutcome + displayName: Host health outcome + type: string + header: Host + orderOnDetailsPage: 1740 + - name: travelHistory + displayName: Travel history + type: string + header: Host + orderOnDetailsPage: 1760 + - name: exposureEvent + displayName: Exposure event + type: string + header: Host + orderOnDetailsPage: 1780 + - name: hostRole + displayName: Host role + type: string + header: Host + orderOnDetailsPage: 1800 + - name: exposureSetting + displayName: Exposure setting + type: string + header: Host + orderOnDetailsPage: 1820 + - name: exposureDetails + displayName: Exposure details + type: string + header: Host + orderOnDetailsPage: 1840 + - name: previousInfectionDisease + displayName: Previous infection (disease) + type: string + header: Host + orderOnDetailsPage: 1860 + - name: previousInfectionOrganism + displayName: Previous infection (organism) + type: string + header: Host + orderOnDetailsPage: 1880 + - name: hostVaccinationStatus + displayName: Host vaccination status + type: string + header: Host + orderOnDetailsPage: 1900 + - name: purposeOfSequencing + displayName: Purpose of sequencing + type: string + header: Sequencing + orderOnDetailsPage: 2000 + - name: sequencingDate + displayName: Sequencing date + type: date + header: Sequencing + orderOnDetailsPage: 2020 + - name: ampliconPcrPrimerScheme + displayName: Amplicon PCR primer scheme + type: string + header: Sequencing + orderOnDetailsPage: 2040 + - name: ampliconSize + displayName: Amplicon size + type: string + header: Sequencing + orderOnDetailsPage: 2060 + - name: sequencingInstrument + displayName: Sequencing instrument + type: string + header: Sequencing + orderOnDetailsPage: 2080 + - name: sequencingProtocol + displayName: Sequencing protocol + type: string + header: Sequencing + orderOnDetailsPage: 2100 + - name: sequencingAssayType + displayName: Sequencing assay type + type: string + header: Sequencing + orderOnDetailsPage: 2120 + - name: sequencedByOrganization + displayName: Sequenced by + type: string + header: Sequencing + orderOnDetailsPage: 2140 + - name: sequencedByContactName + displayName: Sequenced by - contact name + type: string + header: Sequencing + orderOnDetailsPage: 2160 + - name: sequencedByContactEmail + displayName: Sequenced by - contact email + type: string + header: Sequencing + orderOnDetailsPage: 2180 + - name: rawSequenceDataProcessingMethod + displayName: Raw sequence data processing method + type: string + header: Sequencing + orderOnDetailsPage: 2200 + - name: dehostingMethod + displayName: Dehosting method + type: string + header: Sequencing + orderOnDetailsPage: 2220 + - name: referenceGenomeAccession + displayName: Reference genome accession + type: string + header: Sequencing + orderOnDetailsPage: 2240 + - name: consensusSequenceSoftwareName + displayName: Consensus sequence software name + type: string + header: Sequencing + orderOnDetailsPage: 2260 + - name: consensusSequenceSoftwareVersion + displayName: Consensus sequence software version + type: string + header: Sequencing + orderOnDetailsPage: 2280 + - name: depthOfCoverage + displayName: Depth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2300 + - name: breadthOfCoverage + displayName: Breadth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2320 + - name: qualityControlMethodName + displayName: Quality control method name + type: string + header: Sequencing + orderOnDetailsPage: 2340 + - name: qualityControlMethodVersion + displayName: Quality control method version + type: string + header: Sequencing + orderOnDetailsPage: 2360 + - name: qualityControlDetermination + displayName: Quality control determination + type: string + header: Sequencing + orderOnDetailsPage: 2380 + - name: qualityControlIssues + displayName: Quality control issues + type: string + header: Sequencing + orderOnDetailsPage: 2400 + - name: qualityControlDetails + displayName: Quality control details + type: string + header: Diagnostics + orderOnDetailsPage: 2500 + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + type: string + header: Diagnostics + orderOnDetailsPage: 2520 + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + type: string + header: Diagnostics + orderOnDetailsPage: 2540 + - name: diagnosticTargetGeneName + displayName: Gene name + type: string + header: Diagnostics + orderOnDetailsPage: 2560 + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + type: string + header: Diagnostics + orderOnDetailsPage: 2580 + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + type: string + header: Diagnostics + orderOnDetailsPage: 2600 + - name: hostTaxonId + displayName: Host species + type: string + header: Host + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + orderOnDetailsPage: 1540 + - name: hostNameScientific + displayName: Host name - scientific + type: string + header: Host + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + orderOnDetailsPage: 1560 + - name: hostNameCommon + displayName: Host name - common + type: string + header: Host + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + orderOnDetailsPage: 1580 + - name: isLabHost + displayName: Is lab host + type: boolean + header: Host + autocomplete: true + orderOnDetailsPage: 1920 + - name: cellLine + displayName: Cell line + type: string + header: Host + generateIndex: true + autocomplete: true + orderOnDetailsPage: 1940 + - name: passageNumber + displayName: Passage number + type: int + header: Host + orderOnDetailsPage: 1960 + - name: passageMethod + displayName: Passage method + type: string + header: Host + generateIndex: true + autocomplete: true + orderOnDetailsPage: 1980 + - name: ncbiSourceDb + displayName: NCBI source DB + type: string + header: INSDC + generateIndex: true + autocomplete: true + - name: ncbiVirusName + displayName: NCBI Virus name + type: string + header: INSDC + generateIndex: true + autocomplete: true + - name: ncbiVirusTaxId + displayName: NCBI Virus tax ID + type: int + header: INSDC + autocomplete: true + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/labs/virus/vssi/#/virus?SeqType_s=Nucleotide&VirusLineage_ss=taxid:__value__ + - name: insdcRawReadsAccession + displayName: Raw reads accession + type: string + header: INSDC + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/sra/?term=__value__ + orderOnDetailsPage: 1120 + - name: versionComment + displayName: versionComment + type: string + header: Sample details + - name: organism + displayName: Organism + type: string + generateIndex: true + autocomplete: true + header: Sample details + initiallyVisible: true + includeInDownloadsByDefault: true + primaryKey: accessionVersion + defaultOrderBy: sampleCollectionDate + defaultOrder: descending + features: + - name: generalizedAdvancedQuery + tableColumns: + - organism + - geoLocCountry + - sampleCollectionDate + - displayName + - authors + - ncbiReleaseDate + co-infections: + displayName: Co-infections + lapisUrl: "http://loculus-lapis-service-co-infections:8080" + sequenceData: + unalignedNucleotideSequences: + enabled: true + segments: + - main + - L + - M + - S + query: | + with real_organisms as ( + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, + authors, authorAffiliations, + sequencedByOrganization, + hostNameScientific, hostTaxonId, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + versionComment, + 'CCHF (Multi-Ref)' as organism + from "cchf-multi-ref" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, + authors, authorAffiliations, + sequencedByOrganization, + hostNameScientific, hostTaxonId, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + versionComment, + 'Crimean-Congo Hemorrhagic Fever Virus' as organism + from "cchf" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, + authors, authorAffiliations, + sequencedByOrganization, + hostNameScientific, hostTaxonId, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + versionComment, + 'Ebola Sudan' as organism + from "ebola-sudan" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, + authors, authorAffiliations, + sequencedByOrganization, + hostNameScientific, hostTaxonId, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + versionComment, + 'Enterovirus' as organism + from "enteroviruses" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + sampleCollectionDate, sampleCollectionDateRangeLower, sampleCollectionDateRangeUpper, + displayName, ncbiReleaseDate, earliestReleaseDate, + geoLocLatitude, geoLocLongitude, geoLocCountry, + geoLocAdmin1, geoLocAdmin2, geoLocCity, + geoLocSite, specimenCollectorSampleId, + authors, authorAffiliations, + sequencedByOrganization, + hostNameScientific, hostTaxonId, + ncbiSourceDb, ncbiVirusName, ncbiVirusTaxId, + versionComment, + 'West Nile Virus' as organism + from "west-nile" + ), + co_infection_samples as ( + select groupId, specimenCollectorSampleId + from real_organisms + where specimenCollectorSampleId is not null + and trim(specimenCollectorSampleId) <> '' + group by groupId, specimenCollectorSampleId + having count(distinct organism) >= 2 + ) + select real_organisms.* + from real_organisms + join co_infection_samples using (groupId, specimenCollectorSampleId) + schema: | + schema: + instanceName: Co-infections + opennessLevel: OPEN + metadata: + - name: accessionVersion + displayName: Accession version + type: string + header: Loculus + - name: accession + displayName: Accession + type: string + header: Loculus + - name: version + displayName: Version + type: int + header: Loculus + - name: submissionId + displayName: Submission ID + type: string + header: Submission details + substringSearch: true + includeInDownloadsByDefault: true + - name: isRevocation + displayName: Is revocation + type: boolean + autocomplete: true + header: Submission details + - name: submitter + displayName: Submitter + type: string + generateIndex: true + autocomplete: true + header: Submission details + - name: groupName + displayName: Submitting group + type: string + generateIndex: true + autocomplete: true + header: Submission details + includeInDownloadsByDefault: true + - name: groupId + displayName: Submitting group (numeric ID) + type: int + autocomplete: true + header: Submission details + - name: submittedAtTimestamp + displayName: Date submitted + type: timestamp + header: Submission details + - name: submittedDate + displayName: Date submitted (exact) + type: string + generateIndex: true + autocomplete: true + header: Submission details + - name: releasedAtTimestamp + displayName: Date released + type: timestamp + header: Submission details + - name: releasedDate + displayName: Date released (exact) + type: string + generateIndex: true + autocomplete: true + header: Submission details + - name: versionStatus + displayName: Version status + type: string + generateIndex: true + autocomplete: true + header: Submission details + - name: pipelineVersion + displayName: Pipeline version + type: int + header: Loculus + - name: dataUseTerms + displayName: Data use terms + type: string + header: Data use terms + generateIndex: true + autocomplete: true + - name: dataUseTermsRestrictedUntil + displayName: Restricted until + type: date + header: Data use terms + - name: dataBecameOpenAt + displayName: Data became open + type: date + header: Data use terms + - name: dataUseTermsUrl + displayName: Data use terms URL + type: string + header: Data use terms + - name: sampleCollectionDate + displayName: Collection date + type: string + header: Sample details + rangeSearch: true + includeInDownloadsByDefault: true + initiallyVisible: true + - name: sampleCollectionDateRangeLower + displayName: Collection date (lower bound) + type: date + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: lower + - name: sampleCollectionDateRangeUpper + displayName: Collection date (upper bound) + type: date + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: upper + - name: displayName + displayName: Display name + type: string + header: Sample details + initiallyVisible: true + - name: ncbiReleaseDate + displayName: NCBI release date + type: date + header: INSDC + initiallyVisible: true + - name: earliestReleaseDate + displayName: Earliest release date + type: date + header: Sample details + rangeSearch: true + - name: geoLocLatitude + displayName: Latitude + type: float + header: Sample details + - name: geoLocLongitude + displayName: Longitude + type: float + header: Sample details + - name: geoLocCountry + displayName: Collection country + type: string + header: Sample details + generateIndex: true + autocomplete: true + initiallyVisible: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + type: string + header: Sample details + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + type: string + header: Sample details + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + - name: geoLocCity + displayName: Collection city + type: string + header: Sample details + generateIndex: true + autocomplete: true + - name: geoLocSite + displayName: Collection site + type: string + header: Sample details + - name: specimenCollectorSampleId + displayName: Isolate name + type: string + header: Sample details + substringSearch: true + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + - name: authors + displayName: Authors + type: authors + header: Authors + substringSearch: true + initiallyVisible: true + includeInDownloadsByDefault: true + - name: authorAffiliations + displayName: Author affiliations + type: string + header: Authors + substringSearch: true + - name: sequencedByOrganization + displayName: Sequenced by + type: string + header: Sequencing + generateIndex: true + autocomplete: true + - name: hostNameScientific + displayName: Host name - scientific + type: string + header: Host + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + - name: hostTaxonId + displayName: Host species + type: string + header: Host + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + - name: ncbiSourceDb + displayName: NCBI source DB + type: string + header: INSDC + generateIndex: true + autocomplete: true + - name: ncbiVirusName + displayName: NCBI Virus name + type: string + header: INSDC + generateIndex: true + autocomplete: true + - name: ncbiVirusTaxId + displayName: NCBI Virus tax ID + type: int + header: INSDC + autocomplete: true + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/labs/virus/vssi/#/virus?SeqType_s=Nucleotide&VirusLineage_ss=taxid:__value__ + - name: versionComment + displayName: versionComment + type: string + header: Sample details + - name: organism + displayName: Organism + type: string + generateIndex: true + autocomplete: true + header: Sample details + initiallyVisible: true + includeInDownloadsByDefault: true + primaryKey: accessionVersion + defaultOrderBy: sampleCollectionDate + defaultOrder: descending + features: + - name: generalizedAdvancedQuery + tableColumns: + - organism + - specimenCollectorSampleId + - geoLocCountry + - sampleCollectionDate + - displayName + - authors + - ncbiReleaseDate + test-organisms: + displayName: Test organisms + lapisUrl: "http://loculus-lapis-service-test-organisms:8080" + sequenceData: + unalignedNucleotideSequences: + enabled: true + segments: + - main + query: | + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + date, region, country, + division, host, lineage, + versionComment, + 'Test organism (with files)' as organism + from "dummy-organism-with-files" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + date, region, country, + division, host, lineage, + versionComment, + 'Test Dummy Organism' as organism + from "dummy-organism" + union all + select + accessionVersion, accession, version, + submissionId, isRevocation, submitter, + groupName, groupId, submittedAtTimestamp, + submittedDate, releasedAtTimestamp, releasedDate, + versionStatus, pipelineVersion, dataUseTerms, + dataUseTermsRestrictedUntil, dataBecameOpenAt, dataUseTermsUrl, + date, region, country, + division, host, lineage, + versionComment, + 'Test organism (without alignment)' as organism + from "not-aligned-organism" + schema: | + schema: + instanceName: Test organisms + opennessLevel: OPEN + metadata: + - name: accessionVersion + displayName: Accession version + type: string + header: Loculus + - name: accession + displayName: Accession + type: string + header: Loculus + - name: version + displayName: Version + type: int + header: Loculus + - name: submissionId + displayName: Submission ID + type: string + header: Submission details + substringSearch: true + - name: isRevocation + displayName: Is revocation + type: boolean + header: Loculus + autocomplete: true + - name: submitter + displayName: Submitter + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: groupName + displayName: Submitting group + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: groupId + displayName: Submitting group (numeric ID) + type: int + header: Submission details + - name: submittedAtTimestamp + displayName: Date submitted + type: timestamp + header: Submission details + - name: submittedDate + displayName: Date submitted (exact) + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: releasedAtTimestamp + displayName: Date released + type: timestamp + header: Submission details + - name: releasedDate + displayName: Date released (exact) + type: string + header: Submission details + generateIndex: true + autocomplete: true + - name: versionStatus + displayName: Version status + type: string + header: Loculus + generateIndex: true + autocomplete: true + - name: pipelineVersion + displayName: Pipeline version + type: int + header: Loculus + - name: dataUseTerms + displayName: Data use terms + type: string + header: Data use terms + generateIndex: true + autocomplete: true + - name: dataUseTermsRestrictedUntil + displayName: Data use terms restricted until + type: date + header: Data use terms + - name: dataBecameOpenAt + displayName: Date data became open + type: date + header: Data use terms + - name: dataUseTermsUrl + displayName: Data use terms URL + type: string + header: Data use terms + - name: date + displayName: Date + type: date + header: Collection Details + initiallyVisible: true + - name: region + displayName: Region + type: string + header: Collection Details + generateIndex: true + autocomplete: true + initiallyVisible: true + - name: country + displayName: Country + type: string + header: Collection Details + generateIndex: true + autocomplete: true + initiallyVisible: true + - name: division + displayName: Division + type: string + header: Collection Details + generateIndex: true + autocomplete: true + initiallyVisible: true + - name: host + displayName: Host + type: string + header: Collection Details + autocomplete: true + initiallyVisible: true + - name: lineage + displayName: Lineage + type: string + header: Sample details + initiallyVisible: true + - name: versionComment + displayName: versionComment + type: string + header: Sample details + - name: organism + displayName: Organism + type: string + generateIndex: true + autocomplete: true + header: Sample details + initiallyVisible: true + includeInDownloadsByDefault: true + primaryKey: accessionVersion + defaultOrderBy: date + defaultOrder: descending + features: + - name: generalizedAdvancedQuery + tableColumns: + - organism + - country + - date + - region + - division + - host + - lineage diff --git a/kubernetes/loculus/fixtures/organisms/cchf-multi-ref.yaml b/kubernetes/loculus/fixtures/organisms/cchf-multi-ref.yaml new file mode 100644 index 0000000000..8b1c9c74d8 --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/cchf-multi-ref.yaml @@ -0,0 +1,1692 @@ +schema: + referenceIdentifierField: reference + submissionDataTypes: + consensusSequences: true + maxSequencesPerEntry: 3 + loadSequencesAutomatically: true + earliestReleaseDate: + enabled: true + externalFields: + - ncbiReleaseDate + richFastaHeaderFields: + - displayName + files: + - name: annotations + displayName: Annotations + metadata: + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + header: Sample details + ingest: ncbiCollectionDate + order: 10 + orderOnDetailsPage: 200 + includeInDownloadsByDefault: true + notSearchable: true + columnWidth: 100 + type: string + - name: sampleCollectionDateRangeLower + displayName: Collection date (lower bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: lower + noInput: true + - name: sampleCollectionDateRangeUpper + displayName: Collection date (upper bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: upper + noInput: true + - name: displayName + displayName: Display name + definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{accessionVersion}/{sampleCollectionDate}`. + noInput: true + type: string + - name: ncbiReleaseDate + displayName: NCBI release date + definition: Date the viral nucleotide accession was first released on the INSDC. + type: date + header: INSDC + orderOnDetailsPage: 1000 + noInput: true + columnWidth: 100 + - name: earliestReleaseDate + displayName: Earliest release date + definition: The earliest release date for the accession, across all versions in both Loculus and the INSDC. + header: Sample details + type: date + rangeSearch: true + noInput: true + includeInDownloadsByDefault: true + orderOnDetailsPage: 300 + - name: ncbiUpdateDate + type: date + displayName: NCBI update date + definition: Date the viral nucleotide accession was last updated on the INSDC. + header: INSDC + orderOnDetailsPage: 1020 + noInput: true + perSegment: true + oneHeader: true + columnWidth: 100 + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 420 + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 440 + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 20 + orderOnDetailsPage: 400 + ingest: country + options: &id001 + - name: Afghanistan + - name: Albania + - name: Algeria + - name: American Samoa + - name: Andorra + - name: Angola + - name: Anguilla + - name: Antarctica + - name: Antigua and Barbuda + - name: Arctic Ocean + - name: Argentina + - name: Armenia + - name: Aruba + - name: Ashmore and Cartier Islands + - name: Atlantic Ocean + - name: Australia + - name: Austria + - name: Azerbaijan + - name: Bahamas + - name: Bahrain + - name: Baltic Sea + - name: Baker Island + - name: Bangladesh + - name: Barbados + - name: Bassas da India + - name: Belarus + - name: Belgium + - name: Belize + - name: Benin + - name: Bermuda + - name: Bhutan + - name: Bolivia + - name: Borneo + - name: Bosnia and Herzegovina + - name: Botswana + - name: Bouvet Island + - name: Brazil + - name: British Virgin Islands + - name: Brunei + - name: Bulgaria + - name: Burkina Faso + - name: Burundi + - name: Cambodia + - name: Cameroon + - name: Canada + - name: Cape Verde + - name: Cayman Islands + - name: Central African Republic + - name: Chad + - name: Chile + - name: China + - name: Christmas Island + - name: Clipperton Island + - name: Cocos Islands + - name: Colombia + - name: Comoros + - name: Cook Islands + - name: Coral Sea Islands + - name: Costa Rica + - name: Cote d'Ivoire + - name: Croatia + - name: Cuba + - name: Curacao + - name: Cyprus + - name: Czechia + - name: Democratic Republic of the Congo + - name: Denmark + - name: Djibouti + - name: Dominica + - name: Dominican Republic + - name: Ecuador + - name: Egypt + - name: El Salvador + - name: Equatorial Guinea + - name: Eritrea + - name: Estonia + - name: Eswatini + - name: Ethiopia + - name: Europa Island + - name: Falkland Islands (Islas Malvinas) + - name: Faroe Islands + - name: Fiji + - name: Finland + - name: France + - name: French Guiana + - name: French Polynesia + - name: French Southern and Antarctic Lands + - name: Gabon + - name: Gambia + - name: Gaza Strip + - name: Georgia + - name: Germany + - name: Ghana + - name: Gibraltar + - name: Glorioso Islands + - name: Greece + - name: Greenland + - name: Grenada + - name: Guadeloupe + - name: Guam + - name: Guatemala + - name: Guernsey + - name: Guinea + - name: Guinea-Bissau + - name: Guyana + - name: Haiti + - name: Heard Island and McDonald Islands + - name: Honduras + - name: Hong Kong + - name: Howland Island + - name: Hungary + - name: Iceland + - name: India + - name: Indian Ocean + - name: Indonesia + - name: Iran + - name: Iraq + - name: Ireland + - name: Isle of Man + - name: Israel + - name: Italy + - name: Jamaica + - name: Jan Mayen + - name: Japan + - name: Jarvis Island + - name: Jersey + - name: Johnston Atoll + - name: Jordan + - name: Juan de Nova Island + - name: Kazakhstan + - name: Kenya + - name: Kerguelen Archipelago + - name: Kingman Reef + - name: Kiribati + - name: Kosovo + - name: Kuwait + - name: Kyrgyzstan + - name: Laos + - name: Latvia + - name: Lebanon + - name: Lesotho + - name: Liberia + - name: Libya + - name: Liechtenstein + - name: Line Islands + - name: Lithuania + - name: Luxembourg + - name: Macau + - name: Madagascar + - name: Malawi + - name: Malaysia + - name: Maldives + - name: Mali + - name: Malta + - name: Marshall Islands + - name: Martinique + - name: Mauritania + - name: Mauritius + - name: Mayotte + - name: Mediterranean Sea + - name: Mexico + - name: Micronesia, Federated States of + - name: Midway Islands + - name: Moldova + - name: Monaco + - name: Mongolia + - name: Montenegro + - name: Montserrat + - name: Morocco + - name: Mozambique + - name: Myanmar + - name: Namibia + - name: Nauru + - name: Navassa Island + - name: Nepal + - name: Netherlands + - name: New Caledonia + - name: New Zealand + - name: Nicaragua + - name: Niger + - name: Nigeria + - name: Niue + - name: Norfolk Island + - name: North Korea + - name: North Macedonia + - name: North Sea + - name: Northern Mariana Islands + - name: Norway + - name: Oman + - name: Pacific Ocean + - name: Pakistan + - name: Palau + - name: Palmyra Atoll + - name: Panama + - name: Papua New Guinea + - name: Paracel Islands + - name: Paraguay + - name: Peru + - name: Philippines + - name: Pitcairn Islands + - name: Poland + - name: Portugal + - name: Puerto Rico + - name: Qatar + - name: Republic of the Congo + - name: Reunion + - name: Romania + - name: Ross Sea + - name: Russia + - name: Rwanda + - name: Saint Barthelemy + - name: Saint Helena + - name: Saint Kitts and Nevis + - name: Saint Lucia + - name: Saint Martin + - name: Saint Pierre and Miquelon + - name: Saint Vincent and the Grenadines + - name: Samoa + - name: San Marino + - name: Sao Tome and Principe + - name: Saudi Arabia + - name: Senegal + - name: Serbia + - name: Seychelles + - name: Sierra Leone + - name: Singapore + - name: Sint Maarten + - name: Slovakia + - name: Slovenia + - name: Solomon Islands + - name: Somalia + - name: South Africa + - name: South Georgia and the South Sandwich Islands + - name: South Korea + - name: South Sudan + - name: Southern Ocean + - name: Spain + - name: Spratly Islands + - name: Sri Lanka + - name: State of Palestine + - name: Sudan + - name: Suriname + - name: Svalbard + - name: Sweden + - name: Switzerland + - name: Syria + - name: Taiwan + - name: Tajikistan + - name: Tanzania + - name: Tasman Sea + - name: Thailand + - name: Timor-Leste + - name: Togo + - name: Tokelau + - name: Tonga + - name: Trinidad and Tobago + - name: Tromelin Island + - name: Tunisia + - name: Turkey + - name: Turkmenistan + - name: Turks and Caicos Islands + - name: Tuvalu + - name: Uganda + - name: Ukraine + - name: United Arab Emirates + - name: United Kingdom + - name: Uruguay + - name: USA + - name: Uzbekistan + - name: Vanuatu + - name: Venezuela + - name: Viet Nam + - name: Virgin Islands + - name: Wake Island + - name: Wallis and Futuna + - name: West Bank + - name: Western Sahara + - name: Yemen + - name: Zambia + - name: Zimbabwe + - name: Belgian Congo + - name: British Guiana + - name: Burma + - name: Czechoslovakia + - name: Czech Republic + - name: East Timor + - name: Korea + - name: Macedonia + - name: Micronesia + - name: Netherlands Antilles + - name: Serbia and Montenegro + - name: Siam + - name: Swaziland + - name: The former Yugoslav Republic of Macedonia + - name: USSR + - name: Yugoslavia + - name: Zaire + type: string + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + generateIndex: true + autocomplete: true + initiallyVisible: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 30 + orderOnDetailsPage: 460 + ingest: division + type: string + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + orderOnDetailsPage: 480 + type: string + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + generateIndex: true + autocomplete: true + header: Sample details + orderOnDetailsPage: 500 + type: string + - name: geoLocSite + ontology_id: GENEPIO:0100436 + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + header: Sample details + orderOnDetailsPage: 520 + type: string + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + header: Sample details + ingest: ncbiIsolateName + substringSearch: true + orderOnDetailsPage: 600 + type: string + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + type: authors + header: Authors + substringSearch: true + order: 40 + orderOnDetailsPage: 800 + includeInDownloadsByDefault: true + ingest: ncbiSubmitterNames + columnWidth: 140 + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + substringSearch: true + header: Authors + ingest: ncbiSubmitterAffiliation + includeInDownloadsByDefault: true + orderOnDetailsPage: 820 + type: string + - name: ncbiSubmitterCountry + displayName: NCBI submitter country + definition: The country representing the submitter's affilation. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: insdcAccessionBase + displayName: INSDC accession base + definition: The base accession assigned by the INSDC (GenBank/ENA/DDBJ) for this sequence, without the version number. + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + type: string + - name: insdcVersion + displayName: INSDC version + definition: The version number of the INSDC record, incremented each time the sequence record is updated. + type: int + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + - name: insdcAccessionFull + displayName: INSDC accession + definition: The full versioned INSDC accession (base accession plus version number), searchable across GenBank, ENA, and DDBJ. + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/nuccore/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://getentry.ddbj.nig.ac.jp/getentry/na/__value__ + header: INSDC + orderOnDetailsPage: 1040 + ingest: genbankAccession + noInput: true + perSegment: true + oneHeader: true + type: string + - name: bioprojectAccession + displayName: BioProject accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/bioproject/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/bioproject/__value__ + header: INSDC + orderOnDetailsPage: 1060 + ingest: bioprojects + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + type: string + - name: gcaAccession + displayName: GCA accession + definition: The GenBank genome assembly accession number for the sequence. + customDisplay: + type: link + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + header: INSDC + orderOnDetailsPage: 1100 + noInput: true + oneHeader: true + type: string + - name: biosampleAccession + displayName: BioSample accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/biosample/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/biosample/__value__ + header: INSDC + orderOnDetailsPage: 1080 + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + oneHeader: true + type: string + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + header: Sample details + orderOnDetailsPage: 620 + type: string + - name: sampleReceivedDate + ontology_id: GENEPIO:0001177 + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + type: date + orderOnDetailsPage: 260 + header: Sample details + - name: sampleType + displayName: Sample type + definition: Method of sampling. + header: Sampling + orderOnDetailsPage: 1200 + type: string + - name: purposeOfSampling + displayName: Purpose of sampling + ontology_id: GENEPIO:0001198 + definition: The reason that the sample was collected. + header: Sampling + orderOnDetailsPage: 1220 + ingest: ncbiPurposeOfSampling + type: string + - name: presamplingActivity + ontology_id: GENEPIO:0100433 + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + header: Sampling + orderOnDetailsPage: 1240 + type: string + - name: anatomicalMaterial + ontology_id: GENEPIO:0001211 + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + header: Sampling + orderOnDetailsPage: 1260 + type: string + - name: anatomicalPart + ontology_id: GENEPIO:0001214 + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + header: Sampling + orderOnDetailsPage: 1280 + type: string + - name: bodyProduct + ontology_id: GENEPIO:0001216 + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + header: Sampling + orderOnDetailsPage: 1300 + type: string + - name: environmentalMaterial + ontology_id: GENEPIO:0001223 + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + header: Sampling + orderOnDetailsPage: 1320 + type: string + - name: environmentalSite + ontology_id: GENEPIO:0001232 + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + header: Sampling + orderOnDetailsPage: 1340 + type: string + - name: collectionDevice + ontology_id: GENEPIO:0001234 + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + header: Sampling + orderOnDetailsPage: 1360 + type: string + - name: collectionMethod + ontology_id: GENEPIO:0001241 + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + header: Sampling + orderOnDetailsPage: 1380 + type: string + - name: foodProduct + ontology_id: GENEPIO:0100444 + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + header: Sampling + orderOnDetailsPage: 1400 + type: string + - name: foodProductProperties + ontology_id: GENEPIO:0100445 + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + header: Sampling + orderOnDetailsPage: 1420 + type: string + - name: specimenProcessing + ontology_id: GENEPIO:0100435 + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + header: Specimen processing + orderOnDetailsPage: 1500 + type: string + - name: specimenProcessingDetails + ontology_id: GENEPIO:0100311 + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + header: Specimen processing + orderOnDetailsPage: 1520 + type: string + - name: experimentalSpecimenRoleType + ontology_id: GENEPIO:0100921 + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + header: Specimen processing + type: string + - name: hostAge + ontology_id: GENEPIO:0001392 + definition: Age of host at the time of sampling. + displayName: Host age + type: int + header: Host + rangeSearch: true + - name: hostAgeBin + ontology_id: GENEPIO:0001394 + definition: The age category of the host at the time of sampling. + displayName: Host age bin + header: Host + type: string + - name: hostGender + ontology_id: GENEPIO:0001395 + definition: The gender of the host at the time of sample collection. + displayName: Host gender + header: Host + ingest: ncbiHostSex + orderOnDetailsPage: 1640 + type: string + - name: hostOriginCountry + ontology_id: GENEPIO:0100438 + definition: The country of origin of the host. + displayName: Host origin country + header: Host + type: string + - name: hostDisease + ontology_id: GENEPIO:0001391 + definition: The name of the disease experienced by the host. + displayName: Host disease + header: Host + type: string + - name: signsAndSymptoms + ontology_id: GENEPIO:0001400 + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + header: Host + type: string + - name: hostHealthState + ontology_id: GENEPIO:0001388 + definition: Health status of the host at the time of sample collection. + displayName: Host health state + header: Host + type: string + - name: hostHealthOutcome + ontology_id: GENEPIO:0001390 + definition: Disease outcome in the host. + displayName: Host health outcome + header: Host + orderOnDetailsPage: 1740 + type: string + - name: travelHistory + ontology_id: GENEPIO:0001416 + definition: Travel history in last six months. + displayName: Travel history + header: Host + orderOnDetailsPage: 1760 + type: string + - name: exposureEvent + ontology_id: GENEPIO:0001417 + definition: Event leading to exposure. + displayName: Exposure event + header: Host + orderOnDetailsPage: 1780 + type: string + - name: hostRole + ontology_id: GENEPIO:0001419 + definition: The role of the host in relation to the exposure setting. + displayName: Host role + header: Host + orderOnDetailsPage: 1800 + type: string + - name: exposureSetting + ontology_id: GENEPIO:0001428 + definition: The setting leading to exposure. + displayName: Exposure setting + header: Host + orderOnDetailsPage: 1820 + type: string + - name: exposureDetails + ontology_id: GENEPIO:0001431 + definition: Additional host exposure information. + displayName: Exposure details + header: Host + orderOnDetailsPage: 1840 + type: string + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + header: Host + orderOnDetailsPage: 1860 + type: string + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + header: Host + orderOnDetailsPage: 1880 + type: string + - name: hostVaccinationStatus + ontology_id: GENEPIO:0001404 + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + header: Host + orderOnDetailsPage: 1900 + type: string + - name: purposeOfSequencing + ontology_id: GENEPIO:0001445 + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + header: Sequencing + orderOnDetailsPage: 2000 + type: string + - name: sequencingDate + ontology_id: GENEPIO:0001447 + definition: The date the sample was sequenced. + displayName: Sequencing date + type: date + orderOnDetailsPage: 2020 + header: Sequencing + - name: ampliconPcrPrimerScheme + ontology_id: GENEPIO:0001456 + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + header: Sequencing + orderOnDetailsPage: 2040 + type: string + - name: ampliconSize + ontology_id: GENEPIO:0001449 + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + header: Sequencing + orderOnDetailsPage: 2060 + type: string + - name: sequencingInstrument + ontology_id: GENEPIO:0001452 + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + header: Sequencing + orderOnDetailsPage: 2080 + type: string + - name: sequencingProtocol + ontology_id: GENEPIO:0001454 + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + header: Sequencing + orderOnDetailsPage: 2100 + type: string + - name: sequencingAssayType + ontology_id: GENEPIO:0100997 + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + header: Sequencing + orderOnDetailsPage: 2120 + type: string + - name: sequencedByOrganization + ontology_id: GENEPIO:0100416 + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + header: Sequencing + orderOnDetailsPage: 2140 + type: string + - name: sequencedByContactName + ontology_id: GENEPIO:0100471 + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + header: Sequencing + orderOnDetailsPage: 2160 + type: string + - name: sequencedByContactEmail + ontology_id: GENEPIO:0100422 + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + header: Sequencing + orderOnDetailsPage: 2180 + type: string + - name: rawSequenceDataProcessingMethod + ontology_id: GENEPIO:0001458 + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + header: Sequencing + orderOnDetailsPage: 2200 + type: string + - name: dehostingMethod + ontology_id: GENEPIO:0001459 + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + header: Sequencing + orderOnDetailsPage: 2220 + type: string + - name: referenceGenomeAccession + ontology_id: GENEPIO:0001485 + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + header: Sequencing + orderOnDetailsPage: 2240 + type: string + - name: consensusSequenceSoftwareName + ontology_id: GENEPIO:0001463 + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + header: Sequencing + orderOnDetailsPage: 2260 + type: string + - name: consensusSequenceSoftwareVersion + ontology_id: GENEPIO:0001469 + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + header: Sequencing + orderOnDetailsPage: 2280 + type: string + - name: depthOfCoverage + ontology_id: GENEPIO:0001474 + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2300 + - name: breadthOfCoverage + ontology_id: GENEPIO:0001475 + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2320 + - name: qualityControlMethodName + ontology_id: GENEPIO:0100557 + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + header: Sequencing + orderOnDetailsPage: 2340 + type: string + - name: qualityControlMethodVersion + ontology_id: GENEPIO:0100558 + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + header: Sequencing + orderOnDetailsPage: 2360 + type: string + - name: qualityControlDetermination + ontology_id: GENEPIO:0100559 + definition: The determination of a quality control assessment. + displayName: Quality control determination + header: Sequencing + orderOnDetailsPage: 2380 + type: string + - name: qualityControlIssues + ontology_id: GENEPIO:0100560 + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + header: Sequencing + orderOnDetailsPage: 2400 + type: string + - name: qualityControlDetails + ontology_id: GENEPIO:0100561 + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + header: Diagnostics + orderOnDetailsPage: 2500 + type: string + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + header: Diagnostics + orderOnDetailsPage: 2520 + type: string + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + header: Diagnostics + orderOnDetailsPage: 2540 + type: string + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + header: Diagnostics + orderOnDetailsPage: 2560 + type: string + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + header: Diagnostics + orderOnDetailsPage: 2580 + type: string + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + orderOnDetailsPage: 2600 + header: Diagnostics + type: string + - name: length + type: int + header: Alignment and QC metrics + isSequenceFilter: true + noInput: true + rangeSearch: true + initiallyVisible: true + perSegment: true + displayName: Length + definition: The length of the sequence. + orderOnDetailsPage: 2700 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: hostTaxonId + type: string + autocomplete: true + displayName: Host species + definition: Taxon ID for the host. + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostTaxId + noInput: true + orderOnDetailsPage: 1540 + - name: hostNameScientific + displayName: Host name - scientific + definition: The scientific name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostName + noInput: true + orderOnDetailsPage: 1560 + type: string + - name: hostNameCommon + displayName: Host name - common + definition: The common name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostCommonName + noInput: true + orderOnDetailsPage: 1580 + type: string + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + type: boolean + autocomplete: true + header: Host + ingest: ncbiIsLabHost + orderOnDetailsPage: 1920 + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + generateIndex: true + orderOnDetailsPage: 1940 + autocomplete: true + header: Host + type: string + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + type: int + header: Host + orderOnDetailsPage: 1960 + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + generateIndex: true + autocomplete: true + header: Host + orderOnDetailsPage: 1980 + type: string + - name: ncbiSourceDb + displayName: NCBI source DB + definition: Indicates if the source of the viral nucleotide record is from a GenBank submitter or from NCBI-derived curation (RefSeq). + generateIndex: true + autocomplete: true + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + type: string + - name: ncbiVirusName + displayName: NCBI Virus name + definition: Scientific name for the organism from the NCBI Taxonomy. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: ncbiVirusTaxId + displayName: NCBI Virus tax ID + definition: Identifier for the organism from the NCBI Taxonomy. + type: int + autocomplete: true + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/labs/virus/vssi/#/virus?SeqType_s=Nucleotide&VirusLineage_ss=taxid:__value__ + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/sra/?term=__value__ + header: INSDC + orderOnDetailsPage: 1120 + ingest: ncbiSraAccessions + type: string + - name: totalSnps + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total SNPs + definition: Total count of nucleotide substitutions. + orderOnDetailsPage: 2720 + - name: totalInsertedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total inserted nucs + definition: Total count of inserted nucleotide positions. + orderOnDetailsPage: 2740 + - name: totalDeletedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + orderOnDetailsPage: 2760 + displayName: Total deleted nucs + definition: Total count of deleted nucleotide positions. + - name: totalAmbiguousNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total ambiguous nucs + definition: Total count of ambiguous nucleotides (not A, C, G, T, or N). + orderOnDetailsPage: 2780 + - name: totalUnknownNucs + orderOnDetailsPage: 2800 + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total unknown nucs + definition: Total count of missing (N) nucleotides. + - name: totalFrameShifts + isSequenceFilter: true + type: int + rangeSearch: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Total frame shifts + definition: Total count of detected frame shifts. + orderOnDetailsPage: 2820 + - name: frameShifts + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Frame shifts + definition: Frame-shifting insertions or deletions detected in CDS regions. + orderOnDetailsPage: 2840 + type: string + - name: totalStopCodons + isSequenceFilter: true + perSegment: true + displayName: Total stop codons + definition: Number of penalized premature stop codons. + type: int + header: Alignment and QC metrics + noInput: true + - name: stopCodons + isSequenceFilter: true + perSegment: true + displayName: Stop codons + definition: Premature stop codons not in the ignored list (penalized). + header: Alignment and QC metrics + noInput: true + type: string + - name: completeness + isSequenceFilter: true + type: float + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Completeness + definition: Fraction of reference positions covered by the sequence (0.0 to 1.0). + rangeSearch: true + percentage: true + orderOnDetailsPage: 2860 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: reference_L + type: string + - name: reference_M + type: string + - name: reference_S + type: string + - name: variant_L + type: boolean + - name: variant_M + type: boolean + - name: variant_S + type: boolean + - name: versionComment + type: string + extraInputFields: + - name: host + type: string + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + desired: true + position: last + organismName: CCHF (Multi-Ref) + image: /images/organisms/cchf_small.jpg + linkOuts: + - name: Nextclade (L) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:L+rich|fasta]}}&dataset-name=community/pathoplexus/cchfv/L + onlyForReferences: + L: singleReference + category: L + - name: Nextclade (M MH396653) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:M+rich|fasta]}}&dataset-name=cchfv/M-MH396653&dataset-server=https://raw.githubusercontent.com/genspectrum/nextclade-datasets/cchf-multi/data + onlyForReferences: + M: MH396653 + category: M + - name: Nextclade (M OR047158) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:M+rich|fasta]}}&dataset-name=cchfv/M-OR047158&dataset-server=https://raw.githubusercontent.com/genspectrum/nextclade-datasets/cchf-multi/data + onlyForReferences: + M: OR047158 + category: M + - name: Nextclade (S 1and6) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:S+rich|fasta]}}&dataset-name=cchfv/S-1and6&dataset-server=https://raw.githubusercontent.com/genspectrum/nextclade-datasets/cchf-multi/data + onlyForReferences: + S: 1and6 + category: S + - name: Nextclade (S 2to5) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:S+rich|fasta]}}&dataset-name=cchfv/S-2to5&dataset-server=https://raw.githubusercontent.com/genspectrum/nextclade-datasets/cchf-multi/data + onlyForReferences: + S: 2to5 + category: S + metadataAdd: + - name: hostNameScientific + displayName: Host name - scientific + definition: The scientific name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostName + initiallyVisible: true + - name: variant_L + isSequenceFilter: true + header: Clade & Lineage + oneHeader: true + displayName: Variant L + definition: Indicates whether the segment is sufficiently divergent from its closest reference to be classified as a distinct variant, based on whether the number of nucleotide substitutions exceeds a threshold relative to segment length. + relatesToSegment: L + type: boolean + noInput: true + autocomplete: true + initiallyVisible: false + includeInDownloadsByDefault: false + customDisplay: + type: variantReference + displayGroup: reference_L + label: Closest reference L + - name: variant_M + isSequenceFilter: true + header: Clade & Lineage + oneHeader: true + displayName: Variant M + definition: Indicates whether the segment is sufficiently divergent from its closest reference to be classified as a distinct variant, based on whether the number of nucleotide substitutions exceeds a threshold relative to segment length. + relatesToSegment: M + type: boolean + noInput: true + autocomplete: true + initiallyVisible: false + includeInDownloadsByDefault: false + customDisplay: + type: variantReference + displayGroup: reference_M + label: Closest reference M + - name: variant_S + isSequenceFilter: true + header: Clade & Lineage + oneHeader: true + displayName: Variant S + definition: Indicates whether the segment is sufficiently divergent from its closest reference to be classified as a distinct variant, based on whether the number of nucleotide substitutions exceeds a threshold relative to segment length. + relatesToSegment: S + type: boolean + noInput: true + autocomplete: true + initiallyVisible: false + includeInDownloadsByDefault: false + customDisplay: + type: variantReference + displayGroup: reference_S + label: Closest reference S + - name: reference + oneHeader: true + header: Clade & Lineage + isSequenceFilter: true + displayName: Closest reference + definition: The reference genome this sequence was most closely assigned to according to Nextclade. + customDisplay: + type: variantReference + displayGroup: reference + label: Closest reference + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + perSegment: true + tableColumns: + - sampleCollectionDate + - geoLocCountry + - geoLocAdmin1 + - lineage + - authors + - authorAffiliations + - ncbiReleaseDate + - hostNameScientific + - length_M + - length_S + - length_L + defaultOrderBy: sampleCollectionDate + defaultOrder: descending + multiFieldSearches: + - name: identifier + displayName: Sample Identifier + orderInSearchDisplay: -10000 + fields: + - accessionVersion + - submissionId + - insdcAccessionFull + - bioprojectAccession + - gcaAccession + - biosampleAccession + - insdcRawReadsAccession + - name: contributor + displayName: Contributor + orderInSearchDisplay: -9999 + fields: + - authors + - authorAffiliations + - sequencedByOrganization + - sequencedByContactName + - submitter + - groupName + inputFields: + - name: id + displayName: ID + example: GJP123 + noEdit: true + required: true + definition: Your sample identifier. (If no `fastaIds` column is provided, this sample ID will be used to associate the metadata with the sequence.) + - name: fastaIds + displayName: FASTA IDs + definition: Space-separated list of FASTA IDs of each sequence to be associated with this metadata entry. + example: GJP123 GJP124 + noEdit: true + desired: true + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + required: true + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + required: true + options: *id001 + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + - name: geoLocSite + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + - name: bioprojectAccession + displayName: BioProject accession + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + - name: biosampleAccession + displayName: BioSample accession + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + - name: sampleReceivedDate + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + - name: sampleType + displayName: Sample type + definition: Method of sampling. + - name: purposeOfSampling + displayName: Purpose of sampling + definition: The reason that the sample was collected. + - name: presamplingActivity + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + - name: anatomicalMaterial + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + - name: anatomicalPart + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + - name: bodyProduct + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + - name: environmentalMaterial + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + - name: environmentalSite + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + - name: collectionDevice + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + - name: collectionMethod + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + - name: foodProduct + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + - name: foodProductProperties + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + - name: specimenProcessing + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + - name: specimenProcessingDetails + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + - name: experimentalSpecimenRoleType + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + - name: hostAge + definition: Age of host at the time of sampling. + displayName: Host age + - name: hostAgeBin + definition: The age category of the host at the time of sampling. + displayName: Host age bin + - name: hostGender + definition: The gender of the host at the time of sample collection. + displayName: Host gender + - name: hostOriginCountry + definition: The country of origin of the host. + displayName: Host origin country + - name: hostDisease + definition: The name of the disease experienced by the host. + displayName: Host disease + - name: signsAndSymptoms + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + - name: hostHealthState + definition: Health status of the host at the time of sample collection. + displayName: Host health state + - name: hostHealthOutcome + definition: Disease outcome in the host. + displayName: Host health outcome + - name: travelHistory + definition: Travel history in last six months. + displayName: Travel history + - name: exposureEvent + definition: Event leading to exposure. + displayName: Exposure event + - name: hostRole + definition: The role of the host in relation to the exposure setting. + displayName: Host role + - name: exposureSetting + definition: The setting leading to exposure. + displayName: Exposure setting + - name: exposureDetails + definition: Additional host exposure information. + displayName: Exposure details + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + - name: hostVaccinationStatus + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + - name: purposeOfSequencing + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + - name: sequencingDate + definition: The date the sample was sequenced. + displayName: Sequencing date + - name: ampliconPcrPrimerScheme + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + - name: ampliconSize + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + - name: sequencingInstrument + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + - name: sequencingProtocol + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + - name: sequencingAssayType + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + - name: sequencedByOrganization + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + - name: sequencedByContactName + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + - name: sequencedByContactEmail + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + - name: rawSequenceDataProcessingMethod + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + - name: dehostingMethod + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + - name: referenceGenomeAccession + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + - name: consensusSequenceSoftwareName + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + - name: consensusSequenceSoftwareVersion + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + - name: depthOfCoverage + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + - name: breadthOfCoverage + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + - name: qualityControlMethodName + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + - name: qualityControlMethodVersion + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + - name: qualityControlDetermination + definition: The determination of a quality control assessment. + displayName: Quality control determination + - name: qualityControlIssues + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + - name: qualityControlDetails + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + - name: host + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + desired: true +referenceGenomes: + - name: L + displayName: L segment + references: + - name: singleReference + sequence: 'TCTCAAAGATATCAATCCCCCCGTTACCCACGTTAACACAGAGAGCTCTAGTAGTGGTCTTTCCTTTTGTGAAACCATGGACTTCTTGAGAAGCCTTGACTGGACTCAAGTGATTGCTGGTCAATATGTGTCCAACCCTAGGTTCAACATTTCTGATTATTTTGAGATTGTGCGGCAGCCTGGTGATGGGAACTGCTTCTATCACAGCATAGCTGAGTTAACCATGCCTAACAAAACAGATCACTCATATCATTACATCAAACGCCTAACCGAGTCGGCAGCACGGAAGTATTACCAAGAGGAGCCTGAAGCCAGACTTGTTGGCCTGAGCCTGGAAGATTACCTCAAGAGGATGCTGTCTGACAACGAGTGGGGATCAACTCTAGAAGCATCTATGTTGGCTAAAGAAATGGGCATTACCATCATCATTTGGACTGTTGCTGCCAGTGATGAAGTGGAAGCAGGTATAAAGTTCGGAGACGGTGATGTGTTTACAGCTGTGAACCTTTTGCACTCTGGACAAACACACTTTGATGCGCTCAGAATACTTCCGCAGTTTGAAACAGACACAAGAGAGGCCTTGAGCTTGATGGACAGGGTTATAGCTGTGGATCAGTTGACATCATCTTCTAGTGATGAACTGCAAGACTATGAAGACCTTGCCTTGGCACTCACAAGCGCAGAAGAATCAAATAGACGGTCAAGCTTGGATGAGGTCACATTGTCCAAGAAGCAAGCAGAGATACTAAGGCAAAAAGCATCTCAGTTGTCTAAATTGGTTAATAAAAGTCAGAACATACCGACCAGAGTCGGTAGAGTCTTGGATTGCATGTTCAACTGCAAATTATGTGTTGAGATATCAGCTGACACTTTAATTCTGAGGCCAGAATCAAAAGAGAAAATCGGTGAAATCATGTCATTGCGGCAGTTGGGGCATAAACTGCTGACACGAGACAAACAGATTAAGCAAGAGTTCTCCAGAATGAAACTCTACGTCACTAAAGATTTGCTTGACCATCTAGACGTTGGTGGGCTCTTGAGGGCTGCTTTCCCTGGAACAGGAATAGAAAGGCATATGCAGCTGCTACACTCTGAGATGATACTGGACATCTGCACTGTATCACTTGGTGTCATGCTGTCAACATTCTTATATGGTTCTAATAATAAAAACAAGAAGAAATTCATTACCAACTGTCTGCTCAGCACAGCCCTATCCGGAAAGAAGGTGTATAAAGTTCTCGGCAACCTAGGAAATGAACTGTTGTACAAGGCACCTAGAAAGGCCTTAGCAACTGTCTGCAGTGCCTTGTTTGGGAAGCAGATAAACAAACTTCAGAATTGCTTCAGGACCATAAGCCCTGTCAGCTTACTTGCATTGAGAAATCTAGACTTTGATTGTCTCAGTGTGCAAGACTATAATGGTATGATAGAAAACATGTCTAAATTAGACAACACTGATGTTGAATTCAACCACAGGGAGATAGCTGATCTCAACCAATTAACTTCTCGGCTCATCACATTAAGAAAGGAGAAAGACACTGACCTCCTCAAACAATGGTTTCCTGAAAGTGACCTCACCCGCAGAAGCATCAGGAATGCTGCAAACGCGGAGGAATTTGTCATATCTGAGTTCTTTAAGAAGAAGGACATTATGAAATTCATCAGCACTTCAGGCAGAGCAATGAGTGCAGGCAAGATTGGTAATGTCCTATCCTATGCACATAATCTTTATTTGAGTAAGTCAAGCCTAAATATGACCTCTGAAGACATCTCACAGCTTTTGATCGAGATTAAGCGACTGTATGCTTTACAAGAAGATTCTGAAGTGGAGCCGATAGCCATAATTTGTGATGGCATAGAAAGCAACATGAAACAGTTATTTGCTATATTGCCTCCTGACTGTGCAAGAGAGTGTGAAGTCCTCTTCGATGACATAAGAAATTCTCCAACACACAGCACAGCCTGGAAGCATGCACTCCGATTAAAAGGGACTGCATACGAAGGTCTGTTTGCAAATTGTTACGGCTGGCAATACATTCCGGAAGACATTAAACCAAGCCTGACCATGTTGATACAGACTTTGTTTCCTGACAAGTTCGAAGATTTCCTGGATCGAACCCAGTTGCATCCGGAGTTCAGAGACCTGACTCCCGACTTTTCGCTCACACAAAAGGTTCACTTTAAAAGAAATCAGATACCCAGTGTCGAAAATGTGCAAATCTCCATTGATGCGACGTTGCCTGAATCTGTGGAAGCAGTACCAGTGACAGAAAGAAAGATGTTCCCCCTTCCTGAGACTCCGCTAAGTGAGGTGCATTCAATAGAGCGTATAATGGAAAACTTTACTCGCCTCATGCATGGAGGAAGACTTTCGACCAAGAAAAGAGATGGAGATCCGGCGGAACAGGGCAACCAGCAGAGTATCACTGAACACGAGAGTTCCAGCATCTCTGCCTTTAAAGACTACGGAGAGAGAGGGATAGTCGAGGAAAATCACATGAAGTTTAGTGGAGAAGATCAGCTAGAGACAAGGCAGCTGTTGTTGGTGGAAGTTGGTTTTCAAACTGACATCGATGGGAAAATAAGGACAGACCACAAGAAGTGGAAAGACATATTAAAGCTATTAGAGCTACTAGGAATCAAGTGCTCATTCATTGCCTGTGCAGATTGCTCATCCACACCACCAGACAGATGGTGGATTACGGAGGACAGAGTGCGAGTCCTAAAAAATTCAGTCAGCTTCTTGTTCAATAAACTCTCCAGAAACTCACCTACAGAAGTAACTGACATAGTTGTTGGAGCTATAAGTACTCAAAAGGTTAGAAGTTATCTAAAGGCAGGAACTGCAACAAAAACCCCTGTGTCGACTAAAGACGTTCTGGAGACTTGGGAAAAGATGAAGGAGCACATACTCAACAGACCAACAGGACTGACACTGCCCACCAGTTTGGAACAGGCAATGCGCAAAGGACTGGTCGAAGGTGTGGTCATCTCCAAGGAAGGTTCTGAGTCATGTATCAATATGTTGAAGGAAAATTTGGACCGAATAACTGACGAATTCGAGCGAACAAAATTTAAACATGAACTTACTCAGAATATTACCACAAGTGAGAAGATGCTATTGAGTTGGTTGAGTGAAGACATCAAATCATCGAGATGTGGTGAGTGCCTCTCAAATATAAAGAAAGCCGTTGATGAAACTGCCAATCTATCAGGAAAGATTGAGCTGCTCGCTTATAATCTGCAACTCACCAATCACTGCAGCAACTGTCACCCCAATGGTGTAAACATTAGTAACACTTCTAATGTGTGCAAGAGATGCCCCAAGATTGAAGTGGTTAGCCATTGTGAAAATAAAGGCTTTGAGGACAGCAATGAATGCTTAACAGACCTAGATAGGCTTGTTAGGCTCACATTACCAGGGAAAACTGAGAAGGAGAGAAGAGTCAAACGTAATGTGGAATATCTTATAAAACTGATGATGAGCATGTCAGGCATTGATTGTATAAAATATCCCACAGGGCAGCTTATCACCCATGGAAGAGTGAGTGCAAAACATAACGATGGGAACCTGAAAGATAGAAGCGATGACGACCAAAGACTAGCTGAGAAGATAGACACTGTTAGGAAAGAGCTTTCAGAATCTAAACTGAAAGATTATTCAACTTATGCAAGGGGAGTGATATCAAATTCACTAAAAAACCTCTCAAGGCAAGGTAAATCAAAGTGTTCTGTGCCAAGATCTTGGCTCGAGAAAGTACTGTTTGACCTGAAGGTGCCTACTAAGGACGAAGAAGTGCTTATAAACATCAGAAACTCATTGAAAGCTAGATCCGAGTTTGTTAGAAATAACGATAAACTACTCATAAGGTCAAAAGAAGAACTAAAAAAATGTTTCGATGTGCAGTCTTTTAAATTGAAAAAAAACAAGCAACCTGTGCCCTTTCAGGTTGACTGTATATTGTTCAAAGAAGTGGCAGCTGAATGCATGAAGAGGTACATTGGCACACCTTATGAGGGAATTGTAGACACCTTAGTTTCTCTGATTAATGTGTTAACAAGGTTTACTTGGTTCCAGGAAGTGGTGCTATATGGTAAAATATGTGAGACCTTCCTCAGATGCTGCACAGAATTTAATAGGTCAGGGGTCAAACTGGTTAAGATAAGGCACTGTAACATTAACCTATCAGTCAAATTGCCATCAAATAAGAAAGAGAATATGTTATGTTGTCTATATAGTGGAAACATGGAGCTCTTGCAAGGACCTTTCTATTTGAACAGGAGACAAGCTGTCCTTGGTTCTTCATACCTTTACATTGTCATTACACTTTACATACAAGTGCTGCAGCAGTACAGGTGTCTAGAAGTTATAAACAGTGTGAGTGAAAAAACATTGCAAGACATTGAAAATCATTCCATGACTCTACTAGAAGATTCATTCAGGGAAATCACTTTTGCTCTTGAAGGTAGGTTTGAAGAATCTTATAAAATACGAACCTCGAGGTGCAGAGCCAGTGGGAATTTTCTGAACAGGAGCAGTAGAGACCACTTTATAAGCGTTGTTTCAGGCTTGAACCTAGTTTATGGCTTCCTCATAAAAGATAACTTACTAGCCAACTCTCAGCAACAGAACAAACAACTACAGATGCTTCGTTTTGGCATGCTTGCAGGGCTTAGTAGGCTTGTTTGTCCTAATGAGCTAGGAAAGAAATTTTCAACGAGCTGTAGAAGAATTGAAGACAACATTGCAAGGCTTTACCTGCAGACATCCATTTACTGTTCAGTCAGGGATGTGGAGGACAATGTTAAGCACTGGAAACAAAGAGATCTATGTCCTGAAGTAACCATTCCATGCTTTACAGTCTATGGAACCTTTGTCAACAGCGATAGACAACTGATCTTTGACATTTACAATGTGCATATATATAATAAAGAAATGGACAACTTTGATGAAGGATGTATCAGCGTCTTGGAAGAAACAGCAGAAAGGCACATGCTTTGGGAACTCGATCTGATGAATTCACTTTGTTCTGACGAAAAAAAAGATACTAGACCCGCAAGACTTTTACTAGGCTGCCCAAATGTGAGGAAAGCAGCAACCAGAGAAGGGAAGAAGCTGTTGAAGTTAAACAGTGACACATCCACAGACACACAGAGCATTGCTTCTGAAGTGTCGGACAGAAGGTCTTATAGTTCAAGTAAGAGTAGAATCCGTAGTATATTTGGTAGATACAACTCTCAGAAGAAACCATTTGAATTAAGGTCAGGTCTTGAGGTTTTCAATGATCCTTTCAATGATTATCAGCAAGCAATAACGGACATTTGCCAATTTTCTGAGTACACACCAAACAAAGAAAGCATTTTGAAAGACTGTCTTCAAATCATACGAAAAAACCCTAGCCACACAATGGGTTCTTTTGAGCTGATCCAGGCAATCTCAGAGTTCGGCATGAGCAAGTTTCCTCCCGAAAATATAGACAAAGCAAGAAGGGATCCGAAGAACTGGGTTAGCATCTCTGAAGTAACCGAAACAACAAGTATAGTTGCATCACCTAGAACTCATATGATGCTCAAGGATTGTTTTAAAATTATACTAGGTACTGAGAATAAGAAGATAGTCAAAATGCTTCGAGGTAAGCTAAAGAAACTCGGTGCTATCTCAACAAACATAGAGATCGGGAAAAGGGATTGCCTAGATCTACTCAGCACAGTAGATGGGCTAACAGACCAGCAGAAAGAAAATATTGTAAATGGGATATTTGAGCCCTCAAAGTTATCCTTCTACCATTGGAAGGAACTGGTCAAAAAAAACATTGATGAAGTTTTACTAACTGAAGATGGAAATCTGATCTTCTGCTGGCTGAAAACAATCTCTTCTTCAGTCAAAGGAAGCCTAAAGAAGAGACTCAAATTCATGAATATACATTCTCCAGAATTGATGCCGGAAAACTGTCTCTTTTCTAGTGAGGAATTCAATGAGTTAATTAAGTTGAAGAAACTCCTCCTCAATGAACAACAAGATGAACAGGAGCTGAAACAAGATCTTTTGATATCTTCTTGGATCAAGTGCATAACAGCTTGCAAGGATTTTGCAAGCATCAATGACAAGATTCAGAAGTTCATTTACCACCTGTCTGAAGAGCTATATGACATAAGGCTGCAGCATCTGGAACTGTCAAAGCTTAAGCAAGAGCACCCTAGTGTCAGCTTCACAAAAGAAGAAGTCTTAATAAAGCGGCTGGAGAAAAATTTCCTTAAGCAGCATAATTTAGAGATTATGGAAACTGTGAATCTTGTATTCTTTGCAGCCCTCTCAGCTCCCTGGTGCTTACACTATAAAGCACTAGAGTCTTATTTGGTGAGACATCCAGAAATACTTGACTGTGGATCTAAGGAGGACTGCAAACTCACCTTGCTTGATCTGTCAGTTTCTAAGCTCTTGGTTTGTTTGTATCAAAAAGATGATGAGGAGCTGATAAATAGCTCAAGTTTGAAACTTGGGTTTTTAGTGAAATATGTTGTCACCTTGTTCACATCCAATGGTGAACCTTTTTCACTCAGTCTCAATGACGGGGGTTTGGATCTTGATTTACACAAGACTACTGACGAAAAGTTACTACATCAAACAAAGATAGTTTTTGCTAAAATTGGTTTATCTGGGAACAGTTATGACTTTATCTGGACTACCCAAATGATAGCAAACAGCAATTTTAACGTCTGCAAAAGATTAACGGGAAGGAGTACTGGGGAAAGGCTCCCTAGAAGCGTTAGAAGCAAGGTCATATATGAGATGGTAAAATTAGTGGGAGAAACAGGCATGGCAATACTACAACAATTAGCTTTTGCACAAGCACTAAATTATGAGCACCGCTTCTATGCGGTCTTAGCACCTAAAGCGCAACTAGGAGGAGCAAGAGATTTGTTAGTGCAAGAGACTGGGACTAAAGTCATGCATGCAACCACTGAAATGTTTAGTAGAAATCTTTTAAAAACAACATCAGATGATGGCCTCACAAACCCACATCTTAAAGAAACAATCCTTAATGTGGGATTAGACTGTCTTGCTAACATGCGAAATCTTGACGGTAAGCCCATAAGTGAAGGTAGTAACTTGGTCAATTTCTACAAAGTCATATGTATCTCGGGTGATAATACCAAGTGGGGCCCGATACACTGCTGTTCTTTCTTTTCTGGCATGATGCAACAGGTTCTGAAAAATGTACCAGATTGGTGTTCATTTTATAAATTAACATTCATTAAAAACTTATGTAGACAGGTAGAAATACCTGCTGGCAGTATTAAGAAGATCTTAAATGTTCTTAGGTATAGATTGTGCAGCAAGGGAGGTGTAGAACAACATAGTGAAGAAGATCTGAGAAGACTGTTGACAGATAATTTAGACAGTTGGGATGGAAACGACACAGTTAAGTTCTTAGTTACAACTTATATAAGCAAAGGACTCATGGCGTTAAACAGTTACAATCATATGGGTCAGGGTATTCACCATGCAACATCTTCGGTGTTAACTTCCCTAGCTGCTGTGCTCTTTGAGGAGCTGGCAATTTTTTATCTTAAGAGAAGCTTACCCCAGACAACAGTACATGTTGAACATGCCGGTAGTTCAGATGATTACGCAAAGTGTATAGTGGTGACTGGTATACTATCCAAAGAGCTCTACTCCCAGTATGATGAAACATTTTGGAAACACGCTTGCAGACTCAAAAACTTCACGGCCGCGGTACAAAGATGCTGTCAAATGAAAGATAGTGCCAAAACCTTGGTGAGCGACTGCTTTCTCGAGTTTTACAGTGAGTTTATGATGGGCTACAGAGTAACCCCTGCTGTAATAAAGTTCATGTTTACTGGACTGATAAACAGCTCTGTGACCTCTCCTCAGAGTTTGATGCAAGCATGCCAAGTTTCATCCCAACAAGCAATGTATAATAGTGTTCCTCTTGTCACCAACACTGCCTTCACCCTATTAAGGCAGCAAATTTTCTTTAACCATGTTGAAGACTTTATCAGAAGGTATGGTATACTGACTCTTGGGACTTTGTCACCCTTTGGTAGGTTGTTCGTACCAACCTACTCTGGATTAGTCAGCTCAGCGGTTGCTTTAGAAGATGCTGAAGTCATTGCTAGAGCAGCCCAAACACTTCAAATGAACAGTGTGTCAATACAGTCAAGTAGCTTGACCACATTAGATAGCCTAGGTCGTAGTAGGACAAGTTCCACAGCTGAGGATAGCAGCAGTGTGAGCGACACAACTGCTGCTTCCCATGACTCAGGATCATCATCCTCAAGCTTCTCTTTTGAGCTCAATAGACCCCTGTCTGAAACTGAACTACAGTTCATTAAAGCACTAAGTAGTCTCAAGTCAACACAAGCCTGTGAAGTGATTCAAAATAGAATTACAGGTCTTTATTGCAACAGCAACGAAGGACCTCTTGATAGGCATAATGTCATTTACAGCAGCAGAATGGCAGACTCTTGCGATTGGCTAAAGGATGGTAAAAGGAGAGGGAATCTAGAACTAGCGAATAGGATCCAATCTGTACTGTGTATTCTGATAGCAGGATATTACAGGTCATTTGGAGGGGAAGGAACCGAGAAACAGGTAAAGGCATCATTGAATAGAGACGACAATAAAATCATAGAGGATCCTATGATACAACTAATTCCAGAAAAGCTGAGGAGAGAGTTAGAAAGGTTAGGTGTTTCTAGAATGGAAGTCGATGAGCTAATGCCAAGCATTAGTCCTGATGACACCTTAGCCCAGCTTGTAGCGAAAAAACTCATTAGCCTCAATGTTTCGACAGAAGAATACTCAGCTGAGGTATCTAGACTCAAACAAACACTGACAGCCCGAAATGTTTTGCACGGGTTAGCTGGAGGGATTAAGGAGCTTTCGCTTCCAATATATACAATATTCATGAAATCTTACTTCTTTAAAGACAATGTTTTCCTGTCACTAACAGATAGATGGTCTACCAAGCACAGTACAAACTATCGTGATAGTTGTGGCAAACAATTAAAAGGTAGAATAATTACCAAGTATACTCACTGGTTGGACACTTTTCTGGGCTGCTCTGTCTCCATCAACAGGCATACTACTGTTAAAGAGCCCTCCTTATTCAATCCGAACATCAGATGTGTGAATCTGATCACATTTGAGGACGGCCTGAGAGAACTATCAGTGATACAGAGTCACCTTAAAGTCTTTGAAAATGAGTTCACCAACTTAAATCTTCAATTCTCTGATCCGAACAGACAGAAACTTAGAATAGTTGAGTCTAGACCTGCAGAATCTGAGCTAGAGGCAAACCGTGCAGTAATTGTCAAGACCAAATTGTTTTCAGCAACTGAACAAGTTCGACTATCCAACAACCCTGCAGTTGTCATGGGCTACCTATTGGATGAATCAGCAATTTCAGAAGTCAAGCCTACCAAGGTTGACTTCTCAAATTTACTTAAAGACCGCTTCAAAATAATGCAATTTTTTCCTTCAGTGTTCACTTTGATTAAGATGCTGACAGATGAATCGTCAGATTCAGAAAAGAGTGGCCTTAGTCCAGATTTGCAACAAGTTGCAAGATACTCAAACCATTTGACCTTGCTCAGCAGAATGATTCAACAAGCAAAGCCAACCGTGACTGTTTTCTACATGCTAAAAGGTAACTTGATGAACACAGAGCCAACAGTTGCTGAGCTTGTCAGCTATGGTATAAAGGAAGGCAGATTTTTTAGGCTTTCCGACACCGGAGTCGATGCAAGCACATACTCTGTAAAATATTGGAAAATTCTTCACTGCATCTCTGCCATTGGATGTTTACCTTTGAGTCAAGCAGACAAATCTTCACTACTTATGAGCTTCTTAAACTGGAGGGTCAACATGGACATTAGAACATCTGACTGTCCACTATCTAGTCATGAAGCAAGTATACTGAGTGAATTTGATGGACAAGTTATCGCCAACATACTTGCCAGTGAATTGAGTTCTGTGAAACGAGATTCTGAACGCGAGGGTCTAACTGATCTCCTTGATTATCTAAACTCACCAACTGAATTGTTGAAGAAGAAGCCTTACTTAGGGACAACTTGCAAGTTCAACACCTGGGGAGACTCGAATAGATCTGGAAAGTTCACATACAGCAGCAGATCTGGAGAATCCATTGGAATCTTCATTGCAGGGAAATTGCACATCCATCTCTCATCTGAGTCCGTTGCCTTGTTGTGTGAAACTGAAAGACAAGTGCTTTCTTGGATGAGTAAGAGGAGGACTGAGGTAATAACTAAAGAACAGCATCAACTGTTTCTAAGTCTTCTCCCACAGTCTCATGAGTGTTTACAAAAGCACAAGGACGGTAGTGCGCTATCAGTCATACCTGATAGCAGCAACCCCCGATTACTTAAGTTTGTGCCCCTCAAAAAAGGTCTAGCAGTGGTGAAAATCAAAAAACAAATTTTAACAGTGAAGAAGCAGGTTGTGTTTGATGCAGAGAGCGAGCCTAGACTGCAGTGGGGGCATGGCTGCTTGTCCATTGTTTATGACGAAACTGATACTCAGACCACATACCATGAAAATCTTTTGAAGGTGAAGCATCTTGTAGACTGCTCTACAGATAGAAAAAAGCTTTTGCCCCAGTCAGTGTTTTCTGACTCCAAAGTTGTCCTTTCAAGGATCAAGTTCAAGACGGAGCTTCTCCTCAACTCATTGACGCTGCTCCACTGTTTCCTAAAACATGCTCCTAGTGATGCCATAATGGAGGTAGAGAGCAAAAGTAGCTTGCTACACAAGTACCTCAAATCGGGAGGTGTCAGGCAACGGAACACTGAAGTGCTCTTCAGAGAGAAGTTAAACAAGGTTGTTATAAAAGACAATCTTGAGCAAGGTGTGGAAGAGGAGATTGAGTTTTGCAACAACTTGACTAAGACTGTTTCAGAGAACCCATTACCACTCAGCTGTTGGTCTGAAGTTCAAAATTACATTGAAGACATAGGCTTTAACAATGTACTTGTTAACATTGACAGAAACACGGTGAAAAGTGAACTTTTATGGAAATTTACGTTAGACACCAATGTAAGCACCACAAGTACTATAAAAGACGTGAGGACATTGGTGTCCTACGTTAGCACTGAAACCATCCCTAAGTTCTTGCTTGCATTCTTACTTTATGAAGAAGTGTTAATGAACTTAATCAACCAGTGTAAGGCAGTAAAAGAACTCATCAACAGCACAGGACTCTCAGACTTGGAACTGGAAAGCTTACTCACTTTATGTGCTTTCTATTTCCAAAGTGAGTGCAGTAAGAGAGATGGTCCTAGATGCTCCTTTGCAGCACTATTAAGTCTAATCCATGAGGATTGGCAGAGGATAGGTAAAAACATTCTTGTTCGTGCAAACAATGAACTAGGTGATGTGTCACTGAAGGTTAACATTGTCCTGGTGCCTCTCAAGGACATGTCTAAGCCAAAGTCTGAGAGAGTGGTCATGGCTAGAAGGTCACTAAATCATGCTCTATCCTTGATGTTTCTGGACGAGATGTCACTACCTGAGCTAAAATCCTTATCCGTGAACTGCAAAATGGGGAACTTTGAAGGGCAGGAGTGCTTTGAGTTCACTATTCTGAAGGACAATAGCGCAAGACTAGATTACAACAAGTTGATTGACCACTGTGTGGACATGGAAAAAAAGAGGGAAGCGGTTAGAGCAGTAGAAGATTTAATTTTGATGTTGACAGGCAGAGCAGTCAAACCCAGCGCTGTAACACAGTTTGTACACGGGGACGAGCAGTGTCAAGAGCAAATAAGCTTAGATGATCTGATGGCAAACGACACGGTAACAGACTTTCCTGATAGGGAAGCAGAAGCCCTCAAAACAGGAAATCTTGGCTTTAACTGGGACTCAGATTGAACATGCCGCTTATAAGCCATTAATACCTTTCGGCGTCACAAGGACAAATGATGCAGTTTTAGCTGCATCATTCATTAACATTAAAGCCTTCAAACAAGCTAACTACTCTGCATTCTCCTCAATCAACTCAATTGCTTCAACTGATCTATTTTACTAGCTCATCGATCCTCTCTTTCTTAGCTATATCTTTGAGA' + insdcAccessionFull: NC_005301.3 + genes: + - name: RdRp + sequence: 'MDFLRSLDWTQVIAGQYVSNPRFNISDYFEIVRQPGDGNCFYHSIAELTMPNKTDHSYHYIKRLTESAARKYYQEEPEARLVGLSLEDYLKRMLSDNEWGSTLEASMLAKEMGITIIIWTVAASDEVEAGIKFGDGDVFTAVNLLHSGQTHFDALRILPQFETDTREALSLMDRVIAVDQLTSSSSDELQDYEDLALALTSAEESNRRSSLDEVTLSKKQAEILRQKASQLSKLVNKSQNIPTRVGRVLDCMFNCKLCVEISADTLILRPESKEKIGEIMSLRQLGHKLLTRDKQIKQEFSRMKLYVTKDLLDHLDVGGLLRAAFPGTGIERHMQLLHSEMILDICTVSLGVMLSTFLYGSNNKNKKKFITNCLLSTALSGKKVYKVLGNLGNELLYKAPRKALATVCSALFGKQINKLQNCFRTISPVSLLALRNLDFDCLSVQDYNGMIENMSKLDNTDVEFNHREIADLNQLTSRLITLRKEKDTDLLKQWFPESDLTRRSIRNAANAEEFVISEFFKKKDIMKFISTSGRAMSAGKIGNVLSYAHNLYLSKSSLNMTSEDISQLLIEIKRLYALQEDSEVEPIAIICDGIESNMKQLFAILPPDCARECEVLFDDIRNSPTHSTAWKHALRLKGTAYEGLFANCYGWQYIPEDIKPSLTMLIQTLFPDKFEDFLDRTQLHPEFRDLTPDFSLTQKVHFKRNQIPSVENVQISIDATLPESVEAVPVTERKMFPLPETPLSEVHSIERIMENFTRLMHGGRLSTKKRDGDPAEQGNQQSITEHESSSISAFKDYGERGIVEENHMKFSGEDQLETRQLLLVEVGFQTDIDGKIRTDHKKWKDILKLLELLGIKCSFIACADCSSTPPDRWWITEDRVRVLKNSVSFLFNKLSRNSPTEVTDIVVGAISTQKVRSYLKAGTATKTPVSTKDVLETWEKMKEHILNRPTGLTLPTSLEQAMRKGLVEGVVISKEGSESCINMLKENLDRITDEFERTKFKHELTQNITTSEKMLLSWLSEDIKSSRCGECLSNIKKAVDETANLSGKIELLAYNLQLTNHCSNCHPNGVNISNTSNVCKRCPKIEVVSHCENKGFEDSNECLTDLDRLVRLTLPGKTEKERRVKRNVEYLIKLMMSMSGIDCIKYPTGQLITHGRVSAKHNDGNLKDRSDDDQRLAEKIDTVRKELSESKLKDYSTYARGVISNSLKNLSRQGKSKCSVPRSWLEKVLFDLKVPTKDEEVLINIRNSLKARSEFVRNNDKLLIRSKEELKKCFDVQSFKLKKNKQPVPFQVDCILFKEVAAECMKRYIGTPYEGIVDTLVSLINVLTRFTWFQEVVLYGKICETFLRCCTEFNRSGVKLVKIRHCNINLSVKLPSNKKENMLCCLYSGNMELLQGPFYLNRRQAVLGSSYLYIVITLYIQVLQQYRCLEVINSVSEKTLQDIENHSMTLLEDSFREITFALEGRFEESYKIRTSRCRASGNFLNRSSRDHFISVVSGLNLVYGFLIKDNLLANSQQQNKQLQMLRFGMLAGLSRLVCPNELGKKFSTSCRRIEDNIARLYLQTSIYCSVRDVEDNVKHWKQRDLCPEVTIPCFTVYGTFVNSDRQLIFDIYNVHIYNKEMDNFDEGCISVLEETAERHMLWELDLMNSLCSDEKKDTRPARLLLGCPNVRKAATREGKKLLKLNSDTSTDTQSIASEVSDRRSYSSSKSRIRSIFGRYNSQKKPFELRSGLEVFNDPFNDYQQAITDICQFSEYTPNKESILKDCLQIIRKNPSHTMGSFELIQAISEFGMSKFPPENIDKARRDPKNWVSISEVTETTSIVASPRTHMMLKDCFKIILGTENKKIVKMLRGKLKKLGAISTNIEIGKRDCLDLLSTVDGLTDQQKENIVNGIFEPSKLSFYHWKELVKKNIDEVLLTEDGNLIFCWLKTISSSVKGSLKKRLKFMNIHSPELMPENCLFSSEEFNELIKLKKLLLNEQQDEQELKQDLLISSWIKCITACKDFASINDKIQKFIYHLSEELYDIRLQHLELSKLKQEHPSVSFTKEEVLIKRLEKNFLKQHNLEIMETVNLVFFAALSAPWCLHYKALESYLVRHPEILDCGSKEDCKLTLLDLSVSKLLVCLYQKDDEELINSSSLKLGFLVKYVVTLFTSNGEPFSLSLNDGGLDLDLHKTTDEKLLHQTKIVFAKIGLSGNSYDFIWTTQMIANSNFNVCKRLTGRSTGERLPRSVRSKVIYEMVKLVGETGMAILQQLAFAQALNYEHRFYAVLAPKAQLGGARDLLVQETGTKVMHATTEMFSRNLLKTTSDDGLTNPHLKETILNVGLDCLANMRNLDGKPISEGSNLVNFYKVICISGDNTKWGPIHCCSFFSGMMQQVLKNVPDWCSFYKLTFIKNLCRQVEIPAGSIKKILNVLRYRLCSKGGVEQHSEEDLRRLLTDNLDSWDGNDTVKFLVTTYISKGLMALNSYNHMGQGIHHATSSVLTSLAAVLFEELAIFYLKRSLPQTTVHVEHAGSSDDYAKCIVVTGILSKELYSQYDETFWKHACRLKNFTAAVQRCCQMKDSAKTLVSDCFLEFYSEFMMGYRVTPAVIKFMFTGLINSSVTSPQSLMQACQVSSQQAMYNSVPLVTNTAFTLLRQQIFFNHVEDFIRRYGILTLGTLSPFGRLFVPTYSGLVSSAVALEDAEVIARAAQTLQMNSVSIQSSSLTTLDSLGRSRTSSTAEDSSSVSDTTAASHDSGSSSSSFSFELNRPLSETELQFIKALSSLKSTQACEVIQNRITGLYCNSNEGPLDRHNVIYSSRMADSCDWLKDGKRRGNLELANRIQSVLCILIAGYYRSFGGEGTEKQVKASLNRDDNKIIEDPMIQLIPEKLRRELERLGVSRMEVDELMPSISPDDTLAQLVAKKLISLNVSTEEYSAEVSRLKQTLTARNVLHGLAGGIKELSLPIYTIFMKSYFFKDNVFLSLTDRWSTKHSTNYRDSCGKQLKGRIITKYTHWLDTFLGCSVSINRHTTVKEPSLFNPNIRCVNLITFEDGLRELSVIQSHLKVFENEFTNLNLQFSDPNRQKLRIVESRPAESELEANRAVIVKTKLFSATEQVRLSNNPAVVMGYLLDESAISEVKPTKVDFSNLLKDRFKIMQFFPSVFTLIKMLTDESSDSEKSGLSPDLQQVARYSNHLTLLSRMIQQAKPTVTVFYMLKGNLMNTEPTVAELVSYGIKEGRFFRLSDTGVDASTYSVKYWKILHCISAIGCLPLSQADKSSLLMSFLNWRVNMDIRTSDCPLSSHEASILSEFDGQVIANILASELSSVKRDSEREGLTDLLDYLNSPTELLKKKPYLGTTCKFNTWGDSNRSGKFTYSSRSGESIGIFIAGKLHIHLSSESVALLCETERQVLSWMSKRRTEVITKEQHQLFLSLLPQSHECLQKHKDGSALSVIPDSSNPRLLKFVPLKKGLAVVKIKKQILTVKKQVVFDAESEPRLQWGHGCLSIVYDETDTQTTYHENLLKVKHLVDCSTDRKKLLPQSVFSDSKVVLSRIKFKTELLLNSLTLLHCFLKHAPSDAIMEVESKSSLLHKYLKSGGVRQRNTEVLFREKLNKVVIKDNLEQGVEEEIEFCNNLTKTVSENPLPLSCWSEVQNYIEDIGFNNVLVNIDRNTVKSELLWKFTLDTNVSTTSTIKDVRTLVSYVSTETIPKFLLAFLLYEEVLMNLINQCKAVKELINSTGLSDLELESLLTLCAFYFQSECSKRDGPRCSFAALLSLIHEDWQRIGKNILVRANNELGDVSLKVNIVLVPLKDMSKPKSERVVMARRSLNHALSLMFLDEMSLPELKSLSVNCKMGNFEGQECFEFTILKDNSARLDYNKLIDHCVDMEKKREAVRAVEDLILMLTGRAVKPSAVTQFVHGDEQCQEQISLDDLMANDTVTDFPDREAEALKTGNLGFNWDSD*' + - name: M + displayName: M segment + references: + - name: MH396653 + displayName: M (MH396653.1) + sequence: 'AAAGAAATACTTGCGGCACGTCAGTACGTAAGTGTTAACTTTGAGGAAGTGGATTGAGCATCTTGATTGCAGCATACTTGTCAACATCATGCATATATCATTGATGTATGCAGTTTTCTGCTTGCAACTGTGCGGTCTAGGGAAGACTAACGGACCACACAATGGGACTGAACACAATAATACACACGTTATGACAACGCCTGATGACAGTCAGAGCCCTGAACCGCCAGTGAGCACAGCCTTACCTGTCACACCGGACCCTTCAACTGTCACACCTTCAACACCAGCCAGCGGATTAGAAGGCTCAGGAGAGGTTTACACATCCTCTCCAATCACCACCAAGGGTTTGTCTCTGCCGGAGGCCACATCTGAGCCCCCTGCGACTACCAGCGTGGTCACTTCAAGTGCAAGTGATACCGATTCTAGCACACAGGCAGCCGGAGACACCCCCACACCAACAGTCCGCACGAGTCTGCCCAGCAGCCCTAGCACACCATCCACATCACAAGGCACACACTATCCCGTGAGGAGTCTGCTTTCAGTCACGAGCCCTAAGCCAGAAGAAACACCAACACCGTCAAAATCAGGCAAAGATAACTTAGCAACCAACAGCCCCCACCCAGCCACCAGCAGGCCAACAACCCCTCCCACAACAGCCCAGAAACCCACTGAAAACAACAGCCACAACACCACCGAACAGCTTGAGTCCTTAACACACTCAGCAACTTTAGGTTCAATGATCTCTCCAACACAGACAGTCCTCCCACAGAGTGTTACCTCTATAGCCATTCAAGACATTCATATCAGCCCAACAAACAGGTCTAAAAGAAACCTTGATATGGAAATAATCTTAACGTTATCTCAGGGTCTGAAAAAGTATTATGGCAAAATACTCAAGCTCCTGCATCTCACCTTAGAGGAAGACACTGAAGGCTTGTTAGAGTGGTGCAAGAGAAATCTCGGTCTTGACTGTGATGACACCTTCTTCCAAAAAAGAATTGAAGAATTCTTCATAACTGGTGAGGGTCATTTCAATGAAGTTTTACAATTTAGAACACTAGGCACATTGAGTACCACAGAGTCAACGCATGCTGGATCACCAACAGTTGAACCCTTCAAATCCTACTTTACTAAAGGTTTCCTTTCAATAGATTCAGGTTACTTCTCTGCCAAATGTTATTCAAGAACATCCAATTCAGGGCTCCAATTGATTAATGTTACCCGACATTCAACTAGGATAGCTGACACGCCTGGGCCCAAGATCACTAACCTAAAGACCATCAATTGCATAAACTTAAAAGCATCCGTCTTTAAAGAACATAGAGAGGTTGAAATCAATGTGCTTCTCCCTCAGGTTGCAGTCAACCTCTCAAACTGTCACATTGCAATCAAATCACATATCTGTGACTATTCTTTAGACACCGACGGGGCGATTAGGCTTCCTCAAATTCATCATGAAGGCACTTTTATCCCAGGTACTTACAAAATAGTGATAGACAAAAAAAGTAAGCTGAATGACAGGTGCACCCTATTCACCAACTGTGTGATAAAAGGAAGAGAAGTTCGTAAAGGCCAGTCAGTCCTAAGGCAATATAAGACAGAAATTAGAATTGGCAGGGCATCAACTGGTTCTAGGAGATTGCTTTCCGAAGAATCTGGTGATGACTGCATATCAAGAACTCAGCTACTGAGGACAGAGACTGCAGAGGTCCATGGCGATAACTATGGTGGTCCAGGTGATAAGATAACCATCTGTAATGGTTCAACTGTTGTAGATCAAAGACTGGGTAGTGAACTGGGGTGTTACACTATCAATAGAGTGAGGTCATTCAAGCTATGCGAAAACAGTGCCACAGGGAAGAGCTGTGAAATAGACAGTATCCCAGTCAAGTGTAAGCAGGGTTATTGCCTAAAAATCACTCAGGAAGGAAGGGGCCATGTGAAGTTATCTAGAGGCTCAGAAGTTGTCTTGGATGTATGTGATTCAAGCTGTGAAGTGATGATACCTAAGGGCACTGGTGACATTCTAGTAGATTGTTCAGGTGGTCAGCAACATTTTTTAAAAGACAACCTGGTTGACCTAGGGTGTCCCAAAATTCCGTTACTGGGCAAAATGGCTATTTATATCTGCAGGATGTCGAATCACCCCAGGACAACCATGGCCTTCCTCTTTTGGTTCAGCTTTGGCTATGTGATAACTTGTATACTTTGCAAGGCCATTTTTTTCTTATTAATAATTTTTGGAACACTAGGGAAAAGGTTCAAGCAGTACAGAGAGTTGAAACCCCAGACCTGCACCATATGTGAGACAACACCTGTAAATGCAATAGATGCTGAAATGCATGATCTCAATTGCAGTTACAATATATGTCCCTATTGTGCGTCTAGACTGACTTCAGATGGGCTTGCTAGACATGTAACACAATGTCCTAGACGGAAGGAGAAAGTGGAGGAAACCGAATTATACCTGAATTTAGAGAGAATTCCCTGGGTTGTAAGAAAGCTATTGCAGGTGTCAGAGTCCACTGGTACAGTATTAAAAAGGAGCAGCTGGCTAATTGTATTACTTGTGCTGTTCACAGTTTCATTGTCACCAGTTCAATCAGCACCCATTGGTCACGGGAAAACAATTGAAACATACCGGGTTAGGGAAGAATACACAAGTATTTGCCTCTTTGTACTAGGAAGTATCCTGTTTATAGTCTCTTTTCTAATGAAAGGACTGGTTGACGGTGTTGGCAACATCTTCTTTCCTGGGCTGTCCGTCTGTAAGACATGCTCTATAGGTAGCATTAATGGCTTTGAAATTGAGTCTCATAAGTGCTACTGTAGCTTGTTTTGTTGCCCTTACTGTAGGCACTGCTCTGCTGATGGAGAAATCCATCAGCTGCACTTGAGCATCTGCAAAAAAAGGAAGACAGGAAGTAATGTTATGCTGGCTGTTTGCAAACGCATGTGTTTCAGGGCAACTATGGAAGTAAGCAACAAAGCCCTATTTATCCGTAGCATTATCAACACCACTTTTGTTGTGTGCATACTGATACTAGCGGTTTGTGTTGTTAGCACCTCAGCAGTAGAGATGGAAAGCCTACCAGCTGGGACCTGGGAAAGAGAAGAAGACCTAACAAATTTCTGCCATCAGGAATGCCAGGTCACAGAGACTGAGTGCCTCTGCCCTTATGAAGCTCTAGTGCTCAGAAGGCCCCTATTTCTAGATAGCATAGTCAAAGGTATGAAAAATCTGCTAAACTCAACAAGTCTAGAAACAAGCTTATCAATTGAGGCACCGTGGGGAGCAATTAATGTTCAGTCAACCTACAAACCAACTGTATCAACTGCAAACATAGCACTTAGTTGGAGCTCAGTGGAACACAGAGGCAATAAGGTTTTGGTCTCAGGCAGATCAGAATCAATCATGAAGCTGGAAGAAAGGACAGGAATCAGCTGGGATCTTGGCGTGGAAGATGCCTCTGAGTCTAAGCTACTTACAGTTTCAGTCATGGACTTGTCTCAGATGTACTCTCCTGTCTTCGAGTACTTATCAGGTGACAGACAAGTGGAAGAGTGGCCTAAAGCAACCTGCACAGGTGACTGCCCAGAAAGATGTGGCTGCACATCATCAACCTGCTTACACAAAGAGTGGCCCCACTCAAGGAATTGGAGATGTAATCCTACTTGGTGCTGGGGTGTAGGGACTGGCTGCACCTGTTGTGGTTTAGATGTGAAAGACCTTTTCACAGATTACATGTTCGTCAAGTGGAAAGTTGAGTACATTAAGACAGAGGCCATAGTATGTGTAGAACTAACCAGTCAGGAAAGACAGTGTAGCTTGATTGAGGCGGGCACAAGATTCAATTTAGGTTCTGTGACTATTACATTGTCAGAACCAAGGAACATTCAACAAAAGCTCCCTCCTGAAATAATCACACTGCACCCCAAGATTGAGGAAGGTTTTTTTGACCTAATGCATGTACAAAAAGTGCTATCGGCAAGCACAGTGTGTAAGTTGCAGAGTTGCACACATGGTGTGCCAGGAGATCTGCAGGTCTACCACATCGGAAACCTATTAAAAGGGGACAGAGTAAACGGACATCTGATTCATAAAATTGAGCAACACTTCAACACATCCTGGATGTCTTGGGATGGTTGTGACCTAGACTACTACTGTAACATGGGAGACTGGCCTTCCTGCACATATACCGGAGTCACTCAGCATAACCATGCTTCATTTGTAAACCTGCTCAACATTGAAACTGATTATACAAAAACCTTTCACTTTCACTCTAAAAGGGTTACTGCACATGGAGACACACCACAACTAGATCTGAAGGCAAGGCCAACCTATGGTGCGGGTGAGATCACCGTGCTGGTGGAAGTTGCTGACATGGAATTACACACAAAGAAGATTGAAATATCAGGCTTAAAATTTGCAAGCCTAACTTGTACAGGTTGTTATGCTTGTAGTTCTGGCATCTCTTGTAAAGTTAGAATTCATGTAGATGAACCAGATGAACTTACAGTACATGTTAAAAGTGATGACCCAGATGTAGTTGCAGCTAGCTCAAGTCTCATGGCGAGGAAGCTTGAATTTGGAACAGACAGTACATTTAAAGCTTTCTCAGCCATGCCTAAAACCTCCCTATGTTTCTACATTGTGGAAAGAGAATACTGTAAGAGCTGCAGTAAAGAAGATACACAAAAATGTGTTAACACGAAACTCGAACAACCACAGAGCATTTTGATCGAACATAAGGGAACTATAATTGGAAAGCAAAACAATACTTGCACGGCTAAAGCGAGTTGCTGGTTAGAGTCAGTTAAGAGTTTTTTTTATGGTCTGAAGAATATGCTCGGTGGCATATTTGGCAATGTTTTTATAGGCATTTTCACATTTCTTACCCCCTTTATCTTGTTAATACTTTTCTTTATGTTTGGGTGGAGGATCCTGTTTTGCTTCAAGTGTTGCAGAAGAACCAGAGGCCTATTCAAGTACAGACACCTCAAAGACGATGAAGAAACTGGTTACAGAAAGATCATTGAAAGACTGAACAACAAAAAAGGAAAAAACAGGCTGCTTGATGGTGAAAGACTTGCTGACAGAAAGATTGCTGAACTGTTCTCCACAAAAACACACATTGGCTAGATCAACCGGAGGGGCCTGGGAGGTGACGGCCCTGCGCCGGCTGGTGCTGCTGCTCATGCTAATTCCTTTAATTGCATCCCCACCATATTACCATCACAATATGCCACATCTAAGCTGCTTCATTGTATCTACAAACAGACTCTGTAATGCTTGAAACTGCCTTCGCTCTGTTTGCTTTGACCTAAATCTTGACTGCGTGCCGCCACTAGGTTACTGCACATGGAGACACACCACAACTAGATCTGAAGGCAAGGCCAACCTATGGTGCGGGTGAGATCACCGTGCTGGTGGAAGTTGCTGACATGGAATTACAC' + insdcAccessionFull: MH396653.1 + genes: + - name: GPC + sequence: 'MHISLMYAVFCLQLCGLGKTNGPHNGTEHNNTHVMTTPDDSQSPEPPVSTALPVTPDPSTVTPSTPASGLEGSGEVYTSSPITTKGLSLPEATSEPPATTSVVTSSASDTDSSTQAAGDTPTPTVRTSLPSSPSTPSTSQGTHYPVRSLLSVTSPKPEETPTPSKSGKDNLATNSPHPATSRPTTPPTTAQKPTENNSHNTTEQLESLTHSATLGSMISPTQTVLPQSVTSIAIQDIHISPTNRSKRNLDMEIILTLSQGLKKYYGKILKLLHLTLEEDTEGLLEWCKRNLGLDCDDTFFQKRIEEFFITGEGHFNEVLQFRTLGTLSTTESTHAGSPTVEPFKSYFTKGFLSIDSGYFSAKCYSRTSNSGLQLINVTRHSTRIADTPGPKITNLKTINCINLKASVFKEHREVEINVLLPQVAVNLSNCHIAIKSHICDYSLDTDGAIRLPQIHHEGTFIPGTYKIVIDKKSKLNDRCTLFTNCVIKGREVRKGQSVLRQYKTEIRIGRASTGSRRLLSEESGDDCISRTQLLRTETAEVHGDNYGGPGDKITICNGSTVVDQRLGSELGCYTINRVRSFKLCENSATGKSCEIDSIPVKCKQGYCLKITQEGRGHVKLSRGSEVVLDVCDSSCEVMIPKGTGDILVDCSGGQQHFLKDNLVDLGCPKIPLLGKMAIYICRMSNHPRTTMAFLFWFSFGYVITCILCKAIFFLLIIFGTLGKRFKQYRELKPQTCTICETTPVNAIDAEMHDLNCSYNICPYCASRLTSDGLARHVTQCPRRKEKVEETELYLNLERIPWVVRKLLQVSESTGTVLKRSSWLIVLLVLFTVSLSPVQSAPIGHGKTIETYRVREEYTSICLFVLGSILFIVSFLMKGLVDGVGNIFFPGLSVCKTCSIGSINGFEIESHKCYCSLFCCPYCRHCSADGEIHQLHLSICKKRKTGSNVMLAVCKRMCFRATMEVSNKALFIRSIINTTFVVCILILAVCVVSTSAVEMESLPAGTWEREEDLTNFCHQECQVTETECLCPYEALVLRRPLFLDSIVKGMKNLLNSTSLETSLSIEAPWGAINVQSTYKPTVSTANIALSWSSVEHRGNKVLVSGRSESIMKLEERTGISWDLGVEDASESKLLTVSVMDLSQMYSPVFEYLSGDRQVEEWPKATCTGDCPERCGCTSSTCLHKEWPHSRNWRCNPTWCWGVGTGCTCCGLDVKDLFTDYMFVKWKVEYIKTEAIVCVELTSQERQCSLIEAGTRFNLGSVTITLSEPRNIQQKLPPEIITLHPKIEEGFFDLMHVQKVLSASTVCKLQSCTHGVPGDLQVYHIGNLLKGDRVNGHLIHKIEQHFNTSWMSWDGCDLDYYCNMGDWPSCTYTGVTQHNHASFVNLLNIETDYTKTFHFHSKRVTAHGDTPQLDLKARPTYGAGEITVLVEVADMELHTKKIEISGLKFASLTCTGCYACSSGISCKVRIHVDEPDELTVHVKSDDPDVVAASSSLMARKLEFGTDSTFKAFSAMPKTSLCFYIVEREYCKSCSKEDTQKCVNTKLEQPQSILIEHKGTIIGKQNNTCTAKASCWLESVKSFFYGLKNMLGGIFGNVFIGIFTFLTPFILLILFFMFGWRILFCFKCCRRTRGLFKYRHLKDDEETGYRKIIERLNNKKGKNRLLDGERLADRKIAELFSTKTHIG*' + - name: OR047158 + displayName: M (OR047158.1) + sequence: 'GCCCAGCGCCGAATCCCCGCCGCGCGTCGCGGCGTGGGAAATGTGGCGTACGGAAGGGGAACATCTCATTTGAAGCATGTTGTTCCACTTCGAACATATGCTGTTGATTAACTTCGTTTTCTGCCAGATTCTCTGGAGCGGTGGAGGACTTGCTGATGAGGCCAGAACGAACTCCAGTATGACACAGGGCGCCACAGCACCCTCGATCACCCCGAATGACACATCCCCCGTCTCAGAAACACCGACAGAAGCAGCCACAACAGCAGCACCAGATATGACCACAGAAACATTAGACACACAGGCCGCCACAGACTATGGTTCTGGTGAGTCTGGCACCAATCTCCCAACCACCAGAGAAGCAGCCCCTCCCCCACCAACCATCACCGTCCAGCCACCGGCAACAGAGAAACATACAGCCCAAAAATCAAGCACCACAACAAGCCCGACCGCCACCACTCCGGCCGTCCCCACTCCAGGGCCACCAGCTGCAGGCTTGCCCACGCCATCAACCATACAAGCAGGCCCATCGTCAGACCCTGACGATATAACAACACCCCAAACGAATCATCACCTCTCCAGAAGTCTTCTCTCTGCAGCAGGCACTGAAACAAGTCAACCAGCACCAGTGCTAACCGCATCAGAGGAGGCCGCAAGCACTGAAAGCCAACAGACAGCCACAAACAGCCCAGCAGCCATACCAGCCCAGAACATCACATCTCACAACATCTCAATACAAACCAGTCCTGCAGTCCAAGAAACAACCTCCGGAGCACTAACGGCACAAAGCCTACAAACCACTACACCAGAGACTACACTTCCCTCAGCCACTGCTGCACCCATAGGTCCAACAAATAGATCCAAAAGAGATTCCAAAGTTGAGATAATTCTAACCTTCTCTCAGGGTCTTAGAAAGTATTATAGCAAGGTACTAAAGCTGTTGCACCTAACACAAGAAGAGGACTCTGAAGGTCTACTTGAATGGTGCACACGAATACTAAAACTGACATGCAATGATGACTATTTCTACAAGAGAATAGAAGAATTCTTTATAACCGGAGAAGGTCACTTTAACGATGCTTTACAATTCAAGTTGCATGGCACACCAAGTACCACAGAATCCACTCAAACTGCTTCACCCACAACCATGCCCTTCAAATCTTACTATGCAAAAGGGTATCTGACCTTTGATTCAGGATACTTCTCTGCTAAATGCTATCCAAGAGCATCAAACTCAGGATTACAGTTGATTAATGTCACACAACACCCTTTAAAAGTAGCTAACACTCCTGGTCCAAAGCTCTCCAACCTTAAAACCATCAATTGTATAAATTTAAAAGTGTCAACCGACAAAGAGCACAGCGAGCTTGAGATAAACGTGCTGCTACCACAAGTTGCTATTAACCTTACGAACTGTCATGCTTCAATTAAATCACATGTCTGTGACTACTCCTTAAACACTGATGGAACAATAAAGCTTCCAGAAGTTACACACAATGGAATTTTCATACCGGGAACTTACAAAATTGTAATAGACAAGAAAAACAGACAAAATGACAGATGCATACTAACCACCAACTGTGTTATAAAAGGAAGAGAGGTTCGAAAGGGACAGTCAGCTTTGAGACAATATAAAACGGAAATAAGAGTTGGGCAAACATTCATAGGTTCAAGAAGGTTACTTGCAGAAGAGGGAAACAGCGATTGTGTTTCAAGAACACAGCTAATCAAAACAGAAATTGCAGAAATTCATGAAGACAGCTACGGGGGACCAGGTGAGAAAATAACAATTTGCAATGGCTCTACAATTGTAGATCAAAGGCTCGGCAGTGAATTAGGATGTTACACAATCAACAGAATAAAATCCTATAAGTTGTGTGAGAACAGTGCAACAGGAAAAACCTGTGAAATAGACAGTGTCCCAGTCAAGTGTGGACAAGGACACTGTCTTAAAATAACACAAGAAGGAAGAGGCCATGTTAAGTTATCCAGAGGGTCGGAGGTTGTTTTAGATGTCTGTGATTCTAGCTGTGAGCTGATGATACCTAAAGGGACTGGAGACATCTTAGTTGACTGCTCTGGGGGGCAGCAGCACTTTTTGCAGAACAATCTTGTTGATTTAGGATGCCCAAATGTTCCTTTGTTAGGAAAAATGGCCATATATGTCTGTAGGATGTCAAATCACCCTAAGACAACTATGGCTTTTTTGTTCTGGTTCAGCTTTGGTTATGTCTTAACATGCATAATATGCAAAGTGCTCTTCTACCTGCTAATTGTTGTTGGAACACTAGGAAAAAAACTGAAACAGTATAGGGAACTAAAGCCCCAAATATGTATTGTTTGCGAGTCCGTCCCTGTCAATGCAATAGATGCTGAGATGCACGATCTTAATTGCAATTATAATATATGTCCGTACTGTGCTTCCAGGCTAACTTCAGATGGGCTCATAAGACATGTGACACAATGCCCTAAACGGAAAGAGAAGGTCGAAGAAACTGAGCTCTACCTGAACTTGGAGAGAATACCCTGGCTGGTAAGAAAACTACTACAAGTATCAGAATCGACAGGAGTAGCTCTGAAGAGAAGCTGCTGGCTAACAACACTCCTAATACTGTTGTTAATCTCAATGTCACCAGTTCAATCTGCACCAGTTGCCCAGGGAAAAGCAATTGAAATCTATCAAGTCAGAGAAAGTTATACTGGCATGTGTCTTTTTTTATTAGGAAGTGTTCTCTTTGCAGTCTCTTGGCTAATTAAGGCTTTGACTGACAACATAGGCAACAGTTTCTTTCCTGGACTTTCAATTTGCAAGACATGTTCCATCGGCAGTATAAACGGGTTTGAAATTGAGTCTCACAAGTGTTATTGTAGTCTTCTTTGTTGTCCCTACTGTAGATTTTGTTCAGCTGACAAAATCACTCATCAAATGCACCTAAATATTTGCAAAAGAAGGAAAGAGAGTAGCAATGTCATGCTGGCTGTCTGTAAACGCATGTGCTTTAAAGCAACTATTGAAGCAAGCAGCAAAGCCCTGCTCATTAAGAGTATCATAAACTCTACTTTTGTATTGTGTGTGTTAATATTAGCAATCTGTGTGGTCAGCACTTCTGCTGTCGACATGGAGGATTTACCAGCAGGCACTTGGGAAAGGGAAGAAGACCTCACAAACTTCTGTCATCAGGAATGTCAGGTAACAGAGACTGAGTGTCTTTGTCCGTATGAAGCTTTGGTACTTAGAAAGCCTCTCTTCCTTGACAGTATAGCCAAAGGTATGAAAAGCCCGTTGAACTCTACAAGTTTAGAAACAAGCTTGTCAATTGAAGCACCATGGGGAGCAATCAATGTTCAATCAACTTTCAAACCAACAGTGTCAGCTGCTAACATAGCACTCAGCTGGAGCTCAGTAGAACACAGAGGCAACAAGATCCTTGTTTCAGGTAGATCAGAGTCAATCATGAAATTAGAAGAGAGGACAGGAATCAGTTGGAACTTGGGTGTAGAAGATGCCTCTGAATCAAAAACACTCACTGTATCAGTCATGGACCTGTCACAAATGTACTCTCCTGTTTTTGAGTATTTGTCAGGAGATCGGCAAGTGGAAGAATGGCCAAAGGCAACCTGCACTGGTGACTGCCCAGAGAGGTGTGGTTGCACTTCATCAACCTGCCTACATAAAGAATGGCCACACTCGAGAAACTGGCGATGCAATCCTACTTGGTGCTGGGGAGTGGGAACCGGCTGCACCTGCTGTGGGTTAGATGTTAAGGATCTTTTCACTGACTATATGCTGGTCAAATGGAAAGTGGACTACATAAAAACAGAAGCCATTGTATGTGTGGAACTGACTAGTCAGGAGAGGCAGTGTAGTTTAATTGAAGCAGGCACAAGGTTTAACTTGGGTCCAGTCACAATAACCTTATCTGAACCAAGGAACATACAACAGAGGCTTCCCCCTGAAATTGTTACATTACACCCAAAAATAGAGGATGGATTTTTTGATTTAATGCATGTACAAAAGGTGCTATCTGCAAGCACAGTTTGCAAACTACAAAGCTGCACACATGGTGTGCCAGGTGATTTACAGGTCTACCATGTTGGTAATCTGCTGAAGGGGGACAGAGTCAACGGTCACACAATTCACAAAATAGAATCACATCTCAACACCTCATGGATGTCGTGGGACGGATGTGATCTAGATTATTACTGCAACATGGGAGATTGGCCTTCGTGCACATATACTGGAGTGACACAACACAATCGTGCTGCATTCATAAACTTACTCAATATCGAAACTGATTATACAAAGACTTTCCACTTTCATTCAAAAAGGATTACAGCACATGGAGACACCCCTCAGTTGGATCTTAAGGCGAGGCCAACATATGGTGCTGGTGAAGTTACTGTCCTGGTGGAAGTTGCGGACATGGAGCTACACACAAAGAAAATCGAGGTGTCAGGTTTAAAATTTGCAAGCTTAACCTGTTCGGGCTGTTATGCCTGTAGTTCAGGCATCTCTTGCAAAGTTAGAATTCATGTGAATGAGCCAGATGAATTCACAGTGCATGTAAAGAGTAGCGATCCTGATGTTGTGGCAGCAGGGTCAAGCCTCATAGCAAGGAAATTAGAACTTGGGGCCGACAGCACATTTAAGGCCTTTTCATCAATGCCAAAAAGCTCTCTGTGCTTTTACATTGTAGAAAGAGAGTTCTGCAGCAGTTGCACCGAAGACGACACTCAAAAGTGTGTTAACACAAAACTTGAACACCCACAGAGTATACTTATTGAGCACAAAGGGACAATAATTGGCAAGCAAAATGACACCTGTTCGACCAGAACAAGCTGCTGGTTTGAGTCAGTTAAAAGTTTTTTTTACGGACTAAAAAACATGTTAAGCGGTATTTTTGGGAATGTCTTTATAGGCATCTTCTTGTTCCTTGCTCCTTTTGCACTGCTGGTGTTGTTTTTCTTTTTTGGATGGAGACTTCTGTTTTGTCTGAAATGTTGCAGAAGAACCAGAGGGCTGCTTAAATACAAACACCTTAAAGACGATGAAGAGGCAGGATACAAAAAAATCATTGAAAGGCTGAATGACAAGAAGGGTAAAAGCCGATTGTTTGATGGTGAAAGACTTGCAGATAGGAAGATTGCCGAACTCTTTTCAACTAAGACTCACATAGGTTAACGGAAAAAGTGAAGTTCTCTTGACCACAACAACATCAACACTGCTGATGTTGCTATATGCACTTCTCTAACAAAACATGCTTTAACTAACTTAAGGAACCATGACCGGCCAGCTCAGTGATCTACTGCTTTGCCAAATGTATTCAGCATGTTCTTCCAAAGCCACGGCATCCTCCCTTGGGGCCTCCACCCTTGCCAATGATTAATGGTTATAATATTAGAA' + insdcAccessionFull: OR047158.1 + genes: + - name: GPC + sequence: 'MLFHFEHMLLINFVFCQILWSGGGLADEARTNSSMTQGATAPSITPNDTSPVSETPTEAATTAAPDMTTETLDTQAATDYGSGESGTNLPTTREAAPPPPTITVQPPATEKHTAQKSSTTTSPTATTPAVPTPGPPAAGLPTPSTIQAGPSSDPDDITTPQTNHHLSRSLLSAAGTETSQPAPVLTASEEAASTESQQTATNSPAAIPAQNITSHNISIQTSPAVQETTSGALTAQSLQTTTPETTLPSATAAPIGPTNRSKRDSKVEIILTFSQGLRKYYSKVLKLLHLTQEEDSEGLLEWCTRILKLTCNDDYFYKRIEEFFITGEGHFNDALQFKLHGTPSTTESTQTASPTTMPFKSYYAKGYLTFDSGYFSAKCYPRASNSGLQLINVTQHPLKVANTPGPKLSNLKTINCINLKVSTDKEHSELEINVLLPQVAINLTNCHASIKSHVCDYSLNTDGTIKLPEVTHNGIFIPGTYKIVIDKKNRQNDRCILTTNCVIKGREVRKGQSALRQYKTEIRVGQTFIGSRRLLAEEGNSDCVSRTQLIKTEIAEIHEDSYGGPGEKITICNGSTIVDQRLGSELGCYTINRIKSYKLCENSATGKTCEIDSVPVKCGQGHCLKITQEGRGHVKLSRGSEVVLDVCDSSCELMIPKGTGDILVDCSGGQQHFLQNNLVDLGCPNVPLLGKMAIYVCRMSNHPKTTMAFLFWFSFGYVLTCIICKVLFYLLIVVGTLGKKLKQYRELKPQICIVCESVPVNAIDAEMHDLNCNYNICPYCASRLTSDGLIRHVTQCPKRKEKVEETELYLNLERIPWLVRKLLQVSESTGVALKRSCWLTTLLILLLISMSPVQSAPVAQGKAIEIYQVRESYTGMCLFLLGSVLFAVSWLIKALTDNIGNSFFPGLSICKTCSIGSINGFEIESHKCYCSLLCCPYCRFCSADKITHQMHLNICKRRKESSNVMLAVCKRMCFKATIEASSKALLIKSIINSTFVLCVLILAICVVSTSAVDMEDLPAGTWEREEDLTNFCHQECQVTETECLCPYEALVLRKPLFLDSIAKGMKSPLNSTSLETSLSIEAPWGAINVQSTFKPTVSAANIALSWSSVEHRGNKILVSGRSESIMKLEERTGISWNLGVEDASESKTLTVSVMDLSQMYSPVFEYLSGDRQVEEWPKATCTGDCPERCGCTSSTCLHKEWPHSRNWRCNPTWCWGVGTGCTCCGLDVKDLFTDYMLVKWKVDYIKTEAIVCVELTSQERQCSLIEAGTRFNLGPVTITLSEPRNIQQRLPPEIVTLHPKIEDGFFDLMHVQKVLSASTVCKLQSCTHGVPGDLQVYHVGNLLKGDRVNGHTIHKIESHLNTSWMSWDGCDLDYYCNMGDWPSCTYTGVTQHNRAAFINLLNIETDYTKTFHFHSKRITAHGDTPQLDLKARPTYGAGEVTVLVEVADMELHTKKIEVSGLKFASLTCSGCYACSSGISCKVRIHVNEPDEFTVHVKSSDPDVVAAGSSLIARKLELGADSTFKAFSSMPKSSLCFYIVEREFCSSCTEDDTQKCVNTKLEHPQSILIEHKGTIIGKQNDTCSTRTSCWFESVKSFFYGLKNMLSGIFGNVFIGIFLFLAPFALLVLFFFFGWRLLFCLKCCRRTRGLLKYKHLKDDEEAGYKKIIERLNDKKGKSRLFDGERLADRKIAELFSTKTHIG*' + - name: S + displayName: S segment + references: + - name: 1and6 + displayName: S11 (MK299338.1) + sequence: 'CTCAAAGAAACACGTGCCGCTCACGCCCACAGTGTTATCTTGAGTGTAAGCAAAATGGAGAACAAAATCGAGGTGAACAGCAAAGACGATCTGAACAAGTGGTTTGAAGAGTTCAAGAAAGGAAATGGGCTTGTGGACACCTTCACAAACTCTTACTCCTTTTGTGAAAATGTGCCAAACTTGGACAGATTTGTGTACCAAATGGCCAGTGCAACTGATGATGCACAAAAGGACTCCATCTATGCAACTGCCCTTGTTGAAGCGACCAAATTCTGTGCACCAATATATGAGTGTGCTTGGGTTAGCTCTACAGGAATTGTAAAGAAAGGTCTTGAGTGGTTCGAGAAAAACACAAGCACCATCAAGTCCTGGGATGAGAGCTACACTGAGCTAAAGGTTGACATTCCTAAAATAGAGCAACTCTCTAGCTATCAGCAGGCTGCCCTTAAGTGGAGAAAGGACATAGGTTTCCGTATCAATGCAAACACAACGGCATTGAGCAACAAGGTGCTTGCAGAGTACAAGGTTCCTGGGGAGATCTTGGTGCCTGTCAAAGAAATGCTGTCAGACATGATAAGGAGAAGGAACATCATCCTTAACAGAGGCAGTGACGAAAACCCACGAGGCCCAGTAAGCCACGAGCACATTGAGTGGTGCAGAGAGTTCATCAAGGGGAAGTACATCATGGCTTTCAATCCACCTTGGGGCGACATCAACAAGTCAGGCCGCTCTGGGATTGCACTTGTCGCAACCGGCCTTGCCAAGCTGGCGGAGACTGAAGGTAAAGGAGTTTTCGAGGAGGCCAAGAAGACTGTGGAGGCACTCAAGGATTACCTTGATAAACACAGAGATGAAGTCGATAAGGCAAGTGCTGACAACATGGTGACAAGCCTCTTGAAACACATTGCCAAGGCTCAAGAGCTCTACAAGAACTCATCTGCACTTCGTGCTCAGGGTGCACAGATTGACACTCCTTTCAGCTCATTCTACTGGCTCTACAAAGCAGGAGTTACTCCGGAAACATTTCCCACAGTCTCCCAGTTTCTCTTTGAGCTTGGAAAGCAGCCAAGGGGTACCAAGAAAATGAAAAAAGCCCTGCTCAGCACTCCCATGAAATGGGGAAAGAAGCTCTATGAGCTCTTTGCAGATGACTCTTTCCAGCAGAACAGGATCTACATGCACCCTGCTGTGTTGACAGCCGGCAGGATTAGCGAAATGGGTGTCTGCTTTGGAACAATTCCTGTAGCTAATCCAGATGATGCAGCCCAAGGATCTGGACACACCAAATCCATCCTGAATCTACGAACAAACACTGAGTCCAACAATCCTTGTGCCAGGACCATTGTCAAGCTCTTCGAGATTCAGAAGACAGGCTTTGACATAAAGGACATGGACATCGTGGCTTCTGAGCACCTGCTGCATCAATCCTTGGTTGGAAAGCAGTCACCCTTTCAGAATGCCTACAATGTTAAGGGTAACGCCACTAGCGCCAACATCATCTAGCTACTCAGGTGCTCTGCACTCCACCTATCCAACTCCGGATCAGTGCTTTCAGTTGCAACCAGTAATCATGCCTGCTTCAAACGCTGCTTTACTAAATTTTTTATTTTTATTCTTTCTTCATTTTTATTCTTTCTTCATTTTACACACAAAGGGGCTGTGCGGCAACGATATCTTTGA' + insdcAccessionFull: MK299338.1 + genes: + - name: NP + sequence: 'MENKIEVNSKDDLNKWFEEFKKGNGLVDTFTNSYSFCENVPNLDRFVYQMASATDDAQKDSIYATALVEATKFCAPIYECAWVSSTGIVKKGLEWFEKNTSTIKSWDESYTELKVDIPKIEQLSSYQQAALKWRKDIGFRINANTTALSNKVLAEYKVPGEILVPVKEMLSDMIRRRNIILNRGSDENPRGPVSHEHIEWCREFIKGKYIMAFNPPWGDINKSGRSGIALVATGLAKLAETEGKGVFEEAKKTVEALKDYLDKHRDEVDKASADNMVTSLLKHIAKAQELYKNSSALRAQGAQIDTPFSSFYWLYKAGVTPETFPTVSQFLFELGKQPRGTKKMKKALLSTPMKWGKKLYELFADDSFQQNRIYMHPAVLTAGRISEMGVCFGTIPVANPDDAAQGSGHTKSILNLRTNTESNNPCARTIVKLFEIQKTGFDIKDMDIVASEHLLHQSLVGKQSPFQNAYNVKGNATSANII*' + - name: 2to5 + displayName: S1 (OL774851.1) + sequence: 'GCCGCTTACGCCCACTGTGTTCTCTTGAGTCTTGGCAAAATGGAAAACAAAATCGAGGTGAATAACAAAGATGAGATGAACAAATGGTTTGAAGAGTACAAAAAAGGAAATGGACTTGTGGACACCTTCACAAACTCCTATTCCTTTTGCGAGAGTGTTCCAAATTTGGACAAGTTTGTGTTCCAAATGGCCAGTGCCACCGATGATGCACAAAAGGATTCCATCTACGCGTCTGCTCTGGTGGAGGCAACAAAATTTTGTGCACCTATATATGAGTGTGCGTGGGTTAGCTCCACTGGCATTGTGAAAAAGGGACTTGAATGGTTCGAGAAAAATGCGGGCACCATTAAGTCTTGGGATGAAAGTTATACTGAGCTAAAAGTTGATGTCCCAAAAATAGAACAACTTGCCAATTACCAACAAGCTGCCTTGAAATGGAGAAAGGACATAGGTTTCCGTGTCAATGCAAACACAGCGGCTCTGAGCAACAAAGTCCTCGCAGAGTACAAAGTTCCTGGCGAGATTGTAATGTCTGTCAAAGAGATGCTGTCAGACATGATTCGGAGAAGAAACCAAATTCTAAACAGGGGTGGTGATGAGAATCCACGTGGCCCTGTGAGCCGTGAGCATGTGGACTGGTGCAGGGAGTTTGTCAAAGGCAAATACATCATGGCCTTCAACCCACCATGGGGGGACATCAACAAGTCAGGCCGTTCAGGAATAGCACTTGTCGCAACAGGCCTTGCCAAGCTTGCAGAGACTGAGGGAAAGGGAGTTTTTGACGAAGCCAAAAAGACCGTAGAGGCCCTCAATGGATATCTGGACAAGCATAAGGACGAAGTTGACAGGGCGAGTGCTGACAGTATGATAACAAACCTTCTCAAGCACATTGCCAAGGCACAGGAGCTCTATAAGAATTCGTCTGCACTCCGTGCACAAGGTGCACAGATTGACACTGCCTTCAGCTCATACTACTGGCTTTACAAAGCTGGCGTGACCCCAGAAACCTTCCCGACGGTGTCGCAGTTCCTCTTCGAGCTAGGGAAGCAGCCAAGAGGTACTAAGAAAATGAAGAAGGCTCTGCTGAGCACCCCAATGAAGTGGGGAAAGAAACTTTATGAACTCTTTGCCGACGATTCTTTCCAGCAGAACAGGATCTACATGCACCCTGCCGTGCTTACAGCTGGCAGAATCAGTGAAATGGGAGTCTGCTTTGGGACAATCCCCGTGGCCAATCCTGATGATGCTGCCCAAGGATCTGGACATACCAAGTCTATTCTCAACCTCCGGACTAACACCGAAACCAACAATCCATGTGCCAGGACCATTGTCAAGCTGTTTGAAATCCAGAAAACAGGGTTCAACATTCAGGACATGGACATAGTGGCCTCTGAGCACTTGCTACATCAGTCTCTTGTTGGCAAGCAGTCTCCATTCCAGAACGCCTACAACGTCAAGGGCAATGCCACCAGTGCTAACATTATCTAAAATGCAAACTGCTCTGTATCCAGCTTCCTTCCTTCTGAACCGCCACCCATAGCTGCAATACTCAATCATGCTTATTTTACTTGCTCATATAACCTTATTTTATTAACCTTTCTCTATTTTCTCTTGTCTTAAACACTTGGAGGGCTGTGCGGCAACGATATCTTCTTCTCCTCAGCTAGGCGGAACCTGCTCTGCTGGGCCCAACGGCTGCAGAAGAACTTTCTCCCGCCCGAACCGTAGCCGCGACCCGCAGTCTCGCCCGCTCAGCGGCCTGAGGCGCGTCGCTTTCTCGTGTCCCCTCGTCGGGCCTAATCGCCG' + insdcAccessionFull: OL774851.1 + genes: + - name: NP + sequence: 'MENKIEVNNKDEMNKWFEEYKKGNGLVDTFTNSYSFCESVPNLDKFVFQMASATDDAQKDSIYASALVEATKFCAPIYECAWVSSTGIVKKGLEWFEKNAGTIKSWDESYTELKVDVPKIEQLANYQQAALKWRKDIGFRVNANTAALSNKVLAEYKVPGEIVMSVKEMLSDMIRRRNQILNRGGDENPRGPVSREHVDWCREFVKGKYIMAFNPPWGDINKSGRSGIALVATGLAKLAETEGKGVFDEAKKTVEALNGYLDKHKDEVDRASADSMITNLLKHIAKAQELYKNSSALRAQGAQIDTAFSSYYWLYKAGVTPETFPTVSQFLFELGKQPRGTKKMKKALLSTPMKWGKKLYELFADDSFQQNRIYMHPAVLTAGRISEMGVCFGTIPVANPDDAAQGSGHTKSILNLRTNTETNNPCARTIVKLFEIQKTGFNIQDMDIVASEHLLHQSLVGKQSPFQNAYNVKGNATSANII*' +referenceGenome: + nucleotideSequences: + - name: L + sequence: 'TCTCAAAGATATCAATCCCCCCGTTACCCACGTTAACACAGAGAGCTCTAGTAGTGGTCTTTCCTTTTGTGAAACCATGGACTTCTTGAGAAGCCTTGACTGGACTCAAGTGATTGCTGGTCAATATGTGTCCAACCCTAGGTTCAACATTTCTGATTATTTTGAGATTGTGCGGCAGCCTGGTGATGGGAACTGCTTCTATCACAGCATAGCTGAGTTAACCATGCCTAACAAAACAGATCACTCATATCATTACATCAAACGCCTAACCGAGTCGGCAGCACGGAAGTATTACCAAGAGGAGCCTGAAGCCAGACTTGTTGGCCTGAGCCTGGAAGATTACCTCAAGAGGATGCTGTCTGACAACGAGTGGGGATCAACTCTAGAAGCATCTATGTTGGCTAAAGAAATGGGCATTACCATCATCATTTGGACTGTTGCTGCCAGTGATGAAGTGGAAGCAGGTATAAAGTTCGGAGACGGTGATGTGTTTACAGCTGTGAACCTTTTGCACTCTGGACAAACACACTTTGATGCGCTCAGAATACTTCCGCAGTTTGAAACAGACACAAGAGAGGCCTTGAGCTTGATGGACAGGGTTATAGCTGTGGATCAGTTGACATCATCTTCTAGTGATGAACTGCAAGACTATGAAGACCTTGCCTTGGCACTCACAAGCGCAGAAGAATCAAATAGACGGTCAAGCTTGGATGAGGTCACATTGTCCAAGAAGCAAGCAGAGATACTAAGGCAAAAAGCATCTCAGTTGTCTAAATTGGTTAATAAAAGTCAGAACATACCGACCAGAGTCGGTAGAGTCTTGGATTGCATGTTCAACTGCAAATTATGTGTTGAGATATCAGCTGACACTTTAATTCTGAGGCCAGAATCAAAAGAGAAAATCGGTGAAATCATGTCATTGCGGCAGTTGGGGCATAAACTGCTGACACGAGACAAACAGATTAAGCAAGAGTTCTCCAGAATGAAACTCTACGTCACTAAAGATTTGCTTGACCATCTAGACGTTGGTGGGCTCTTGAGGGCTGCTTTCCCTGGAACAGGAATAGAAAGGCATATGCAGCTGCTACACTCTGAGATGATACTGGACATCTGCACTGTATCACTTGGTGTCATGCTGTCAACATTCTTATATGGTTCTAATAATAAAAACAAGAAGAAATTCATTACCAACTGTCTGCTCAGCACAGCCCTATCCGGAAAGAAGGTGTATAAAGTTCTCGGCAACCTAGGAAATGAACTGTTGTACAAGGCACCTAGAAAGGCCTTAGCAACTGTCTGCAGTGCCTTGTTTGGGAAGCAGATAAACAAACTTCAGAATTGCTTCAGGACCATAAGCCCTGTCAGCTTACTTGCATTGAGAAATCTAGACTTTGATTGTCTCAGTGTGCAAGACTATAATGGTATGATAGAAAACATGTCTAAATTAGACAACACTGATGTTGAATTCAACCACAGGGAGATAGCTGATCTCAACCAATTAACTTCTCGGCTCATCACATTAAGAAAGGAGAAAGACACTGACCTCCTCAAACAATGGTTTCCTGAAAGTGACCTCACCCGCAGAAGCATCAGGAATGCTGCAAACGCGGAGGAATTTGTCATATCTGAGTTCTTTAAGAAGAAGGACATTATGAAATTCATCAGCACTTCAGGCAGAGCAATGAGTGCAGGCAAGATTGGTAATGTCCTATCCTATGCACATAATCTTTATTTGAGTAAGTCAAGCCTAAATATGACCTCTGAAGACATCTCACAGCTTTTGATCGAGATTAAGCGACTGTATGCTTTACAAGAAGATTCTGAAGTGGAGCCGATAGCCATAATTTGTGATGGCATAGAAAGCAACATGAAACAGTTATTTGCTATATTGCCTCCTGACTGTGCAAGAGAGTGTGAAGTCCTCTTCGATGACATAAGAAATTCTCCAACACACAGCACAGCCTGGAAGCATGCACTCCGATTAAAAGGGACTGCATACGAAGGTCTGTTTGCAAATTGTTACGGCTGGCAATACATTCCGGAAGACATTAAACCAAGCCTGACCATGTTGATACAGACTTTGTTTCCTGACAAGTTCGAAGATTTCCTGGATCGAACCCAGTTGCATCCGGAGTTCAGAGACCTGACTCCCGACTTTTCGCTCACACAAAAGGTTCACTTTAAAAGAAATCAGATACCCAGTGTCGAAAATGTGCAAATCTCCATTGATGCGACGTTGCCTGAATCTGTGGAAGCAGTACCAGTGACAGAAAGAAAGATGTTCCCCCTTCCTGAGACTCCGCTAAGTGAGGTGCATTCAATAGAGCGTATAATGGAAAACTTTACTCGCCTCATGCATGGAGGAAGACTTTCGACCAAGAAAAGAGATGGAGATCCGGCGGAACAGGGCAACCAGCAGAGTATCACTGAACACGAGAGTTCCAGCATCTCTGCCTTTAAAGACTACGGAGAGAGAGGGATAGTCGAGGAAAATCACATGAAGTTTAGTGGAGAAGATCAGCTAGAGACAAGGCAGCTGTTGTTGGTGGAAGTTGGTTTTCAAACTGACATCGATGGGAAAATAAGGACAGACCACAAGAAGTGGAAAGACATATTAAAGCTATTAGAGCTACTAGGAATCAAGTGCTCATTCATTGCCTGTGCAGATTGCTCATCCACACCACCAGACAGATGGTGGATTACGGAGGACAGAGTGCGAGTCCTAAAAAATTCAGTCAGCTTCTTGTTCAATAAACTCTCCAGAAACTCACCTACAGAAGTAACTGACATAGTTGTTGGAGCTATAAGTACTCAAAAGGTTAGAAGTTATCTAAAGGCAGGAACTGCAACAAAAACCCCTGTGTCGACTAAAGACGTTCTGGAGACTTGGGAAAAGATGAAGGAGCACATACTCAACAGACCAACAGGACTGACACTGCCCACCAGTTTGGAACAGGCAATGCGCAAAGGACTGGTCGAAGGTGTGGTCATCTCCAAGGAAGGTTCTGAGTCATGTATCAATATGTTGAAGGAAAATTTGGACCGAATAACTGACGAATTCGAGCGAACAAAATTTAAACATGAACTTACTCAGAATATTACCACAAGTGAGAAGATGCTATTGAGTTGGTTGAGTGAAGACATCAAATCATCGAGATGTGGTGAGTGCCTCTCAAATATAAAGAAAGCCGTTGATGAAACTGCCAATCTATCAGGAAAGATTGAGCTGCTCGCTTATAATCTGCAACTCACCAATCACTGCAGCAACTGTCACCCCAATGGTGTAAACATTAGTAACACTTCTAATGTGTGCAAGAGATGCCCCAAGATTGAAGTGGTTAGCCATTGTGAAAATAAAGGCTTTGAGGACAGCAATGAATGCTTAACAGACCTAGATAGGCTTGTTAGGCTCACATTACCAGGGAAAACTGAGAAGGAGAGAAGAGTCAAACGTAATGTGGAATATCTTATAAAACTGATGATGAGCATGTCAGGCATTGATTGTATAAAATATCCCACAGGGCAGCTTATCACCCATGGAAGAGTGAGTGCAAAACATAACGATGGGAACCTGAAAGATAGAAGCGATGACGACCAAAGACTAGCTGAGAAGATAGACACTGTTAGGAAAGAGCTTTCAGAATCTAAACTGAAAGATTATTCAACTTATGCAAGGGGAGTGATATCAAATTCACTAAAAAACCTCTCAAGGCAAGGTAAATCAAAGTGTTCTGTGCCAAGATCTTGGCTCGAGAAAGTACTGTTTGACCTGAAGGTGCCTACTAAGGACGAAGAAGTGCTTATAAACATCAGAAACTCATTGAAAGCTAGATCCGAGTTTGTTAGAAATAACGATAAACTACTCATAAGGTCAAAAGAAGAACTAAAAAAATGTTTCGATGTGCAGTCTTTTAAATTGAAAAAAAACAAGCAACCTGTGCCCTTTCAGGTTGACTGTATATTGTTCAAAGAAGTGGCAGCTGAATGCATGAAGAGGTACATTGGCACACCTTATGAGGGAATTGTAGACACCTTAGTTTCTCTGATTAATGTGTTAACAAGGTTTACTTGGTTCCAGGAAGTGGTGCTATATGGTAAAATATGTGAGACCTTCCTCAGATGCTGCACAGAATTTAATAGGTCAGGGGTCAAACTGGTTAAGATAAGGCACTGTAACATTAACCTATCAGTCAAATTGCCATCAAATAAGAAAGAGAATATGTTATGTTGTCTATATAGTGGAAACATGGAGCTCTTGCAAGGACCTTTCTATTTGAACAGGAGACAAGCTGTCCTTGGTTCTTCATACCTTTACATTGTCATTACACTTTACATACAAGTGCTGCAGCAGTACAGGTGTCTAGAAGTTATAAACAGTGTGAGTGAAAAAACATTGCAAGACATTGAAAATCATTCCATGACTCTACTAGAAGATTCATTCAGGGAAATCACTTTTGCTCTTGAAGGTAGGTTTGAAGAATCTTATAAAATACGAACCTCGAGGTGCAGAGCCAGTGGGAATTTTCTGAACAGGAGCAGTAGAGACCACTTTATAAGCGTTGTTTCAGGCTTGAACCTAGTTTATGGCTTCCTCATAAAAGATAACTTACTAGCCAACTCTCAGCAACAGAACAAACAACTACAGATGCTTCGTTTTGGCATGCTTGCAGGGCTTAGTAGGCTTGTTTGTCCTAATGAGCTAGGAAAGAAATTTTCAACGAGCTGTAGAAGAATTGAAGACAACATTGCAAGGCTTTACCTGCAGACATCCATTTACTGTTCAGTCAGGGATGTGGAGGACAATGTTAAGCACTGGAAACAAAGAGATCTATGTCCTGAAGTAACCATTCCATGCTTTACAGTCTATGGAACCTTTGTCAACAGCGATAGACAACTGATCTTTGACATTTACAATGTGCATATATATAATAAAGAAATGGACAACTTTGATGAAGGATGTATCAGCGTCTTGGAAGAAACAGCAGAAAGGCACATGCTTTGGGAACTCGATCTGATGAATTCACTTTGTTCTGACGAAAAAAAAGATACTAGACCCGCAAGACTTTTACTAGGCTGCCCAAATGTGAGGAAAGCAGCAACCAGAGAAGGGAAGAAGCTGTTGAAGTTAAACAGTGACACATCCACAGACACACAGAGCATTGCTTCTGAAGTGTCGGACAGAAGGTCTTATAGTTCAAGTAAGAGTAGAATCCGTAGTATATTTGGTAGATACAACTCTCAGAAGAAACCATTTGAATTAAGGTCAGGTCTTGAGGTTTTCAATGATCCTTTCAATGATTATCAGCAAGCAATAACGGACATTTGCCAATTTTCTGAGTACACACCAAACAAAGAAAGCATTTTGAAAGACTGTCTTCAAATCATACGAAAAAACCCTAGCCACACAATGGGTTCTTTTGAGCTGATCCAGGCAATCTCAGAGTTCGGCATGAGCAAGTTTCCTCCCGAAAATATAGACAAAGCAAGAAGGGATCCGAAGAACTGGGTTAGCATCTCTGAAGTAACCGAAACAACAAGTATAGTTGCATCACCTAGAACTCATATGATGCTCAAGGATTGTTTTAAAATTATACTAGGTACTGAGAATAAGAAGATAGTCAAAATGCTTCGAGGTAAGCTAAAGAAACTCGGTGCTATCTCAACAAACATAGAGATCGGGAAAAGGGATTGCCTAGATCTACTCAGCACAGTAGATGGGCTAACAGACCAGCAGAAAGAAAATATTGTAAATGGGATATTTGAGCCCTCAAAGTTATCCTTCTACCATTGGAAGGAACTGGTCAAAAAAAACATTGATGAAGTTTTACTAACTGAAGATGGAAATCTGATCTTCTGCTGGCTGAAAACAATCTCTTCTTCAGTCAAAGGAAGCCTAAAGAAGAGACTCAAATTCATGAATATACATTCTCCAGAATTGATGCCGGAAAACTGTCTCTTTTCTAGTGAGGAATTCAATGAGTTAATTAAGTTGAAGAAACTCCTCCTCAATGAACAACAAGATGAACAGGAGCTGAAACAAGATCTTTTGATATCTTCTTGGATCAAGTGCATAACAGCTTGCAAGGATTTTGCAAGCATCAATGACAAGATTCAGAAGTTCATTTACCACCTGTCTGAAGAGCTATATGACATAAGGCTGCAGCATCTGGAACTGTCAAAGCTTAAGCAAGAGCACCCTAGTGTCAGCTTCACAAAAGAAGAAGTCTTAATAAAGCGGCTGGAGAAAAATTTCCTTAAGCAGCATAATTTAGAGATTATGGAAACTGTGAATCTTGTATTCTTTGCAGCCCTCTCAGCTCCCTGGTGCTTACACTATAAAGCACTAGAGTCTTATTTGGTGAGACATCCAGAAATACTTGACTGTGGATCTAAGGAGGACTGCAAACTCACCTTGCTTGATCTGTCAGTTTCTAAGCTCTTGGTTTGTTTGTATCAAAAAGATGATGAGGAGCTGATAAATAGCTCAAGTTTGAAACTTGGGTTTTTAGTGAAATATGTTGTCACCTTGTTCACATCCAATGGTGAACCTTTTTCACTCAGTCTCAATGACGGGGGTTTGGATCTTGATTTACACAAGACTACTGACGAAAAGTTACTACATCAAACAAAGATAGTTTTTGCTAAAATTGGTTTATCTGGGAACAGTTATGACTTTATCTGGACTACCCAAATGATAGCAAACAGCAATTTTAACGTCTGCAAAAGATTAACGGGAAGGAGTACTGGGGAAAGGCTCCCTAGAAGCGTTAGAAGCAAGGTCATATATGAGATGGTAAAATTAGTGGGAGAAACAGGCATGGCAATACTACAACAATTAGCTTTTGCACAAGCACTAAATTATGAGCACCGCTTCTATGCGGTCTTAGCACCTAAAGCGCAACTAGGAGGAGCAAGAGATTTGTTAGTGCAAGAGACTGGGACTAAAGTCATGCATGCAACCACTGAAATGTTTAGTAGAAATCTTTTAAAAACAACATCAGATGATGGCCTCACAAACCCACATCTTAAAGAAACAATCCTTAATGTGGGATTAGACTGTCTTGCTAACATGCGAAATCTTGACGGTAAGCCCATAAGTGAAGGTAGTAACTTGGTCAATTTCTACAAAGTCATATGTATCTCGGGTGATAATACCAAGTGGGGCCCGATACACTGCTGTTCTTTCTTTTCTGGCATGATGCAACAGGTTCTGAAAAATGTACCAGATTGGTGTTCATTTTATAAATTAACATTCATTAAAAACTTATGTAGACAGGTAGAAATACCTGCTGGCAGTATTAAGAAGATCTTAAATGTTCTTAGGTATAGATTGTGCAGCAAGGGAGGTGTAGAACAACATAGTGAAGAAGATCTGAGAAGACTGTTGACAGATAATTTAGACAGTTGGGATGGAAACGACACAGTTAAGTTCTTAGTTACAACTTATATAAGCAAAGGACTCATGGCGTTAAACAGTTACAATCATATGGGTCAGGGTATTCACCATGCAACATCTTCGGTGTTAACTTCCCTAGCTGCTGTGCTCTTTGAGGAGCTGGCAATTTTTTATCTTAAGAGAAGCTTACCCCAGACAACAGTACATGTTGAACATGCCGGTAGTTCAGATGATTACGCAAAGTGTATAGTGGTGACTGGTATACTATCCAAAGAGCTCTACTCCCAGTATGATGAAACATTTTGGAAACACGCTTGCAGACTCAAAAACTTCACGGCCGCGGTACAAAGATGCTGTCAAATGAAAGATAGTGCCAAAACCTTGGTGAGCGACTGCTTTCTCGAGTTTTACAGTGAGTTTATGATGGGCTACAGAGTAACCCCTGCTGTAATAAAGTTCATGTTTACTGGACTGATAAACAGCTCTGTGACCTCTCCTCAGAGTTTGATGCAAGCATGCCAAGTTTCATCCCAACAAGCAATGTATAATAGTGTTCCTCTTGTCACCAACACTGCCTTCACCCTATTAAGGCAGCAAATTTTCTTTAACCATGTTGAAGACTTTATCAGAAGGTATGGTATACTGACTCTTGGGACTTTGTCACCCTTTGGTAGGTTGTTCGTACCAACCTACTCTGGATTAGTCAGCTCAGCGGTTGCTTTAGAAGATGCTGAAGTCATTGCTAGAGCAGCCCAAACACTTCAAATGAACAGTGTGTCAATACAGTCAAGTAGCTTGACCACATTAGATAGCCTAGGTCGTAGTAGGACAAGTTCCACAGCTGAGGATAGCAGCAGTGTGAGCGACACAACTGCTGCTTCCCATGACTCAGGATCATCATCCTCAAGCTTCTCTTTTGAGCTCAATAGACCCCTGTCTGAAACTGAACTACAGTTCATTAAAGCACTAAGTAGTCTCAAGTCAACACAAGCCTGTGAAGTGATTCAAAATAGAATTACAGGTCTTTATTGCAACAGCAACGAAGGACCTCTTGATAGGCATAATGTCATTTACAGCAGCAGAATGGCAGACTCTTGCGATTGGCTAAAGGATGGTAAAAGGAGAGGGAATCTAGAACTAGCGAATAGGATCCAATCTGTACTGTGTATTCTGATAGCAGGATATTACAGGTCATTTGGAGGGGAAGGAACCGAGAAACAGGTAAAGGCATCATTGAATAGAGACGACAATAAAATCATAGAGGATCCTATGATACAACTAATTCCAGAAAAGCTGAGGAGAGAGTTAGAAAGGTTAGGTGTTTCTAGAATGGAAGTCGATGAGCTAATGCCAAGCATTAGTCCTGATGACACCTTAGCCCAGCTTGTAGCGAAAAAACTCATTAGCCTCAATGTTTCGACAGAAGAATACTCAGCTGAGGTATCTAGACTCAAACAAACACTGACAGCCCGAAATGTTTTGCACGGGTTAGCTGGAGGGATTAAGGAGCTTTCGCTTCCAATATATACAATATTCATGAAATCTTACTTCTTTAAAGACAATGTTTTCCTGTCACTAACAGATAGATGGTCTACCAAGCACAGTACAAACTATCGTGATAGTTGTGGCAAACAATTAAAAGGTAGAATAATTACCAAGTATACTCACTGGTTGGACACTTTTCTGGGCTGCTCTGTCTCCATCAACAGGCATACTACTGTTAAAGAGCCCTCCTTATTCAATCCGAACATCAGATGTGTGAATCTGATCACATTTGAGGACGGCCTGAGAGAACTATCAGTGATACAGAGTCACCTTAAAGTCTTTGAAAATGAGTTCACCAACTTAAATCTTCAATTCTCTGATCCGAACAGACAGAAACTTAGAATAGTTGAGTCTAGACCTGCAGAATCTGAGCTAGAGGCAAACCGTGCAGTAATTGTCAAGACCAAATTGTTTTCAGCAACTGAACAAGTTCGACTATCCAACAACCCTGCAGTTGTCATGGGCTACCTATTGGATGAATCAGCAATTTCAGAAGTCAAGCCTACCAAGGTTGACTTCTCAAATTTACTTAAAGACCGCTTCAAAATAATGCAATTTTTTCCTTCAGTGTTCACTTTGATTAAGATGCTGACAGATGAATCGTCAGATTCAGAAAAGAGTGGCCTTAGTCCAGATTTGCAACAAGTTGCAAGATACTCAAACCATTTGACCTTGCTCAGCAGAATGATTCAACAAGCAAAGCCAACCGTGACTGTTTTCTACATGCTAAAAGGTAACTTGATGAACACAGAGCCAACAGTTGCTGAGCTTGTCAGCTATGGTATAAAGGAAGGCAGATTTTTTAGGCTTTCCGACACCGGAGTCGATGCAAGCACATACTCTGTAAAATATTGGAAAATTCTTCACTGCATCTCTGCCATTGGATGTTTACCTTTGAGTCAAGCAGACAAATCTTCACTACTTATGAGCTTCTTAAACTGGAGGGTCAACATGGACATTAGAACATCTGACTGTCCACTATCTAGTCATGAAGCAAGTATACTGAGTGAATTTGATGGACAAGTTATCGCCAACATACTTGCCAGTGAATTGAGTTCTGTGAAACGAGATTCTGAACGCGAGGGTCTAACTGATCTCCTTGATTATCTAAACTCACCAACTGAATTGTTGAAGAAGAAGCCTTACTTAGGGACAACTTGCAAGTTCAACACCTGGGGAGACTCGAATAGATCTGGAAAGTTCACATACAGCAGCAGATCTGGAGAATCCATTGGAATCTTCATTGCAGGGAAATTGCACATCCATCTCTCATCTGAGTCCGTTGCCTTGTTGTGTGAAACTGAAAGACAAGTGCTTTCTTGGATGAGTAAGAGGAGGACTGAGGTAATAACTAAAGAACAGCATCAACTGTTTCTAAGTCTTCTCCCACAGTCTCATGAGTGTTTACAAAAGCACAAGGACGGTAGTGCGCTATCAGTCATACCTGATAGCAGCAACCCCCGATTACTTAAGTTTGTGCCCCTCAAAAAAGGTCTAGCAGTGGTGAAAATCAAAAAACAAATTTTAACAGTGAAGAAGCAGGTTGTGTTTGATGCAGAGAGCGAGCCTAGACTGCAGTGGGGGCATGGCTGCTTGTCCATTGTTTATGACGAAACTGATACTCAGACCACATACCATGAAAATCTTTTGAAGGTGAAGCATCTTGTAGACTGCTCTACAGATAGAAAAAAGCTTTTGCCCCAGTCAGTGTTTTCTGACTCCAAAGTTGTCCTTTCAAGGATCAAGTTCAAGACGGAGCTTCTCCTCAACTCATTGACGCTGCTCCACTGTTTCCTAAAACATGCTCCTAGTGATGCCATAATGGAGGTAGAGAGCAAAAGTAGCTTGCTACACAAGTACCTCAAATCGGGAGGTGTCAGGCAACGGAACACTGAAGTGCTCTTCAGAGAGAAGTTAAACAAGGTTGTTATAAAAGACAATCTTGAGCAAGGTGTGGAAGAGGAGATTGAGTTTTGCAACAACTTGACTAAGACTGTTTCAGAGAACCCATTACCACTCAGCTGTTGGTCTGAAGTTCAAAATTACATTGAAGACATAGGCTTTAACAATGTACTTGTTAACATTGACAGAAACACGGTGAAAAGTGAACTTTTATGGAAATTTACGTTAGACACCAATGTAAGCACCACAAGTACTATAAAAGACGTGAGGACATTGGTGTCCTACGTTAGCACTGAAACCATCCCTAAGTTCTTGCTTGCATTCTTACTTTATGAAGAAGTGTTAATGAACTTAATCAACCAGTGTAAGGCAGTAAAAGAACTCATCAACAGCACAGGACTCTCAGACTTGGAACTGGAAAGCTTACTCACTTTATGTGCTTTCTATTTCCAAAGTGAGTGCAGTAAGAGAGATGGTCCTAGATGCTCCTTTGCAGCACTATTAAGTCTAATCCATGAGGATTGGCAGAGGATAGGTAAAAACATTCTTGTTCGTGCAAACAATGAACTAGGTGATGTGTCACTGAAGGTTAACATTGTCCTGGTGCCTCTCAAGGACATGTCTAAGCCAAAGTCTGAGAGAGTGGTCATGGCTAGAAGGTCACTAAATCATGCTCTATCCTTGATGTTTCTGGACGAGATGTCACTACCTGAGCTAAAATCCTTATCCGTGAACTGCAAAATGGGGAACTTTGAAGGGCAGGAGTGCTTTGAGTTCACTATTCTGAAGGACAATAGCGCAAGACTAGATTACAACAAGTTGATTGACCACTGTGTGGACATGGAAAAAAAGAGGGAAGCGGTTAGAGCAGTAGAAGATTTAATTTTGATGTTGACAGGCAGAGCAGTCAAACCCAGCGCTGTAACACAGTTTGTACACGGGGACGAGCAGTGTCAAGAGCAAATAAGCTTAGATGATCTGATGGCAAACGACACGGTAACAGACTTTCCTGATAGGGAAGCAGAAGCCCTCAAAACAGGAAATCTTGGCTTTAACTGGGACTCAGATTGAACATGCCGCTTATAAGCCATTAATACCTTTCGGCGTCACAAGGACAAATGATGCAGTTTTAGCTGCATCATTCATTAACATTAAAGCCTTCAAACAAGCTAACTACTCTGCATTCTCCTCAATCAACTCAATTGCTTCAACTGATCTATTTTACTAGCTCATCGATCCTCTCTTTCTTAGCTATATCTTTGAGA' + - name: M-MH396653 + sequence: 'AAAGAAATACTTGCGGCACGTCAGTACGTAAGTGTTAACTTTGAGGAAGTGGATTGAGCATCTTGATTGCAGCATACTTGTCAACATCATGCATATATCATTGATGTATGCAGTTTTCTGCTTGCAACTGTGCGGTCTAGGGAAGACTAACGGACCACACAATGGGACTGAACACAATAATACACACGTTATGACAACGCCTGATGACAGTCAGAGCCCTGAACCGCCAGTGAGCACAGCCTTACCTGTCACACCGGACCCTTCAACTGTCACACCTTCAACACCAGCCAGCGGATTAGAAGGCTCAGGAGAGGTTTACACATCCTCTCCAATCACCACCAAGGGTTTGTCTCTGCCGGAGGCCACATCTGAGCCCCCTGCGACTACCAGCGTGGTCACTTCAAGTGCAAGTGATACCGATTCTAGCACACAGGCAGCCGGAGACACCCCCACACCAACAGTCCGCACGAGTCTGCCCAGCAGCCCTAGCACACCATCCACATCACAAGGCACACACTATCCCGTGAGGAGTCTGCTTTCAGTCACGAGCCCTAAGCCAGAAGAAACACCAACACCGTCAAAATCAGGCAAAGATAACTTAGCAACCAACAGCCCCCACCCAGCCACCAGCAGGCCAACAACCCCTCCCACAACAGCCCAGAAACCCACTGAAAACAACAGCCACAACACCACCGAACAGCTTGAGTCCTTAACACACTCAGCAACTTTAGGTTCAATGATCTCTCCAACACAGACAGTCCTCCCACAGAGTGTTACCTCTATAGCCATTCAAGACATTCATATCAGCCCAACAAACAGGTCTAAAAGAAACCTTGATATGGAAATAATCTTAACGTTATCTCAGGGTCTGAAAAAGTATTATGGCAAAATACTCAAGCTCCTGCATCTCACCTTAGAGGAAGACACTGAAGGCTTGTTAGAGTGGTGCAAGAGAAATCTCGGTCTTGACTGTGATGACACCTTCTTCCAAAAAAGAATTGAAGAATTCTTCATAACTGGTGAGGGTCATTTCAATGAAGTTTTACAATTTAGAACACTAGGCACATTGAGTACCACAGAGTCAACGCATGCTGGATCACCAACAGTTGAACCCTTCAAATCCTACTTTACTAAAGGTTTCCTTTCAATAGATTCAGGTTACTTCTCTGCCAAATGTTATTCAAGAACATCCAATTCAGGGCTCCAATTGATTAATGTTACCCGACATTCAACTAGGATAGCTGACACGCCTGGGCCCAAGATCACTAACCTAAAGACCATCAATTGCATAAACTTAAAAGCATCCGTCTTTAAAGAACATAGAGAGGTTGAAATCAATGTGCTTCTCCCTCAGGTTGCAGTCAACCTCTCAAACTGTCACATTGCAATCAAATCACATATCTGTGACTATTCTTTAGACACCGACGGGGCGATTAGGCTTCCTCAAATTCATCATGAAGGCACTTTTATCCCAGGTACTTACAAAATAGTGATAGACAAAAAAAGTAAGCTGAATGACAGGTGCACCCTATTCACCAACTGTGTGATAAAAGGAAGAGAAGTTCGTAAAGGCCAGTCAGTCCTAAGGCAATATAAGACAGAAATTAGAATTGGCAGGGCATCAACTGGTTCTAGGAGATTGCTTTCCGAAGAATCTGGTGATGACTGCATATCAAGAACTCAGCTACTGAGGACAGAGACTGCAGAGGTCCATGGCGATAACTATGGTGGTCCAGGTGATAAGATAACCATCTGTAATGGTTCAACTGTTGTAGATCAAAGACTGGGTAGTGAACTGGGGTGTTACACTATCAATAGAGTGAGGTCATTCAAGCTATGCGAAAACAGTGCCACAGGGAAGAGCTGTGAAATAGACAGTATCCCAGTCAAGTGTAAGCAGGGTTATTGCCTAAAAATCACTCAGGAAGGAAGGGGCCATGTGAAGTTATCTAGAGGCTCAGAAGTTGTCTTGGATGTATGTGATTCAAGCTGTGAAGTGATGATACCTAAGGGCACTGGTGACATTCTAGTAGATTGTTCAGGTGGTCAGCAACATTTTTTAAAAGACAACCTGGTTGACCTAGGGTGTCCCAAAATTCCGTTACTGGGCAAAATGGCTATTTATATCTGCAGGATGTCGAATCACCCCAGGACAACCATGGCCTTCCTCTTTTGGTTCAGCTTTGGCTATGTGATAACTTGTATACTTTGCAAGGCCATTTTTTTCTTATTAATAATTTTTGGAACACTAGGGAAAAGGTTCAAGCAGTACAGAGAGTTGAAACCCCAGACCTGCACCATATGTGAGACAACACCTGTAAATGCAATAGATGCTGAAATGCATGATCTCAATTGCAGTTACAATATATGTCCCTATTGTGCGTCTAGACTGACTTCAGATGGGCTTGCTAGACATGTAACACAATGTCCTAGACGGAAGGAGAAAGTGGAGGAAACCGAATTATACCTGAATTTAGAGAGAATTCCCTGGGTTGTAAGAAAGCTATTGCAGGTGTCAGAGTCCACTGGTACAGTATTAAAAAGGAGCAGCTGGCTAATTGTATTACTTGTGCTGTTCACAGTTTCATTGTCACCAGTTCAATCAGCACCCATTGGTCACGGGAAAACAATTGAAACATACCGGGTTAGGGAAGAATACACAAGTATTTGCCTCTTTGTACTAGGAAGTATCCTGTTTATAGTCTCTTTTCTAATGAAAGGACTGGTTGACGGTGTTGGCAACATCTTCTTTCCTGGGCTGTCCGTCTGTAAGACATGCTCTATAGGTAGCATTAATGGCTTTGAAATTGAGTCTCATAAGTGCTACTGTAGCTTGTTTTGTTGCCCTTACTGTAGGCACTGCTCTGCTGATGGAGAAATCCATCAGCTGCACTTGAGCATCTGCAAAAAAAGGAAGACAGGAAGTAATGTTATGCTGGCTGTTTGCAAACGCATGTGTTTCAGGGCAACTATGGAAGTAAGCAACAAAGCCCTATTTATCCGTAGCATTATCAACACCACTTTTGTTGTGTGCATACTGATACTAGCGGTTTGTGTTGTTAGCACCTCAGCAGTAGAGATGGAAAGCCTACCAGCTGGGACCTGGGAAAGAGAAGAAGACCTAACAAATTTCTGCCATCAGGAATGCCAGGTCACAGAGACTGAGTGCCTCTGCCCTTATGAAGCTCTAGTGCTCAGAAGGCCCCTATTTCTAGATAGCATAGTCAAAGGTATGAAAAATCTGCTAAACTCAACAAGTCTAGAAACAAGCTTATCAATTGAGGCACCGTGGGGAGCAATTAATGTTCAGTCAACCTACAAACCAACTGTATCAACTGCAAACATAGCACTTAGTTGGAGCTCAGTGGAACACAGAGGCAATAAGGTTTTGGTCTCAGGCAGATCAGAATCAATCATGAAGCTGGAAGAAAGGACAGGAATCAGCTGGGATCTTGGCGTGGAAGATGCCTCTGAGTCTAAGCTACTTACAGTTTCAGTCATGGACTTGTCTCAGATGTACTCTCCTGTCTTCGAGTACTTATCAGGTGACAGACAAGTGGAAGAGTGGCCTAAAGCAACCTGCACAGGTGACTGCCCAGAAAGATGTGGCTGCACATCATCAACCTGCTTACACAAAGAGTGGCCCCACTCAAGGAATTGGAGATGTAATCCTACTTGGTGCTGGGGTGTAGGGACTGGCTGCACCTGTTGTGGTTTAGATGTGAAAGACCTTTTCACAGATTACATGTTCGTCAAGTGGAAAGTTGAGTACATTAAGACAGAGGCCATAGTATGTGTAGAACTAACCAGTCAGGAAAGACAGTGTAGCTTGATTGAGGCGGGCACAAGATTCAATTTAGGTTCTGTGACTATTACATTGTCAGAACCAAGGAACATTCAACAAAAGCTCCCTCCTGAAATAATCACACTGCACCCCAAGATTGAGGAAGGTTTTTTTGACCTAATGCATGTACAAAAAGTGCTATCGGCAAGCACAGTGTGTAAGTTGCAGAGTTGCACACATGGTGTGCCAGGAGATCTGCAGGTCTACCACATCGGAAACCTATTAAAAGGGGACAGAGTAAACGGACATCTGATTCATAAAATTGAGCAACACTTCAACACATCCTGGATGTCTTGGGATGGTTGTGACCTAGACTACTACTGTAACATGGGAGACTGGCCTTCCTGCACATATACCGGAGTCACTCAGCATAACCATGCTTCATTTGTAAACCTGCTCAACATTGAAACTGATTATACAAAAACCTTTCACTTTCACTCTAAAAGGGTTACTGCACATGGAGACACACCACAACTAGATCTGAAGGCAAGGCCAACCTATGGTGCGGGTGAGATCACCGTGCTGGTGGAAGTTGCTGACATGGAATTACACACAAAGAAGATTGAAATATCAGGCTTAAAATTTGCAAGCCTAACTTGTACAGGTTGTTATGCTTGTAGTTCTGGCATCTCTTGTAAAGTTAGAATTCATGTAGATGAACCAGATGAACTTACAGTACATGTTAAAAGTGATGACCCAGATGTAGTTGCAGCTAGCTCAAGTCTCATGGCGAGGAAGCTTGAATTTGGAACAGACAGTACATTTAAAGCTTTCTCAGCCATGCCTAAAACCTCCCTATGTTTCTACATTGTGGAAAGAGAATACTGTAAGAGCTGCAGTAAAGAAGATACACAAAAATGTGTTAACACGAAACTCGAACAACCACAGAGCATTTTGATCGAACATAAGGGAACTATAATTGGAAAGCAAAACAATACTTGCACGGCTAAAGCGAGTTGCTGGTTAGAGTCAGTTAAGAGTTTTTTTTATGGTCTGAAGAATATGCTCGGTGGCATATTTGGCAATGTTTTTATAGGCATTTTCACATTTCTTACCCCCTTTATCTTGTTAATACTTTTCTTTATGTTTGGGTGGAGGATCCTGTTTTGCTTCAAGTGTTGCAGAAGAACCAGAGGCCTATTCAAGTACAGACACCTCAAAGACGATGAAGAAACTGGTTACAGAAAGATCATTGAAAGACTGAACAACAAAAAAGGAAAAAACAGGCTGCTTGATGGTGAAAGACTTGCTGACAGAAAGATTGCTGAACTGTTCTCCACAAAAACACACATTGGCTAGATCAACCGGAGGGGCCTGGGAGGTGACGGCCCTGCGCCGGCTGGTGCTGCTGCTCATGCTAATTCCTTTAATTGCATCCCCACCATATTACCATCACAATATGCCACATCTAAGCTGCTTCATTGTATCTACAAACAGACTCTGTAATGCTTGAAACTGCCTTCGCTCTGTTTGCTTTGACCTAAATCTTGACTGCGTGCCGCCACTAGGTTACTGCACATGGAGACACACCACAACTAGATCTGAAGGCAAGGCCAACCTATGGTGCGGGTGAGATCACCGTGCTGGTGGAAGTTGCTGACATGGAATTACAC' + - name: M-OR047158 + sequence: 'GCCCAGCGCCGAATCCCCGCCGCGCGTCGCGGCGTGGGAAATGTGGCGTACGGAAGGGGAACATCTCATTTGAAGCATGTTGTTCCACTTCGAACATATGCTGTTGATTAACTTCGTTTTCTGCCAGATTCTCTGGAGCGGTGGAGGACTTGCTGATGAGGCCAGAACGAACTCCAGTATGACACAGGGCGCCACAGCACCCTCGATCACCCCGAATGACACATCCCCCGTCTCAGAAACACCGACAGAAGCAGCCACAACAGCAGCACCAGATATGACCACAGAAACATTAGACACACAGGCCGCCACAGACTATGGTTCTGGTGAGTCTGGCACCAATCTCCCAACCACCAGAGAAGCAGCCCCTCCCCCACCAACCATCACCGTCCAGCCACCGGCAACAGAGAAACATACAGCCCAAAAATCAAGCACCACAACAAGCCCGACCGCCACCACTCCGGCCGTCCCCACTCCAGGGCCACCAGCTGCAGGCTTGCCCACGCCATCAACCATACAAGCAGGCCCATCGTCAGACCCTGACGATATAACAACACCCCAAACGAATCATCACCTCTCCAGAAGTCTTCTCTCTGCAGCAGGCACTGAAACAAGTCAACCAGCACCAGTGCTAACCGCATCAGAGGAGGCCGCAAGCACTGAAAGCCAACAGACAGCCACAAACAGCCCAGCAGCCATACCAGCCCAGAACATCACATCTCACAACATCTCAATACAAACCAGTCCTGCAGTCCAAGAAACAACCTCCGGAGCACTAACGGCACAAAGCCTACAAACCACTACACCAGAGACTACACTTCCCTCAGCCACTGCTGCACCCATAGGTCCAACAAATAGATCCAAAAGAGATTCCAAAGTTGAGATAATTCTAACCTTCTCTCAGGGTCTTAGAAAGTATTATAGCAAGGTACTAAAGCTGTTGCACCTAACACAAGAAGAGGACTCTGAAGGTCTACTTGAATGGTGCACACGAATACTAAAACTGACATGCAATGATGACTATTTCTACAAGAGAATAGAAGAATTCTTTATAACCGGAGAAGGTCACTTTAACGATGCTTTACAATTCAAGTTGCATGGCACACCAAGTACCACAGAATCCACTCAAACTGCTTCACCCACAACCATGCCCTTCAAATCTTACTATGCAAAAGGGTATCTGACCTTTGATTCAGGATACTTCTCTGCTAAATGCTATCCAAGAGCATCAAACTCAGGATTACAGTTGATTAATGTCACACAACACCCTTTAAAAGTAGCTAACACTCCTGGTCCAAAGCTCTCCAACCTTAAAACCATCAATTGTATAAATTTAAAAGTGTCAACCGACAAAGAGCACAGCGAGCTTGAGATAAACGTGCTGCTACCACAAGTTGCTATTAACCTTACGAACTGTCATGCTTCAATTAAATCACATGTCTGTGACTACTCCTTAAACACTGATGGAACAATAAAGCTTCCAGAAGTTACACACAATGGAATTTTCATACCGGGAACTTACAAAATTGTAATAGACAAGAAAAACAGACAAAATGACAGATGCATACTAACCACCAACTGTGTTATAAAAGGAAGAGAGGTTCGAAAGGGACAGTCAGCTTTGAGACAATATAAAACGGAAATAAGAGTTGGGCAAACATTCATAGGTTCAAGAAGGTTACTTGCAGAAGAGGGAAACAGCGATTGTGTTTCAAGAACACAGCTAATCAAAACAGAAATTGCAGAAATTCATGAAGACAGCTACGGGGGACCAGGTGAGAAAATAACAATTTGCAATGGCTCTACAATTGTAGATCAAAGGCTCGGCAGTGAATTAGGATGTTACACAATCAACAGAATAAAATCCTATAAGTTGTGTGAGAACAGTGCAACAGGAAAAACCTGTGAAATAGACAGTGTCCCAGTCAAGTGTGGACAAGGACACTGTCTTAAAATAACACAAGAAGGAAGAGGCCATGTTAAGTTATCCAGAGGGTCGGAGGTTGTTTTAGATGTCTGTGATTCTAGCTGTGAGCTGATGATACCTAAAGGGACTGGAGACATCTTAGTTGACTGCTCTGGGGGGCAGCAGCACTTTTTGCAGAACAATCTTGTTGATTTAGGATGCCCAAATGTTCCTTTGTTAGGAAAAATGGCCATATATGTCTGTAGGATGTCAAATCACCCTAAGACAACTATGGCTTTTTTGTTCTGGTTCAGCTTTGGTTATGTCTTAACATGCATAATATGCAAAGTGCTCTTCTACCTGCTAATTGTTGTTGGAACACTAGGAAAAAAACTGAAACAGTATAGGGAACTAAAGCCCCAAATATGTATTGTTTGCGAGTCCGTCCCTGTCAATGCAATAGATGCTGAGATGCACGATCTTAATTGCAATTATAATATATGTCCGTACTGTGCTTCCAGGCTAACTTCAGATGGGCTCATAAGACATGTGACACAATGCCCTAAACGGAAAGAGAAGGTCGAAGAAACTGAGCTCTACCTGAACTTGGAGAGAATACCCTGGCTGGTAAGAAAACTACTACAAGTATCAGAATCGACAGGAGTAGCTCTGAAGAGAAGCTGCTGGCTAACAACACTCCTAATACTGTTGTTAATCTCAATGTCACCAGTTCAATCTGCACCAGTTGCCCAGGGAAAAGCAATTGAAATCTATCAAGTCAGAGAAAGTTATACTGGCATGTGTCTTTTTTTATTAGGAAGTGTTCTCTTTGCAGTCTCTTGGCTAATTAAGGCTTTGACTGACAACATAGGCAACAGTTTCTTTCCTGGACTTTCAATTTGCAAGACATGTTCCATCGGCAGTATAAACGGGTTTGAAATTGAGTCTCACAAGTGTTATTGTAGTCTTCTTTGTTGTCCCTACTGTAGATTTTGTTCAGCTGACAAAATCACTCATCAAATGCACCTAAATATTTGCAAAAGAAGGAAAGAGAGTAGCAATGTCATGCTGGCTGTCTGTAAACGCATGTGCTTTAAAGCAACTATTGAAGCAAGCAGCAAAGCCCTGCTCATTAAGAGTATCATAAACTCTACTTTTGTATTGTGTGTGTTAATATTAGCAATCTGTGTGGTCAGCACTTCTGCTGTCGACATGGAGGATTTACCAGCAGGCACTTGGGAAAGGGAAGAAGACCTCACAAACTTCTGTCATCAGGAATGTCAGGTAACAGAGACTGAGTGTCTTTGTCCGTATGAAGCTTTGGTACTTAGAAAGCCTCTCTTCCTTGACAGTATAGCCAAAGGTATGAAAAGCCCGTTGAACTCTACAAGTTTAGAAACAAGCTTGTCAATTGAAGCACCATGGGGAGCAATCAATGTTCAATCAACTTTCAAACCAACAGTGTCAGCTGCTAACATAGCACTCAGCTGGAGCTCAGTAGAACACAGAGGCAACAAGATCCTTGTTTCAGGTAGATCAGAGTCAATCATGAAATTAGAAGAGAGGACAGGAATCAGTTGGAACTTGGGTGTAGAAGATGCCTCTGAATCAAAAACACTCACTGTATCAGTCATGGACCTGTCACAAATGTACTCTCCTGTTTTTGAGTATTTGTCAGGAGATCGGCAAGTGGAAGAATGGCCAAAGGCAACCTGCACTGGTGACTGCCCAGAGAGGTGTGGTTGCACTTCATCAACCTGCCTACATAAAGAATGGCCACACTCGAGAAACTGGCGATGCAATCCTACTTGGTGCTGGGGAGTGGGAACCGGCTGCACCTGCTGTGGGTTAGATGTTAAGGATCTTTTCACTGACTATATGCTGGTCAAATGGAAAGTGGACTACATAAAAACAGAAGCCATTGTATGTGTGGAACTGACTAGTCAGGAGAGGCAGTGTAGTTTAATTGAAGCAGGCACAAGGTTTAACTTGGGTCCAGTCACAATAACCTTATCTGAACCAAGGAACATACAACAGAGGCTTCCCCCTGAAATTGTTACATTACACCCAAAAATAGAGGATGGATTTTTTGATTTAATGCATGTACAAAAGGTGCTATCTGCAAGCACAGTTTGCAAACTACAAAGCTGCACACATGGTGTGCCAGGTGATTTACAGGTCTACCATGTTGGTAATCTGCTGAAGGGGGACAGAGTCAACGGTCACACAATTCACAAAATAGAATCACATCTCAACACCTCATGGATGTCGTGGGACGGATGTGATCTAGATTATTACTGCAACATGGGAGATTGGCCTTCGTGCACATATACTGGAGTGACACAACACAATCGTGCTGCATTCATAAACTTACTCAATATCGAAACTGATTATACAAAGACTTTCCACTTTCATTCAAAAAGGATTACAGCACATGGAGACACCCCTCAGTTGGATCTTAAGGCGAGGCCAACATATGGTGCTGGTGAAGTTACTGTCCTGGTGGAAGTTGCGGACATGGAGCTACACACAAAGAAAATCGAGGTGTCAGGTTTAAAATTTGCAAGCTTAACCTGTTCGGGCTGTTATGCCTGTAGTTCAGGCATCTCTTGCAAAGTTAGAATTCATGTGAATGAGCCAGATGAATTCACAGTGCATGTAAAGAGTAGCGATCCTGATGTTGTGGCAGCAGGGTCAAGCCTCATAGCAAGGAAATTAGAACTTGGGGCCGACAGCACATTTAAGGCCTTTTCATCAATGCCAAAAAGCTCTCTGTGCTTTTACATTGTAGAAAGAGAGTTCTGCAGCAGTTGCACCGAAGACGACACTCAAAAGTGTGTTAACACAAAACTTGAACACCCACAGAGTATACTTATTGAGCACAAAGGGACAATAATTGGCAAGCAAAATGACACCTGTTCGACCAGAACAAGCTGCTGGTTTGAGTCAGTTAAAAGTTTTTTTTACGGACTAAAAAACATGTTAAGCGGTATTTTTGGGAATGTCTTTATAGGCATCTTCTTGTTCCTTGCTCCTTTTGCACTGCTGGTGTTGTTTTTCTTTTTTGGATGGAGACTTCTGTTTTGTCTGAAATGTTGCAGAAGAACCAGAGGGCTGCTTAAATACAAACACCTTAAAGACGATGAAGAGGCAGGATACAAAAAAATCATTGAAAGGCTGAATGACAAGAAGGGTAAAAGCCGATTGTTTGATGGTGAAAGACTTGCAGATAGGAAGATTGCCGAACTCTTTTCAACTAAGACTCACATAGGTTAACGGAAAAAGTGAAGTTCTCTTGACCACAACAACATCAACACTGCTGATGTTGCTATATGCACTTCTCTAACAAAACATGCTTTAACTAACTTAAGGAACCATGACCGGCCAGCTCAGTGATCTACTGCTTTGCCAAATGTATTCAGCATGTTCTTCCAAAGCCACGGCATCCTCCCTTGGGGCCTCCACCCTTGCCAATGATTAATGGTTATAATATTAGAA' + - name: S-1and6 + sequence: 'CTCAAAGAAACACGTGCCGCTCACGCCCACAGTGTTATCTTGAGTGTAAGCAAAATGGAGAACAAAATCGAGGTGAACAGCAAAGACGATCTGAACAAGTGGTTTGAAGAGTTCAAGAAAGGAAATGGGCTTGTGGACACCTTCACAAACTCTTACTCCTTTTGTGAAAATGTGCCAAACTTGGACAGATTTGTGTACCAAATGGCCAGTGCAACTGATGATGCACAAAAGGACTCCATCTATGCAACTGCCCTTGTTGAAGCGACCAAATTCTGTGCACCAATATATGAGTGTGCTTGGGTTAGCTCTACAGGAATTGTAAAGAAAGGTCTTGAGTGGTTCGAGAAAAACACAAGCACCATCAAGTCCTGGGATGAGAGCTACACTGAGCTAAAGGTTGACATTCCTAAAATAGAGCAACTCTCTAGCTATCAGCAGGCTGCCCTTAAGTGGAGAAAGGACATAGGTTTCCGTATCAATGCAAACACAACGGCATTGAGCAACAAGGTGCTTGCAGAGTACAAGGTTCCTGGGGAGATCTTGGTGCCTGTCAAAGAAATGCTGTCAGACATGATAAGGAGAAGGAACATCATCCTTAACAGAGGCAGTGACGAAAACCCACGAGGCCCAGTAAGCCACGAGCACATTGAGTGGTGCAGAGAGTTCATCAAGGGGAAGTACATCATGGCTTTCAATCCACCTTGGGGCGACATCAACAAGTCAGGCCGCTCTGGGATTGCACTTGTCGCAACCGGCCTTGCCAAGCTGGCGGAGACTGAAGGTAAAGGAGTTTTCGAGGAGGCCAAGAAGACTGTGGAGGCACTCAAGGATTACCTTGATAAACACAGAGATGAAGTCGATAAGGCAAGTGCTGACAACATGGTGACAAGCCTCTTGAAACACATTGCCAAGGCTCAAGAGCTCTACAAGAACTCATCTGCACTTCGTGCTCAGGGTGCACAGATTGACACTCCTTTCAGCTCATTCTACTGGCTCTACAAAGCAGGAGTTACTCCGGAAACATTTCCCACAGTCTCCCAGTTTCTCTTTGAGCTTGGAAAGCAGCCAAGGGGTACCAAGAAAATGAAAAAAGCCCTGCTCAGCACTCCCATGAAATGGGGAAAGAAGCTCTATGAGCTCTTTGCAGATGACTCTTTCCAGCAGAACAGGATCTACATGCACCCTGCTGTGTTGACAGCCGGCAGGATTAGCGAAATGGGTGTCTGCTTTGGAACAATTCCTGTAGCTAATCCAGATGATGCAGCCCAAGGATCTGGACACACCAAATCCATCCTGAATCTACGAACAAACACTGAGTCCAACAATCCTTGTGCCAGGACCATTGTCAAGCTCTTCGAGATTCAGAAGACAGGCTTTGACATAAAGGACATGGACATCGTGGCTTCTGAGCACCTGCTGCATCAATCCTTGGTTGGAAAGCAGTCACCCTTTCAGAATGCCTACAATGTTAAGGGTAACGCCACTAGCGCCAACATCATCTAGCTACTCAGGTGCTCTGCACTCCACCTATCCAACTCCGGATCAGTGCTTTCAGTTGCAACCAGTAATCATGCCTGCTTCAAACGCTGCTTTACTAAATTTTTTATTTTTATTCTTTCTTCATTTTTATTCTTTCTTCATTTTACACACAAAGGGGCTGTGCGGCAACGATATCTTTGA' + - name: S-2to5 + sequence: 'GCCGCTTACGCCCACTGTGTTCTCTTGAGTCTTGGCAAAATGGAAAACAAAATCGAGGTGAATAACAAAGATGAGATGAACAAATGGTTTGAAGAGTACAAAAAAGGAAATGGACTTGTGGACACCTTCACAAACTCCTATTCCTTTTGCGAGAGTGTTCCAAATTTGGACAAGTTTGTGTTCCAAATGGCCAGTGCCACCGATGATGCACAAAAGGATTCCATCTACGCGTCTGCTCTGGTGGAGGCAACAAAATTTTGTGCACCTATATATGAGTGTGCGTGGGTTAGCTCCACTGGCATTGTGAAAAAGGGACTTGAATGGTTCGAGAAAAATGCGGGCACCATTAAGTCTTGGGATGAAAGTTATACTGAGCTAAAAGTTGATGTCCCAAAAATAGAACAACTTGCCAATTACCAACAAGCTGCCTTGAAATGGAGAAAGGACATAGGTTTCCGTGTCAATGCAAACACAGCGGCTCTGAGCAACAAAGTCCTCGCAGAGTACAAAGTTCCTGGCGAGATTGTAATGTCTGTCAAAGAGATGCTGTCAGACATGATTCGGAGAAGAAACCAAATTCTAAACAGGGGTGGTGATGAGAATCCACGTGGCCCTGTGAGCCGTGAGCATGTGGACTGGTGCAGGGAGTTTGTCAAAGGCAAATACATCATGGCCTTCAACCCACCATGGGGGGACATCAACAAGTCAGGCCGTTCAGGAATAGCACTTGTCGCAACAGGCCTTGCCAAGCTTGCAGAGACTGAGGGAAAGGGAGTTTTTGACGAAGCCAAAAAGACCGTAGAGGCCCTCAATGGATATCTGGACAAGCATAAGGACGAAGTTGACAGGGCGAGTGCTGACAGTATGATAACAAACCTTCTCAAGCACATTGCCAAGGCACAGGAGCTCTATAAGAATTCGTCTGCACTCCGTGCACAAGGTGCACAGATTGACACTGCCTTCAGCTCATACTACTGGCTTTACAAAGCTGGCGTGACCCCAGAAACCTTCCCGACGGTGTCGCAGTTCCTCTTCGAGCTAGGGAAGCAGCCAAGAGGTACTAAGAAAATGAAGAAGGCTCTGCTGAGCACCCCAATGAAGTGGGGAAAGAAACTTTATGAACTCTTTGCCGACGATTCTTTCCAGCAGAACAGGATCTACATGCACCCTGCCGTGCTTACAGCTGGCAGAATCAGTGAAATGGGAGTCTGCTTTGGGACAATCCCCGTGGCCAATCCTGATGATGCTGCCCAAGGATCTGGACATACCAAGTCTATTCTCAACCTCCGGACTAACACCGAAACCAACAATCCATGTGCCAGGACCATTGTCAAGCTGTTTGAAATCCAGAAAACAGGGTTCAACATTCAGGACATGGACATAGTGGCCTCTGAGCACTTGCTACATCAGTCTCTTGTTGGCAAGCAGTCTCCATTCCAGAACGCCTACAACGTCAAGGGCAATGCCACCAGTGCTAACATTATCTAAAATGCAAACTGCTCTGTATCCAGCTTCCTTCCTTCTGAACCGCCACCCATAGCTGCAATACTCAATCATGCTTATTTTACTTGCTCATATAACCTTATTTTATTAACCTTTCTCTATTTTCTCTTGTCTTAAACACTTGGAGGGCTGTGCGGCAACGATATCTTCTTCTCCTCAGCTAGGCGGAACCTGCTCTGCTGGGCCCAACGGCTGCAGAAGAACTTTCTCCCGCCCGAACCGTAGCCGCGACCCGCAGTCTCGCCCGCTCAGCGGCCTGAGGCGCGTCGCTTTCTCGTGTCCCCTCGTCGGGCCTAATCGCCG' + genes: + - name: RdRp + sequence: 'MDFLRSLDWTQVIAGQYVSNPRFNISDYFEIVRQPGDGNCFYHSIAELTMPNKTDHSYHYIKRLTESAARKYYQEEPEARLVGLSLEDYLKRMLSDNEWGSTLEASMLAKEMGITIIIWTVAASDEVEAGIKFGDGDVFTAVNLLHSGQTHFDALRILPQFETDTREALSLMDRVIAVDQLTSSSSDELQDYEDLALALTSAEESNRRSSLDEVTLSKKQAEILRQKASQLSKLVNKSQNIPTRVGRVLDCMFNCKLCVEISADTLILRPESKEKIGEIMSLRQLGHKLLTRDKQIKQEFSRMKLYVTKDLLDHLDVGGLLRAAFPGTGIERHMQLLHSEMILDICTVSLGVMLSTFLYGSNNKNKKKFITNCLLSTALSGKKVYKVLGNLGNELLYKAPRKALATVCSALFGKQINKLQNCFRTISPVSLLALRNLDFDCLSVQDYNGMIENMSKLDNTDVEFNHREIADLNQLTSRLITLRKEKDTDLLKQWFPESDLTRRSIRNAANAEEFVISEFFKKKDIMKFISTSGRAMSAGKIGNVLSYAHNLYLSKSSLNMTSEDISQLLIEIKRLYALQEDSEVEPIAIICDGIESNMKQLFAILPPDCARECEVLFDDIRNSPTHSTAWKHALRLKGTAYEGLFANCYGWQYIPEDIKPSLTMLIQTLFPDKFEDFLDRTQLHPEFRDLTPDFSLTQKVHFKRNQIPSVENVQISIDATLPESVEAVPVTERKMFPLPETPLSEVHSIERIMENFTRLMHGGRLSTKKRDGDPAEQGNQQSITEHESSSISAFKDYGERGIVEENHMKFSGEDQLETRQLLLVEVGFQTDIDGKIRTDHKKWKDILKLLELLGIKCSFIACADCSSTPPDRWWITEDRVRVLKNSVSFLFNKLSRNSPTEVTDIVVGAISTQKVRSYLKAGTATKTPVSTKDVLETWEKMKEHILNRPTGLTLPTSLEQAMRKGLVEGVVISKEGSESCINMLKENLDRITDEFERTKFKHELTQNITTSEKMLLSWLSEDIKSSRCGECLSNIKKAVDETANLSGKIELLAYNLQLTNHCSNCHPNGVNISNTSNVCKRCPKIEVVSHCENKGFEDSNECLTDLDRLVRLTLPGKTEKERRVKRNVEYLIKLMMSMSGIDCIKYPTGQLITHGRVSAKHNDGNLKDRSDDDQRLAEKIDTVRKELSESKLKDYSTYARGVISNSLKNLSRQGKSKCSVPRSWLEKVLFDLKVPTKDEEVLINIRNSLKARSEFVRNNDKLLIRSKEELKKCFDVQSFKLKKNKQPVPFQVDCILFKEVAAECMKRYIGTPYEGIVDTLVSLINVLTRFTWFQEVVLYGKICETFLRCCTEFNRSGVKLVKIRHCNINLSVKLPSNKKENMLCCLYSGNMELLQGPFYLNRRQAVLGSSYLYIVITLYIQVLQQYRCLEVINSVSEKTLQDIENHSMTLLEDSFREITFALEGRFEESYKIRTSRCRASGNFLNRSSRDHFISVVSGLNLVYGFLIKDNLLANSQQQNKQLQMLRFGMLAGLSRLVCPNELGKKFSTSCRRIEDNIARLYLQTSIYCSVRDVEDNVKHWKQRDLCPEVTIPCFTVYGTFVNSDRQLIFDIYNVHIYNKEMDNFDEGCISVLEETAERHMLWELDLMNSLCSDEKKDTRPARLLLGCPNVRKAATREGKKLLKLNSDTSTDTQSIASEVSDRRSYSSSKSRIRSIFGRYNSQKKPFELRSGLEVFNDPFNDYQQAITDICQFSEYTPNKESILKDCLQIIRKNPSHTMGSFELIQAISEFGMSKFPPENIDKARRDPKNWVSISEVTETTSIVASPRTHMMLKDCFKIILGTENKKIVKMLRGKLKKLGAISTNIEIGKRDCLDLLSTVDGLTDQQKENIVNGIFEPSKLSFYHWKELVKKNIDEVLLTEDGNLIFCWLKTISSSVKGSLKKRLKFMNIHSPELMPENCLFSSEEFNELIKLKKLLLNEQQDEQELKQDLLISSWIKCITACKDFASINDKIQKFIYHLSEELYDIRLQHLELSKLKQEHPSVSFTKEEVLIKRLEKNFLKQHNLEIMETVNLVFFAALSAPWCLHYKALESYLVRHPEILDCGSKEDCKLTLLDLSVSKLLVCLYQKDDEELINSSSLKLGFLVKYVVTLFTSNGEPFSLSLNDGGLDLDLHKTTDEKLLHQTKIVFAKIGLSGNSYDFIWTTQMIANSNFNVCKRLTGRSTGERLPRSVRSKVIYEMVKLVGETGMAILQQLAFAQALNYEHRFYAVLAPKAQLGGARDLLVQETGTKVMHATTEMFSRNLLKTTSDDGLTNPHLKETILNVGLDCLANMRNLDGKPISEGSNLVNFYKVICISGDNTKWGPIHCCSFFSGMMQQVLKNVPDWCSFYKLTFIKNLCRQVEIPAGSIKKILNVLRYRLCSKGGVEQHSEEDLRRLLTDNLDSWDGNDTVKFLVTTYISKGLMALNSYNHMGQGIHHATSSVLTSLAAVLFEELAIFYLKRSLPQTTVHVEHAGSSDDYAKCIVVTGILSKELYSQYDETFWKHACRLKNFTAAVQRCCQMKDSAKTLVSDCFLEFYSEFMMGYRVTPAVIKFMFTGLINSSVTSPQSLMQACQVSSQQAMYNSVPLVTNTAFTLLRQQIFFNHVEDFIRRYGILTLGTLSPFGRLFVPTYSGLVSSAVALEDAEVIARAAQTLQMNSVSIQSSSLTTLDSLGRSRTSSTAEDSSSVSDTTAASHDSGSSSSSFSFELNRPLSETELQFIKALSSLKSTQACEVIQNRITGLYCNSNEGPLDRHNVIYSSRMADSCDWLKDGKRRGNLELANRIQSVLCILIAGYYRSFGGEGTEKQVKASLNRDDNKIIEDPMIQLIPEKLRRELERLGVSRMEVDELMPSISPDDTLAQLVAKKLISLNVSTEEYSAEVSRLKQTLTARNVLHGLAGGIKELSLPIYTIFMKSYFFKDNVFLSLTDRWSTKHSTNYRDSCGKQLKGRIITKYTHWLDTFLGCSVSINRHTTVKEPSLFNPNIRCVNLITFEDGLRELSVIQSHLKVFENEFTNLNLQFSDPNRQKLRIVESRPAESELEANRAVIVKTKLFSATEQVRLSNNPAVVMGYLLDESAISEVKPTKVDFSNLLKDRFKIMQFFPSVFTLIKMLTDESSDSEKSGLSPDLQQVARYSNHLTLLSRMIQQAKPTVTVFYMLKGNLMNTEPTVAELVSYGIKEGRFFRLSDTGVDASTYSVKYWKILHCISAIGCLPLSQADKSSLLMSFLNWRVNMDIRTSDCPLSSHEASILSEFDGQVIANILASELSSVKRDSEREGLTDLLDYLNSPTELLKKKPYLGTTCKFNTWGDSNRSGKFTYSSRSGESIGIFIAGKLHIHLSSESVALLCETERQVLSWMSKRRTEVITKEQHQLFLSLLPQSHECLQKHKDGSALSVIPDSSNPRLLKFVPLKKGLAVVKIKKQILTVKKQVVFDAESEPRLQWGHGCLSIVYDETDTQTTYHENLLKVKHLVDCSTDRKKLLPQSVFSDSKVVLSRIKFKTELLLNSLTLLHCFLKHAPSDAIMEVESKSSLLHKYLKSGGVRQRNTEVLFREKLNKVVIKDNLEQGVEEEIEFCNNLTKTVSENPLPLSCWSEVQNYIEDIGFNNVLVNIDRNTVKSELLWKFTLDTNVSTTSTIKDVRTLVSYVSTETIPKFLLAFLLYEEVLMNLINQCKAVKELINSTGLSDLELESLLTLCAFYFQSECSKRDGPRCSFAALLSLIHEDWQRIGKNILVRANNELGDVSLKVNIVLVPLKDMSKPKSERVVMARRSLNHALSLMFLDEMSLPELKSLSVNCKMGNFEGQECFEFTILKDNSARLDYNKLIDHCVDMEKKREAVRAVEDLILMLTGRAVKPSAVTQFVHGDEQCQEQISLDDLMANDTVTDFPDREAEALKTGNLGFNWDSD*' + - name: GPC-MH396653 + sequence: 'MHISLMYAVFCLQLCGLGKTNGPHNGTEHNNTHVMTTPDDSQSPEPPVSTALPVTPDPSTVTPSTPASGLEGSGEVYTSSPITTKGLSLPEATSEPPATTSVVTSSASDTDSSTQAAGDTPTPTVRTSLPSSPSTPSTSQGTHYPVRSLLSVTSPKPEETPTPSKSGKDNLATNSPHPATSRPTTPPTTAQKPTENNSHNTTEQLESLTHSATLGSMISPTQTVLPQSVTSIAIQDIHISPTNRSKRNLDMEIILTLSQGLKKYYGKILKLLHLTLEEDTEGLLEWCKRNLGLDCDDTFFQKRIEEFFITGEGHFNEVLQFRTLGTLSTTESTHAGSPTVEPFKSYFTKGFLSIDSGYFSAKCYSRTSNSGLQLINVTRHSTRIADTPGPKITNLKTINCINLKASVFKEHREVEINVLLPQVAVNLSNCHIAIKSHICDYSLDTDGAIRLPQIHHEGTFIPGTYKIVIDKKSKLNDRCTLFTNCVIKGREVRKGQSVLRQYKTEIRIGRASTGSRRLLSEESGDDCISRTQLLRTETAEVHGDNYGGPGDKITICNGSTVVDQRLGSELGCYTINRVRSFKLCENSATGKSCEIDSIPVKCKQGYCLKITQEGRGHVKLSRGSEVVLDVCDSSCEVMIPKGTGDILVDCSGGQQHFLKDNLVDLGCPKIPLLGKMAIYICRMSNHPRTTMAFLFWFSFGYVITCILCKAIFFLLIIFGTLGKRFKQYRELKPQTCTICETTPVNAIDAEMHDLNCSYNICPYCASRLTSDGLARHVTQCPRRKEKVEETELYLNLERIPWVVRKLLQVSESTGTVLKRSSWLIVLLVLFTVSLSPVQSAPIGHGKTIETYRVREEYTSICLFVLGSILFIVSFLMKGLVDGVGNIFFPGLSVCKTCSIGSINGFEIESHKCYCSLFCCPYCRHCSADGEIHQLHLSICKKRKTGSNVMLAVCKRMCFRATMEVSNKALFIRSIINTTFVVCILILAVCVVSTSAVEMESLPAGTWEREEDLTNFCHQECQVTETECLCPYEALVLRRPLFLDSIVKGMKNLLNSTSLETSLSIEAPWGAINVQSTYKPTVSTANIALSWSSVEHRGNKVLVSGRSESIMKLEERTGISWDLGVEDASESKLLTVSVMDLSQMYSPVFEYLSGDRQVEEWPKATCTGDCPERCGCTSSTCLHKEWPHSRNWRCNPTWCWGVGTGCTCCGLDVKDLFTDYMFVKWKVEYIKTEAIVCVELTSQERQCSLIEAGTRFNLGSVTITLSEPRNIQQKLPPEIITLHPKIEEGFFDLMHVQKVLSASTVCKLQSCTHGVPGDLQVYHIGNLLKGDRVNGHLIHKIEQHFNTSWMSWDGCDLDYYCNMGDWPSCTYTGVTQHNHASFVNLLNIETDYTKTFHFHSKRVTAHGDTPQLDLKARPTYGAGEITVLVEVADMELHTKKIEISGLKFASLTCTGCYACSSGISCKVRIHVDEPDELTVHVKSDDPDVVAASSSLMARKLEFGTDSTFKAFSAMPKTSLCFYIVEREYCKSCSKEDTQKCVNTKLEQPQSILIEHKGTIIGKQNNTCTAKASCWLESVKSFFYGLKNMLGGIFGNVFIGIFTFLTPFILLILFFMFGWRILFCFKCCRRTRGLFKYRHLKDDEETGYRKIIERLNNKKGKNRLLDGERLADRKIAELFSTKTHIG*' + - name: GPC-OR047158 + sequence: 'MLFHFEHMLLINFVFCQILWSGGGLADEARTNSSMTQGATAPSITPNDTSPVSETPTEAATTAAPDMTTETLDTQAATDYGSGESGTNLPTTREAAPPPPTITVQPPATEKHTAQKSSTTTSPTATTPAVPTPGPPAAGLPTPSTIQAGPSSDPDDITTPQTNHHLSRSLLSAAGTETSQPAPVLTASEEAASTESQQTATNSPAAIPAQNITSHNISIQTSPAVQETTSGALTAQSLQTTTPETTLPSATAAPIGPTNRSKRDSKVEIILTFSQGLRKYYSKVLKLLHLTQEEDSEGLLEWCTRILKLTCNDDYFYKRIEEFFITGEGHFNDALQFKLHGTPSTTESTQTASPTTMPFKSYYAKGYLTFDSGYFSAKCYPRASNSGLQLINVTQHPLKVANTPGPKLSNLKTINCINLKVSTDKEHSELEINVLLPQVAINLTNCHASIKSHVCDYSLNTDGTIKLPEVTHNGIFIPGTYKIVIDKKNRQNDRCILTTNCVIKGREVRKGQSALRQYKTEIRVGQTFIGSRRLLAEEGNSDCVSRTQLIKTEIAEIHEDSYGGPGEKITICNGSTIVDQRLGSELGCYTINRIKSYKLCENSATGKTCEIDSVPVKCGQGHCLKITQEGRGHVKLSRGSEVVLDVCDSSCELMIPKGTGDILVDCSGGQQHFLQNNLVDLGCPNVPLLGKMAIYVCRMSNHPKTTMAFLFWFSFGYVLTCIICKVLFYLLIVVGTLGKKLKQYRELKPQICIVCESVPVNAIDAEMHDLNCNYNICPYCASRLTSDGLIRHVTQCPKRKEKVEETELYLNLERIPWLVRKLLQVSESTGVALKRSCWLTTLLILLLISMSPVQSAPVAQGKAIEIYQVRESYTGMCLFLLGSVLFAVSWLIKALTDNIGNSFFPGLSICKTCSIGSINGFEIESHKCYCSLLCCPYCRFCSADKITHQMHLNICKRRKESSNVMLAVCKRMCFKATIEASSKALLIKSIINSTFVLCVLILAICVVSTSAVDMEDLPAGTWEREEDLTNFCHQECQVTETECLCPYEALVLRKPLFLDSIAKGMKSPLNSTSLETSLSIEAPWGAINVQSTFKPTVSAANIALSWSSVEHRGNKILVSGRSESIMKLEERTGISWNLGVEDASESKTLTVSVMDLSQMYSPVFEYLSGDRQVEEWPKATCTGDCPERCGCTSSTCLHKEWPHSRNWRCNPTWCWGVGTGCTCCGLDVKDLFTDYMLVKWKVDYIKTEAIVCVELTSQERQCSLIEAGTRFNLGPVTITLSEPRNIQQRLPPEIVTLHPKIEDGFFDLMHVQKVLSASTVCKLQSCTHGVPGDLQVYHVGNLLKGDRVNGHTIHKIESHLNTSWMSWDGCDLDYYCNMGDWPSCTYTGVTQHNRAAFINLLNIETDYTKTFHFHSKRITAHGDTPQLDLKARPTYGAGEVTVLVEVADMELHTKKIEVSGLKFASLTCSGCYACSSGISCKVRIHVNEPDEFTVHVKSSDPDVVAAGSSLIARKLELGADSTFKAFSSMPKSSLCFYIVEREFCSSCTEDDTQKCVNTKLEHPQSILIEHKGTIIGKQNDTCSTRTSCWFESVKSFFYGLKNMLSGIFGNVFIGIFLFLAPFALLVLFFFFGWRLLFCLKCCRRTRGLLKYKHLKDDEEAGYKKIIERLNDKKGKSRLFDGERLADRKIAELFSTKTHIG*' + - name: NP-1and6 + sequence: 'MENKIEVNSKDDLNKWFEEFKKGNGLVDTFTNSYSFCENVPNLDRFVYQMASATDDAQKDSIYATALVEATKFCAPIYECAWVSSTGIVKKGLEWFEKNTSTIKSWDESYTELKVDIPKIEQLSSYQQAALKWRKDIGFRINANTTALSNKVLAEYKVPGEILVPVKEMLSDMIRRRNIILNRGSDENPRGPVSHEHIEWCREFIKGKYIMAFNPPWGDINKSGRSGIALVATGLAKLAETEGKGVFEEAKKTVEALKDYLDKHRDEVDKASADNMVTSLLKHIAKAQELYKNSSALRAQGAQIDTPFSSFYWLYKAGVTPETFPTVSQFLFELGKQPRGTKKMKKALLSTPMKWGKKLYELFADDSFQQNRIYMHPAVLTAGRISEMGVCFGTIPVANPDDAAQGSGHTKSILNLRTNTESNNPCARTIVKLFEIQKTGFDIKDMDIVASEHLLHQSLVGKQSPFQNAYNVKGNATSANII*' + - name: NP-2to5 + sequence: 'MENKIEVNNKDEMNKWFEEYKKGNGLVDTFTNSYSFCESVPNLDKFVFQMASATDDAQKDSIYASALVEATKFCAPIYECAWVSSTGIVKKGLEWFEKNAGTIKSWDESYTELKVDVPKIEQLANYQQAALKWRKDIGFRVNANTAALSNKVLAEYKVPGEIVMSVKEMLSDMIRRRNQILNRGGDENPRGPVSREHVDWCREFVKGKYIMAFNPPWGDINKSGRSGIALVATGLAKLAETEGKGVFDEAKKTVEALNGYLDKHKDEVDRASADSMITNLLKHIAKAQELYKNSSALRAQGAQIDTAFSSYYWLYKAGVTPETFPTVSQFLFELGKQPRGTKKMKKALLSTPMKWGKKLYELFADDSFQQNRIYMHPAVLTAGRISEMGVCFGTIPVANPDDAAQGSGHTKSILNLRTNTETNNPCARTIVKLFEIQKTGFNIQDMDIVASEHLLHQSLVGKQSPFQNAYNVKGNATSANII*' +displayName: CCHF (Multi-Ref) + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-cchf-multi-ref:8080" diff --git a/kubernetes/loculus/fixtures/organisms/cchf.yaml b/kubernetes/loculus/fixtures/organisms/cchf.yaml new file mode 100644 index 0000000000..ae121bc47b --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/cchf.yaml @@ -0,0 +1,1584 @@ +schema: + submissionDataTypes: + consensusSequences: true + maxSequencesPerEntry: 3 + loadSequencesAutomatically: true + earliestReleaseDate: + enabled: true + externalFields: + - ncbiReleaseDate + richFastaHeaderFields: + - displayName + files: + - name: annotations + displayName: Annotations + metadata: + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + header: Sample details + ingest: ncbiCollectionDate + order: 10 + orderOnDetailsPage: 200 + includeInDownloadsByDefault: true + notSearchable: true + columnWidth: 100 + type: string + - name: sampleCollectionDateRangeLower + displayName: Collection date (lower bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: lower + noInput: true + - name: sampleCollectionDateRangeUpper + displayName: Collection date (upper bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: upper + noInput: true + - name: displayName + displayName: Display name + definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{accessionVersion}/{sampleCollectionDate}`. + noInput: true + type: string + - name: ncbiReleaseDate + displayName: NCBI release date + definition: Date the viral nucleotide accession was first released on the INSDC. + type: date + header: INSDC + orderOnDetailsPage: 1000 + noInput: true + columnWidth: 100 + - name: earliestReleaseDate + displayName: Earliest release date + definition: The earliest release date for the accession, across all versions in both Loculus and the INSDC. + header: Sample details + type: date + rangeSearch: true + noInput: true + includeInDownloadsByDefault: true + orderOnDetailsPage: 300 + - name: ncbiUpdateDate + type: date + displayName: NCBI update date + definition: Date the viral nucleotide accession was last updated on the INSDC. + header: INSDC + orderOnDetailsPage: 1020 + noInput: true + perSegment: true + oneHeader: true + columnWidth: 100 + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 420 + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 440 + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 20 + orderOnDetailsPage: 400 + ingest: country + options: &id001 + - name: Afghanistan + - name: Albania + - name: Algeria + - name: American Samoa + - name: Andorra + - name: Angola + - name: Anguilla + - name: Antarctica + - name: Antigua and Barbuda + - name: Arctic Ocean + - name: Argentina + - name: Armenia + - name: Aruba + - name: Ashmore and Cartier Islands + - name: Atlantic Ocean + - name: Australia + - name: Austria + - name: Azerbaijan + - name: Bahamas + - name: Bahrain + - name: Baltic Sea + - name: Baker Island + - name: Bangladesh + - name: Barbados + - name: Bassas da India + - name: Belarus + - name: Belgium + - name: Belize + - name: Benin + - name: Bermuda + - name: Bhutan + - name: Bolivia + - name: Borneo + - name: Bosnia and Herzegovina + - name: Botswana + - name: Bouvet Island + - name: Brazil + - name: British Virgin Islands + - name: Brunei + - name: Bulgaria + - name: Burkina Faso + - name: Burundi + - name: Cambodia + - name: Cameroon + - name: Canada + - name: Cape Verde + - name: Cayman Islands + - name: Central African Republic + - name: Chad + - name: Chile + - name: China + - name: Christmas Island + - name: Clipperton Island + - name: Cocos Islands + - name: Colombia + - name: Comoros + - name: Cook Islands + - name: Coral Sea Islands + - name: Costa Rica + - name: Cote d'Ivoire + - name: Croatia + - name: Cuba + - name: Curacao + - name: Cyprus + - name: Czechia + - name: Democratic Republic of the Congo + - name: Denmark + - name: Djibouti + - name: Dominica + - name: Dominican Republic + - name: Ecuador + - name: Egypt + - name: El Salvador + - name: Equatorial Guinea + - name: Eritrea + - name: Estonia + - name: Eswatini + - name: Ethiopia + - name: Europa Island + - name: Falkland Islands (Islas Malvinas) + - name: Faroe Islands + - name: Fiji + - name: Finland + - name: France + - name: French Guiana + - name: French Polynesia + - name: French Southern and Antarctic Lands + - name: Gabon + - name: Gambia + - name: Gaza Strip + - name: Georgia + - name: Germany + - name: Ghana + - name: Gibraltar + - name: Glorioso Islands + - name: Greece + - name: Greenland + - name: Grenada + - name: Guadeloupe + - name: Guam + - name: Guatemala + - name: Guernsey + - name: Guinea + - name: Guinea-Bissau + - name: Guyana + - name: Haiti + - name: Heard Island and McDonald Islands + - name: Honduras + - name: Hong Kong + - name: Howland Island + - name: Hungary + - name: Iceland + - name: India + - name: Indian Ocean + - name: Indonesia + - name: Iran + - name: Iraq + - name: Ireland + - name: Isle of Man + - name: Israel + - name: Italy + - name: Jamaica + - name: Jan Mayen + - name: Japan + - name: Jarvis Island + - name: Jersey + - name: Johnston Atoll + - name: Jordan + - name: Juan de Nova Island + - name: Kazakhstan + - name: Kenya + - name: Kerguelen Archipelago + - name: Kingman Reef + - name: Kiribati + - name: Kosovo + - name: Kuwait + - name: Kyrgyzstan + - name: Laos + - name: Latvia + - name: Lebanon + - name: Lesotho + - name: Liberia + - name: Libya + - name: Liechtenstein + - name: Line Islands + - name: Lithuania + - name: Luxembourg + - name: Macau + - name: Madagascar + - name: Malawi + - name: Malaysia + - name: Maldives + - name: Mali + - name: Malta + - name: Marshall Islands + - name: Martinique + - name: Mauritania + - name: Mauritius + - name: Mayotte + - name: Mediterranean Sea + - name: Mexico + - name: Micronesia, Federated States of + - name: Midway Islands + - name: Moldova + - name: Monaco + - name: Mongolia + - name: Montenegro + - name: Montserrat + - name: Morocco + - name: Mozambique + - name: Myanmar + - name: Namibia + - name: Nauru + - name: Navassa Island + - name: Nepal + - name: Netherlands + - name: New Caledonia + - name: New Zealand + - name: Nicaragua + - name: Niger + - name: Nigeria + - name: Niue + - name: Norfolk Island + - name: North Korea + - name: North Macedonia + - name: North Sea + - name: Northern Mariana Islands + - name: Norway + - name: Oman + - name: Pacific Ocean + - name: Pakistan + - name: Palau + - name: Palmyra Atoll + - name: Panama + - name: Papua New Guinea + - name: Paracel Islands + - name: Paraguay + - name: Peru + - name: Philippines + - name: Pitcairn Islands + - name: Poland + - name: Portugal + - name: Puerto Rico + - name: Qatar + - name: Republic of the Congo + - name: Reunion + - name: Romania + - name: Ross Sea + - name: Russia + - name: Rwanda + - name: Saint Barthelemy + - name: Saint Helena + - name: Saint Kitts and Nevis + - name: Saint Lucia + - name: Saint Martin + - name: Saint Pierre and Miquelon + - name: Saint Vincent and the Grenadines + - name: Samoa + - name: San Marino + - name: Sao Tome and Principe + - name: Saudi Arabia + - name: Senegal + - name: Serbia + - name: Seychelles + - name: Sierra Leone + - name: Singapore + - name: Sint Maarten + - name: Slovakia + - name: Slovenia + - name: Solomon Islands + - name: Somalia + - name: South Africa + - name: South Georgia and the South Sandwich Islands + - name: South Korea + - name: South Sudan + - name: Southern Ocean + - name: Spain + - name: Spratly Islands + - name: Sri Lanka + - name: State of Palestine + - name: Sudan + - name: Suriname + - name: Svalbard + - name: Sweden + - name: Switzerland + - name: Syria + - name: Taiwan + - name: Tajikistan + - name: Tanzania + - name: Tasman Sea + - name: Thailand + - name: Timor-Leste + - name: Togo + - name: Tokelau + - name: Tonga + - name: Trinidad and Tobago + - name: Tromelin Island + - name: Tunisia + - name: Turkey + - name: Turkmenistan + - name: Turks and Caicos Islands + - name: Tuvalu + - name: Uganda + - name: Ukraine + - name: United Arab Emirates + - name: United Kingdom + - name: Uruguay + - name: USA + - name: Uzbekistan + - name: Vanuatu + - name: Venezuela + - name: Viet Nam + - name: Virgin Islands + - name: Wake Island + - name: Wallis and Futuna + - name: West Bank + - name: Western Sahara + - name: Yemen + - name: Zambia + - name: Zimbabwe + - name: Belgian Congo + - name: British Guiana + - name: Burma + - name: Czechoslovakia + - name: Czech Republic + - name: East Timor + - name: Korea + - name: Macedonia + - name: Micronesia + - name: Netherlands Antilles + - name: Serbia and Montenegro + - name: Siam + - name: Swaziland + - name: The former Yugoslav Republic of Macedonia + - name: USSR + - name: Yugoslavia + - name: Zaire + type: string + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + generateIndex: true + autocomplete: true + initiallyVisible: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 30 + orderOnDetailsPage: 460 + ingest: division + type: string + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + orderOnDetailsPage: 480 + type: string + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + generateIndex: true + autocomplete: true + header: Sample details + orderOnDetailsPage: 500 + type: string + - name: geoLocSite + ontology_id: GENEPIO:0100436 + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + header: Sample details + orderOnDetailsPage: 520 + type: string + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + header: Sample details + ingest: ncbiIsolateName + substringSearch: true + orderOnDetailsPage: 600 + type: string + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + type: authors + header: Authors + substringSearch: true + order: 40 + orderOnDetailsPage: 800 + includeInDownloadsByDefault: true + ingest: ncbiSubmitterNames + columnWidth: 140 + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + substringSearch: true + header: Authors + ingest: ncbiSubmitterAffiliation + includeInDownloadsByDefault: true + orderOnDetailsPage: 820 + type: string + - name: ncbiSubmitterCountry + displayName: NCBI submitter country + definition: The country representing the submitter's affilation. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: insdcAccessionBase + displayName: INSDC accession base + definition: The base accession assigned by the INSDC (GenBank/ENA/DDBJ) for this sequence, without the version number. + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + type: string + - name: insdcVersion + displayName: INSDC version + definition: The version number of the INSDC record, incremented each time the sequence record is updated. + type: int + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + - name: insdcAccessionFull + displayName: INSDC accession + definition: The full versioned INSDC accession (base accession plus version number), searchable across GenBank, ENA, and DDBJ. + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/nuccore/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://getentry.ddbj.nig.ac.jp/getentry/na/__value__ + header: INSDC + orderOnDetailsPage: 1040 + ingest: genbankAccession + noInput: true + perSegment: true + oneHeader: true + type: string + - name: bioprojectAccession + displayName: BioProject accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/bioproject/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/bioproject/__value__ + header: INSDC + orderOnDetailsPage: 1060 + ingest: bioprojects + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + type: string + - name: gcaAccession + displayName: GCA accession + definition: The GenBank genome assembly accession number for the sequence. + customDisplay: + type: link + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + header: INSDC + orderOnDetailsPage: 1100 + noInput: true + oneHeader: true + type: string + - name: biosampleAccession + displayName: BioSample accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/biosample/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/biosample/__value__ + header: INSDC + orderOnDetailsPage: 1080 + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + oneHeader: true + type: string + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + header: Sample details + orderOnDetailsPage: 620 + type: string + - name: sampleReceivedDate + ontology_id: GENEPIO:0001177 + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + type: date + orderOnDetailsPage: 260 + header: Sample details + - name: sampleType + displayName: Sample type + definition: Method of sampling. + header: Sampling + orderOnDetailsPage: 1200 + type: string + - name: purposeOfSampling + displayName: Purpose of sampling + ontology_id: GENEPIO:0001198 + definition: The reason that the sample was collected. + header: Sampling + orderOnDetailsPage: 1220 + ingest: ncbiPurposeOfSampling + type: string + - name: presamplingActivity + ontology_id: GENEPIO:0100433 + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + header: Sampling + orderOnDetailsPage: 1240 + type: string + - name: anatomicalMaterial + ontology_id: GENEPIO:0001211 + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + header: Sampling + orderOnDetailsPage: 1260 + type: string + - name: anatomicalPart + ontology_id: GENEPIO:0001214 + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + header: Sampling + orderOnDetailsPage: 1280 + type: string + - name: bodyProduct + ontology_id: GENEPIO:0001216 + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + header: Sampling + orderOnDetailsPage: 1300 + type: string + - name: environmentalMaterial + ontology_id: GENEPIO:0001223 + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + header: Sampling + orderOnDetailsPage: 1320 + type: string + - name: environmentalSite + ontology_id: GENEPIO:0001232 + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + header: Sampling + orderOnDetailsPage: 1340 + type: string + - name: collectionDevice + ontology_id: GENEPIO:0001234 + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + header: Sampling + orderOnDetailsPage: 1360 + type: string + - name: collectionMethod + ontology_id: GENEPIO:0001241 + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + header: Sampling + orderOnDetailsPage: 1380 + type: string + - name: foodProduct + ontology_id: GENEPIO:0100444 + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + header: Sampling + orderOnDetailsPage: 1400 + type: string + - name: foodProductProperties + ontology_id: GENEPIO:0100445 + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + header: Sampling + orderOnDetailsPage: 1420 + type: string + - name: specimenProcessing + ontology_id: GENEPIO:0100435 + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + header: Specimen processing + orderOnDetailsPage: 1500 + type: string + - name: specimenProcessingDetails + ontology_id: GENEPIO:0100311 + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + header: Specimen processing + orderOnDetailsPage: 1520 + type: string + - name: experimentalSpecimenRoleType + ontology_id: GENEPIO:0100921 + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + header: Specimen processing + type: string + - name: hostAge + ontology_id: GENEPIO:0001392 + definition: Age of host at the time of sampling. + displayName: Host age + type: int + header: Host + rangeSearch: true + - name: hostAgeBin + ontology_id: GENEPIO:0001394 + definition: The age category of the host at the time of sampling. + displayName: Host age bin + header: Host + type: string + - name: hostGender + ontology_id: GENEPIO:0001395 + definition: The gender of the host at the time of sample collection. + displayName: Host gender + header: Host + ingest: ncbiHostSex + orderOnDetailsPage: 1640 + type: string + - name: hostOriginCountry + ontology_id: GENEPIO:0100438 + definition: The country of origin of the host. + displayName: Host origin country + header: Host + type: string + - name: hostDisease + ontology_id: GENEPIO:0001391 + definition: The name of the disease experienced by the host. + displayName: Host disease + header: Host + type: string + - name: signsAndSymptoms + ontology_id: GENEPIO:0001400 + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + header: Host + type: string + - name: hostHealthState + ontology_id: GENEPIO:0001388 + definition: Health status of the host at the time of sample collection. + displayName: Host health state + header: Host + type: string + - name: hostHealthOutcome + ontology_id: GENEPIO:0001390 + definition: Disease outcome in the host. + displayName: Host health outcome + header: Host + orderOnDetailsPage: 1740 + type: string + - name: travelHistory + ontology_id: GENEPIO:0001416 + definition: Travel history in last six months. + displayName: Travel history + header: Host + orderOnDetailsPage: 1760 + type: string + - name: exposureEvent + ontology_id: GENEPIO:0001417 + definition: Event leading to exposure. + displayName: Exposure event + header: Host + orderOnDetailsPage: 1780 + type: string + - name: hostRole + ontology_id: GENEPIO:0001419 + definition: The role of the host in relation to the exposure setting. + displayName: Host role + header: Host + orderOnDetailsPage: 1800 + type: string + - name: exposureSetting + ontology_id: GENEPIO:0001428 + definition: The setting leading to exposure. + displayName: Exposure setting + header: Host + orderOnDetailsPage: 1820 + type: string + - name: exposureDetails + ontology_id: GENEPIO:0001431 + definition: Additional host exposure information. + displayName: Exposure details + header: Host + orderOnDetailsPage: 1840 + type: string + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + header: Host + orderOnDetailsPage: 1860 + type: string + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + header: Host + orderOnDetailsPage: 1880 + type: string + - name: hostVaccinationStatus + ontology_id: GENEPIO:0001404 + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + header: Host + orderOnDetailsPage: 1900 + type: string + - name: purposeOfSequencing + ontology_id: GENEPIO:0001445 + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + header: Sequencing + orderOnDetailsPage: 2000 + type: string + - name: sequencingDate + ontology_id: GENEPIO:0001447 + definition: The date the sample was sequenced. + displayName: Sequencing date + type: date + orderOnDetailsPage: 2020 + header: Sequencing + - name: ampliconPcrPrimerScheme + ontology_id: GENEPIO:0001456 + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + header: Sequencing + orderOnDetailsPage: 2040 + type: string + - name: ampliconSize + ontology_id: GENEPIO:0001449 + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + header: Sequencing + orderOnDetailsPage: 2060 + type: string + - name: sequencingInstrument + ontology_id: GENEPIO:0001452 + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + header: Sequencing + orderOnDetailsPage: 2080 + type: string + - name: sequencingProtocol + ontology_id: GENEPIO:0001454 + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + header: Sequencing + orderOnDetailsPage: 2100 + type: string + - name: sequencingAssayType + ontology_id: GENEPIO:0100997 + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + header: Sequencing + orderOnDetailsPage: 2120 + type: string + - name: sequencedByOrganization + ontology_id: GENEPIO:0100416 + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + header: Sequencing + orderOnDetailsPage: 2140 + type: string + - name: sequencedByContactName + ontology_id: GENEPIO:0100471 + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + header: Sequencing + orderOnDetailsPage: 2160 + type: string + - name: sequencedByContactEmail + ontology_id: GENEPIO:0100422 + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + header: Sequencing + orderOnDetailsPage: 2180 + type: string + - name: rawSequenceDataProcessingMethod + ontology_id: GENEPIO:0001458 + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + header: Sequencing + orderOnDetailsPage: 2200 + type: string + - name: dehostingMethod + ontology_id: GENEPIO:0001459 + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + header: Sequencing + orderOnDetailsPage: 2220 + type: string + - name: referenceGenomeAccession + ontology_id: GENEPIO:0001485 + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + header: Sequencing + orderOnDetailsPage: 2240 + type: string + - name: consensusSequenceSoftwareName + ontology_id: GENEPIO:0001463 + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + header: Sequencing + orderOnDetailsPage: 2260 + type: string + - name: consensusSequenceSoftwareVersion + ontology_id: GENEPIO:0001469 + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + header: Sequencing + orderOnDetailsPage: 2280 + type: string + - name: depthOfCoverage + ontology_id: GENEPIO:0001474 + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2300 + - name: breadthOfCoverage + ontology_id: GENEPIO:0001475 + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2320 + - name: qualityControlMethodName + ontology_id: GENEPIO:0100557 + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + header: Sequencing + orderOnDetailsPage: 2340 + type: string + - name: qualityControlMethodVersion + ontology_id: GENEPIO:0100558 + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + header: Sequencing + orderOnDetailsPage: 2360 + type: string + - name: qualityControlDetermination + ontology_id: GENEPIO:0100559 + definition: The determination of a quality control assessment. + displayName: Quality control determination + header: Sequencing + orderOnDetailsPage: 2380 + type: string + - name: qualityControlIssues + ontology_id: GENEPIO:0100560 + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + header: Sequencing + orderOnDetailsPage: 2400 + type: string + - name: qualityControlDetails + ontology_id: GENEPIO:0100561 + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + header: Diagnostics + orderOnDetailsPage: 2500 + type: string + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + header: Diagnostics + orderOnDetailsPage: 2520 + type: string + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + header: Diagnostics + orderOnDetailsPage: 2540 + type: string + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + header: Diagnostics + orderOnDetailsPage: 2560 + type: string + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + header: Diagnostics + orderOnDetailsPage: 2580 + type: string + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + orderOnDetailsPage: 2600 + header: Diagnostics + type: string + - name: length + type: int + header: Alignment and QC metrics + isSequenceFilter: true + noInput: true + rangeSearch: true + initiallyVisible: true + perSegment: true + displayName: Length + definition: The length of the sequence. + orderOnDetailsPage: 2700 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: hostTaxonId + type: string + autocomplete: true + displayName: Host species + definition: Taxon ID for the host. + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostTaxId + noInput: true + orderOnDetailsPage: 1540 + - name: hostNameScientific + displayName: Host name - scientific + definition: The scientific name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostName + noInput: true + orderOnDetailsPage: 1560 + type: string + - name: hostNameCommon + displayName: Host name - common + definition: The common name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostCommonName + noInput: true + orderOnDetailsPage: 1580 + type: string + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + type: boolean + autocomplete: true + header: Host + ingest: ncbiIsLabHost + orderOnDetailsPage: 1920 + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + generateIndex: true + orderOnDetailsPage: 1940 + autocomplete: true + header: Host + type: string + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + type: int + header: Host + orderOnDetailsPage: 1960 + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + generateIndex: true + autocomplete: true + header: Host + orderOnDetailsPage: 1980 + type: string + - name: ncbiSourceDb + displayName: NCBI source DB + definition: Indicates if the source of the viral nucleotide record is from a GenBank submitter or from NCBI-derived curation (RefSeq). + generateIndex: true + autocomplete: true + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + type: string + - name: ncbiVirusName + displayName: NCBI Virus name + definition: Scientific name for the organism from the NCBI Taxonomy. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: ncbiVirusTaxId + displayName: NCBI Virus tax ID + definition: Identifier for the organism from the NCBI Taxonomy. + type: int + autocomplete: true + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/labs/virus/vssi/#/virus?SeqType_s=Nucleotide&VirusLineage_ss=taxid:__value__ + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/sra/?term=__value__ + header: INSDC + orderOnDetailsPage: 1120 + ingest: ncbiSraAccessions + type: string + - name: totalSnps + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total SNPs + definition: Total count of nucleotide substitutions. + orderOnDetailsPage: 2720 + - name: totalInsertedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total inserted nucs + definition: Total count of inserted nucleotide positions. + orderOnDetailsPage: 2740 + - name: totalDeletedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + orderOnDetailsPage: 2760 + displayName: Total deleted nucs + definition: Total count of deleted nucleotide positions. + - name: totalAmbiguousNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total ambiguous nucs + definition: Total count of ambiguous nucleotides (not A, C, G, T, or N). + orderOnDetailsPage: 2780 + - name: totalUnknownNucs + orderOnDetailsPage: 2800 + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total unknown nucs + definition: Total count of missing (N) nucleotides. + - name: totalFrameShifts + isSequenceFilter: true + type: int + rangeSearch: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Total frame shifts + definition: Total count of detected frame shifts. + orderOnDetailsPage: 2820 + - name: frameShifts + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Frame shifts + definition: Frame-shifting insertions or deletions detected in CDS regions. + orderOnDetailsPage: 2840 + type: string + - name: totalStopCodons + isSequenceFilter: true + perSegment: true + displayName: Total stop codons + definition: Number of penalized premature stop codons. + type: int + header: Alignment and QC metrics + noInput: true + - name: stopCodons + isSequenceFilter: true + perSegment: true + displayName: Stop codons + definition: Premature stop codons not in the ignored list (penalized). + header: Alignment and QC metrics + noInput: true + type: string + - name: completeness + isSequenceFilter: true + type: float + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Completeness + definition: Fraction of reference positions covered by the sequence (0.0 to 1.0). + rangeSearch: true + percentage: true + orderOnDetailsPage: 2860 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: lineage + type: string + - name: versionComment + type: string + extraInputFields: + - name: host + type: string + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + desired: true + position: last + organismName: Crimean-Congo Hemorrhagic Fever Virus + image: /images/organisms/cchf_small.jpg + linkOuts: + - name: Nextclade (L) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:L+rich|fasta]}}&dataset-name=community/pathoplexus/cchfv/L + - name: Nextclade (M) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:M+rich|fasta]}}&dataset-name=community/pathoplexus/cchfv/M + - name: Nextclade (S) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:S+rich|fasta]}}&dataset-name=community/pathoplexus/cchfv/S + metadataAdd: + - name: hostNameScientific + displayName: Host name - scientific + definition: The scientific name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostName + initiallyVisible: true + - name: lineage + isSequenceFilter: true + relatesToSegment: S + displayName: Segment S Lineage + definition: Assigned clade label from the nearest reference tree node. + header: Lineage + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + lineageSystem: cchfS + tableColumns: + - sampleCollectionDate + - geoLocCountry + - geoLocAdmin1 + - lineage + - authors + - authorAffiliations + - ncbiReleaseDate + - hostNameScientific + - length_M + - length_S + - length_L + defaultOrderBy: sampleCollectionDate + defaultOrder: descending + multiFieldSearches: + - name: identifier + displayName: Sample Identifier + orderInSearchDisplay: -10000 + fields: + - accessionVersion + - submissionId + - insdcAccessionFull + - bioprojectAccession + - gcaAccession + - biosampleAccession + - insdcRawReadsAccession + - name: contributor + displayName: Contributor + orderInSearchDisplay: -9999 + fields: + - authors + - authorAffiliations + - sequencedByOrganization + - sequencedByContactName + - submitter + - groupName + inputFields: + - name: id + displayName: ID + example: GJP123 + noEdit: true + required: true + definition: Your sample identifier. (If no `fastaIds` column is provided, this sample ID will be used to associate the metadata with the sequence.) + - name: fastaIds + displayName: FASTA IDs + definition: Space-separated list of FASTA IDs of each sequence to be associated with this metadata entry. + example: GJP123 GJP124 + noEdit: true + desired: true + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + required: true + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + required: true + options: *id001 + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + - name: geoLocSite + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + - name: bioprojectAccession + displayName: BioProject accession + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + - name: biosampleAccession + displayName: BioSample accession + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + - name: sampleReceivedDate + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + - name: sampleType + displayName: Sample type + definition: Method of sampling. + - name: purposeOfSampling + displayName: Purpose of sampling + definition: The reason that the sample was collected. + - name: presamplingActivity + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + - name: anatomicalMaterial + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + - name: anatomicalPart + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + - name: bodyProduct + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + - name: environmentalMaterial + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + - name: environmentalSite + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + - name: collectionDevice + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + - name: collectionMethod + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + - name: foodProduct + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + - name: foodProductProperties + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + - name: specimenProcessing + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + - name: specimenProcessingDetails + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + - name: experimentalSpecimenRoleType + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + - name: hostAge + definition: Age of host at the time of sampling. + displayName: Host age + - name: hostAgeBin + definition: The age category of the host at the time of sampling. + displayName: Host age bin + - name: hostGender + definition: The gender of the host at the time of sample collection. + displayName: Host gender + - name: hostOriginCountry + definition: The country of origin of the host. + displayName: Host origin country + - name: hostDisease + definition: The name of the disease experienced by the host. + displayName: Host disease + - name: signsAndSymptoms + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + - name: hostHealthState + definition: Health status of the host at the time of sample collection. + displayName: Host health state + - name: hostHealthOutcome + definition: Disease outcome in the host. + displayName: Host health outcome + - name: travelHistory + definition: Travel history in last six months. + displayName: Travel history + - name: exposureEvent + definition: Event leading to exposure. + displayName: Exposure event + - name: hostRole + definition: The role of the host in relation to the exposure setting. + displayName: Host role + - name: exposureSetting + definition: The setting leading to exposure. + displayName: Exposure setting + - name: exposureDetails + definition: Additional host exposure information. + displayName: Exposure details + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + - name: hostVaccinationStatus + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + - name: purposeOfSequencing + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + - name: sequencingDate + definition: The date the sample was sequenced. + displayName: Sequencing date + - name: ampliconPcrPrimerScheme + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + - name: ampliconSize + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + - name: sequencingInstrument + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + - name: sequencingProtocol + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + - name: sequencingAssayType + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + - name: sequencedByOrganization + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + - name: sequencedByContactName + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + - name: sequencedByContactEmail + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + - name: rawSequenceDataProcessingMethod + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + - name: dehostingMethod + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + - name: referenceGenomeAccession + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + - name: consensusSequenceSoftwareName + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + - name: consensusSequenceSoftwareVersion + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + - name: depthOfCoverage + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + - name: breadthOfCoverage + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + - name: qualityControlMethodName + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + - name: qualityControlMethodVersion + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + - name: qualityControlDetermination + definition: The determination of a quality control assessment. + displayName: Quality control determination + - name: qualityControlIssues + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + - name: qualityControlDetails + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + - name: host + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + desired: true +referenceGenomes: + - name: L + references: + - name: singleReference + sequence: 'TCTCAAAGATATCAATCCCCCCGTTACCCACGTTAACACAGAGAGCTCTAGTAGTGGTCTTTCCTTTTGTGAAACCATGGACTTCTTGAGAAGCCTTGACTGGACTCAAGTGATTGCTGGTCAATATGTGTCCAACCCTAGGTTCAACATTTCTGATTATTTTGAGATTGTGCGGCAGCCTGGTGATGGGAACTGCTTCTATCACAGCATAGCTGAGTTAACCATGCCTAACAAAACAGATCACTCATATCATTACATCAAACGCCTAACCGAGTCGGCAGCACGGAAGTATTACCAAGAGGAGCCTGAAGCCAGACTTGTTGGCCTGAGCCTGGAAGATTACCTCAAGAGGATGCTGTCTGACAACGAGTGGGGATCAACTCTAGAAGCATCTATGTTGGCTAAAGAAATGGGCATTACCATCATCATTTGGACTGTTGCTGCCAGTGATGAAGTGGAAGCAGGTATAAAGTTCGGAGACGGTGATGTGTTTACAGCTGTGAACCTTTTGCACTCTGGACAAACACACTTTGATGCGCTCAGAATACTTCCGCAGTTTGAAACAGACACAAGAGAGGCCTTGAGCTTGATGGACAGGGTTATAGCTGTGGATCAGTTGACATCATCTTCTAGTGATGAACTGCAAGACTATGAAGACCTTGCCTTGGCACTCACAAGCGCAGAAGAATCAAATAGACGGTCAAGCTTGGATGAGGTCACATTGTCCAAGAAGCAAGCAGAGATACTAAGGCAAAAAGCATCTCAGTTGTCTAAATTGGTTAATAAAAGTCAGAACATACCGACCAGAGTCGGTAGAGTCTTGGATTGCATGTTCAACTGCAAATTATGTGTTGAGATATCAGCTGACACTTTAATTCTGAGGCCAGAATCAAAAGAGAAAATCGGTGAAATCATGTCATTGCGGCAGTTGGGGCATAAACTGCTGACACGAGACAAACAGATTAAGCAAGAGTTCTCCAGAATGAAACTCTACGTCACTAAAGATTTGCTTGACCATCTAGACGTTGGTGGGCTCTTGAGGGCTGCTTTCCCTGGAACAGGAATAGAAAGGCATATGCAGCTGCTACACTCTGAGATGATACTGGACATCTGCACTGTATCACTTGGTGTCATGCTGTCAACATTCTTATATGGTTCTAATAATAAAAACAAGAAGAAATTCATTACCAACTGTCTGCTCAGCACAGCCCTATCCGGAAAGAAGGTGTATAAAGTTCTCGGCAACCTAGGAAATGAACTGTTGTACAAGGCACCTAGAAAGGCCTTAGCAACTGTCTGCAGTGCCTTGTTTGGGAAGCAGATAAACAAACTTCAGAATTGCTTCAGGACCATAAGCCCTGTCAGCTTACTTGCATTGAGAAATCTAGACTTTGATTGTCTCAGTGTGCAAGACTATAATGGTATGATAGAAAACATGTCTAAATTAGACAACACTGATGTTGAATTCAACCACAGGGAGATAGCTGATCTCAACCAATTAACTTCTCGGCTCATCACATTAAGAAAGGAGAAAGACACTGACCTCCTCAAACAATGGTTTCCTGAAAGTGACCTCACCCGCAGAAGCATCAGGAATGCTGCAAACGCGGAGGAATTTGTCATATCTGAGTTCTTTAAGAAGAAGGACATTATGAAATTCATCAGCACTTCAGGCAGAGCAATGAGTGCAGGCAAGATTGGTAATGTCCTATCCTATGCACATAATCTTTATTTGAGTAAGTCAAGCCTAAATATGACCTCTGAAGACATCTCACAGCTTTTGATCGAGATTAAGCGACTGTATGCTTTACAAGAAGATTCTGAAGTGGAGCCGATAGCCATAATTTGTGATGGCATAGAAAGCAACATGAAACAGTTATTTGCTATATTGCCTCCTGACTGTGCAAGAGAGTGTGAAGTCCTCTTCGATGACATAAGAAATTCTCCAACACACAGCACAGCCTGGAAGCATGCACTCCGATTAAAAGGGACTGCATACGAAGGTCTGTTTGCAAATTGTTACGGCTGGCAATACATTCCGGAAGACATTAAACCAAGCCTGACCATGTTGATACAGACTTTGTTTCCTGACAAGTTCGAAGATTTCCTGGATCGAACCCAGTTGCATCCGGAGTTCAGAGACCTGACTCCCGACTTTTCGCTCACACAAAAGGTTCACTTTAAAAGAAATCAGATACCCAGTGTCGAAAATGTGCAAATCTCCATTGATGCGACGTTGCCTGAATCTGTGGAAGCAGTACCAGTGACAGAAAGAAAGATGTTCCCCCTTCCTGAGACTCCGCTAAGTGAGGTGCATTCAATAGAGCGTATAATGGAAAACTTTACTCGCCTCATGCATGGAGGAAGACTTTCGACCAAGAAAAGAGATGGAGATCCGGCGGAACAGGGCAACCAGCAGAGTATCACTGAACACGAGAGTTCCAGCATCTCTGCCTTTAAAGACTACGGAGAGAGAGGGATAGTCGAGGAAAATCACATGAAGTTTAGTGGAGAAGATCAGCTAGAGACAAGGCAGCTGTTGTTGGTGGAAGTTGGTTTTCAAACTGACATCGATGGGAAAATAAGGACAGACCACAAGAAGTGGAAAGACATATTAAAGCTATTAGAGCTACTAGGAATCAAGTGCTCATTCATTGCCTGTGCAGATTGCTCATCCACACCACCAGACAGATGGTGGATTACGGAGGACAGAGTGCGAGTCCTAAAAAATTCAGTCAGCTTCTTGTTCAATAAACTCTCCAGAAACTCACCTACAGAAGTAACTGACATAGTTGTTGGAGCTATAAGTACTCAAAAGGTTAGAAGTTATCTAAAGGCAGGAACTGCAACAAAAACCCCTGTGTCGACTAAAGACGTTCTGGAGACTTGGGAAAAGATGAAGGAGCACATACTCAACAGACCAACAGGACTGACACTGCCCACCAGTTTGGAACAGGCAATGCGCAAAGGACTGGTCGAAGGTGTGGTCATCTCCAAGGAAGGTTCTGAGTCATGTATCAATATGTTGAAGGAAAATTTGGACCGAATAACTGACGAATTCGAGCGAACAAAATTTAAACATGAACTTACTCAGAATATTACCACAAGTGAGAAGATGCTATTGAGTTGGTTGAGTGAAGACATCAAATCATCGAGATGTGGTGAGTGCCTCTCAAATATAAAGAAAGCCGTTGATGAAACTGCCAATCTATCAGGAAAGATTGAGCTGCTCGCTTATAATCTGCAACTCACCAATCACTGCAGCAACTGTCACCCCAATGGTGTAAACATTAGTAACACTTCTAATGTGTGCAAGAGATGCCCCAAGATTGAAGTGGTTAGCCATTGTGAAAATAAAGGCTTTGAGGACAGCAATGAATGCTTAACAGACCTAGATAGGCTTGTTAGGCTCACATTACCAGGGAAAACTGAGAAGGAGAGAAGAGTCAAACGTAATGTGGAATATCTTATAAAACTGATGATGAGCATGTCAGGCATTGATTGTATAAAATATCCCACAGGGCAGCTTATCACCCATGGAAGAGTGAGTGCAAAACATAACGATGGGAACCTGAAAGATAGAAGCGATGACGACCAAAGACTAGCTGAGAAGATAGACACTGTTAGGAAAGAGCTTTCAGAATCTAAACTGAAAGATTATTCAACTTATGCAAGGGGAGTGATATCAAATTCACTAAAAAACCTCTCAAGGCAAGGTAAATCAAAGTGTTCTGTGCCAAGATCTTGGCTCGAGAAAGTACTGTTTGACCTGAAGGTGCCTACTAAGGACGAAGAAGTGCTTATAAACATCAGAAACTCATTGAAAGCTAGATCCGAGTTTGTTAGAAATAACGATAAACTACTCATAAGGTCAAAAGAAGAACTAAAAAAATGTTTCGATGTGCAGTCTTTTAAATTGAAAAAAAACAAGCAACCTGTGCCCTTTCAGGTTGACTGTATATTGTTCAAAGAAGTGGCAGCTGAATGCATGAAGAGGTACATTGGCACACCTTATGAGGGAATTGTAGACACCTTAGTTTCTCTGATTAATGTGTTAACAAGGTTTACTTGGTTCCAGGAAGTGGTGCTATATGGTAAAATATGTGAGACCTTCCTCAGATGCTGCACAGAATTTAATAGGTCAGGGGTCAAACTGGTTAAGATAAGGCACTGTAACATTAACCTATCAGTCAAATTGCCATCAAATAAGAAAGAGAATATGTTATGTTGTCTATATAGTGGAAACATGGAGCTCTTGCAAGGACCTTTCTATTTGAACAGGAGACAAGCTGTCCTTGGTTCTTCATACCTTTACATTGTCATTACACTTTACATACAAGTGCTGCAGCAGTACAGGTGTCTAGAAGTTATAAACAGTGTGAGTGAAAAAACATTGCAAGACATTGAAAATCATTCCATGACTCTACTAGAAGATTCATTCAGGGAAATCACTTTTGCTCTTGAAGGTAGGTTTGAAGAATCTTATAAAATACGAACCTCGAGGTGCAGAGCCAGTGGGAATTTTCTGAACAGGAGCAGTAGAGACCACTTTATAAGCGTTGTTTCAGGCTTGAACCTAGTTTATGGCTTCCTCATAAAAGATAACTTACTAGCCAACTCTCAGCAACAGAACAAACAACTACAGATGCTTCGTTTTGGCATGCTTGCAGGGCTTAGTAGGCTTGTTTGTCCTAATGAGCTAGGAAAGAAATTTTCAACGAGCTGTAGAAGAATTGAAGACAACATTGCAAGGCTTTACCTGCAGACATCCATTTACTGTTCAGTCAGGGATGTGGAGGACAATGTTAAGCACTGGAAACAAAGAGATCTATGTCCTGAAGTAACCATTCCATGCTTTACAGTCTATGGAACCTTTGTCAACAGCGATAGACAACTGATCTTTGACATTTACAATGTGCATATATATAATAAAGAAATGGACAACTTTGATGAAGGATGTATCAGCGTCTTGGAAGAAACAGCAGAAAGGCACATGCTTTGGGAACTCGATCTGATGAATTCACTTTGTTCTGACGAAAAAAAAGATACTAGACCCGCAAGACTTTTACTAGGCTGCCCAAATGTGAGGAAAGCAGCAACCAGAGAAGGGAAGAAGCTGTTGAAGTTAAACAGTGACACATCCACAGACACACAGAGCATTGCTTCTGAAGTGTCGGACAGAAGGTCTTATAGTTCAAGTAAGAGTAGAATCCGTAGTATATTTGGTAGATACAACTCTCAGAAGAAACCATTTGAATTAAGGTCAGGTCTTGAGGTTTTCAATGATCCTTTCAATGATTATCAGCAAGCAATAACGGACATTTGCCAATTTTCTGAGTACACACCAAACAAAGAAAGCATTTTGAAAGACTGTCTTCAAATCATACGAAAAAACCCTAGCCACACAATGGGTTCTTTTGAGCTGATCCAGGCAATCTCAGAGTTCGGCATGAGCAAGTTTCCTCCCGAAAATATAGACAAAGCAAGAAGGGATCCGAAGAACTGGGTTAGCATCTCTGAAGTAACCGAAACAACAAGTATAGTTGCATCACCTAGAACTCATATGATGCTCAAGGATTGTTTTAAAATTATACTAGGTACTGAGAATAAGAAGATAGTCAAAATGCTTCGAGGTAAGCTAAAGAAACTCGGTGCTATCTCAACAAACATAGAGATCGGGAAAAGGGATTGCCTAGATCTACTCAGCACAGTAGATGGGCTAACAGACCAGCAGAAAGAAAATATTGTAAATGGGATATTTGAGCCCTCAAAGTTATCCTTCTACCATTGGAAGGAACTGGTCAAAAAAAACATTGATGAAGTTTTACTAACTGAAGATGGAAATCTGATCTTCTGCTGGCTGAAAACAATCTCTTCTTCAGTCAAAGGAAGCCTAAAGAAGAGACTCAAATTCATGAATATACATTCTCCAGAATTGATGCCGGAAAACTGTCTCTTTTCTAGTGAGGAATTCAATGAGTTAATTAAGTTGAAGAAACTCCTCCTCAATGAACAACAAGATGAACAGGAGCTGAAACAAGATCTTTTGATATCTTCTTGGATCAAGTGCATAACAGCTTGCAAGGATTTTGCAAGCATCAATGACAAGATTCAGAAGTTCATTTACCACCTGTCTGAAGAGCTATATGACATAAGGCTGCAGCATCTGGAACTGTCAAAGCTTAAGCAAGAGCACCCTAGTGTCAGCTTCACAAAAGAAGAAGTCTTAATAAAGCGGCTGGAGAAAAATTTCCTTAAGCAGCATAATTTAGAGATTATGGAAACTGTGAATCTTGTATTCTTTGCAGCCCTCTCAGCTCCCTGGTGCTTACACTATAAAGCACTAGAGTCTTATTTGGTGAGACATCCAGAAATACTTGACTGTGGATCTAAGGAGGACTGCAAACTCACCTTGCTTGATCTGTCAGTTTCTAAGCTCTTGGTTTGTTTGTATCAAAAAGATGATGAGGAGCTGATAAATAGCTCAAGTTTGAAACTTGGGTTTTTAGTGAAATATGTTGTCACCTTGTTCACATCCAATGGTGAACCTTTTTCACTCAGTCTCAATGACGGGGGTTTGGATCTTGATTTACACAAGACTACTGACGAAAAGTTACTACATCAAACAAAGATAGTTTTTGCTAAAATTGGTTTATCTGGGAACAGTTATGACTTTATCTGGACTACCCAAATGATAGCAAACAGCAATTTTAACGTCTGCAAAAGATTAACGGGAAGGAGTACTGGGGAAAGGCTCCCTAGAAGCGTTAGAAGCAAGGTCATATATGAGATGGTAAAATTAGTGGGAGAAACAGGCATGGCAATACTACAACAATTAGCTTTTGCACAAGCACTAAATTATGAGCACCGCTTCTATGCGGTCTTAGCACCTAAAGCGCAACTAGGAGGAGCAAGAGATTTGTTAGTGCAAGAGACTGGGACTAAAGTCATGCATGCAACCACTGAAATGTTTAGTAGAAATCTTTTAAAAACAACATCAGATGATGGCCTCACAAACCCACATCTTAAAGAAACAATCCTTAATGTGGGATTAGACTGTCTTGCTAACATGCGAAATCTTGACGGTAAGCCCATAAGTGAAGGTAGTAACTTGGTCAATTTCTACAAAGTCATATGTATCTCGGGTGATAATACCAAGTGGGGCCCGATACACTGCTGTTCTTTCTTTTCTGGCATGATGCAACAGGTTCTGAAAAATGTACCAGATTGGTGTTCATTTTATAAATTAACATTCATTAAAAACTTATGTAGACAGGTAGAAATACCTGCTGGCAGTATTAAGAAGATCTTAAATGTTCTTAGGTATAGATTGTGCAGCAAGGGAGGTGTAGAACAACATAGTGAAGAAGATCTGAGAAGACTGTTGACAGATAATTTAGACAGTTGGGATGGAAACGACACAGTTAAGTTCTTAGTTACAACTTATATAAGCAAAGGACTCATGGCGTTAAACAGTTACAATCATATGGGTCAGGGTATTCACCATGCAACATCTTCGGTGTTAACTTCCCTAGCTGCTGTGCTCTTTGAGGAGCTGGCAATTTTTTATCTTAAGAGAAGCTTACCCCAGACAACAGTACATGTTGAACATGCCGGTAGTTCAGATGATTACGCAAAGTGTATAGTGGTGACTGGTATACTATCCAAAGAGCTCTACTCCCAGTATGATGAAACATTTTGGAAACACGCTTGCAGACTCAAAAACTTCACGGCCGCGGTACAAAGATGCTGTCAAATGAAAGATAGTGCCAAAACCTTGGTGAGCGACTGCTTTCTCGAGTTTTACAGTGAGTTTATGATGGGCTACAGAGTAACCCCTGCTGTAATAAAGTTCATGTTTACTGGACTGATAAACAGCTCTGTGACCTCTCCTCAGAGTTTGATGCAAGCATGCCAAGTTTCATCCCAACAAGCAATGTATAATAGTGTTCCTCTTGTCACCAACACTGCCTTCACCCTATTAAGGCAGCAAATTTTCTTTAACCATGTTGAAGACTTTATCAGAAGGTATGGTATACTGACTCTTGGGACTTTGTCACCCTTTGGTAGGTTGTTCGTACCAACCTACTCTGGATTAGTCAGCTCAGCGGTTGCTTTAGAAGATGCTGAAGTCATTGCTAGAGCAGCCCAAACACTTCAAATGAACAGTGTGTCAATACAGTCAAGTAGCTTGACCACATTAGATAGCCTAGGTCGTAGTAGGACAAGTTCCACAGCTGAGGATAGCAGCAGTGTGAGCGACACAACTGCTGCTTCCCATGACTCAGGATCATCATCCTCAAGCTTCTCTTTTGAGCTCAATAGACCCCTGTCTGAAACTGAACTACAGTTCATTAAAGCACTAAGTAGTCTCAAGTCAACACAAGCCTGTGAAGTGATTCAAAATAGAATTACAGGTCTTTATTGCAACAGCAACGAAGGACCTCTTGATAGGCATAATGTCATTTACAGCAGCAGAATGGCAGACTCTTGCGATTGGCTAAAGGATGGTAAAAGGAGAGGGAATCTAGAACTAGCGAATAGGATCCAATCTGTACTGTGTATTCTGATAGCAGGATATTACAGGTCATTTGGAGGGGAAGGAACCGAGAAACAGGTAAAGGCATCATTGAATAGAGACGACAATAAAATCATAGAGGATCCTATGATACAACTAATTCCAGAAAAGCTGAGGAGAGAGTTAGAAAGGTTAGGTGTTTCTAGAATGGAAGTCGATGAGCTAATGCCAAGCATTAGTCCTGATGACACCTTAGCCCAGCTTGTAGCGAAAAAACTCATTAGCCTCAATGTTTCGACAGAAGAATACTCAGCTGAGGTATCTAGACTCAAACAAACACTGACAGCCCGAAATGTTTTGCACGGGTTAGCTGGAGGGATTAAGGAGCTTTCGCTTCCAATATATACAATATTCATGAAATCTTACTTCTTTAAAGACAATGTTTTCCTGTCACTAACAGATAGATGGTCTACCAAGCACAGTACAAACTATCGTGATAGTTGTGGCAAACAATTAAAAGGTAGAATAATTACCAAGTATACTCACTGGTTGGACACTTTTCTGGGCTGCTCTGTCTCCATCAACAGGCATACTACTGTTAAAGAGCCCTCCTTATTCAATCCGAACATCAGATGTGTGAATCTGATCACATTTGAGGACGGCCTGAGAGAACTATCAGTGATACAGAGTCACCTTAAAGTCTTTGAAAATGAGTTCACCAACTTAAATCTTCAATTCTCTGATCCGAACAGACAGAAACTTAGAATAGTTGAGTCTAGACCTGCAGAATCTGAGCTAGAGGCAAACCGTGCAGTAATTGTCAAGACCAAATTGTTTTCAGCAACTGAACAAGTTCGACTATCCAACAACCCTGCAGTTGTCATGGGCTACCTATTGGATGAATCAGCAATTTCAGAAGTCAAGCCTACCAAGGTTGACTTCTCAAATTTACTTAAAGACCGCTTCAAAATAATGCAATTTTTTCCTTCAGTGTTCACTTTGATTAAGATGCTGACAGATGAATCGTCAGATTCAGAAAAGAGTGGCCTTAGTCCAGATTTGCAACAAGTTGCAAGATACTCAAACCATTTGACCTTGCTCAGCAGAATGATTCAACAAGCAAAGCCAACCGTGACTGTTTTCTACATGCTAAAAGGTAACTTGATGAACACAGAGCCAACAGTTGCTGAGCTTGTCAGCTATGGTATAAAGGAAGGCAGATTTTTTAGGCTTTCCGACACCGGAGTCGATGCAAGCACATACTCTGTAAAATATTGGAAAATTCTTCACTGCATCTCTGCCATTGGATGTTTACCTTTGAGTCAAGCAGACAAATCTTCACTACTTATGAGCTTCTTAAACTGGAGGGTCAACATGGACATTAGAACATCTGACTGTCCACTATCTAGTCATGAAGCAAGTATACTGAGTGAATTTGATGGACAAGTTATCGCCAACATACTTGCCAGTGAATTGAGTTCTGTGAAACGAGATTCTGAACGCGAGGGTCTAACTGATCTCCTTGATTATCTAAACTCACCAACTGAATTGTTGAAGAAGAAGCCTTACTTAGGGACAACTTGCAAGTTCAACACCTGGGGAGACTCGAATAGATCTGGAAAGTTCACATACAGCAGCAGATCTGGAGAATCCATTGGAATCTTCATTGCAGGGAAATTGCACATCCATCTCTCATCTGAGTCCGTTGCCTTGTTGTGTGAAACTGAAAGACAAGTGCTTTCTTGGATGAGTAAGAGGAGGACTGAGGTAATAACTAAAGAACAGCATCAACTGTTTCTAAGTCTTCTCCCACAGTCTCATGAGTGTTTACAAAAGCACAAGGACGGTAGTGCGCTATCAGTCATACCTGATAGCAGCAACCCCCGATTACTTAAGTTTGTGCCCCTCAAAAAAGGTCTAGCAGTGGTGAAAATCAAAAAACAAATTTTAACAGTGAAGAAGCAGGTTGTGTTTGATGCAGAGAGCGAGCCTAGACTGCAGTGGGGGCATGGCTGCTTGTCCATTGTTTATGACGAAACTGATACTCAGACCACATACCATGAAAATCTTTTGAAGGTGAAGCATCTTGTAGACTGCTCTACAGATAGAAAAAAGCTTTTGCCCCAGTCAGTGTTTTCTGACTCCAAAGTTGTCCTTTCAAGGATCAAGTTCAAGACGGAGCTTCTCCTCAACTCATTGACGCTGCTCCACTGTTTCCTAAAACATGCTCCTAGTGATGCCATAATGGAGGTAGAGAGCAAAAGTAGCTTGCTACACAAGTACCTCAAATCGGGAGGTGTCAGGCAACGGAACACTGAAGTGCTCTTCAGAGAGAAGTTAAACAAGGTTGTTATAAAAGACAATCTTGAGCAAGGTGTGGAAGAGGAGATTGAGTTTTGCAACAACTTGACTAAGACTGTTTCAGAGAACCCATTACCACTCAGCTGTTGGTCTGAAGTTCAAAATTACATTGAAGACATAGGCTTTAACAATGTACTTGTTAACATTGACAGAAACACGGTGAAAAGTGAACTTTTATGGAAATTTACGTTAGACACCAATGTAAGCACCACAAGTACTATAAAAGACGTGAGGACATTGGTGTCCTACGTTAGCACTGAAACCATCCCTAAGTTCTTGCTTGCATTCTTACTTTATGAAGAAGTGTTAATGAACTTAATCAACCAGTGTAAGGCAGTAAAAGAACTCATCAACAGCACAGGACTCTCAGACTTGGAACTGGAAAGCTTACTCACTTTATGTGCTTTCTATTTCCAAAGTGAGTGCAGTAAGAGAGATGGTCCTAGATGCTCCTTTGCAGCACTATTAAGTCTAATCCATGAGGATTGGCAGAGGATAGGTAAAAACATTCTTGTTCGTGCAAACAATGAACTAGGTGATGTGTCACTGAAGGTTAACATTGTCCTGGTGCCTCTCAAGGACATGTCTAAGCCAAAGTCTGAGAGAGTGGTCATGGCTAGAAGGTCACTAAATCATGCTCTATCCTTGATGTTTCTGGACGAGATGTCACTACCTGAGCTAAAATCCTTATCCGTGAACTGCAAAATGGGGAACTTTGAAGGGCAGGAGTGCTTTGAGTTCACTATTCTGAAGGACAATAGCGCAAGACTAGATTACAACAAGTTGATTGACCACTGTGTGGACATGGAAAAAAAGAGGGAAGCGGTTAGAGCAGTAGAAGATTTAATTTTGATGTTGACAGGCAGAGCAGTCAAACCCAGCGCTGTAACACAGTTTGTACACGGGGACGAGCAGTGTCAAGAGCAAATAAGCTTAGATGATCTGATGGCAAACGACACGGTAACAGACTTTCCTGATAGGGAAGCAGAAGCCCTCAAAACAGGAAATCTTGGCTTTAACTGGGACTCAGATTGAACATGCCGCTTATAAGCCATTAATACCTTTCGGCGTCACAAGGACAAATGATGCAGTTTTAGCTGCATCATTCATTAACATTAAAGCCTTCAAACAAGCTAACTACTCTGCATTCTCCTCAATCAACTCAATTGCTTCAACTGATCTATTTTACTAGCTCATCGATCCTCTCTTTCTTAGCTATATCTTTGAGA' + insdcAccessionFull: NC_005301.3 + genes: + - name: RdRp + sequence: 'MDFLRSLDWTQVIAGQYVSNPRFNISDYFEIVRQPGDGNCFYHSIAELTMPNKTDHSYHYIKRLTESAARKYYQEEPEARLVGLSLEDYLKRMLSDNEWGSTLEASMLAKEMGITIIIWTVAASDEVEAGIKFGDGDVFTAVNLLHSGQTHFDALRILPQFETDTREALSLMDRVIAVDQLTSSSSDELQDYEDLALALTSAEESNRRSSLDEVTLSKKQAEILRQKASQLSKLVNKSQNIPTRVGRVLDCMFNCKLCVEISADTLILRPESKEKIGEIMSLRQLGHKLLTRDKQIKQEFSRMKLYVTKDLLDHLDVGGLLRAAFPGTGIERHMQLLHSEMILDICTVSLGVMLSTFLYGSNNKNKKKFITNCLLSTALSGKKVYKVLGNLGNELLYKAPRKALATVCSALFGKQINKLQNCFRTISPVSLLALRNLDFDCLSVQDYNGMIENMSKLDNTDVEFNHREIADLNQLTSRLITLRKEKDTDLLKQWFPESDLTRRSIRNAANAEEFVISEFFKKKDIMKFISTSGRAMSAGKIGNVLSYAHNLYLSKSSLNMTSEDISQLLIEIKRLYALQEDSEVEPIAIICDGIESNMKQLFAILPPDCARECEVLFDDIRNSPTHSTAWKHALRLKGTAYEGLFANCYGWQYIPEDIKPSLTMLIQTLFPDKFEDFLDRTQLHPEFRDLTPDFSLTQKVHFKRNQIPSVENVQISIDATLPESVEAVPVTERKMFPLPETPLSEVHSIERIMENFTRLMHGGRLSTKKRDGDPAEQGNQQSITEHESSSISAFKDYGERGIVEENHMKFSGEDQLETRQLLLVEVGFQTDIDGKIRTDHKKWKDILKLLELLGIKCSFIACADCSSTPPDRWWITEDRVRVLKNSVSFLFNKLSRNSPTEVTDIVVGAISTQKVRSYLKAGTATKTPVSTKDVLETWEKMKEHILNRPTGLTLPTSLEQAMRKGLVEGVVISKEGSESCINMLKENLDRITDEFERTKFKHELTQNITTSEKMLLSWLSEDIKSSRCGECLSNIKKAVDETANLSGKIELLAYNLQLTNHCSNCHPNGVNISNTSNVCKRCPKIEVVSHCENKGFEDSNECLTDLDRLVRLTLPGKTEKERRVKRNVEYLIKLMMSMSGIDCIKYPTGQLITHGRVSAKHNDGNLKDRSDDDQRLAEKIDTVRKELSESKLKDYSTYARGVISNSLKNLSRQGKSKCSVPRSWLEKVLFDLKVPTKDEEVLINIRNSLKARSEFVRNNDKLLIRSKEELKKCFDVQSFKLKKNKQPVPFQVDCILFKEVAAECMKRYIGTPYEGIVDTLVSLINVLTRFTWFQEVVLYGKICETFLRCCTEFNRSGVKLVKIRHCNINLSVKLPSNKKENMLCCLYSGNMELLQGPFYLNRRQAVLGSSYLYIVITLYIQVLQQYRCLEVINSVSEKTLQDIENHSMTLLEDSFREITFALEGRFEESYKIRTSRCRASGNFLNRSSRDHFISVVSGLNLVYGFLIKDNLLANSQQQNKQLQMLRFGMLAGLSRLVCPNELGKKFSTSCRRIEDNIARLYLQTSIYCSVRDVEDNVKHWKQRDLCPEVTIPCFTVYGTFVNSDRQLIFDIYNVHIYNKEMDNFDEGCISVLEETAERHMLWELDLMNSLCSDEKKDTRPARLLLGCPNVRKAATREGKKLLKLNSDTSTDTQSIASEVSDRRSYSSSKSRIRSIFGRYNSQKKPFELRSGLEVFNDPFNDYQQAITDICQFSEYTPNKESILKDCLQIIRKNPSHTMGSFELIQAISEFGMSKFPPENIDKARRDPKNWVSISEVTETTSIVASPRTHMMLKDCFKIILGTENKKIVKMLRGKLKKLGAISTNIEIGKRDCLDLLSTVDGLTDQQKENIVNGIFEPSKLSFYHWKELVKKNIDEVLLTEDGNLIFCWLKTISSSVKGSLKKRLKFMNIHSPELMPENCLFSSEEFNELIKLKKLLLNEQQDEQELKQDLLISSWIKCITACKDFASINDKIQKFIYHLSEELYDIRLQHLELSKLKQEHPSVSFTKEEVLIKRLEKNFLKQHNLEIMETVNLVFFAALSAPWCLHYKALESYLVRHPEILDCGSKEDCKLTLLDLSVSKLLVCLYQKDDEELINSSSLKLGFLVKYVVTLFTSNGEPFSLSLNDGGLDLDLHKTTDEKLLHQTKIVFAKIGLSGNSYDFIWTTQMIANSNFNVCKRLTGRSTGERLPRSVRSKVIYEMVKLVGETGMAILQQLAFAQALNYEHRFYAVLAPKAQLGGARDLLVQETGTKVMHATTEMFSRNLLKTTSDDGLTNPHLKETILNVGLDCLANMRNLDGKPISEGSNLVNFYKVICISGDNTKWGPIHCCSFFSGMMQQVLKNVPDWCSFYKLTFIKNLCRQVEIPAGSIKKILNVLRYRLCSKGGVEQHSEEDLRRLLTDNLDSWDGNDTVKFLVTTYISKGLMALNSYNHMGQGIHHATSSVLTSLAAVLFEELAIFYLKRSLPQTTVHVEHAGSSDDYAKCIVVTGILSKELYSQYDETFWKHACRLKNFTAAVQRCCQMKDSAKTLVSDCFLEFYSEFMMGYRVTPAVIKFMFTGLINSSVTSPQSLMQACQVSSQQAMYNSVPLVTNTAFTLLRQQIFFNHVEDFIRRYGILTLGTLSPFGRLFVPTYSGLVSSAVALEDAEVIARAAQTLQMNSVSIQSSSLTTLDSLGRSRTSSTAEDSSSVSDTTAASHDSGSSSSSFSFELNRPLSETELQFIKALSSLKSTQACEVIQNRITGLYCNSNEGPLDRHNVIYSSRMADSCDWLKDGKRRGNLELANRIQSVLCILIAGYYRSFGGEGTEKQVKASLNRDDNKIIEDPMIQLIPEKLRRELERLGVSRMEVDELMPSISPDDTLAQLVAKKLISLNVSTEEYSAEVSRLKQTLTARNVLHGLAGGIKELSLPIYTIFMKSYFFKDNVFLSLTDRWSTKHSTNYRDSCGKQLKGRIITKYTHWLDTFLGCSVSINRHTTVKEPSLFNPNIRCVNLITFEDGLRELSVIQSHLKVFENEFTNLNLQFSDPNRQKLRIVESRPAESELEANRAVIVKTKLFSATEQVRLSNNPAVVMGYLLDESAISEVKPTKVDFSNLLKDRFKIMQFFPSVFTLIKMLTDESSDSEKSGLSPDLQQVARYSNHLTLLSRMIQQAKPTVTVFYMLKGNLMNTEPTVAELVSYGIKEGRFFRLSDTGVDASTYSVKYWKILHCISAIGCLPLSQADKSSLLMSFLNWRVNMDIRTSDCPLSSHEASILSEFDGQVIANILASELSSVKRDSEREGLTDLLDYLNSPTELLKKKPYLGTTCKFNTWGDSNRSGKFTYSSRSGESIGIFIAGKLHIHLSSESVALLCETERQVLSWMSKRRTEVITKEQHQLFLSLLPQSHECLQKHKDGSALSVIPDSSNPRLLKFVPLKKGLAVVKIKKQILTVKKQVVFDAESEPRLQWGHGCLSIVYDETDTQTTYHENLLKVKHLVDCSTDRKKLLPQSVFSDSKVVLSRIKFKTELLLNSLTLLHCFLKHAPSDAIMEVESKSSLLHKYLKSGGVRQRNTEVLFREKLNKVVIKDNLEQGVEEEIEFCNNLTKTVSENPLPLSCWSEVQNYIEDIGFNNVLVNIDRNTVKSELLWKFTLDTNVSTTSTIKDVRTLVSYVSTETIPKFLLAFLLYEEVLMNLINQCKAVKELINSTGLSDLELESLLTLCAFYFQSECSKRDGPRCSFAALLSLIHEDWQRIGKNILVRANNELGDVSLKVNIVLVPLKDMSKPKSERVVMARRSLNHALSLMFLDEMSLPELKSLSVNCKMGNFEGQECFEFTILKDNSARLDYNKLIDHCVDMEKKREAVRAVEDLILMLTGRAVKPSAVTQFVHGDEQCQEQISLDDLMANDTVTDFPDREAEALKTGNLGFNWDSD*' + - name: M + references: + - name: singleReference + sequence: 'TCTCAAAGAAATACTTGCGGCACGTCAGTACGTAAGTGTTAACTTTGAGGAAGTGGATTGAGCATCTTAATTGCAGCATACCTGCTAACATCATGCATATATCATTAATGTATGCAATCCTTTGCCTACAGCTGTGTGGTCTGGGAGAGACTCATGGATCACACAATGAAACTAGACACAATAAAACAGACACCATGACAACACCCGGTGATAACCCGAGCTCTGAACCGCCAGTGAGCACGGCCTTGTCTATTACACTTGACCCCTCCACTGTCACACCCACAACACCAGCCAGTGGATTAGAAGGCTCAGGGGAAGTCTACACATCCCCTCCGATCACCACCGGGAGCTTGCCCCTGTCGGAGACAACACCAGAACTCCCTGTTACAACCGGCACAGACACCTTAAGCGCAGGTGATGTCGATCCCAGCACGCAGACAGCCGGAGGCACCTCCGCACCAACAGTCCGCACAAGTCTACCCAACAGCCCTAGCACACCATCTACACCACAAGACACACACCATCCTGTGAGAAATCTACTTTCAGTCACGAGTCCTGGGCCAGATGAAACATCAACACCCTCGGGAACAGGCAAAGAGAGCTCAGCAACCAGTAGCCCTCATCCAGTCTCCAACAGACCACCAACCCCTCCTGCAACAGCCCAGGGACCCACTGAAAATGACAGTCACAACGCCACTGAACACCCTGAGTCCCTGACACAGTCAGCAACCCCAGGCCTAATGACCTCTCCAACACAGATAGTCCACCCACAAAGTGCCACCCCCATAACCGTTCAAGACACACATCCCAGTCCAACGAACAGGTCTAAAAGAAACCTTAAGATGGAAATAATCTTGACTTTATCTCAGGGTTTAAAAAAGTACTATGGGAAAATATTAAGGCTTCTGCAACTCACCTTAGAGGAGGACACTGAAGGTCTACTGGAATGGTGTAAGAGAAATCTTGGTCTTGATTGTGATGACACTTTCTTTCAAAAGAGAATTGAAGAATTCTTTATAACTGGTGAGGGCCATTTTAATGAAGTTTTACAATTTAGAACGCCAGGCACGTTGAGCACCACAGAGTCAACACCTGCTGGGCTGCCAACAGCTGAACCTTTTAAGTCCTACTTCGCCAAAGGATTCCTCTCGATAGATTCAGGTTACTACTCAGCCAAATGTTACTCAGGAACATCCAATTCAGGGCTTCAATTGATTAACATTACCCGACATTCAACTAGAATAGTTGACACACCTGGGCCTAAGATCACTAACCTAAAGACCATCAACTGCATAAACTTGAAGGCATCGATCTTCAAAGAACATAGAGAGGTTGAAATCAATGTGCTTCTCCCCCAAGTTGCAGTTAATCTCTCAAACTGTCACGTTGTAATCAAATCACATGTCTGTGACTACTCTTTAGACATTGACGGTGCGGTGAGGCTTCCTCACATTTACCATGAAGGAGTTTTCATCCCAGGAACTTACAAAATAGTGATAGATAAAAAAAATAAGTTGAATGACAGATGCACCTTATTTACCGACTGTGTGATAAAAGGAAGGGAGGTTCGTAAAGGACAGTCAGTTTTGAGGCAGTACAAGACGGAAATCAGGATTGGCAAGGCATCAACCGGCTCTAGAAGATTGCTTTCAGAAGAACCCAGTGATGACTGTATATCAAGAACTCAACTATTAAGGACAGAGACTGCAGAGATCCACGGCGACAACTATGGTGGCCCGGGTGACAAAATAACCATCTGCAATGGCTCAACTATTGTAGACCAAAGACTGGGCAGTGAACTAGGATGCTACACCATCAATAGAGTGAGGTCATTCAAGCTATGCGAAAACAGTGCCACAGGGAAGAATTGTGAAATAGACAGTGTCCCAGTTAAATGCAGGCAGGGTTATTGCCTAAGAATCACTCAGGAAGGGAGGGGCCACGTAAAATTATCTAGGGGCTCAGAGGTTGTCTTAGATGCATGCGATACAAGCTGTGAAATAATGATACCTAAGGGCACTGGTGACATCCTAGTTGACTGTTCAGGTGGGCAGCAACATTTTCTAAAGGACAATTTGATAGATCTAGGATGCCCCAAAATTCCATTATTGGGCAAAATGGCTATTTACATTTGCAGAATGTCAAACCACCCCAAAACAACCATGGCTTTCCTCTTCTGGTTCAGCTTTGGCTATGTAATAACCTGCATACTTTGCAAGGCCATTTTTTACTTGTTAATAATTGTTGGAACACTAGGGAAAAGGCTCAAGCAGTATAGAGAGTTGAAACCTCAGACTTGCACCATATGTGAGACAACTCCTGTAAATGCAATAGATGCTGAGATGCATGACCTCAATTGCAGTTACAACATTTGTCCCTACTGTGCATCTAGACTAACCTCAGATGGGCTTGCTAGGCATGTGATACAATGCCCTAAGCGGAAGGAGAAAGTGGAAGAAACTGAACTGTACTTGAACTTAGAAAGAATTCCTTGGGTTGTAAGAAAGCTGTTGCAGGTGTCAGAGTCAACTGGTGTGGCATTGAAAAGAAGCAGTTGGCTGATTGTGCTGCTTGTGCTATTCACTGTTTCATTATCACCAGTTCAATCAGCACCCATTGGTCAAGGGAAGACAATTGAGGCATACCGGGCCAGGGAAGGGTACACAAGTATATGCCTCTTTGTACTAGGAAGTATCCTATTTATAGTTTCTTGCCTAATGAAAGGGCTGGTTGACAGTGTTGGCAACTCCTTCTTCCCTGGACTGTCCATTTGCAAAACGTGCTCCATAAGCAGCATTAATGGCTTTGAAATTGAGTCCCATAAGTGCTATTGCAGCTTATTCTGTTGCCCCTATTGTAGGCACTGCTCTACCGATAAAGAAATTCATAAGCTGCACTTGAGCATCTGCAAAAAAAGGAAAAAAGGAAGTAATGTCATGTTGGCTGTCTGCAAGCTCATGTGTTTCAGGGCCACCATGGAAGTAAGTAACAGAGCCCTGTTTATCCGTAGCATCATCAACACCACTTTTGTTTTGTGCATACTGATACTAGCAGTTTGTGTTGTTAGCACCTCAGCAGTGGAGATGGAAAACCTACCAGCAGGGACCTGGGAAAGAGAAGAAGACCTAACAAATTTCTGTCATCAGGAATGCCAGGTTACAGAGACTGAATGCCTCTGCCCTTATGAAGCTCTAGTACTCAGAAAGCCTTTATTCCTAGATAGTACAGCTAAAGGCATGAAAAATCTGCTAAATTCAACAAGTTTAGAAACGAGTTTATCAATTGAGGCACCATGGGGAGCAATAAATGTTCAGTCAACCTACAAACCAACTGTGTCAACTGCAAACATAGCACTCAGTTGGAGCTCAGTGGAACACAGAGGCAATAAGATCTTGGTTTCAGGCAGATCAGAATCAATTATGAAGCTGGAAGAAAGGACAGGAATCAGCTGGGATCTCGGTGTAGAAGATGCCTCTGAATCTAAACTGCTTACAGTATCTGTCATGGACTTGTCTCAGATGTACTCTCCTGTCTTCGAGTACTTATCAGGGGACAGACAGGTGGGAGAGTGGCCCAAAGCAACTTGCACAGGTGACTGCCCAGAAAGATGTGGCTGCACATCATCAACCTGTTTGCACAAAGAATGGCCTCACTCAAGAAATTGGAGATGCAATCCCACTTGGTGCTGGGGTGTAGGGACTGGCTGCACCTGTTGTGGATTAGATGTGAAAGACCTTTTTACAGATTATATGTTTGTCAAGTGGAAAGTTGAATACATCAAGACAGAGGCCATAGTGTGTGTAGAACTTACTAGTCAGGAAAGGCAGTGTAGCTTGATTGAAGCGGGCACAAGGTTCAATTTAGGTCCTGTGACCATCACACTGTCAGAACCAAGAAACATCCAACAAAAACTCCCTCCTGAAATAATCACACTGCATCCTAGGATCGAAGAAGGTTTTTTTGACCTGATGCATGTGCAAAAGGTGTTATCGGCAAGCACAGTGTGTAAGTTGCAGAGTTGCACACATGGTGTGCCAGGAGACCTACAGGTCTACCACATCGGAAATTTATTAAAAGGGGATAAGGTAAATGGACATCTAATTCATAAAATTGAGCCACACTTCAACACCTCCTGGATGTCCTGGGATGGTTGTGACCTAGACTACTACTGCAACATGGGAGATTGGCCTTCTTGCACATACACAGGGGTCACCCAACACAATCATGCTTCATTTGTAAACTTACTCAACATTGAAACTGATTACACAAAGAACTTCCACTTTCACTCTAAAAGGGTCACTGCACACGGAGATACACCACAACTAGATCTTAAGGCAAGACCAACCTATGGTGCAGGCGAGATCACTGTTCTGGTAGAAGTTGCTGACATGGAGTTACATACAAAGAAGATTGAAATATCAGGCTTAAAATTTGCAAGCTTAGCTTGCACAGGTTGTTATGCTTGTAGCTCTGGCATCTCATGCAAAGTTAGAATTCATGTGGATGAACCAGATGAACTTACAGTACATGTTAAAAGTGATGATCCAGATGTGGTTGCAGCTAGCTCAAGTCTCATGGCAAGGAAGCTTGAATTTGGAACAGACAGTACATTTAAAGCTTTCTCGGCCATGCCTAAAACTTCTCTATGTTTCTACATTGTTGAAAGAGAACACTGTAAGAGCTGCAGTGAAGAAGACACAAAAAAATGTGTTAACACAAAACTTGAGCAACCACAAAGCATTTTGATCGAACACAAGGGAACTATAATCGGAAAGCAAAACAGCACTTGCACGGCTAAGGCAAGTTGCTGGTTAGAGTCAGTCAAGAGTTTTTTTTATGGCCTAAAGAACATGCTTAGTGGCATTTTTGGCAATGTCTTTATGGGCATTTTCTTGTTCCTTGCCCCCTTCATCCTGTTAATACTATTCTTTATGTTTGGGTGGAGGATCCTATTCTGCTTTAAATGTTGTAGAAGAACCAGAGGCCTGTTCAAGTATAGACACCTCAAAGACGATGAAGAAACTGGTTATAGAAGGATTATTGAAAAACTAAACAATAAAAAAGGAAAAAACAAACTGCTTGATGGTGAAAGACTTGCTGATAGAAGAATTGCCGAACTGTTCTCTACAAAAACACACATTGGCTAGACCAACTGAATGGGCCTTAAAAATGATGGCATTACACTGAACAATGCTGTCATTCATGCTGACATCTTTAGTTGCAACCCTACTACATTATCATCACAATATACTACATCTAATCTGCTACATTGTATCCATGTACAGACTCTATAATGCTTGAAACTGCCTTTGCTCTATTTACTCTGACCTAAATCTTGACTGCGTGCCGCCACTATATCTTTGAGA' + insdcAccessionFull: NC_005300.2 + genes: + - name: GPC + sequence: 'MHISLMYAILCLQLCGLGETHGSHNETRHNKTDTMTTPGDNPSSEPPVSTALSITLDPSTVTPTTPASGLEGSGEVYTSPPITTGSLPLSETTPELPVTTGTDTLSAGDVDPSTQTAGGTSAPTVRTSLPNSPSTPSTPQDTHHPVRNLLSVTSPGPDETSTPSGTGKESSATSSPHPVSNRPPTPPATAQGPTENDSHNATEHPESLTQSATPGLMTSPTQIVHPQSATPITVQDTHPSPTNRSKRNLKMEIILTLSQGLKKYYGKILRLLQLTLEEDTEGLLEWCKRNLGLDCDDTFFQKRIEEFFITGEGHFNEVLQFRTPGTLSTTESTPAGLPTAEPFKSYFAKGFLSIDSGYYSAKCYSGTSNSGLQLINITRHSTRIVDTPGPKITNLKTINCINLKASIFKEHREVEINVLLPQVAVNLSNCHVVIKSHVCDYSLDIDGAVRLPHIYHEGVFIPGTYKIVIDKKNKLNDRCTLFTDCVIKGREVRKGQSVLRQYKTEIRIGKASTGSRRLLSEEPSDDCISRTQLLRTETAEIHGDNYGGPGDKITICNGSTIVDQRLGSELGCYTINRVRSFKLCENSATGKNCEIDSVPVKCRQGYCLRITQEGRGHVKLSRGSEVVLDACDTSCEIMIPKGTGDILVDCSGGQQHFLKDNLIDLGCPKIPLLGKMAIYICRMSNHPKTTMAFLFWFSFGYVITCILCKAIFYLLIIVGTLGKRLKQYRELKPQTCTICETTPVNAIDAEMHDLNCSYNICPYCASRLTSDGLARHVIQCPKRKEKVEETELYLNLERIPWVVRKLLQVSESTGVALKRSSWLIVLLVLFTVSLSPVQSAPIGQGKTIEAYRAREGYTSICLFVLGSILFIVSCLMKGLVDSVGNSFFPGLSICKTCSISSINGFEIESHKCYCSLFCCPYCRHCSTDKEIHKLHLSICKKRKKGSNVMLAVCKLMCFRATMEVSNRALFIRSIINTTFVLCILILAVCVVSTSAVEMENLPAGTWEREEDLTNFCHQECQVTETECLCPYEALVLRKPLFLDSTAKGMKNLLNSTSLETSLSIEAPWGAINVQSTYKPTVSTANIALSWSSVEHRGNKILVSGRSESIMKLEERTGISWDLGVEDASESKLLTVSVMDLSQMYSPVFEYLSGDRQVGEWPKATCTGDCPERCGCTSSTCLHKEWPHSRNWRCNPTWCWGVGTGCTCCGLDVKDLFTDYMFVKWKVEYIKTEAIVCVELTSQERQCSLIEAGTRFNLGPVTITLSEPRNIQQKLPPEIITLHPRIEEGFFDLMHVQKVLSASTVCKLQSCTHGVPGDLQVYHIGNLLKGDKVNGHLIHKIEPHFNTSWMSWDGCDLDYYCNMGDWPSCTYTGVTQHNHASFVNLLNIETDYTKNFHFHSKRVTAHGDTPQLDLKARPTYGAGEITVLVEVADMELHTKKIEISGLKFASLACTGCYACSSGISCKVRIHVDEPDELTVHVKSDDPDVVAASSSLMARKLEFGTDSTFKAFSAMPKTSLCFYIVEREHCKSCSEEDTKKCVNTKLEQPQSILIEHKGTIIGKQNSTCTAKASCWLESVKSFFYGLKNMLSGIFGNVFMGIFLFLAPFILLILFFMFGWRILFCFKCCRRTRGLFKYRHLKDDEETGYRRIIEKLNNKKGKNKLLDGERLADRRIAELFSTKTHIG*' + - name: S + references: + - name: singleReference + sequence: 'TCTCAAAGAAACACGTGCCGCTTACGCCCACAGTGTTCTCTTGAGTGTTAGCAGAATGGAAAACAAGATCGAGGTGAATAACAAAGATGAGATGAACAGGTGGTTTGAAGAGTTCAAAAAAGGAAATGGACTTGTGGACACCTTCACAAACTCCTATTCCTTTTGCGAGAGTGTTCCCAATTTGGACAGGTTTGTGTTTCAGATGGCCAGTGCCACCGATGATGCACAGAAGGACTCCATCTACGCATCTGCTCTGGTGGAGGCAACAAAGTTTTGTGCACCTATATATGAGTGCGCATGGGTTAGCTCCACTGGCATTGTAAAAAAGGGACTTGAATGGTTCGAGAAAAATGCAGGAACCATTAAGTCCTGGGATGAAAGTTATACTGAGCTAAAGGTCGACGTCCCGAAAATAGAGCAGCTTACCGGTTACCAACAAGCTGCCTTGAAGTGGAGAAAAGACATAGGTTTCCGTGTCAATGCCAACACAGCAGCTCTGAGCAACAAAGTCCTCGCAGAATACAAAGTCCCTGGTGAGATTGTGATGTCTGTCAAAGAGATGCTGTCAGACATGATTAGGAGAAGGAACCTGATTCTAAACAGGGGTGGTGATGAGAACCCACGTGGCCCAGTGAGCCATGAGCATGTAGACTGGTGCAGGGAGTTTGTCAAAGGCAAATACATCATGGCCTTCAACCCACCATGGGGGGACATCAACAAGTCAGGCCGTTCAGGAATAGCACTTGTTGCAACAGGCCTTGCTAAGCTTGCAGAGACTGAAGGAAAGGGAATATTTGATGAAGCCAAAAAGACTGTGGAGGCCCTCAACGGGTATCTGGACAAGCATAAGGACGAAGTTGATAGAGCAAGCGCCGACAGCATGATAACAAACCTTCTTAAGCATATTGCCAAGGCACAGGAGCTCTATAAAAATTCATCTGCACTTCGTGCACAAAGCGCACAGATTGACACTGCTTTCAGCTCATACTATTGGCTTTACAAGGCTGGCGTGACTCCTGAAACCTTCCCGACGGTGTCACAGTTCCTCTTTGAGCTAGGGAAACAGCCAAGAGGTACCAAGAAAATGAAGAAGGCTCTTCTGAGCACCCCAATGAAGTGGGGGAAGAAGCTTTATGAGCTCTTTGCCGATGATTCTTTCCAGCAGAACAGGATTTACATGCATCCTGCCGTGCTTACAGCTGGTAGAATCAGTGAAATGGGAGTCTGCTTTGGGACAATCCCTGTGGCCAATCCTGATGATGCTGCCCAAGGATCTGGACACACTAAGTCTATTCTCAACCTCCGTACCAACACTGAGACCAATAATCCGTGTGCCAAAACCATCGTCAAGCTATTTGAAGTTCAAAAAACAGGGTTCAACATTCAGGACATGGACATAGTGGCCTCTGAGCACTTGCTACACCAATCCCTTGTTGGCAAGCAATCCCCATTCCAGAACGCCTACAACGTCAAGGGCAATGCCACCAGTGCTAACATCATTTAAAATACAAACTGCTCTGTACTCAACTTCCTTCCTTCTGAACCGCCATCCATAATTGCAATACTTAATCATGCTTTTTTACTTGCTTATGTAACCTTATTTTATTAACCTTTCTCTATTTTCTCTTGTTTTAAACACTTAAAGGGCTGTGCGGCAACGGTATCTTTGAGA' + insdcAccessionFull: NC_005302.1 + genes: + - name: NP + sequence: 'MENKIEVNNKDEMNRWFEEFKKGNGLVDTFTNSYSFCESVPNLDRFVFQMASATDDAQKDSIYASALVEATKFCAPIYECAWVSSTGIVKKGLEWFEKNAGTIKSWDESYTELKVDVPKIEQLTGYQQAALKWRKDIGFRVNANTAALSNKVLAEYKVPGEIVMSVKEMLSDMIRRRNLILNRGGDENPRGPVSHEHVDWCREFVKGKYIMAFNPPWGDINKSGRSGIALVATGLAKLAETEGKGIFDEAKKTVEALNGYLDKHKDEVDRASADSMITNLLKHIAKAQELYKNSSALRAQSAQIDTAFSSYYWLYKAGVTPETFPTVSQFLFELGKQPRGTKKMKKALLSTPMKWGKKLYELFADDSFQQNRIYMHPAVLTAGRISEMGVCFGTIPVANPDDAAQGSGHTKSILNLRTNTETNNPCAKTIVKLFEVQKTGFNIQDMDIVASEHLLHQSLVGKQSPFQNAYNVKGNATSANII*' +referenceGenome: + nucleotideSequences: + - name: L + sequence: 'TCTCAAAGATATCAATCCCCCCGTTACCCACGTTAACACAGAGAGCTCTAGTAGTGGTCTTTCCTTTTGTGAAACCATGGACTTCTTGAGAAGCCTTGACTGGACTCAAGTGATTGCTGGTCAATATGTGTCCAACCCTAGGTTCAACATTTCTGATTATTTTGAGATTGTGCGGCAGCCTGGTGATGGGAACTGCTTCTATCACAGCATAGCTGAGTTAACCATGCCTAACAAAACAGATCACTCATATCATTACATCAAACGCCTAACCGAGTCGGCAGCACGGAAGTATTACCAAGAGGAGCCTGAAGCCAGACTTGTTGGCCTGAGCCTGGAAGATTACCTCAAGAGGATGCTGTCTGACAACGAGTGGGGATCAACTCTAGAAGCATCTATGTTGGCTAAAGAAATGGGCATTACCATCATCATTTGGACTGTTGCTGCCAGTGATGAAGTGGAAGCAGGTATAAAGTTCGGAGACGGTGATGTGTTTACAGCTGTGAACCTTTTGCACTCTGGACAAACACACTTTGATGCGCTCAGAATACTTCCGCAGTTTGAAACAGACACAAGAGAGGCCTTGAGCTTGATGGACAGGGTTATAGCTGTGGATCAGTTGACATCATCTTCTAGTGATGAACTGCAAGACTATGAAGACCTTGCCTTGGCACTCACAAGCGCAGAAGAATCAAATAGACGGTCAAGCTTGGATGAGGTCACATTGTCCAAGAAGCAAGCAGAGATACTAAGGCAAAAAGCATCTCAGTTGTCTAAATTGGTTAATAAAAGTCAGAACATACCGACCAGAGTCGGTAGAGTCTTGGATTGCATGTTCAACTGCAAATTATGTGTTGAGATATCAGCTGACACTTTAATTCTGAGGCCAGAATCAAAAGAGAAAATCGGTGAAATCATGTCATTGCGGCAGTTGGGGCATAAACTGCTGACACGAGACAAACAGATTAAGCAAGAGTTCTCCAGAATGAAACTCTACGTCACTAAAGATTTGCTTGACCATCTAGACGTTGGTGGGCTCTTGAGGGCTGCTTTCCCTGGAACAGGAATAGAAAGGCATATGCAGCTGCTACACTCTGAGATGATACTGGACATCTGCACTGTATCACTTGGTGTCATGCTGTCAACATTCTTATATGGTTCTAATAATAAAAACAAGAAGAAATTCATTACCAACTGTCTGCTCAGCACAGCCCTATCCGGAAAGAAGGTGTATAAAGTTCTCGGCAACCTAGGAAATGAACTGTTGTACAAGGCACCTAGAAAGGCCTTAGCAACTGTCTGCAGTGCCTTGTTTGGGAAGCAGATAAACAAACTTCAGAATTGCTTCAGGACCATAAGCCCTGTCAGCTTACTTGCATTGAGAAATCTAGACTTTGATTGTCTCAGTGTGCAAGACTATAATGGTATGATAGAAAACATGTCTAAATTAGACAACACTGATGTTGAATTCAACCACAGGGAGATAGCTGATCTCAACCAATTAACTTCTCGGCTCATCACATTAAGAAAGGAGAAAGACACTGACCTCCTCAAACAATGGTTTCCTGAAAGTGACCTCACCCGCAGAAGCATCAGGAATGCTGCAAACGCGGAGGAATTTGTCATATCTGAGTTCTTTAAGAAGAAGGACATTATGAAATTCATCAGCACTTCAGGCAGAGCAATGAGTGCAGGCAAGATTGGTAATGTCCTATCCTATGCACATAATCTTTATTTGAGTAAGTCAAGCCTAAATATGACCTCTGAAGACATCTCACAGCTTTTGATCGAGATTAAGCGACTGTATGCTTTACAAGAAGATTCTGAAGTGGAGCCGATAGCCATAATTTGTGATGGCATAGAAAGCAACATGAAACAGTTATTTGCTATATTGCCTCCTGACTGTGCAAGAGAGTGTGAAGTCCTCTTCGATGACATAAGAAATTCTCCAACACACAGCACAGCCTGGAAGCATGCACTCCGATTAAAAGGGACTGCATACGAAGGTCTGTTTGCAAATTGTTACGGCTGGCAATACATTCCGGAAGACATTAAACCAAGCCTGACCATGTTGATACAGACTTTGTTTCCTGACAAGTTCGAAGATTTCCTGGATCGAACCCAGTTGCATCCGGAGTTCAGAGACCTGACTCCCGACTTTTCGCTCACACAAAAGGTTCACTTTAAAAGAAATCAGATACCCAGTGTCGAAAATGTGCAAATCTCCATTGATGCGACGTTGCCTGAATCTGTGGAAGCAGTACCAGTGACAGAAAGAAAGATGTTCCCCCTTCCTGAGACTCCGCTAAGTGAGGTGCATTCAATAGAGCGTATAATGGAAAACTTTACTCGCCTCATGCATGGAGGAAGACTTTCGACCAAGAAAAGAGATGGAGATCCGGCGGAACAGGGCAACCAGCAGAGTATCACTGAACACGAGAGTTCCAGCATCTCTGCCTTTAAAGACTACGGAGAGAGAGGGATAGTCGAGGAAAATCACATGAAGTTTAGTGGAGAAGATCAGCTAGAGACAAGGCAGCTGTTGTTGGTGGAAGTTGGTTTTCAAACTGACATCGATGGGAAAATAAGGACAGACCACAAGAAGTGGAAAGACATATTAAAGCTATTAGAGCTACTAGGAATCAAGTGCTCATTCATTGCCTGTGCAGATTGCTCATCCACACCACCAGACAGATGGTGGATTACGGAGGACAGAGTGCGAGTCCTAAAAAATTCAGTCAGCTTCTTGTTCAATAAACTCTCCAGAAACTCACCTACAGAAGTAACTGACATAGTTGTTGGAGCTATAAGTACTCAAAAGGTTAGAAGTTATCTAAAGGCAGGAACTGCAACAAAAACCCCTGTGTCGACTAAAGACGTTCTGGAGACTTGGGAAAAGATGAAGGAGCACATACTCAACAGACCAACAGGACTGACACTGCCCACCAGTTTGGAACAGGCAATGCGCAAAGGACTGGTCGAAGGTGTGGTCATCTCCAAGGAAGGTTCTGAGTCATGTATCAATATGTTGAAGGAAAATTTGGACCGAATAACTGACGAATTCGAGCGAACAAAATTTAAACATGAACTTACTCAGAATATTACCACAAGTGAGAAGATGCTATTGAGTTGGTTGAGTGAAGACATCAAATCATCGAGATGTGGTGAGTGCCTCTCAAATATAAAGAAAGCCGTTGATGAAACTGCCAATCTATCAGGAAAGATTGAGCTGCTCGCTTATAATCTGCAACTCACCAATCACTGCAGCAACTGTCACCCCAATGGTGTAAACATTAGTAACACTTCTAATGTGTGCAAGAGATGCCCCAAGATTGAAGTGGTTAGCCATTGTGAAAATAAAGGCTTTGAGGACAGCAATGAATGCTTAACAGACCTAGATAGGCTTGTTAGGCTCACATTACCAGGGAAAACTGAGAAGGAGAGAAGAGTCAAACGTAATGTGGAATATCTTATAAAACTGATGATGAGCATGTCAGGCATTGATTGTATAAAATATCCCACAGGGCAGCTTATCACCCATGGAAGAGTGAGTGCAAAACATAACGATGGGAACCTGAAAGATAGAAGCGATGACGACCAAAGACTAGCTGAGAAGATAGACACTGTTAGGAAAGAGCTTTCAGAATCTAAACTGAAAGATTATTCAACTTATGCAAGGGGAGTGATATCAAATTCACTAAAAAACCTCTCAAGGCAAGGTAAATCAAAGTGTTCTGTGCCAAGATCTTGGCTCGAGAAAGTACTGTTTGACCTGAAGGTGCCTACTAAGGACGAAGAAGTGCTTATAAACATCAGAAACTCATTGAAAGCTAGATCCGAGTTTGTTAGAAATAACGATAAACTACTCATAAGGTCAAAAGAAGAACTAAAAAAATGTTTCGATGTGCAGTCTTTTAAATTGAAAAAAAACAAGCAACCTGTGCCCTTTCAGGTTGACTGTATATTGTTCAAAGAAGTGGCAGCTGAATGCATGAAGAGGTACATTGGCACACCTTATGAGGGAATTGTAGACACCTTAGTTTCTCTGATTAATGTGTTAACAAGGTTTACTTGGTTCCAGGAAGTGGTGCTATATGGTAAAATATGTGAGACCTTCCTCAGATGCTGCACAGAATTTAATAGGTCAGGGGTCAAACTGGTTAAGATAAGGCACTGTAACATTAACCTATCAGTCAAATTGCCATCAAATAAGAAAGAGAATATGTTATGTTGTCTATATAGTGGAAACATGGAGCTCTTGCAAGGACCTTTCTATTTGAACAGGAGACAAGCTGTCCTTGGTTCTTCATACCTTTACATTGTCATTACACTTTACATACAAGTGCTGCAGCAGTACAGGTGTCTAGAAGTTATAAACAGTGTGAGTGAAAAAACATTGCAAGACATTGAAAATCATTCCATGACTCTACTAGAAGATTCATTCAGGGAAATCACTTTTGCTCTTGAAGGTAGGTTTGAAGAATCTTATAAAATACGAACCTCGAGGTGCAGAGCCAGTGGGAATTTTCTGAACAGGAGCAGTAGAGACCACTTTATAAGCGTTGTTTCAGGCTTGAACCTAGTTTATGGCTTCCTCATAAAAGATAACTTACTAGCCAACTCTCAGCAACAGAACAAACAACTACAGATGCTTCGTTTTGGCATGCTTGCAGGGCTTAGTAGGCTTGTTTGTCCTAATGAGCTAGGAAAGAAATTTTCAACGAGCTGTAGAAGAATTGAAGACAACATTGCAAGGCTTTACCTGCAGACATCCATTTACTGTTCAGTCAGGGATGTGGAGGACAATGTTAAGCACTGGAAACAAAGAGATCTATGTCCTGAAGTAACCATTCCATGCTTTACAGTCTATGGAACCTTTGTCAACAGCGATAGACAACTGATCTTTGACATTTACAATGTGCATATATATAATAAAGAAATGGACAACTTTGATGAAGGATGTATCAGCGTCTTGGAAGAAACAGCAGAAAGGCACATGCTTTGGGAACTCGATCTGATGAATTCACTTTGTTCTGACGAAAAAAAAGATACTAGACCCGCAAGACTTTTACTAGGCTGCCCAAATGTGAGGAAAGCAGCAACCAGAGAAGGGAAGAAGCTGTTGAAGTTAAACAGTGACACATCCACAGACACACAGAGCATTGCTTCTGAAGTGTCGGACAGAAGGTCTTATAGTTCAAGTAAGAGTAGAATCCGTAGTATATTTGGTAGATACAACTCTCAGAAGAAACCATTTGAATTAAGGTCAGGTCTTGAGGTTTTCAATGATCCTTTCAATGATTATCAGCAAGCAATAACGGACATTTGCCAATTTTCTGAGTACACACCAAACAAAGAAAGCATTTTGAAAGACTGTCTTCAAATCATACGAAAAAACCCTAGCCACACAATGGGTTCTTTTGAGCTGATCCAGGCAATCTCAGAGTTCGGCATGAGCAAGTTTCCTCCCGAAAATATAGACAAAGCAAGAAGGGATCCGAAGAACTGGGTTAGCATCTCTGAAGTAACCGAAACAACAAGTATAGTTGCATCACCTAGAACTCATATGATGCTCAAGGATTGTTTTAAAATTATACTAGGTACTGAGAATAAGAAGATAGTCAAAATGCTTCGAGGTAAGCTAAAGAAACTCGGTGCTATCTCAACAAACATAGAGATCGGGAAAAGGGATTGCCTAGATCTACTCAGCACAGTAGATGGGCTAACAGACCAGCAGAAAGAAAATATTGTAAATGGGATATTTGAGCCCTCAAAGTTATCCTTCTACCATTGGAAGGAACTGGTCAAAAAAAACATTGATGAAGTTTTACTAACTGAAGATGGAAATCTGATCTTCTGCTGGCTGAAAACAATCTCTTCTTCAGTCAAAGGAAGCCTAAAGAAGAGACTCAAATTCATGAATATACATTCTCCAGAATTGATGCCGGAAAACTGTCTCTTTTCTAGTGAGGAATTCAATGAGTTAATTAAGTTGAAGAAACTCCTCCTCAATGAACAACAAGATGAACAGGAGCTGAAACAAGATCTTTTGATATCTTCTTGGATCAAGTGCATAACAGCTTGCAAGGATTTTGCAAGCATCAATGACAAGATTCAGAAGTTCATTTACCACCTGTCTGAAGAGCTATATGACATAAGGCTGCAGCATCTGGAACTGTCAAAGCTTAAGCAAGAGCACCCTAGTGTCAGCTTCACAAAAGAAGAAGTCTTAATAAAGCGGCTGGAGAAAAATTTCCTTAAGCAGCATAATTTAGAGATTATGGAAACTGTGAATCTTGTATTCTTTGCAGCCCTCTCAGCTCCCTGGTGCTTACACTATAAAGCACTAGAGTCTTATTTGGTGAGACATCCAGAAATACTTGACTGTGGATCTAAGGAGGACTGCAAACTCACCTTGCTTGATCTGTCAGTTTCTAAGCTCTTGGTTTGTTTGTATCAAAAAGATGATGAGGAGCTGATAAATAGCTCAAGTTTGAAACTTGGGTTTTTAGTGAAATATGTTGTCACCTTGTTCACATCCAATGGTGAACCTTTTTCACTCAGTCTCAATGACGGGGGTTTGGATCTTGATTTACACAAGACTACTGACGAAAAGTTACTACATCAAACAAAGATAGTTTTTGCTAAAATTGGTTTATCTGGGAACAGTTATGACTTTATCTGGACTACCCAAATGATAGCAAACAGCAATTTTAACGTCTGCAAAAGATTAACGGGAAGGAGTACTGGGGAAAGGCTCCCTAGAAGCGTTAGAAGCAAGGTCATATATGAGATGGTAAAATTAGTGGGAGAAACAGGCATGGCAATACTACAACAATTAGCTTTTGCACAAGCACTAAATTATGAGCACCGCTTCTATGCGGTCTTAGCACCTAAAGCGCAACTAGGAGGAGCAAGAGATTTGTTAGTGCAAGAGACTGGGACTAAAGTCATGCATGCAACCACTGAAATGTTTAGTAGAAATCTTTTAAAAACAACATCAGATGATGGCCTCACAAACCCACATCTTAAAGAAACAATCCTTAATGTGGGATTAGACTGTCTTGCTAACATGCGAAATCTTGACGGTAAGCCCATAAGTGAAGGTAGTAACTTGGTCAATTTCTACAAAGTCATATGTATCTCGGGTGATAATACCAAGTGGGGCCCGATACACTGCTGTTCTTTCTTTTCTGGCATGATGCAACAGGTTCTGAAAAATGTACCAGATTGGTGTTCATTTTATAAATTAACATTCATTAAAAACTTATGTAGACAGGTAGAAATACCTGCTGGCAGTATTAAGAAGATCTTAAATGTTCTTAGGTATAGATTGTGCAGCAAGGGAGGTGTAGAACAACATAGTGAAGAAGATCTGAGAAGACTGTTGACAGATAATTTAGACAGTTGGGATGGAAACGACACAGTTAAGTTCTTAGTTACAACTTATATAAGCAAAGGACTCATGGCGTTAAACAGTTACAATCATATGGGTCAGGGTATTCACCATGCAACATCTTCGGTGTTAACTTCCCTAGCTGCTGTGCTCTTTGAGGAGCTGGCAATTTTTTATCTTAAGAGAAGCTTACCCCAGACAACAGTACATGTTGAACATGCCGGTAGTTCAGATGATTACGCAAAGTGTATAGTGGTGACTGGTATACTATCCAAAGAGCTCTACTCCCAGTATGATGAAACATTTTGGAAACACGCTTGCAGACTCAAAAACTTCACGGCCGCGGTACAAAGATGCTGTCAAATGAAAGATAGTGCCAAAACCTTGGTGAGCGACTGCTTTCTCGAGTTTTACAGTGAGTTTATGATGGGCTACAGAGTAACCCCTGCTGTAATAAAGTTCATGTTTACTGGACTGATAAACAGCTCTGTGACCTCTCCTCAGAGTTTGATGCAAGCATGCCAAGTTTCATCCCAACAAGCAATGTATAATAGTGTTCCTCTTGTCACCAACACTGCCTTCACCCTATTAAGGCAGCAAATTTTCTTTAACCATGTTGAAGACTTTATCAGAAGGTATGGTATACTGACTCTTGGGACTTTGTCACCCTTTGGTAGGTTGTTCGTACCAACCTACTCTGGATTAGTCAGCTCAGCGGTTGCTTTAGAAGATGCTGAAGTCATTGCTAGAGCAGCCCAAACACTTCAAATGAACAGTGTGTCAATACAGTCAAGTAGCTTGACCACATTAGATAGCCTAGGTCGTAGTAGGACAAGTTCCACAGCTGAGGATAGCAGCAGTGTGAGCGACACAACTGCTGCTTCCCATGACTCAGGATCATCATCCTCAAGCTTCTCTTTTGAGCTCAATAGACCCCTGTCTGAAACTGAACTACAGTTCATTAAAGCACTAAGTAGTCTCAAGTCAACACAAGCCTGTGAAGTGATTCAAAATAGAATTACAGGTCTTTATTGCAACAGCAACGAAGGACCTCTTGATAGGCATAATGTCATTTACAGCAGCAGAATGGCAGACTCTTGCGATTGGCTAAAGGATGGTAAAAGGAGAGGGAATCTAGAACTAGCGAATAGGATCCAATCTGTACTGTGTATTCTGATAGCAGGATATTACAGGTCATTTGGAGGGGAAGGAACCGAGAAACAGGTAAAGGCATCATTGAATAGAGACGACAATAAAATCATAGAGGATCCTATGATACAACTAATTCCAGAAAAGCTGAGGAGAGAGTTAGAAAGGTTAGGTGTTTCTAGAATGGAAGTCGATGAGCTAATGCCAAGCATTAGTCCTGATGACACCTTAGCCCAGCTTGTAGCGAAAAAACTCATTAGCCTCAATGTTTCGACAGAAGAATACTCAGCTGAGGTATCTAGACTCAAACAAACACTGACAGCCCGAAATGTTTTGCACGGGTTAGCTGGAGGGATTAAGGAGCTTTCGCTTCCAATATATACAATATTCATGAAATCTTACTTCTTTAAAGACAATGTTTTCCTGTCACTAACAGATAGATGGTCTACCAAGCACAGTACAAACTATCGTGATAGTTGTGGCAAACAATTAAAAGGTAGAATAATTACCAAGTATACTCACTGGTTGGACACTTTTCTGGGCTGCTCTGTCTCCATCAACAGGCATACTACTGTTAAAGAGCCCTCCTTATTCAATCCGAACATCAGATGTGTGAATCTGATCACATTTGAGGACGGCCTGAGAGAACTATCAGTGATACAGAGTCACCTTAAAGTCTTTGAAAATGAGTTCACCAACTTAAATCTTCAATTCTCTGATCCGAACAGACAGAAACTTAGAATAGTTGAGTCTAGACCTGCAGAATCTGAGCTAGAGGCAAACCGTGCAGTAATTGTCAAGACCAAATTGTTTTCAGCAACTGAACAAGTTCGACTATCCAACAACCCTGCAGTTGTCATGGGCTACCTATTGGATGAATCAGCAATTTCAGAAGTCAAGCCTACCAAGGTTGACTTCTCAAATTTACTTAAAGACCGCTTCAAAATAATGCAATTTTTTCCTTCAGTGTTCACTTTGATTAAGATGCTGACAGATGAATCGTCAGATTCAGAAAAGAGTGGCCTTAGTCCAGATTTGCAACAAGTTGCAAGATACTCAAACCATTTGACCTTGCTCAGCAGAATGATTCAACAAGCAAAGCCAACCGTGACTGTTTTCTACATGCTAAAAGGTAACTTGATGAACACAGAGCCAACAGTTGCTGAGCTTGTCAGCTATGGTATAAAGGAAGGCAGATTTTTTAGGCTTTCCGACACCGGAGTCGATGCAAGCACATACTCTGTAAAATATTGGAAAATTCTTCACTGCATCTCTGCCATTGGATGTTTACCTTTGAGTCAAGCAGACAAATCTTCACTACTTATGAGCTTCTTAAACTGGAGGGTCAACATGGACATTAGAACATCTGACTGTCCACTATCTAGTCATGAAGCAAGTATACTGAGTGAATTTGATGGACAAGTTATCGCCAACATACTTGCCAGTGAATTGAGTTCTGTGAAACGAGATTCTGAACGCGAGGGTCTAACTGATCTCCTTGATTATCTAAACTCACCAACTGAATTGTTGAAGAAGAAGCCTTACTTAGGGACAACTTGCAAGTTCAACACCTGGGGAGACTCGAATAGATCTGGAAAGTTCACATACAGCAGCAGATCTGGAGAATCCATTGGAATCTTCATTGCAGGGAAATTGCACATCCATCTCTCATCTGAGTCCGTTGCCTTGTTGTGTGAAACTGAAAGACAAGTGCTTTCTTGGATGAGTAAGAGGAGGACTGAGGTAATAACTAAAGAACAGCATCAACTGTTTCTAAGTCTTCTCCCACAGTCTCATGAGTGTTTACAAAAGCACAAGGACGGTAGTGCGCTATCAGTCATACCTGATAGCAGCAACCCCCGATTACTTAAGTTTGTGCCCCTCAAAAAAGGTCTAGCAGTGGTGAAAATCAAAAAACAAATTTTAACAGTGAAGAAGCAGGTTGTGTTTGATGCAGAGAGCGAGCCTAGACTGCAGTGGGGGCATGGCTGCTTGTCCATTGTTTATGACGAAACTGATACTCAGACCACATACCATGAAAATCTTTTGAAGGTGAAGCATCTTGTAGACTGCTCTACAGATAGAAAAAAGCTTTTGCCCCAGTCAGTGTTTTCTGACTCCAAAGTTGTCCTTTCAAGGATCAAGTTCAAGACGGAGCTTCTCCTCAACTCATTGACGCTGCTCCACTGTTTCCTAAAACATGCTCCTAGTGATGCCATAATGGAGGTAGAGAGCAAAAGTAGCTTGCTACACAAGTACCTCAAATCGGGAGGTGTCAGGCAACGGAACACTGAAGTGCTCTTCAGAGAGAAGTTAAACAAGGTTGTTATAAAAGACAATCTTGAGCAAGGTGTGGAAGAGGAGATTGAGTTTTGCAACAACTTGACTAAGACTGTTTCAGAGAACCCATTACCACTCAGCTGTTGGTCTGAAGTTCAAAATTACATTGAAGACATAGGCTTTAACAATGTACTTGTTAACATTGACAGAAACACGGTGAAAAGTGAACTTTTATGGAAATTTACGTTAGACACCAATGTAAGCACCACAAGTACTATAAAAGACGTGAGGACATTGGTGTCCTACGTTAGCACTGAAACCATCCCTAAGTTCTTGCTTGCATTCTTACTTTATGAAGAAGTGTTAATGAACTTAATCAACCAGTGTAAGGCAGTAAAAGAACTCATCAACAGCACAGGACTCTCAGACTTGGAACTGGAAAGCTTACTCACTTTATGTGCTTTCTATTTCCAAAGTGAGTGCAGTAAGAGAGATGGTCCTAGATGCTCCTTTGCAGCACTATTAAGTCTAATCCATGAGGATTGGCAGAGGATAGGTAAAAACATTCTTGTTCGTGCAAACAATGAACTAGGTGATGTGTCACTGAAGGTTAACATTGTCCTGGTGCCTCTCAAGGACATGTCTAAGCCAAAGTCTGAGAGAGTGGTCATGGCTAGAAGGTCACTAAATCATGCTCTATCCTTGATGTTTCTGGACGAGATGTCACTACCTGAGCTAAAATCCTTATCCGTGAACTGCAAAATGGGGAACTTTGAAGGGCAGGAGTGCTTTGAGTTCACTATTCTGAAGGACAATAGCGCAAGACTAGATTACAACAAGTTGATTGACCACTGTGTGGACATGGAAAAAAAGAGGGAAGCGGTTAGAGCAGTAGAAGATTTAATTTTGATGTTGACAGGCAGAGCAGTCAAACCCAGCGCTGTAACACAGTTTGTACACGGGGACGAGCAGTGTCAAGAGCAAATAAGCTTAGATGATCTGATGGCAAACGACACGGTAACAGACTTTCCTGATAGGGAAGCAGAAGCCCTCAAAACAGGAAATCTTGGCTTTAACTGGGACTCAGATTGAACATGCCGCTTATAAGCCATTAATACCTTTCGGCGTCACAAGGACAAATGATGCAGTTTTAGCTGCATCATTCATTAACATTAAAGCCTTCAAACAAGCTAACTACTCTGCATTCTCCTCAATCAACTCAATTGCTTCAACTGATCTATTTTACTAGCTCATCGATCCTCTCTTTCTTAGCTATATCTTTGAGA' + - name: M + sequence: 'TCTCAAAGAAATACTTGCGGCACGTCAGTACGTAAGTGTTAACTTTGAGGAAGTGGATTGAGCATCTTAATTGCAGCATACCTGCTAACATCATGCATATATCATTAATGTATGCAATCCTTTGCCTACAGCTGTGTGGTCTGGGAGAGACTCATGGATCACACAATGAAACTAGACACAATAAAACAGACACCATGACAACACCCGGTGATAACCCGAGCTCTGAACCGCCAGTGAGCACGGCCTTGTCTATTACACTTGACCCCTCCACTGTCACACCCACAACACCAGCCAGTGGATTAGAAGGCTCAGGGGAAGTCTACACATCCCCTCCGATCACCACCGGGAGCTTGCCCCTGTCGGAGACAACACCAGAACTCCCTGTTACAACCGGCACAGACACCTTAAGCGCAGGTGATGTCGATCCCAGCACGCAGACAGCCGGAGGCACCTCCGCACCAACAGTCCGCACAAGTCTACCCAACAGCCCTAGCACACCATCTACACCACAAGACACACACCATCCTGTGAGAAATCTACTTTCAGTCACGAGTCCTGGGCCAGATGAAACATCAACACCCTCGGGAACAGGCAAAGAGAGCTCAGCAACCAGTAGCCCTCATCCAGTCTCCAACAGACCACCAACCCCTCCTGCAACAGCCCAGGGACCCACTGAAAATGACAGTCACAACGCCACTGAACACCCTGAGTCCCTGACACAGTCAGCAACCCCAGGCCTAATGACCTCTCCAACACAGATAGTCCACCCACAAAGTGCCACCCCCATAACCGTTCAAGACACACATCCCAGTCCAACGAACAGGTCTAAAAGAAACCTTAAGATGGAAATAATCTTGACTTTATCTCAGGGTTTAAAAAAGTACTATGGGAAAATATTAAGGCTTCTGCAACTCACCTTAGAGGAGGACACTGAAGGTCTACTGGAATGGTGTAAGAGAAATCTTGGTCTTGATTGTGATGACACTTTCTTTCAAAAGAGAATTGAAGAATTCTTTATAACTGGTGAGGGCCATTTTAATGAAGTTTTACAATTTAGAACGCCAGGCACGTTGAGCACCACAGAGTCAACACCTGCTGGGCTGCCAACAGCTGAACCTTTTAAGTCCTACTTCGCCAAAGGATTCCTCTCGATAGATTCAGGTTACTACTCAGCCAAATGTTACTCAGGAACATCCAATTCAGGGCTTCAATTGATTAACATTACCCGACATTCAACTAGAATAGTTGACACACCTGGGCCTAAGATCACTAACCTAAAGACCATCAACTGCATAAACTTGAAGGCATCGATCTTCAAAGAACATAGAGAGGTTGAAATCAATGTGCTTCTCCCCCAAGTTGCAGTTAATCTCTCAAACTGTCACGTTGTAATCAAATCACATGTCTGTGACTACTCTTTAGACATTGACGGTGCGGTGAGGCTTCCTCACATTTACCATGAAGGAGTTTTCATCCCAGGAACTTACAAAATAGTGATAGATAAAAAAAATAAGTTGAATGACAGATGCACCTTATTTACCGACTGTGTGATAAAAGGAAGGGAGGTTCGTAAAGGACAGTCAGTTTTGAGGCAGTACAAGACGGAAATCAGGATTGGCAAGGCATCAACCGGCTCTAGAAGATTGCTTTCAGAAGAACCCAGTGATGACTGTATATCAAGAACTCAACTATTAAGGACAGAGACTGCAGAGATCCACGGCGACAACTATGGTGGCCCGGGTGACAAAATAACCATCTGCAATGGCTCAACTATTGTAGACCAAAGACTGGGCAGTGAACTAGGATGCTACACCATCAATAGAGTGAGGTCATTCAAGCTATGCGAAAACAGTGCCACAGGGAAGAATTGTGAAATAGACAGTGTCCCAGTTAAATGCAGGCAGGGTTATTGCCTAAGAATCACTCAGGAAGGGAGGGGCCACGTAAAATTATCTAGGGGCTCAGAGGTTGTCTTAGATGCATGCGATACAAGCTGTGAAATAATGATACCTAAGGGCACTGGTGACATCCTAGTTGACTGTTCAGGTGGGCAGCAACATTTTCTAAAGGACAATTTGATAGATCTAGGATGCCCCAAAATTCCATTATTGGGCAAAATGGCTATTTACATTTGCAGAATGTCAAACCACCCCAAAACAACCATGGCTTTCCTCTTCTGGTTCAGCTTTGGCTATGTAATAACCTGCATACTTTGCAAGGCCATTTTTTACTTGTTAATAATTGTTGGAACACTAGGGAAAAGGCTCAAGCAGTATAGAGAGTTGAAACCTCAGACTTGCACCATATGTGAGACAACTCCTGTAAATGCAATAGATGCTGAGATGCATGACCTCAATTGCAGTTACAACATTTGTCCCTACTGTGCATCTAGACTAACCTCAGATGGGCTTGCTAGGCATGTGATACAATGCCCTAAGCGGAAGGAGAAAGTGGAAGAAACTGAACTGTACTTGAACTTAGAAAGAATTCCTTGGGTTGTAAGAAAGCTGTTGCAGGTGTCAGAGTCAACTGGTGTGGCATTGAAAAGAAGCAGTTGGCTGATTGTGCTGCTTGTGCTATTCACTGTTTCATTATCACCAGTTCAATCAGCACCCATTGGTCAAGGGAAGACAATTGAGGCATACCGGGCCAGGGAAGGGTACACAAGTATATGCCTCTTTGTACTAGGAAGTATCCTATTTATAGTTTCTTGCCTAATGAAAGGGCTGGTTGACAGTGTTGGCAACTCCTTCTTCCCTGGACTGTCCATTTGCAAAACGTGCTCCATAAGCAGCATTAATGGCTTTGAAATTGAGTCCCATAAGTGCTATTGCAGCTTATTCTGTTGCCCCTATTGTAGGCACTGCTCTACCGATAAAGAAATTCATAAGCTGCACTTGAGCATCTGCAAAAAAAGGAAAAAAGGAAGTAATGTCATGTTGGCTGTCTGCAAGCTCATGTGTTTCAGGGCCACCATGGAAGTAAGTAACAGAGCCCTGTTTATCCGTAGCATCATCAACACCACTTTTGTTTTGTGCATACTGATACTAGCAGTTTGTGTTGTTAGCACCTCAGCAGTGGAGATGGAAAACCTACCAGCAGGGACCTGGGAAAGAGAAGAAGACCTAACAAATTTCTGTCATCAGGAATGCCAGGTTACAGAGACTGAATGCCTCTGCCCTTATGAAGCTCTAGTACTCAGAAAGCCTTTATTCCTAGATAGTACAGCTAAAGGCATGAAAAATCTGCTAAATTCAACAAGTTTAGAAACGAGTTTATCAATTGAGGCACCATGGGGAGCAATAAATGTTCAGTCAACCTACAAACCAACTGTGTCAACTGCAAACATAGCACTCAGTTGGAGCTCAGTGGAACACAGAGGCAATAAGATCTTGGTTTCAGGCAGATCAGAATCAATTATGAAGCTGGAAGAAAGGACAGGAATCAGCTGGGATCTCGGTGTAGAAGATGCCTCTGAATCTAAACTGCTTACAGTATCTGTCATGGACTTGTCTCAGATGTACTCTCCTGTCTTCGAGTACTTATCAGGGGACAGACAGGTGGGAGAGTGGCCCAAAGCAACTTGCACAGGTGACTGCCCAGAAAGATGTGGCTGCACATCATCAACCTGTTTGCACAAAGAATGGCCTCACTCAAGAAATTGGAGATGCAATCCCACTTGGTGCTGGGGTGTAGGGACTGGCTGCACCTGTTGTGGATTAGATGTGAAAGACCTTTTTACAGATTATATGTTTGTCAAGTGGAAAGTTGAATACATCAAGACAGAGGCCATAGTGTGTGTAGAACTTACTAGTCAGGAAAGGCAGTGTAGCTTGATTGAAGCGGGCACAAGGTTCAATTTAGGTCCTGTGACCATCACACTGTCAGAACCAAGAAACATCCAACAAAAACTCCCTCCTGAAATAATCACACTGCATCCTAGGATCGAAGAAGGTTTTTTTGACCTGATGCATGTGCAAAAGGTGTTATCGGCAAGCACAGTGTGTAAGTTGCAGAGTTGCACACATGGTGTGCCAGGAGACCTACAGGTCTACCACATCGGAAATTTATTAAAAGGGGATAAGGTAAATGGACATCTAATTCATAAAATTGAGCCACACTTCAACACCTCCTGGATGTCCTGGGATGGTTGTGACCTAGACTACTACTGCAACATGGGAGATTGGCCTTCTTGCACATACACAGGGGTCACCCAACACAATCATGCTTCATTTGTAAACTTACTCAACATTGAAACTGATTACACAAAGAACTTCCACTTTCACTCTAAAAGGGTCACTGCACACGGAGATACACCACAACTAGATCTTAAGGCAAGACCAACCTATGGTGCAGGCGAGATCACTGTTCTGGTAGAAGTTGCTGACATGGAGTTACATACAAAGAAGATTGAAATATCAGGCTTAAAATTTGCAAGCTTAGCTTGCACAGGTTGTTATGCTTGTAGCTCTGGCATCTCATGCAAAGTTAGAATTCATGTGGATGAACCAGATGAACTTACAGTACATGTTAAAAGTGATGATCCAGATGTGGTTGCAGCTAGCTCAAGTCTCATGGCAAGGAAGCTTGAATTTGGAACAGACAGTACATTTAAAGCTTTCTCGGCCATGCCTAAAACTTCTCTATGTTTCTACATTGTTGAAAGAGAACACTGTAAGAGCTGCAGTGAAGAAGACACAAAAAAATGTGTTAACACAAAACTTGAGCAACCACAAAGCATTTTGATCGAACACAAGGGAACTATAATCGGAAAGCAAAACAGCACTTGCACGGCTAAGGCAAGTTGCTGGTTAGAGTCAGTCAAGAGTTTTTTTTATGGCCTAAAGAACATGCTTAGTGGCATTTTTGGCAATGTCTTTATGGGCATTTTCTTGTTCCTTGCCCCCTTCATCCTGTTAATACTATTCTTTATGTTTGGGTGGAGGATCCTATTCTGCTTTAAATGTTGTAGAAGAACCAGAGGCCTGTTCAAGTATAGACACCTCAAAGACGATGAAGAAACTGGTTATAGAAGGATTATTGAAAAACTAAACAATAAAAAAGGAAAAAACAAACTGCTTGATGGTGAAAGACTTGCTGATAGAAGAATTGCCGAACTGTTCTCTACAAAAACACACATTGGCTAGACCAACTGAATGGGCCTTAAAAATGATGGCATTACACTGAACAATGCTGTCATTCATGCTGACATCTTTAGTTGCAACCCTACTACATTATCATCACAATATACTACATCTAATCTGCTACATTGTATCCATGTACAGACTCTATAATGCTTGAAACTGCCTTTGCTCTATTTACTCTGACCTAAATCTTGACTGCGTGCCGCCACTATATCTTTGAGA' + - name: S + sequence: 'TCTCAAAGAAACACGTGCCGCTTACGCCCACAGTGTTCTCTTGAGTGTTAGCAGAATGGAAAACAAGATCGAGGTGAATAACAAAGATGAGATGAACAGGTGGTTTGAAGAGTTCAAAAAAGGAAATGGACTTGTGGACACCTTCACAAACTCCTATTCCTTTTGCGAGAGTGTTCCCAATTTGGACAGGTTTGTGTTTCAGATGGCCAGTGCCACCGATGATGCACAGAAGGACTCCATCTACGCATCTGCTCTGGTGGAGGCAACAAAGTTTTGTGCACCTATATATGAGTGCGCATGGGTTAGCTCCACTGGCATTGTAAAAAAGGGACTTGAATGGTTCGAGAAAAATGCAGGAACCATTAAGTCCTGGGATGAAAGTTATACTGAGCTAAAGGTCGACGTCCCGAAAATAGAGCAGCTTACCGGTTACCAACAAGCTGCCTTGAAGTGGAGAAAAGACATAGGTTTCCGTGTCAATGCCAACACAGCAGCTCTGAGCAACAAAGTCCTCGCAGAATACAAAGTCCCTGGTGAGATTGTGATGTCTGTCAAAGAGATGCTGTCAGACATGATTAGGAGAAGGAACCTGATTCTAAACAGGGGTGGTGATGAGAACCCACGTGGCCCAGTGAGCCATGAGCATGTAGACTGGTGCAGGGAGTTTGTCAAAGGCAAATACATCATGGCCTTCAACCCACCATGGGGGGACATCAACAAGTCAGGCCGTTCAGGAATAGCACTTGTTGCAACAGGCCTTGCTAAGCTTGCAGAGACTGAAGGAAAGGGAATATTTGATGAAGCCAAAAAGACTGTGGAGGCCCTCAACGGGTATCTGGACAAGCATAAGGACGAAGTTGATAGAGCAAGCGCCGACAGCATGATAACAAACCTTCTTAAGCATATTGCCAAGGCACAGGAGCTCTATAAAAATTCATCTGCACTTCGTGCACAAAGCGCACAGATTGACACTGCTTTCAGCTCATACTATTGGCTTTACAAGGCTGGCGTGACTCCTGAAACCTTCCCGACGGTGTCACAGTTCCTCTTTGAGCTAGGGAAACAGCCAAGAGGTACCAAGAAAATGAAGAAGGCTCTTCTGAGCACCCCAATGAAGTGGGGGAAGAAGCTTTATGAGCTCTTTGCCGATGATTCTTTCCAGCAGAACAGGATTTACATGCATCCTGCCGTGCTTACAGCTGGTAGAATCAGTGAAATGGGAGTCTGCTTTGGGACAATCCCTGTGGCCAATCCTGATGATGCTGCCCAAGGATCTGGACACACTAAGTCTATTCTCAACCTCCGTACCAACACTGAGACCAATAATCCGTGTGCCAAAACCATCGTCAAGCTATTTGAAGTTCAAAAAACAGGGTTCAACATTCAGGACATGGACATAGTGGCCTCTGAGCACTTGCTACACCAATCCCTTGTTGGCAAGCAATCCCCATTCCAGAACGCCTACAACGTCAAGGGCAATGCCACCAGTGCTAACATCATTTAAAATACAAACTGCTCTGTACTCAACTTCCTTCCTTCTGAACCGCCATCCATAATTGCAATACTTAATCATGCTTTTTTACTTGCTTATGTAACCTTATTTTATTAACCTTTCTCTATTTTCTCTTGTTTTAAACACTTAAAGGGCTGTGCGGCAACGGTATCTTTGAGA' + genes: + - name: RdRp + sequence: 'MDFLRSLDWTQVIAGQYVSNPRFNISDYFEIVRQPGDGNCFYHSIAELTMPNKTDHSYHYIKRLTESAARKYYQEEPEARLVGLSLEDYLKRMLSDNEWGSTLEASMLAKEMGITIIIWTVAASDEVEAGIKFGDGDVFTAVNLLHSGQTHFDALRILPQFETDTREALSLMDRVIAVDQLTSSSSDELQDYEDLALALTSAEESNRRSSLDEVTLSKKQAEILRQKASQLSKLVNKSQNIPTRVGRVLDCMFNCKLCVEISADTLILRPESKEKIGEIMSLRQLGHKLLTRDKQIKQEFSRMKLYVTKDLLDHLDVGGLLRAAFPGTGIERHMQLLHSEMILDICTVSLGVMLSTFLYGSNNKNKKKFITNCLLSTALSGKKVYKVLGNLGNELLYKAPRKALATVCSALFGKQINKLQNCFRTISPVSLLALRNLDFDCLSVQDYNGMIENMSKLDNTDVEFNHREIADLNQLTSRLITLRKEKDTDLLKQWFPESDLTRRSIRNAANAEEFVISEFFKKKDIMKFISTSGRAMSAGKIGNVLSYAHNLYLSKSSLNMTSEDISQLLIEIKRLYALQEDSEVEPIAIICDGIESNMKQLFAILPPDCARECEVLFDDIRNSPTHSTAWKHALRLKGTAYEGLFANCYGWQYIPEDIKPSLTMLIQTLFPDKFEDFLDRTQLHPEFRDLTPDFSLTQKVHFKRNQIPSVENVQISIDATLPESVEAVPVTERKMFPLPETPLSEVHSIERIMENFTRLMHGGRLSTKKRDGDPAEQGNQQSITEHESSSISAFKDYGERGIVEENHMKFSGEDQLETRQLLLVEVGFQTDIDGKIRTDHKKWKDILKLLELLGIKCSFIACADCSSTPPDRWWITEDRVRVLKNSVSFLFNKLSRNSPTEVTDIVVGAISTQKVRSYLKAGTATKTPVSTKDVLETWEKMKEHILNRPTGLTLPTSLEQAMRKGLVEGVVISKEGSESCINMLKENLDRITDEFERTKFKHELTQNITTSEKMLLSWLSEDIKSSRCGECLSNIKKAVDETANLSGKIELLAYNLQLTNHCSNCHPNGVNISNTSNVCKRCPKIEVVSHCENKGFEDSNECLTDLDRLVRLTLPGKTEKERRVKRNVEYLIKLMMSMSGIDCIKYPTGQLITHGRVSAKHNDGNLKDRSDDDQRLAEKIDTVRKELSESKLKDYSTYARGVISNSLKNLSRQGKSKCSVPRSWLEKVLFDLKVPTKDEEVLINIRNSLKARSEFVRNNDKLLIRSKEELKKCFDVQSFKLKKNKQPVPFQVDCILFKEVAAECMKRYIGTPYEGIVDTLVSLINVLTRFTWFQEVVLYGKICETFLRCCTEFNRSGVKLVKIRHCNINLSVKLPSNKKENMLCCLYSGNMELLQGPFYLNRRQAVLGSSYLYIVITLYIQVLQQYRCLEVINSVSEKTLQDIENHSMTLLEDSFREITFALEGRFEESYKIRTSRCRASGNFLNRSSRDHFISVVSGLNLVYGFLIKDNLLANSQQQNKQLQMLRFGMLAGLSRLVCPNELGKKFSTSCRRIEDNIARLYLQTSIYCSVRDVEDNVKHWKQRDLCPEVTIPCFTVYGTFVNSDRQLIFDIYNVHIYNKEMDNFDEGCISVLEETAERHMLWELDLMNSLCSDEKKDTRPARLLLGCPNVRKAATREGKKLLKLNSDTSTDTQSIASEVSDRRSYSSSKSRIRSIFGRYNSQKKPFELRSGLEVFNDPFNDYQQAITDICQFSEYTPNKESILKDCLQIIRKNPSHTMGSFELIQAISEFGMSKFPPENIDKARRDPKNWVSISEVTETTSIVASPRTHMMLKDCFKIILGTENKKIVKMLRGKLKKLGAISTNIEIGKRDCLDLLSTVDGLTDQQKENIVNGIFEPSKLSFYHWKELVKKNIDEVLLTEDGNLIFCWLKTISSSVKGSLKKRLKFMNIHSPELMPENCLFSSEEFNELIKLKKLLLNEQQDEQELKQDLLISSWIKCITACKDFASINDKIQKFIYHLSEELYDIRLQHLELSKLKQEHPSVSFTKEEVLIKRLEKNFLKQHNLEIMETVNLVFFAALSAPWCLHYKALESYLVRHPEILDCGSKEDCKLTLLDLSVSKLLVCLYQKDDEELINSSSLKLGFLVKYVVTLFTSNGEPFSLSLNDGGLDLDLHKTTDEKLLHQTKIVFAKIGLSGNSYDFIWTTQMIANSNFNVCKRLTGRSTGERLPRSVRSKVIYEMVKLVGETGMAILQQLAFAQALNYEHRFYAVLAPKAQLGGARDLLVQETGTKVMHATTEMFSRNLLKTTSDDGLTNPHLKETILNVGLDCLANMRNLDGKPISEGSNLVNFYKVICISGDNTKWGPIHCCSFFSGMMQQVLKNVPDWCSFYKLTFIKNLCRQVEIPAGSIKKILNVLRYRLCSKGGVEQHSEEDLRRLLTDNLDSWDGNDTVKFLVTTYISKGLMALNSYNHMGQGIHHATSSVLTSLAAVLFEELAIFYLKRSLPQTTVHVEHAGSSDDYAKCIVVTGILSKELYSQYDETFWKHACRLKNFTAAVQRCCQMKDSAKTLVSDCFLEFYSEFMMGYRVTPAVIKFMFTGLINSSVTSPQSLMQACQVSSQQAMYNSVPLVTNTAFTLLRQQIFFNHVEDFIRRYGILTLGTLSPFGRLFVPTYSGLVSSAVALEDAEVIARAAQTLQMNSVSIQSSSLTTLDSLGRSRTSSTAEDSSSVSDTTAASHDSGSSSSSFSFELNRPLSETELQFIKALSSLKSTQACEVIQNRITGLYCNSNEGPLDRHNVIYSSRMADSCDWLKDGKRRGNLELANRIQSVLCILIAGYYRSFGGEGTEKQVKASLNRDDNKIIEDPMIQLIPEKLRRELERLGVSRMEVDELMPSISPDDTLAQLVAKKLISLNVSTEEYSAEVSRLKQTLTARNVLHGLAGGIKELSLPIYTIFMKSYFFKDNVFLSLTDRWSTKHSTNYRDSCGKQLKGRIITKYTHWLDTFLGCSVSINRHTTVKEPSLFNPNIRCVNLITFEDGLRELSVIQSHLKVFENEFTNLNLQFSDPNRQKLRIVESRPAESELEANRAVIVKTKLFSATEQVRLSNNPAVVMGYLLDESAISEVKPTKVDFSNLLKDRFKIMQFFPSVFTLIKMLTDESSDSEKSGLSPDLQQVARYSNHLTLLSRMIQQAKPTVTVFYMLKGNLMNTEPTVAELVSYGIKEGRFFRLSDTGVDASTYSVKYWKILHCISAIGCLPLSQADKSSLLMSFLNWRVNMDIRTSDCPLSSHEASILSEFDGQVIANILASELSSVKRDSEREGLTDLLDYLNSPTELLKKKPYLGTTCKFNTWGDSNRSGKFTYSSRSGESIGIFIAGKLHIHLSSESVALLCETERQVLSWMSKRRTEVITKEQHQLFLSLLPQSHECLQKHKDGSALSVIPDSSNPRLLKFVPLKKGLAVVKIKKQILTVKKQVVFDAESEPRLQWGHGCLSIVYDETDTQTTYHENLLKVKHLVDCSTDRKKLLPQSVFSDSKVVLSRIKFKTELLLNSLTLLHCFLKHAPSDAIMEVESKSSLLHKYLKSGGVRQRNTEVLFREKLNKVVIKDNLEQGVEEEIEFCNNLTKTVSENPLPLSCWSEVQNYIEDIGFNNVLVNIDRNTVKSELLWKFTLDTNVSTTSTIKDVRTLVSYVSTETIPKFLLAFLLYEEVLMNLINQCKAVKELINSTGLSDLELESLLTLCAFYFQSECSKRDGPRCSFAALLSLIHEDWQRIGKNILVRANNELGDVSLKVNIVLVPLKDMSKPKSERVVMARRSLNHALSLMFLDEMSLPELKSLSVNCKMGNFEGQECFEFTILKDNSARLDYNKLIDHCVDMEKKREAVRAVEDLILMLTGRAVKPSAVTQFVHGDEQCQEQISLDDLMANDTVTDFPDREAEALKTGNLGFNWDSD*' + - name: GPC + sequence: 'MHISLMYAILCLQLCGLGETHGSHNETRHNKTDTMTTPGDNPSSEPPVSTALSITLDPSTVTPTTPASGLEGSGEVYTSPPITTGSLPLSETTPELPVTTGTDTLSAGDVDPSTQTAGGTSAPTVRTSLPNSPSTPSTPQDTHHPVRNLLSVTSPGPDETSTPSGTGKESSATSSPHPVSNRPPTPPATAQGPTENDSHNATEHPESLTQSATPGLMTSPTQIVHPQSATPITVQDTHPSPTNRSKRNLKMEIILTLSQGLKKYYGKILRLLQLTLEEDTEGLLEWCKRNLGLDCDDTFFQKRIEEFFITGEGHFNEVLQFRTPGTLSTTESTPAGLPTAEPFKSYFAKGFLSIDSGYYSAKCYSGTSNSGLQLINITRHSTRIVDTPGPKITNLKTINCINLKASIFKEHREVEINVLLPQVAVNLSNCHVVIKSHVCDYSLDIDGAVRLPHIYHEGVFIPGTYKIVIDKKNKLNDRCTLFTDCVIKGREVRKGQSVLRQYKTEIRIGKASTGSRRLLSEEPSDDCISRTQLLRTETAEIHGDNYGGPGDKITICNGSTIVDQRLGSELGCYTINRVRSFKLCENSATGKNCEIDSVPVKCRQGYCLRITQEGRGHVKLSRGSEVVLDACDTSCEIMIPKGTGDILVDCSGGQQHFLKDNLIDLGCPKIPLLGKMAIYICRMSNHPKTTMAFLFWFSFGYVITCILCKAIFYLLIIVGTLGKRLKQYRELKPQTCTICETTPVNAIDAEMHDLNCSYNICPYCASRLTSDGLARHVIQCPKRKEKVEETELYLNLERIPWVVRKLLQVSESTGVALKRSSWLIVLLVLFTVSLSPVQSAPIGQGKTIEAYRAREGYTSICLFVLGSILFIVSCLMKGLVDSVGNSFFPGLSICKTCSISSINGFEIESHKCYCSLFCCPYCRHCSTDKEIHKLHLSICKKRKKGSNVMLAVCKLMCFRATMEVSNRALFIRSIINTTFVLCILILAVCVVSTSAVEMENLPAGTWEREEDLTNFCHQECQVTETECLCPYEALVLRKPLFLDSTAKGMKNLLNSTSLETSLSIEAPWGAINVQSTYKPTVSTANIALSWSSVEHRGNKILVSGRSESIMKLEERTGISWDLGVEDASESKLLTVSVMDLSQMYSPVFEYLSGDRQVGEWPKATCTGDCPERCGCTSSTCLHKEWPHSRNWRCNPTWCWGVGTGCTCCGLDVKDLFTDYMFVKWKVEYIKTEAIVCVELTSQERQCSLIEAGTRFNLGPVTITLSEPRNIQQKLPPEIITLHPRIEEGFFDLMHVQKVLSASTVCKLQSCTHGVPGDLQVYHIGNLLKGDKVNGHLIHKIEPHFNTSWMSWDGCDLDYYCNMGDWPSCTYTGVTQHNHASFVNLLNIETDYTKNFHFHSKRVTAHGDTPQLDLKARPTYGAGEITVLVEVADMELHTKKIEISGLKFASLACTGCYACSSGISCKVRIHVDEPDELTVHVKSDDPDVVAASSSLMARKLEFGTDSTFKAFSAMPKTSLCFYIVEREHCKSCSEEDTKKCVNTKLEQPQSILIEHKGTIIGKQNSTCTAKASCWLESVKSFFYGLKNMLSGIFGNVFMGIFLFLAPFILLILFFMFGWRILFCFKCCRRTRGLFKYRHLKDDEETGYRRIIEKLNNKKGKNKLLDGERLADRRIAELFSTKTHIG*' + - name: NP + sequence: 'MENKIEVNNKDEMNRWFEEFKKGNGLVDTFTNSYSFCESVPNLDRFVFQMASATDDAQKDSIYASALVEATKFCAPIYECAWVSSTGIVKKGLEWFEKNAGTIKSWDESYTELKVDVPKIEQLTGYQQAALKWRKDIGFRVNANTAALSNKVLAEYKVPGEIVMSVKEMLSDMIRRRNLILNRGGDENPRGPVSHEHVDWCREFVKGKYIMAFNPPWGDINKSGRSGIALVATGLAKLAETEGKGIFDEAKKTVEALNGYLDKHKDEVDRASADSMITNLLKHIAKAQELYKNSSALRAQSAQIDTAFSSYYWLYKAGVTPETFPTVSQFLFELGKQPRGTKKMKKALLSTPMKWGKKLYELFADDSFQQNRIYMHPAVLTAGRISEMGVCFGTIPVANPDDAAQGSGHTKSILNLRTNTETNNPCAKTIVKLFEVQKTGFNIQDMDIVASEHLLHQSLVGKQSPFQNAYNVKGNATSANII*' +displayName: Crimean-Congo Hemorrhagic Fever Virus + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-cchf:8080" diff --git a/kubernetes/loculus/fixtures/organisms/dummy-organism-with-files.yaml b/kubernetes/loculus/fixtures/organisms/dummy-organism-with-files.yaml new file mode 100644 index 0000000000..180530af3f --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/dummy-organism-with-files.yaml @@ -0,0 +1,117 @@ +schema: + image: /images/organisms/hmpv.jpg + organismName: Test organism (with files) + submissionDataTypes: + consensusSequences: false + files: + enabled: true + categories: + - name: raw_reads + displayName: Raw reads + files: + - name: raw_reads + displayName: Raw reads + metadata: + - name: date + displayName: Date + type: date + initiallyVisible: true + header: Collection Details + - name: region + displayName: Region + type: string + initiallyVisible: true + generateIndex: true + autocomplete: true + header: Collection Details + - name: country + displayName: Country + initiallyVisible: true + type: string + generateIndex: true + autocomplete: true + options: &id001 + - name: Germany + - name: Canada + - name: New Zealand + - name: Colombia + header: Collection Details + - name: division + displayName: Division + initiallyVisible: true + type: string + generateIndex: true + autocomplete: true + header: Collection Details + - name: host + displayName: Host + initiallyVisible: true + type: string + autocomplete: true + header: Collection Details + - name: lineage + displayName: Lineage + isSequenceFilter: true + initiallyVisible: false + notSearchable: true + type: string + - name: pangoLineage + isSequenceFilter: true + initiallyVisible: true + displayName: Pango lineage + autocomplete: true + type: string + lineageSystem: pangoLineage + options: &id002 + - name: A + - name: A.1 + - name: A.1.1 + - name: A.2 + - name: B + - name: hiddenField + displayName: Hidden Field + initiallyVisible: false + type: string + hideInSearchResultsTable: true + - name: versionComment + type: string + tableColumns: + - country + - division + - date + defaultOrderBy: date + defaultOrder: descending + inputFields: + - name: id + displayName: ID + example: GJP123 + noEdit: true + required: true + definition: Your sequence identifier; should match the sequence's id in the FASTA file - this is used to link the metadata to the FASTA sequence. + - name: date + displayName: Date + - name: region + displayName: Region + - name: country + displayName: Country + options: *id001 + - name: division + displayName: Division + - name: host + displayName: Host + - name: lineage + displayName: Lineage + - name: pangoLineage + displayName: Pango lineage + required: true + options: *id002 + - name: hiddenField + displayName: Hidden Field +referenceGenomes: [] +referenceGenome: + nucleotideSequences: [] + genes: [] +displayName: Test organism (with files) + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-dummy-organism-with-files:8080" diff --git a/kubernetes/loculus/fixtures/organisms/dummy-organism.yaml b/kubernetes/loculus/fixtures/organisms/dummy-organism.yaml new file mode 100644 index 0000000000..67be28e4a9 --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/dummy-organism.yaml @@ -0,0 +1,169 @@ +schema: + submissionDataTypes: + consensusSequences: true + maxSequencesPerEntry: 1 + image: /images/organisms/mpox_small.jpg + organismName: Test Dummy Organism + metadataTemplate: + - country + - date + metadata: + - name: date + displayName: Date + type: date + initiallyVisible: true + header: Collection Details + - name: region + displayName: Region + type: string + initiallyVisible: true + generateIndex: true + autocomplete: true + header: Collection Details + - name: country + displayName: Country + initiallyVisible: true + type: string + generateIndex: true + autocomplete: true + options: &id001 + - name: Germany + - name: Canada + - name: New Zealand + - name: Colombia + header: Collection Details + - name: division + displayName: Division + initiallyVisible: true + type: string + generateIndex: true + autocomplete: true + header: Collection Details + - name: host + displayName: Host + initiallyVisible: true + type: string + autocomplete: true + header: Collection Details + - name: lineage + displayName: Lineage + isSequenceFilter: true + initiallyVisible: false + notSearchable: true + type: string + - name: pangoLineage + isSequenceFilter: true + initiallyVisible: true + displayName: Pango lineage + autocomplete: true + type: string + lineageSystem: pangoLineage + options: &id002 + - name: A + - name: A.1 + - name: A.1.1 + - name: A.2 + - name: B + - name: hiddenField + displayName: Hidden Field + initiallyVisible: false + type: string + hideInSearchResultsTable: true + - name: versionComment + type: string + tableColumns: + - country + - division + - date + - pangoLineage + defaultOrderBy: date + defaultOrder: descending + inputFields: + - name: id + displayName: ID + example: GJP123 + noEdit: true + required: true + definition: Your sequence identifier; should match the sequence's id in the FASTA file - this is used to link the metadata to the FASTA sequence. + - name: date + displayName: Date + - name: region + displayName: Region + - name: country + displayName: Country + options: *id001 + - name: division + displayName: Division + - name: host + displayName: Host + - name: lineage + displayName: Lineage + - name: pangoLineage + displayName: Pango lineage + required: true + options: *id002 + - name: hiddenField + displayName: Hidden Field +referenceGenomes: + - name: main + references: + - name: singleReference + sequence: 'ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTTACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAGATGGCACTTGTGGCTTAGTAGAAGTTGAAAAAGGCGTTTTGCCTCAACTTGAACAGCCCTATGTGTTCATCAAACGTTCGGATGCTCGAACTGCACCTCATGGTCATGTTATGGTTGAGCTGGTAGCAGAACTCGAAGGCATTCAGTACGGTCGTAGTGGTGAGACACTTGGTGTCCTTGTCCCTCATGTGGGCGAAATACCAGTGGCTTACCGCAAGGTTCTTCTTCGTAAGAACGGTAATAAAGGAGCTGGTGGCCATAGTTACGGCGCCGATCTAAAGTCATTTGACTTAGGCGACGAGCTTGGCACTGATCCTTATGAAGATTTTCAAGAAAACTGGAACACTAAACATAGCAGTGGTGTTACCCGTGAACTCATGCGTGAGCTTAACGGAGGGGCATACACTCGCTATGTCGATAACAACTTCTGTGGCCCTGATGGCTACCCTCTTGAGTGCATTAAAGACCTTCTAGCACGTGCTGGTAAAGCTTCATGCACTTTGTCCGAACAACTGGACTTTATTGACACTAAGAGGGGTGTATACTGCTGCCGTGAACATGAGCATGAAATTGCTTGGTACACGGAACGTTCTGAAAAGAGCTATGAATTGCAGACACCTTTTGAAATTAAATTGGCAAAGAAATTTGACACCTTCAATGGGGAATGTCCAAATTTTGTATTTCCCTTAAATTCCATAATCAAGACTATTCAACCAAGGGTTGAAAAGAAAAAGCTTGATGGCTTTATGGGTAGAATTCGATCTGTCTATCCAGTTGCGTCACCAAATGAATGCAACCAAATGTGCCTTTCAACTCTCATGAAGTGTGATCATTGTGGTGAAACTTCATGGCAGACGGGCGATTTTGTTAAAGCCACTTGCGAATTTTGTGGCACTGAGAATTTGACTAAAGAAGGTGCCACTACTTGTGGTTACTTACCCCAAAATGCTGTTGTTAAAATTTATTGTCCAGCATGTCACAATTCAGAAGTAGGACCTGAGCATAGTCTTGCCGAATACCATAATGAATCTGGCTTGAAAACCATTCTTCGTAAGGGTGGTCGCACTATTGCCTTTGGAGGCTGTGTGTTCTCTTATGTTGGTTGCCATAACAAGTGTGCCTATTGGGTTCCACGTGCTAGCGCTAACATAGGTTGTAACCATACAGGTGTTGTTGGAGAAGGTTCCGAAGGTCTTAATGACAACCTTCTTGAAATACTCCAAAAAGAGAAAGTCAACATCAATATTGTTGGTGACTTTAAACTTAATGAAGAGATCGCCATTATTTTGGCATCTTTTTCTGCTTCCACAAGTGCTTTTGTGGAAACTGTGAAAGGTTTGGATTATAAAGCATTCAAACAAATTGTTGAATCCTGTGGTAATTTTAAAGTTACAAAAGGAAAAGCTAAAAAAGGTGCCTGGAATATTGGTGAACAGAAATCAATACTGAGTCCTCTTTATGCATTTGCATCAGAGGCTGCTCGTGTTGTACGATCAATTTTCTCCCGCACTCTTGAAACTGCTCAAAATTCTGTGCGTGTTTTACAGAAGGCCGCTATAACAATACTAGATGGAATTTCACAGTATTCACTGAGACTCATTGATGCTATGATGTTCACATCTGATTTGGCTACTAACAATCTAGTTGTAATGGCCTACATTACAGGTGGTGTTGTTCAGTTGACTTCGCAGTGGCTAACTAACATCTTTGGCACTGTTTATGAAAAACTCAAACCCGTCCTTGATTGGCTTGAAGAGAAGTTTAAGGAAGGTGTAGAGTTTCTTAGAGACGGTTGGGAAATTGTTAAATTTATCTCAACCTGTGCTTGTGAAATTGTCGGTGGACAAATTGTCACCTGTGCAAAGGAAATTAAGGAGAGTGTTCAGACATTCTTTAAGCTTGTAAATAAATTTTTGGCTTTGTGTGCTGACTCTATCATTATTGGTGGAGCTAAACTTAAAGCCTTGAATTTAGGTGAAACATTTGTCACGCACTCAAAGGGATTGTACAGAAAGTGTGTTAAATCCAGAGAAGAAACTGGCCTACTCATGCCTCTAAAAGCCCCAAAAGAAATTATCTTCTTAGAGGGAGAAACACTTCCCACAGAAGTGTTAACAGAGGAAGTTGTCTTGAAAACTGGTGATTTACAACCATTAGAACAACCTACTAGTGAAGCTGTTGAAGCTCCATTGGTTGGTACACCAGTTTGTATTAACGGGCTTATGTTGCTCGAAATCAAAGACACAGAAAAGTACTGTGCCCTTGCACCTAATATGATGGTAACAAACAATACCTTCACACTCAAAGGCGGTGCACCAACAAAGGTTACTTTTGGTGATGACACTGTGATAGAAGTGCAAGGTTACAAGAGTGTGAATATCACTTTTGAACTTGATGAAAGGATTGATAAAGTACTTAATGAGAAGTGCTCTGCCTATACAGTTGAACTCGGTACAGAAGTAAATGAGTTCGCCTGTGTTGTGGCAGATGCTGTCATAAAAACTTTGCAACCAGTATCTGAATTACTTACACCACTGGGCATTGATTTAGATGAGTGGAGTATGGCTACATACTACTTATTTGATGAGTCTGGTGAGTTTAAATTGGCTTCACATATGTATTGTTCTTTCTACCCTCCAGATGAGGATGAAGAAGAAGGTGATTGTGAAGAAGAAGAGTTTGAGCCATCAACTCAATATGAGTATGGTACTGAAGATGATTACCAAGGTAAACCTTTGGAATTTGGTGCCACTTCTGCTGCTCTTCAACCTGAAGAAGAGCAAGAAGAAGATTGGTTAGATGATGATAGTCAACAAACTGTTGGTCAACAAGACGGCAGTGAGGACAATCAGACAACTACTATTCAAACAATTGTTGAGGTTCAACCTCAATTAGAGATGGAACTTACACCAGTTGTTCAGACTATTGAAGTGAATAGTTTTAGTGGTTATTTAAAACTTACTGACAATGTATACATTAAAAATGCAGACATTGTGGAAGAAGCTAAAAAGGTAAAACCAACAGTGGTTGTTAATGCAGCCAATGTTTACCTTAAACATGGAGGAGGTGTTGCAGGAGCCTTAAATAAGGCTACTAACAATGCCATGCAAGTTGAATCTGATGATTACATAGCTACTAATGGACCACTTAAAGTGGGTGGTAGTTGTGTTTTAAGCGGACACAATCTTGCTAAACACTGTCTTCATGTTGTCGGCCCAAATGTTAACAAAGGTGAAGACATTCAACTTCTTAAGAGTGCTTATGAAAATTTTAATCAGCACGAAGTTCTACTTGCACCATTATTATCAGCTGGTATTTTTGGTGCTGACCCTATACATTCTTTAAGAGTTTGTGTAGATACTGTTCGCACAAATGTCTACTTAGCTGTCTTTGATAAAAATCTCTATGACAAACTTGTTTCAAGCTTTTTGGAAATGAAGAGTGAAAAGCAAGTTGAACAAAAGATCGCTGAGATTCCTAAAGAGGAAGTTAAGCCATTTATAACTGAAAGTAAACCTTCAGTTGAACAGAGAAAACAAGATGATAAGAAAATCAAAGCTTGTGTTGAAGAAGTTACAACAACTCTGGAAGAAACTAAGTTCCTCACAGAAAACTTGTTACTTTATATTGACATTAATGGCAATCTTCATCCAGATTCTGCCACTCTTGTTAGTGACATTGACATCACTTTCTTAAAGAAAGATGCTCCATATATAGTGGGTGATGTTGTTCAAGAGGGTGTTTTAACTGCTGTGGTTATACCTACTAAAAAGGCTGGTGGCACTACTGAAATGCTAGCGAAAGCTTTGAGAAAAGTGCCAACAGACAATTATATAACCACTTACCCGGGTCAGGGTTTAAATGGTTACACTGTAGAGGAGGCAAAGACAGTGCTTAAAAAGTGTAAAAGTGCCTTTTACATTCTACCATCTATTATCTCTAATGAGAAGCAAGAAATTCTTGGAACTGTTTCTTGGAATTTGCGAGAAATGCTTGCACATGCAGAAGAAACACGCAAATTAATGCCTGTCTGTGTGGAAACTAAAGCCATAGTTTCAACTATACAGCGTAAATATAAGGGTATTAAAATACAAGAGGGTGTGGTTGATTATGGTGCTAGATTTTACTTTTACACCAGTAAAACAACTGTAGCGTCACTTATCAACACACTTAACGATCTAAATGAAACTCTTGTTACAATGCCACTTGGCTATGTAACACATGGCTTAAATTTGGAAGAAGCTGCTCGGTATATGAGATCTCTCAAAGTGCCAGCTACAGTTTCTGTTTCTTCACCTGATGCTGTTACAGCGTATAATGGTTATCTTACTTCTTCTTCTAAAACACCTGAAGAACATTTTATTGAAACCATCTCACTTGCTGGTTCCTATAAAGATTGGTCCTATTCTGGACAATCTACACAACTAGGTATAGAATTTCTTAAGAGAGGTGATAAAAGTGTATATTACACTAGTAATCCTACCACATTCCACCTAGATGGTGAAGTTATCACCTTTGACAATCTTAAGACACTTCTTTCTTTGAGAGAAGTGAGGACTATTAAGGTGTTTACAACAGTAGACAACATTAACCTCCACACGCAAGTTGTGGACATGTCAATGACATATGGACAACAGTTTGGTCCAACTTATTTGGATGGAGCTGATGTTACTAAAATAAAACCTCATAATTCACATGAAGGTAAAACATTTTATGTTTTACCTAATGATGACACTCTACGTGTTGAGGCTTTTGAGTACTACCACACAACTGATCCTAGTTTTCTGGGTAGGTACATGTCAGCATTAAATCACACTAAAAAGTGGAAATACCCACAAGTTAATGGTTTAACTTCTATTAAATGGGCAGATAACAACTGTTATCTTGCCACTGCATTGTTAACACTCCAACAAATAGAGTTGAAGTTTAATCCACCTGCTCTACAAGATGCTTATTACAGAGCAAGGGCTGGTGAAGCTGCTAACTTTTGTGCACTTATCTTAGCCTACTGTAATAAGACAGTAGGTGAGTTAGGTGATGTTAGAGAAACAATGAGTTACTTGTTTCAACATGCCAATTTAGATTCTTGCAAAAGAGTCTTGAACGTGGTGTGTAAAACTTGTGGACAACAGCAGACAACCCTTAAGGGTGTAGAAGCTGTTATGTACATGGGCACACTTTCTTATGAACAATTTAAGAAAGGTGTTCAGATACCTTGTACGTGTGGTAAACAAGCTACAAAATATCTAGTACAACAGGAGTCACCTTTTGTTATGATGTCAGCACCACCTGCTCAGTATGAACTTAAGCATGGTACATTTACTTGTGCTAGTGAGTACACTGGTAATTACCAGTGTGGTCACTATAAACATATAACTTCTAAAGAAACTTTGTATTGCATAGACGGTGCTTTACTTACAAAGTCCTCAGAATACAAAGGTCCTATTACGGATGTTTTCTACAAAGAAAACAGTTACACAACAACCATAAAACCAGTTACTTATAAATTGGATGGTGTTGTTTGTACAGAAATTGACCCTAAGTTGGACAATTATTATAAGAAAGACAATTCTTATTTCACAGAGCAACCAATTGATCTTGTACCAAACCAACCATATCCAAACGCAAGCTTCGATAATTTTAAGTTTGTATGTGATAATATCAAATTTGCTGATGATTTAAACCAGTTAACTGGTTATAAGAAACCTGCTTCAAGAGAGCTTAAAGTTACATTTTTCCCTGACTTAAATGGTGATGTGGTGGCTATTGATTATAAACACTACACACCCTCTTTTAAGAAAGGAGCTAAATTGTTACATAAACCTATTGTTTGGCATGTTAACAATGCAACTAATAAAGCCACGTATAAACCAAATACCTGGTGTATACGTTGTCTTTGGAGCACAAAACCAGTTGAAACATCAAATTCGTTTGATGTACTGAAGTCAGAGGACGCGCAGGGAATGGATAATCTTGCCTGCGAAGATCTAAAACCAGTCTCTGAAGAAGTAGTGGAAAATCCTACCATACAGAAAGACGTTCTTGAGTGTAATGTGAAAACTACCGAAGTTGTAGGAGACATTATACTTAAACCAGCAAATAATAGTTTAAAAATTACAGAAGAGGTTGGCCACACAGATCTAATGGCTGCTTATGTAGACAATTCTAGTCTTACTATTAAGAAACCTAATGAATTATCTAGAGTATTAGGTTTGAAAACCCTTGCTACTCATGGTTTAGCTGCTGTTAATAGTGTCCCTTGGGATACTATAGCTAATTATGCTAAGCCTTTTCTTAACAAAGTTGTTAGTACAACTACTAACATAGTTACACGGTGTTTAAACCGTGTTTGTACTAATTATATGCCTTATTTCTTTACTTTATTGCTACAATTGTGTACTTTTACTAGAAGTACAAATTCTAGAATTAAAGCATCTATGCCGACTACTATAGCAAAGAATACTGTTAAGAGTGTCGGTAAATTTTGTCTAGAGGCTTCATTTAATTATTTGAAGTCACCTAATTTTTCTAAACTGATAAATATTATAATTTGGTTTTTACTATTAAGTGTTTGCCTAGGTTCTTTAATCTACTCAACCGCTGCTTTAGGTGTTTTAATGTCTAATTTAGGCATGCCTTCTTACTGTACTGGTTACAGAGAAGGCTATTTGAACTCTACTAATGTCACTATTGCAACCTACTGTACTGGTTCTATACCTTGTAGTGTTTGTCTTAGTGGTTTAGATTCTTTAGACACCTATCCTTCTTTAGAAACTATACAAATTACCATTTCATCTTTTAAATGGGATTTAACTGCTTTTGGCTTAGTTGCAGAGTGGTTTTTGGCATATATTCTTTTCACTAGGTTTTTCTATGTACTTGGATTGGCTGCAATCATGCAATTGTTTTTCAGCTATTTTGCAGTACATTTTATTAGTAATTCTTGGCTTATGTGGTTAATAATTAATCTTGTACAAATGGCCCCGATTTCAGCTATGGTTAGAATGTACATCTTCTTTGCATCATTTTATTATGTATGGAAAAGTTATGTGCATGTTGTAGACGGTTGTAATTCATCAACTTGTATGATGTGTTACAAACGTAATAGAGCAACAAGAGTCGAATGTACAACTATTGTTAATGGTGTTAGAAGGTCCTTTTATGTCTATGCTAATGGAGGTAAAGGCTTTTGCAAACTACACAATTGGAATTGTGTTAATTGTGATACATTCTGTGCTGGTAGTACATTTATTAGTGATGAAGTTGCGAGAGACTTGTCACTACAGTTTAAAAGACCAATAAATCCTACTGACCAGTCTTCTTACATCGTTGATAGTGTTACAGTGAAGAATGGTTCCATCCATCTTTACTTTGATAAAGCTGGTCAAAAGACTTATGAAAGACATTCTCTCTCTCATTTTGTTAACTTAGACAACCTGAGAGCTAATAACACTAAAGGTTCATTGCCTATTAATGTTATAGTTTTTGATGGTAAATCAAAATGTGAAGAATCATCTGCAAAATCAGCGTCTGTTTACTACAGTCAGCTTATGTGTCAACCTATACTGTTACTAGATCAGGCATTAGTGTCTGATGTTGGTGATAGTGCGGAAGTTGCAGTTAAAATGTTTGATGCTTACGTTAATACGTTTTCATCAACTTTTAACGTACCAATGGAAAAACTCAAAACACTAGTTGCAACTGCAGAAGCTGAACTTGCAAAGAATGTGTCCTTAGACAATGTCTTATCTACTTTTATTTCAGCAGCTCGGCAAGGGTTTGTTGATTCAGATGTAGAAACTAAAGATGTTGTTGAATGTCTTAAATTGTCACATCAATCTGACATAGAAGTTACTGGCGATAGTTGTAATAACTATATGCTCACCTATAACAAAGTTGAAAACATGACACCCCGTGACCTTGGTGCTTGTATTGACTGTAGTGCGCGTCATATTAATGCGCAGGTAGCAAAAAGTCACAACATTGCTTTGATATGGAACGTTAAAGATTTCATGTCATTGTCTGAACAACTACGAAAACAAATACGTAGTGCTGCTAAAAAGAATAACTTACCTTTTAAGTTGACATGTGCAACTACTAGACAAGTTGTTAATGTTGTAACAACAAAGATAGCACTTAAGGGTGGTAAAATTGTTAATAATTGGTTGAAGCAGTTAATTAAAGTTACACTTGTGTTCCTTTTTGTTGCTGCTATTTTCTATTTAATAACACCTGTTCATGTCATGTCTAAACATACTGACTTTTCAAGTGAAATCATAGGATACAAGGCTATTGATGGTGGTGTCACTCGTGACATAGCATCTACAGATACTTGTTTTGCTAACAAACATGCTGATTTTGACACATGGTTTAGCCAGCGTGGTGGTAGTTATACTAATGACAAAGCTTGCCCATTGATTGCTGCAGTCATAACAAGAGAAGTGGGTTTTGTCGTGCCTGGTTTGCCTGGCACGATATTACGCACAACTAATGGTGACTTTTTGCATTTCTTACCTAGAGTTTTTAGTGCAGTTGGTAACATCTGTTACACACCATCAAAACTTATAGAGTACACTGACTTTGCAACATCAGCTTGTGTTTTGGCTGCTGAATGTACAATTTTTAAAGATGCTTCTGGTAAGCCAGTACCATATTGTTATGATACCAATGTACTAGAAGGTTCTGTTGCTTATGAAAGTTTACGCCCTGACACACGTTATGTGCTCATGGATGGCTCTATTATTCAATTTCCTAACACCTACCTTGAAGGTTCTGTTAGAGTGGTAACAACTTTTGATTCTGAGTACTGTAGGCACGGCACTTGTGAAAGATCAGAAGCTGGTGTTTGTGTATCTACTAGTGGTAGATGGGTACTTAACAATGATTATTACAGATCTTTACCAGGAGTTTTCTGTGGTGTAGATGCTGTAAATTTACTTACTAATATGTTTACACCACTAATTCAACCTATTGGTGCTTTGGACATATCAGCATCTATAGTAGCTGGTGGTATTGTAGCTATCGTAGTAACATGCCTTGCCTACTATTTTATGAGGTTTAGAAGAGCTTTTGGTGAATACAGTCATGTAGTTGCCTTTAATACTTTACTATTCCTTATGTCATTCACTGTACTCTGTTTAACACCAGTTTACTCATTCTTACCTGGTGTTTATTCTGTTATTTACTTGTACTTGACATTTTATCTTACTAATGATGTTTCTTTTTTAGCACATATTCAGTGGATGGTTATGTTCACACCTTTAGTACCTTTCTGGATAACAATTGCTTATATCATTTGTATTTCCACAAAGCATTTCTATTGGTTCTTTAGTAATTACCTAAAGAGACGTGTAGTCTTTAATGGTGTTTCCTTTAGTACTTTTGAAGAAGCTGCGCTGTGCACCTTTTTGTTAAATAAAGAAATGTATCTAAAGTTGCGTAGTGATGTGCTATTACCTCTTACGCAATATAATAGATACTTAGCTCTTTATAATAAGTACAAGTATTTTAGTGGAGCAATGGATACAACTAGCTACAGAGAAGCTGCTTGTTGTCATCTCGCAAAGGCTCTCAATGACTTCAGTAACTCAGGTTCTGATGTTCTTTACCAACCACCACAAACCTCTATCACCTCAGCTGTTTTGCAGAGTGGTTTTAGAAAAATGGCATTCCCATCTGGTAAAGTTGAGGGTTGTATGGTACAAGTAACTTGTGGTACAACTACACTTAACGGTCTTTGGCTTGATGACGTAGTTTACTGTCCAAGACATGTGATCTGCACCTCTGAAGACATGCTTAACCCTAATTATGAAGATTTACTCATTCGTAAGTCTAATCATAATTTCTTGGTACAGGCTGGTAATGTTCAACTCAGGGTTATTGGACATTCTATGCAAAATTGTGTACTTAAGCTTAAGGTTGATACAGCCAATCCTAAGACACCTAAGTATAAGTTTGTTCGCATTCAACCAGGACAGACTTTTTCAGTGTTAGCTTGTTACAATGGTTCACCATCTGGTGTTTACCAATGTGCTATGAGGCCCAATTTCACTATTAAGGGTTCATTCCTTAATGGTTCATGTGGTAGTGTTGGTTTTAACATAGATTATGACTGTGTCTCTTTTTGTTACATGCACCATATGGAATTACCAACTGGAGTTCATGCTGGCACAGACTTAGAAGGTAACTTTTATGGACCTTTTGTTGACAGGCAAACAGCACAAGCAGCTGGTACGGACACAACTATTACAGTTAATGTTTTAGCTTGGTTGTACGCTGCTGTTATAAATGGAGACAGGTGGTTTCTCAATCGATTTACCACAACTCTTAATGACTTTAACCTTGTGGCTATGAAGTACAATTATGAACCTCTAACACAAGACCATGTTGACATACTAGGACCTCTTTCTGCTCAAACTGGAATTGCCGTTTTAGATATGTGTGCTTCATTAAAAGAATTACTGCAAAATGGTATGAATGGACGTACCATATTGGGTAGTGCTTTATTAGAAGATGAATTTACACCTTTTGATGTTGTTAGACAATGCTCAGGTGTTACTTTCCAAAGTGCAGTGAAAAGAACAATCAAGGGTACACACCACTGGTTGTTACTCACAATTTTGACTTCACTTTTAGTTTTAGTCCAGAGTACTCAATGGTCTTTGTTCTTTTTTTTGTATGAAAATGCCTTTTTACCTTTTGCTATGGGTATTATTGCTATGTCTGCTTTTGCAATGATGTTTGTCAAACATAAGCATGCATTTCTCTGTTTGTTTTTGTTACCTTCTCTTGCCACTGTAGCTTATTTTAATATGGTCTATATGCCTGCTAGTTGGGTGATGCGTATTATGACATGGTTGGATATGGTTGATACTAGTTTGTCTGGTTTTAAGCTAAAAGACTGTGTTATGTATGCATCAGCTGTAGTGTTACTAATCCTTATGACAGCAAGAACTGTGTATGATGATGGTGCTAGGAGAGTGTGGACACTTATGAATGTCTTGACACTCGTTTATAAAGTTTATTATGGTAATGCTTTAGATCAAGCCATTTCCATGTGGGCTCTTATAATCTCTGTTACTTCTAACTACTCAGGTGTAGTTACAACTGTCATGTTTTTGGCCAGAGGTATTGTTTTTATGTGTGTTGAGTATTGCCCTATTTTCTTCATAACTGGTAATACACTTCAGTGTATAATGCTAGTTTATTGTTTCTTAGGCTATTTTTGTACTTGTTACTTTGGCCTCTTTTGTTTACTCAACCGCTACTTTAGACTGACTCTTGGTGTTTATGATTACTTAGTTTCTACACAGGAGTTTAGATATATGAATTCACAGGGACTACTCCCACCCAAGAATAGCATAGATGCCTTCAAACTCAACATTAAATTGTTGGGTGTTGGTGGCAAACCTTGTATCAAAGTAGCCACTGTACAGTCTAAAATGTCAGATGTAAAGTGCACATCAGTAGTCTTACTCTCAGTTTTGCAACAACTCAGAGTAGAATCATCATCTAAATTGTGGGCTCAATGTGTCCAGTTACACAATGACATTCTCTTAGCTAAAGATACTACTGAAGCCTTTGAAAAAATGGTTTCACTACTTTCTGTTTTGCTTTCCATGCAGGGTGCTGTAGACATAAACAAGCTTTGTGAAGAAATGCTGGACAACAGGGCAACCTTACAAGCTATAGCCTCAGAGTTTAGTTCCCTTCCATCATATGCAGCTTTTGCTACTGCTCAAGAAGCTTATGAGCAGGCTGTTGCTAATGGTGATTCTGAAGTTGTTCTTAAAAAGTTGAAGAAGTCTTTGAATGTGGCTAAATCTGAATTTGACCGTGATGCAGCCATGCAACGTAAGTTGGAAAAGATGGCTGATCAAGCTATGACCCAAATGTATAAACAGGCTAGATCTGAGGACAAGAGGGCAAAAGTTACTAGTGCTATGCAGACAATGCTTTTCACTATGCTTAGAAAGTTGGATAATGATGCACTCAACAACATTATCAACAATGCAAGAGATGGTTGTGTTCCCTTGAACATAATACCTCTTACAACAGCAGCCAAACTAATGGTTGTCATACCAGACTATAACACATATAAAAATACGTGTGATGGTACAACATTTACTTATGCATCAGCATTGTGGGAAATCCAACAGGTTGTAGATGCAGATAGTAAAATTGTTCAACTTAGTGAAATTAGTATGGACAATTCACCTAATTTAGCATGGCCTCTTATTGTAACAGCTTTAAGGGCCAATTCTGCTGTCAAATTACAGAATAATGAGCTTAGTCCTGTTGCACTACGACAGATGTCTTGTGCTGCCGGTACTACACAAACTGCTTGCACTGATGACAATGCGTTAGCTTACTACAACACAACAAAGGGAGGTAGGTTTGTACTTGCACTGTTATCCGATTTACAGGATTTGAAATGGGCTAGATTCCCTAAGAGTGATGGAACTGGTACTATCTATACAGAACTGGAACCACCTTGTAGGTTTGTTACAGACACACCTAAAGGTCCTAAAGTGAAGTATTTATACTTTATTAAAGGATTAAACAACCTAAATAGAGGTATGGTACTTGGTAGTTTAGCTGCCACAGTACGTCTACAAGCTGGTAATGCAACAGAAGTGCCTGCCAATTCAACTGTATTATCTTTCTGTGCTTTTGCTGTAGATGCTGCTAAAGCTTACAAAGATTATCTAGCTAGTGGGGGACAACCAATCACTAATTGTGTTAAGATGTTGTGTACACACACTGGTACTGGTCAGGCAATAACAGTTACACCGGAAGCCAATATGGATCAAGAATCCTTTGGTGGTGCATCGTGTTGTCTGTACTGCCGTTGCCACATAGATCATCCAAATCCTAAAGGATTTTGTGACTTAAAAGGTAAGTATGTACAAATACCTACAACTTGTGCTAATGACCCTGTGGGTTTTACACTTAAAAACACAGTCTGTACCGTCTGCGGTATGTGGAAAGGTTATGGCTGTAGTTGTGATCAACTCCGCGAACCCATGCTTCAGTCAGCTGATGCACAATCGTTTTTAAACGGGTTTGCGGTGTAAGTGCAGCCCGTCTTACACCGTGCGGCACAGGCACTAGTACTGATGTCGTATACAGGGCTTTTGACATCTACAATGATAAAGTAGCTGGTTTTGCTAAATTCCTAAAAACTAATTGTTGTCGCTTCCAAGAAAAGGACGAAGATGACAATTTAATTGATTCTTACTTTGTAGTTAAGAGACACACTTTCTCTAACTACCAACATGAAGAAACAATTTATAATTTACTTAAGGATTGTCCAGCTGTTGCTAAACATGACTTCTTTAAGTTTAGAATAGACGGTGACATGGTACCACATATATCACGTCAACGTCTTACTAAATACACAATGGCAGACCTCGTCTATGCTTTAAGGCATTTTGATGAAGGTAATTGTGACACATTAAAAGAAATACTTGTCACATACAATTGTTGTGATGATGATTATTTCAATAAAAAGGACTGGTATGATTTTGTAGAAAACCCAGATATATTACGCGTATACGCCAACTTAGGTGAACGTGTACGCCAAGCTTTGTTAAAAACAGTACAATTCTGTGATGCCATGCGAAATGCTGGTATTGTTGGTGTACTGACATTAGATAATCAAGATCTCAATGGTAACTGGTATGATTTCGGTGATTTCATACAAACCACGCCAGGTAGTGGAGTTCCTGTTGTAGATTCTTATTATTCATTGTTAATGCCTATATTAACCTTGACCAGGGCTTTAACTGCAGAGTCACATGTTGACACTGACTTAACAAAGCCTTACATTAAGTGGGATTTGTTAAAATATGACTTCACGGAAGAGAGGTTAAAACTCTTTGACCGTTATTTTAAATATTGGGATCAGACATACCACCCAAATTGTGTTAACTGTTTGGATGACAGATGCATTCTGCATTGTGCAAACTTTAATGTTTTATTCTCTACAGTGTTCCCACCTACAAGTTTTGGACCACTAGTGAGAAAAATATTTGTTGATGGTGTTCCATTTGTAGTTTCAACTGGATACCACTTCAGAGAGCTAGGTGTTGTACATAATCAGGATGTAAACTTACATAGCTCTAGACTTAGTTTTAAGGAATTACTTGTGTATGCTGCTGACCCTGCTATGCACGCTGCTTCTGGTAATCTATTACTAGATAAACGCACTACGTGCTTTTCAGTAGCTGCACTTACTAACAATGTTGCTTTTCAAACTGTCAAACCCGGTAATTTTAACAAAGACTTCTATGACTTTGCTGTGTCTAAGGGTTTCTTTAAGGAAGGAAGTTCTGTTGAATTAAAACACTTCTTCTTTGCTCAGGATGGTAATGCTGCTATCAGCGATTATGACTACTATCGTTATAATCTACCAACAATGTGTGATATCAGACAACTACTATTTGTAGTTGAAGTTGTTGATAAGTACTTTGATTGTTACGATGGTGGCTGTATTAATGCTAACCAAGTCATCGTCAACAACCTAGACAAATCAGCTGGTTTTCCATTTAATAAATGGGGTAAGGCTAGACTTTATTATGATTCAATGAGTTATGAGGATCAAGATGCACTTTTCGCATATACAAAACGTAATGTCATCCCTACTATAACTCAAATGAATCTTAAGTATGCCATTAGTGCAAAGAATAGAGCTCGCACCGTAGCTGGTGTCTCTATCTGTAGTACTATGACCAATAGACAGTTTCATCAAAAATTATTGAAATCAATAGCCGCCACTAGAGGAGCTACTGTAGTAATTGGAACAAGCAAATTCTATGGTGGTTGGCACAACATGTTAAAAACTGTTTATAGTGATGTAGAAAACCCTCACCTTATGGGTTGGGATTATCCTAAATGTGATAGAGCCATGCCTAACATGCTTAGAATTATGGCCTCACTTGTTCTTGCTCGCAAACATACAACGTGTTGTAGCTTGTCACACCGTTTCTATAGATTAGCTAATGAGTGTGCTCAAGTATTGAGTGAAATGGTCATGTGTGGCGGTTCACTATATGTTAAACCAGGTGGAACCTCATCAGGAGATGCCACAACTGCTTATGCTAATAGTGTTTTTAACATTTGTCAAGCTGTCACGGCCAATGTTAATGCACTTTTATCTACTGATGGTAACAAAATTGCCGATAAGTATGTCCGCAATTTACAACACAGACTTTATGAGTGTCTCTATAGAAATAGAGATGTTGACACAGACTTTGTGAATGAGTTTTACGCATATTTGCGTAAACATTTCTCAATGATGATACTCTCTGACGATGCTGTTGTGTGTTTCAATAGCACTTATGCATCTCAAGGTCTAGTGGCTAGCATAAAGAACTTTAAGTCAGTTCTTTATTATCAAAACAATGTTTTTATGTCTGAAGCAAAATGTTGGACTGAGACTGACCTTACTAAAGGACCTCATGAATTTTGCTCTCAACATACAATGCTAGTTAAACAGGGTGATGATTATGTGTACCTTCCTTACCCAGATCCATCAAGAATCCTAGGGGCCGGCTGTTTTGTAGATGATATCGTAAAAACAGATGGTACACTTATGATTGAACGGTTCGTGTCTTTAGCTATAGATGCTTACCCACTTACTAAACATCCTAATCAGGAGTATGCTGATGTCTTTCATTTGTACTTACAATACATAAGAAAGCTACATGATGAGTTAACAGGACACATGTTAGACATGTATTCTGTTATGCTTACTAATGATAACACTTCAAGGTATTGGGAACCTGAGTTTTATGAGGCTATGTACACACCGCATACAGTCTTACAGGCTGTTGGGGCTTGTGTTCTTTGCAATTCACAGACTTCATTAAGATGTGGTGCTTGCATACGTAGACCATTCTTATGTTGTAAATGCTGTTACGACCATGTCATATCAACATCACATAAATTAGTCTTGTCTGTTAATCCGTATGTTTGCAATGCTCCAGGTTGTGATGTCACAGATGTGACTCAACTTTACTTAGGAGGTATGAGCTATTATTGTAAATCACATAAACCACCCATTAGTTTTCCATTGTGTGCTAATGGACAAGTTTTTGGTTTATATAAAAATACATGTGTTGGTAGCGATAATGTTACTGACTTTAATGCAATTGCAACATGTGACTGGACAAATGCTGGTGATTACATTTTAGCTAACACCTGTACTGAAAGACTCAAGCTTTTTGCAGCAGAAACGCTCAAAGCTACTGAGGAGACATTTAAACTGTCTTATGGTATTGCTACTGTACGTGAAGTGCTGTCTGACAGAGAATTACATCTTTCATGGGAAGTTGGTAAACCTAGACCACCACTTAACCGAAATTATGTCTTTACTGGTTATCGTGTAACTAAAAACAGTAAAGTACAAATAGGAGAGTACACCTTTGAAAAAGGTGACTATGGTGATGCTGTTGTTTACCGAGGTACAACAACTTACAAATTAAATGTTGGTGATTATTTTGTGCTGACATCACATACAGTAATGCCATTAAGTGCACCTACACTAGTGCCACAAGAGCACTATGTTAGAATTACTGGCTTATACCCAACACTCAATATCTCAGATGAGTTTTCTAGCAATGTTGCAAATTATCAAAAGGTTGGTATGCAAAAGTATTCTACACTCCAGGGACCACCTGGTACTGGTAAGAGTCATTTTGCTATTGGCCTAGCTCTCTACTACCCTTCTGCTCGCATAGTGTATACAGCTTGCTCTCATGCCGCTGTTGATGCACTATGTGAGAAGGCATTAAAATATTTGCCTATAGATAAATGTAGTAGAATTATACCTGCACGTGCTCGTGTAGAGTGTTTTGATAAATTCAAAGTGAATTCAACATTAGAACAGTATGTCTTTTGTACTGTAAATGCATTGCCTGAGACGACAGCAGATATAGTTGTCTTTGATGAAATTTCAATGGCCACAAATTATGATTTGAGTGTTGTCAATGCCAGATTACGTGCTAAGCACTATGTGTACATTGGCGACCCTGCTCAATTACCTGCACCACGCACATTGCTAACTAAGGGCACACTAGAACCAGAATATTTCAATTCAGTGTGTAGACTTATGAAAACTATAGGTCCAGACATGTTCCTCGGAACTTGTCGGCGTTGTCCTGCTGAAATTGTTGACACTGTGAGTGCTTTGGTTTATGATAATAAGCTTAAAGCACATAAAGACAAATCAGCTCAATGCTTTAAAATGTTTTATAAGGGTGTTATCACGCATGATGTTTCATCTGCAATTAACAGGCCACAAATAGGCGTGGTAAGAGAATTCCTTACACGTAACCCTGCTTGGAGAAAAGCTGTCTTTATTTCACCTTATAATTCACAGAATGCTGTAGCCTCAAAGATTTTGGGACTACCAACTCAAACTGTTGATTCATCACAGGGCTCAGAATATGACTATGTCATATTCACTCAAACCACTGAAACAGCTCACTCTTGTAATGTAAACAGATTTAATGTTGCTATTACCAGAGCAAAAGTAGGCATACTTTGCATAATGTCTGATAGAGACCTTTATGACAAGTTGCAATTTACAAGTCTTGAAATTCCACGTAGGAATGTGGCAACTTTACAAGCTGAAAATGTAACAGGACTCTTTAAAGATTGTAGTAAGGTAATCACTGGGTTACATCCTACACAGGCACCTACACACCTCAGTGTTGACACTAAATTCAAAACTGAAGGTTTATGTGTTGACATACCTGGCATACCTAAGGACATGACCTATAGAAGACTCATCTCTATGATGGGTTTTAAAATGAATTATCAAGTTAATGGTTACCCTAACATGTTTATCACCCGCGAAGAAGCTATAAGACATGTACGTGCATGGATTGGCTTCGATGTCGAGGGGTGTCATGCTACTAGAGAAGCTGTTGGTACCAATTTACCTTTACAGCTAGGTTTTTCTACAGGTGTTAACCTAGTTGCTGTACCTACAGGTTATGTTGATACACCTAATAATACAGATTTTTCCAGAGTTAGTGCTAAACCACCGCCTGGAGATCAATTTAAACACCTCATACCACTTATGTACAAAGGACTTCCTTGGAATGTAGTGCGTATAAAGATTGTACAAATGTTAAGTGACACACTTAAAAATCTCTCTGACAGAGTCGTATTTGTCTTATGGGCACATGGCTTTGAGTTGACATCTATGAAGTATTTTGTGAAAATAGGACCTGAGCGCACCTGTTGTCTATGTGATAGACGTGCCACATGCTTTTCCACTGCTTCAGACACTTATGCCTGTTGGCATCATTCTATTGGATTTGATTACGTCTATAATCCGTTTATGATTGATGTTCAACAATGGGGTTTTACAGGTAACCTACAAAGCAACCATGATCTGTATTGTCAAGTCCATGGTAATGCACATGTAGCTAGTTGTGATGCAATCATGACTAGGTGTCTAGCTGTCCACGAGTGCTTTGTTAAGCGTGTTGACTGGACTATTGAATATCCTATAATTGGTGATGAACTGAAGATTAATGCGGCTTGTAGAAAGGTTCAACACATGGTTGTTAAAGCTGCATTATTAGCAGACAAATTCCCAGTTCTTCACGACATTGGTAACCCTAAAGCTATTAAGTGTGTACCTCAAGCTGATGTAGAATGGAAGTTCTATGATGCACAGCCTTGTAGTGACAAAGCTTATAAAATAGAAGAATTATTCTATTCTTATGCCACACATTCTGACAAATTCACAGATGGTGTATGCCTATTTTGGAATTGCAATGTCGATAGATATCCTGCTAATTCCATTGTTTGTAGATTTGACACTAGAGTGCTATCTAACCTTAACTTGCCTGGTTGTGATGGTGGCAGTTTGTATGTAAATAAACATGCATTCCACACACCAGCTTTTGATAAAAGTGCTTTTGTTAATTTAAAACAATTACCATTTTTCTATTACTCTGACAGTCCATGTGAGTCTCATGGAAAACAAGTAGTGTCAGATATAGATTATGTACCACTAAAGTCTGCTACGTGTATAACACGTTGCAATTTAGGTGGTGCTGTCTGTAGACATCATGCTAATGAGTACAGATTGTATCTCGATGCTTATAACATGATGATCTCAGCTGGCTTTAGCTTGTGGGTTTACAAACAATTTGATACTTATAACCTCTGGAACACTTTTACAAGACTTCAGAGTTTAGAAAATGTGGCTTTTAATGTTGTAAATAAGGGACACTTTGATGGACAACAGGGTGAAGTACCAGTTTCTATCATTAATAACACTGTTTACACAAAAGTTGATGGTGTTGATGTAGAATTGTTTGAAAATAAAACAACATTACCTGTTAATGTAGCATTTGAGCTTTGGGCTAAGCGCAACATTAAACCAGTACCAGAGGTGAAAATACTCAATAATTTGGGTGTGGACATTGCTGCTAATACTGTGATCTGGGACTACAAAAGAGATGCTCCAGCACATATATCTACTATTGGTGTTTGTTCTATGACTGACATAGCCAAGAAACCAACTGAAACGATTTGTGCACCACTCACTGTCTTTTTTGATGGTAGAGTTGATGGTCAAGTAGACTTATTTAGAAATGCCCGTAATGGTGTTCTTATTACAGAAGGTAGTGTTAAAGGTTTACAACCATCTGTAGGTCCCAAACAAGCTAGTCTTAATGGAGTCACATTAATTGGAGAAGCCGTAAAAACACAGTTCAATTATTATAAGAAAGTTGATGGTGTTGTCCAACAATTACCTGAAACTTACTTTACTCAGAGTAGAAATTTACAAGAATTTAAACCCAGGAGTCAAATGGAAATTGATTTCTTAGAATTAGCTATGGATGAATTCATTGAACGGTATAAATTAGAAGGCTATGCCTTCGAACATATCGTTTATGGAGATTTTAGTCATAGTCAGTTAGGTGGTTTACATCTACTGATTGGACTAGCTAAACGTTTTAAGGAATCACCTTTTGAATTAGAAGATTTTATTCCTATGGACAGTACAGTTAAAAACTATTTCATAACAGATGCGCAAACAGGTTCATCTAAGTGTGTGTGTTCTGTTATTGATTTATTACTTGATGATTTTGTTGAAATAATAAAATCCCAAGATTTATCTGTAGTTTCTAAGGTTGTCAAAGTGACTATTGACTATACAGAAATTTCATTTATGCTTTGGTGTAAAGATGGCCATGTAGAAACATTTTACCCAAAATTACAATCTAGTCAAGCGTGGCAACCGGGTGTTGCTATGCCTAATCTTTACAAAATGCAAAGAATGCTATTAGAAAAGTGTGACCTTCAAAATTATGGTGATAGTGCAACATTACCTAAAGGCATAATGATGAATGTCGCAAAATATACTCAACTGTGTCAATATTTAAACACATTAACATTAGCTGTACCCTATAATATGAGAGTTATACATTTTGGTGCTGGTTCTGATAAAGGAGTTGCACCAGGTACAGCTGTTTTAAGACAGTGGTTGCCTACGGGTACGCTGCTTGTCGATTCAGATCTTAATGACTTTGTCTCTGATGCAGATTCAACTTTGATTGGTGATTGTGCAACTGTACATACAGCTAATAAATGGGATCTCATTATTAGTGATATGTACGACCCTAAGACTAAAAATGTTACAAAAGAAAATGACTCTAAAGAGGGTTTTTTCACTTACATTTGTGGGTTTATACAACAAAAGCTAGCTCTTGGAGGTTCCGTGGCTATAAAGATAACAGAACATTCTTGGAATGCTGATCTTTATAAGCTCATGGGACACTTCGCATGGTGGACAGCCTTTGTTACTAATGTGAATGCGTCATCATCTGAAGCATTTTTAATTGGATGTAATTATCTTGGCAAACCACGCGAACAAATAGATGGTTATGTCATGCATGCAAATTACATATTTTGGAGGAATACAAATCCAATTCAGTTGTCTTCCTATTCTTTATTTGACATGAGTAAATTTCCCCTTAAATTAAGGGGTACTGCTGTTATGTCTTTAAAAGAAGGTCAAATCAATGATATGATTTTATCTCTTCTTAGTAAAGGTAGACTTATAATTAGAGAAAACAACAGAGTTGTTATTTCTAGTGATGTTCTTGTTAACAACTAAACGAACAATGTTTGTTTTTCTTGTTTTATTGCCACTAGTCTCTAGTCAGTGTGTTAATCTTACAACCAGAACTCAATTACCCCCTGCATACACTAATTCTTTCACACGTGGTGTTTATTACCCTGACAAAGTTTTCAGATCCTCAGTTTTACATTCAACTCAGGACTTGTTCTTACCTTTCTTTTCCAATGTTACTTGGTTCCATGCTATACATGTCTCTGGGACCAATGGTACTAAGAGGTTTGATAACCCTGTCCTACCATTTAATGATGGTGTTTATTTTGCTTCCACTGAGAAGTCTAACATAATAAGAGGCTGGATTTTTGGTACTACTTTAGATTCGAAGACCCAGTCCCTACTTATTGTTAATAACGCTACTAATGTTGTTATTAAAGTCTGTGAATTTCAATTTTGTAATGATCCATTTTTGGGTGTTTATTACCACAAAAACAACAAAAGTTGGATGGAAAGTGAGTTCAGAGTTTATTCTAGTGCGAATAATTGCACTTTTGAATATGTCTCTCAGCCTTTTCTTATGGACCTTGAAGGAAAACAGGGTAATTTCAAAAATCTTAGGGAATTTGTGTTTAAGAATATTGATGGTTATTTTAAAATATATTCTAAGCACACGCCTATTAATTTAGTGCGTGATCTCCCTCAGGGTTTTTCGGCTTTAGAACCATTGGTAGATTTGCCAATAGGTATTAACATCACTAGGTTTCAAACTTTACTTGCTTTACATAGAAGTTATTTGACTCCTGGTGATTCTTCTTCAGGTTGGACAGCTGGTGCTGCAGCTTATTATGTGGGTTATCTTCAACCTAGGACTTTTCTATTAAAATATAATGAAAATGGAACCATTACAGATGCTGTAGACTGTGCACTTGACCCTCTCTCAGAAACAAAGTGTACGTTGAAATCCTTCACTGTAGAAAAAGGAATCTATCAAACTTCTAACTTTAGAGTCCAACCAACAGAATCTATTGTTAGATTTCCTAATATTACAAACTTGTGCCCTTTTGGTGAAGTTTTTAACGCCACCAGATTTGCATCTGTTTATGCTTGGAACAGGAAGAGAATCAGCAACTGTGTTGCTGATTATTCTGTCCTATATAATTCCGCATCATTTTCCACTTTTAAGTGTTATGGAGTGTCTCCTACTAAATTAAATGATCTCTGCTTTACTAATGTCTATGCAGATTCATTTGTAATTAGAGGTGATGAAGTCAGACAAATCGCTCCAGGGCAAACTGGAAAGATTGCTGATTATAATTATAAATTACCAGATGATTTTACAGGCTGCGTTATAGCTTGGAATTCTAACAATCTTGATTCTAAGGTTGGTGGTAATTATAATTACCTGTATAGATTGTTTAGGAAGTCTAATCTCAAACCTTTTGAGAGAGATATTTCAACTGAAATCTATCAGGCCGGTAGCACACCTTGTAATGGTGTTGAAGGTTTTAATTGTTACTTTCCTTTACAATCATATGGTTTCCAACCCACTAATGGTGTTGGTTACCAACCATACAGAGTAGTAGTACTTTCTTTTGAACTTCTACATGCACCAGCAACTGTTTGTGGACCTAAAAAGTCTACTAATTTGGTTAAAAACAAATGTGTCAATTTCAACTTCAATGGTTTAACAGGCACAGGTGTTCTTACTGAGTCTAACAAAAAGTTTCTGCCTTTCCAACAATTTGGCAGAGACATTGCTGACACTACTGATGCTGTCCGTGATCCACAGACACTTGAGATTCTTGACATTACACCATGTTCTTTTGGTGGTGTCAGTGTTATAACACCAGGAACAAATACTTCTAACCAGGTTGCTGTTCTTTATCAGGATGTTAACTGCACAGAAGTCCCTGTTGCTATTCATGCAGATCAACTTACTCCTACTTGGCGTGTTTATTCTACAGGTTCTAATGTTTTTCAAACACGTGCAGGCTGTTTAATAGGGGCTGAACATGTCAACAACTCATATGAGTGTGACATACCCATTGGTGCAGGTATATGCGCTAGTTATCAGACTCAGACTAATTCTCCTCGGCGGGCACGTAGTGTAGCTAGTCAATCCATCATTGCCTACACTATGTCACTTGGTGCAGAAAATTCAGTTGCTTACTCTAATAACTCTATTGCCATACCCACAAATTTTACTATTAGTGTTACCACAGAAATTCTACCAGTGTCTATGACCAAGACATCAGTAGATTGTACAATGTACATTTGTGGTGATTCAACTGAATGCAGCAATCTTTTGTTGCAATATGGCAGTTTTTGTACACAATTAAACCGTGCTTTAACTGGAATAGCTGTTGAACAAGACAAAAACACCCAAGAAGTTTTTGCACAAGTCAAACAAATTTACAAAACACCACCAATTAAAGATTTTGGTGGTTTTAATTTTTCACAAATATTACCAGATCCATCAAAACCAAGCAAGAGGTCATTTATTGAAGATCTACTTTTCAACAAAGTGACACTTGCAGATGCTGGCTTCATCAAACAATATGGTGATTGCCTTGGTGATATTGCTGCTAGAGACCTCATTTGTGCACAAAAGTTTAACGGCCTTACTGTTTTGCCACCTTTGCTCACAGATGAAATGATTGCTCAATACACTTCTGCACTGTTAGCGGGTACAATCACTTCTGGTTGGACCTTTGGTGCAGGTGCTGCATTACAAATACCATTTGCTATGCAAATGGCTTATAGGTTTAATGGTATTGGAGTTACACAGAATGTTCTCTATGAGAACCAAAAATTGATTGCCAACCAATTTAATAGTGCTATTGGCAAAATTCAAGACTCACTTTCTTCCACAGCAAGTGCACTTGGAAAACTTCAAGATGTGGTCAACCAAAATGCACAAGCTTTAAACACGCTTGTTAAACAACTTAGCTCCAATTTTGGTGCAATTTCAAGTGTTTTAAATGATATCCTTTCACGTCTTGACAAAGTTGAGGCTGAAGTGCAAATTGATAGGTTGATCACAGGCAGACTTCAAAGTTTGCAGACATATGTGACTCAACAATTAATTAGAGCTGCAGAAATCAGAGCTTCTGCTAATCTTGCTGCTACTAAAATGTCAGAGTGTGTACTTGGACAATCAAAAAGAGTTGATTTTTGTGGAAAGGGCTATCATCTTATGTCCTTCCCTCAGTCAGCACCTCATGGTGTAGTCTTCTTGCATGTGACTTATGTCCCTGCACAAGAAAAGAACTTCACAACTGCTCCTGCCATTTGTCATGATGGAAAAGCACACTTTCCTCGTGAAGGTGTCTTTGTTTCAAATGGCACACACTGGTTTGTAACACAAAGGAATTTTTATGAACCACAAATCATTACTACAGACAACACATTTGTGTCTGGTAACTGTGATGTTGTAATAGGAATTGTCAACAACACAGTTTATGATCCTTTGCAACCTGAATTAGACTCATTCAAGGAGGAGTTAGATAAATATTTTAAGAATCATACATCACCAGATGTTGATTTAGGTGACATCTCTGGCATTAATGCTTCAGTTGTAAACATTCAAAAAGAAATTGACCGCCTCAATGAGGTTGCCAAGAATTTAAATGAATCTCTCATCGATCTCCAAGAACTTGGAAAGTATGAGCAGTATATAAAATGGCCATGGTACATTTGGCTAGGTTTTATAGCTGGCTTGATTGCCATAGTAATGGTGACAATTATGCTTTGCTGTATGACCAGTTGCTGTAGTTGTCTCAAGGGCTGTTGTTCTTGTGGATCCTGCTGCAAATTTGATGAAGACGACTCTGAGCCAGTGCTCAAAGGAGTCAAATTACATTACACATAAACGAACTTATGGATTTGTTTATGAGAATCTTCACAATTGGAACTGTAACTTTGAAGCAAGGTGAAATCAAGGATGCTACTCCTTCAGATTTTGTTCGCGCTACTGCAACGATACCGATACAAGCCTCACTCCCTTTCGGATGGCTTATTGTTGGCGTTGCACTTCTTGCTGTTTTTCAGAGCGCTTCCAAAATCATAACCCTCAAAAAGAGATGGCAACTAGCACTCTCCAAGGGTGTTCACTTTGTTTGCAACTTGCTGTTGTTGTTTGTAACAGTTTACTCACACCTTTTGCTCGTTGCTGCTGGCCTTGAAGCCCCTTTTCTCTATCTTTATGCTTTAGTCTACTTCTTGCAGAGTATAAACTTTGTAAGAATAATAATGAGGCTTTGGCTTTGCTGGAAATGCCGTTCCAAAAACCCATTACTTTATGATGCCAACTATTTTCTTTGCTGGCATACTAATTGTTACGACTATTGTATACCTTACAATAGTGTAACTTCTTCAATTGTCATTACTTCAGGTGATGGCACAACAAGTCCTATTTCTGAACATGACTACCAGATTGGTGGTTATACTGAAAAATGGGAATCTGGAGTAAAAGACTGTGTTGTATTACACAGTTACTTCACTTCAGACTATTACCAGCTGTACTCAACTCAATTGAGTACAGACACTGGTGTTGAACATGTTACCTTCTTCATCTACAATAAAATTGTTGATGAGCCTGAAGAACATGTCCAAATTCACACAATCGACGGTTCATCCGGAGTTGTTAATCCAGTAATGGAACCAATTTATGATGAACCGACGACGACTACTAGCGTGCCTTTGTAAGCACAAGCTGATGAGTACGAACTTATGTACTCATTCGTTTCGGAAGAGACAGGTACGTTAATAGTTAATAGCGTACTTCTTTTTCTTGCTTTCGTGGTATTCTTGCTAGTTACACTAGCCATCCTTACTGCGCTTCGATTGTGTGCGTACTGCTGCAATATTGTTAACGTGAGTCTTGTAAAACCTTCTTTTTACGTTTACTCTCGTGTTAAAAATCTGAATTCTTCTAGAGTTCCTGATCTTCTGGTCTAAACGAACTAAATATTATATTAGTTTTTCTGTTTGGAACTTTAATTTTAGCCATGGCAGATTCCAACGGTACTATTACCGTTGAAGAGCTTAAAAAGCTCCTTGAACAATGGAACCTAGTAATAGGTTTCCTATTCCTTACATGGATTTGTCTTCTACAATTTGCCTATGCCAACAGGAATAGGTTTTTGTATATAATTAAGTTAATTTTCCTCTGGCTGTTATGGCCAGTAACTTTAGCTTGTTTTGTGCTTGCTGCTGTTTACAGAATAAATTGGATCACCGGTGGAATTGCTATCGCAATGGCTTGTCTTGTAGGCTTGATGTGGCTCAGCTACTTCATTGCTTCTTTCAGACTGTTTGCGCGTACGCGTTCCATGTGGTCATTCAATCCAGAAACTAACATTCTTCTCAACGTGCCACTCCATGGCACTATTCTGACCAGACCGCTTCTAGAAAGTGAACTCGTAATCGGAGCTGTGATCCTTCGTGGACATCTTCGTATTGCTGGACACCATCTAGGACGCTGTGACATCAAGGACCTGCCTAAAGAAATCACTGTTGCTACATCACGAACGCTTTCTTATTACAAATTGGGAGCTTCGCAGCGTGTAGCAGGTGACTCAGGTTTTGCTGCATACAGTCGCTACAGGATTGGCAACTATAAATTAAACACAGACCATTCCAGTAGCAGTGACAATATTGCTTTGCTTGTACAGTAAGTGACAACAGATGTTTCATCTCGTTGACTTTCAGGTTACTATAGCAGAGATATTACTAATTATTATGAGGACTTTTAAAGTTTCCATTTGGAATCTTGATTACATCATAAACCTCATAATTAAAAATTTATCTAAGTCACTAACTGAGAATAAATATTCTCAATTAGATGAAGAGCAACCAATGGAGATTGATTAAACGAACATGAAAATTATTCTTTTCTTGGCACTGATAACACTCGCTACTTGTGAGCTTTATCACTACCAAGAGTGTGTTAGAGGTACAACAGTACTTTTAAAAGAACCTTGCTCTTCTGGAACATACGAGGGCAATTCACCATTTCATCCTCTAGCTGATAACAAATTTGCACTGACTTGCTTTAGCACTCAATTTGCTTTTGCTTGTCCTGACGGCGTAAAACACGTCTATCAGTTACGTGCCAGATCAGTTTCACCTAAACTGTTCATCAGACAAGAGGAAGTTCAAGAACTTTACTCTCCAATTTTTCTTATTGTTGCGGCAATAGTGTTTATAACACTTTGCTTCACACTCAAAAGAAAGACAGAATGATTGAACTTTCATTAATTGACTTCTATTTGTGCTTTTTAGCCTTTCTGCTATTCCTTGTTTTAATTATGCTTATTATCTTTTGGTTCTCACTTGAACTGCAAGATCATAATGAAACTTGTCACGCCTAAACGAACATGAAATTTCTTGTTTTCTTAGGAATCATCACAACTGTAGCTGCATTTCACCAAGAATGTAGTTTACAGTCATGTACTCAACATCAACCATATGTAGTTGATGACCCGTGTCCTATTCACTTCTATTCTAAATGGTATATTAGAGTAGGAGCTAGAAAATCAGCACCTTTAATTGAATTGTGCGTGGATGAGGCTGGTTCTAAATCACCCATTCAGTACATCGATATCGGTAATTATACAGTTTCCTGTTTACCTTTTACAATTAATTGCCAGGAACCTAAATTGGGTAGTCTTGTAGTGCGTTGTTCGTTCTATGAAGACTTTTTAGAGTATCATGACGTTCGTGTTGTTTTAGATTTCATCTAAACGAACAAACTAAAATGTCTGATAATGGACCCCAAAATCAGCGAAATGCACCCCGCATTACGTTTGGTGGACCCTCAGATTCAACTGGCAGTAACCAGAATGGAGAACGCAGTGGGGCGCGATCAAAACAACGTCGGCCCCAAGGTTTACCCAATAATACTGCGTCTTGGTTCACCGCTCTCACTCAACATGGCAAGGAAGACCTTAAATTCCCTCGAGGACAAGGCGTTCCAATTAACACCAATAGCAGTCCAGATGACCAAATTGGCTACTACCGAAGAGCTACCAGACGAATTCGTGGTGGTGACGGTAAAATGAAAGATCTCAGTCCAAGATGGTATTTCTACTACCTAGGAACTGGGCCAGAAGCTGGACTTCCCTATGGTGCTAACAAAGACGGCATCATATGGGTTGCAACTGAGGGAGCCTTGAATACACCAAAAGATCACATTGGCACCCGCAATCCTGCTAACAATGCTGCAATCGTGCTACAACTTCCTCAAGGAACAACATTGCCAAAAGGCTTCTACGCAGAAGGGAGCAGAGGCGGCAGTCAAGCCTCTTCTCGTTCCTCATCACGTAGTCGCAACAGTTCAAGAAATTCAACTCCAGGCAGCAGTAGGGGAACTTCTCCTGCTAGAATGGCTGGCAATGGCGGTGATGCTGCTCTTGCTTTGCTGCTGCTTGACAGATTGAACCAGCTTGAGAGCAAAATGTCTGGTAAAGGCCAACAACAACAAGGCCAAACTGTCACTAAGAAATCTGCTGCTGAGGCTTCTAAGAAGCCTCGGCAAAAACGTACTGCCACTAAAGCATACAATGTAACACAAGCTTTCGGCAGACGTGGTCCAGAACAAACCCAAGGAAATTTTGGGGACCAGGAACTAATCAGACAAGGAACTGATTACAAACATTGGCCGCAAATTGCACAATTTGCCCCCAGCGCTTCAGCGTTCTTCGGAATGTCGCGCATTGGCATGGAAGTCACACCTTCGGGAACGTGGTTGACCTACACAGGTGCCATCAAATTGGATGACAAAGATCCAAATTTCAAAGATCAAGTCATTTTGCTGAATAAGCATATTGACGCATACAAAACATTCCCACCAACAGAGCCTAAAAAGGACAAAAAGAAGAAGGCTGATGAAACTCAAGCCTTACCGCAGAGACAGAAGAAACAGCAAACTGTGACTCTTCTTCCTGCTGCAGATTTGGATGATTTCTCCAAACAATTGCAACAATCCATGAGCAGTGCTGACTCAACTCAGGCCTAAACTCATGCAGACCACACAAGGCAGATGGGCTATATAAACGTTTTCGCTTTTCCGTTTACGATATATAGTCTACTCTTGTGCAGAATGAATTCTCGTAACTACATAGCACAAGTAGATGTAGTTAACTTTAATCTCACATAGCAATCTTTAATCAGTGTGTAACATTAGGGAGGACTTGAAAGAGCCACCACATTTTCACCGAGGCCACGCGGAGTACGATCGAGTGTACAGTGAACAATGCTAGGGAGAGCTGCCTATATGGAAGAGCCCTAATGTGTAAAATTAATTTTAGTAGTGCTATCCCCATGTGATTTTAATAGCTTCTTAGGAGAATGACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + genes: + - name: E + sequence: 'MYSFVSEETGTLIVNSVLLFLAFVVFLLVTLAILTALRLCAYCCNIVNVSLVKPSFYVYSRVKNLNSSRVPDLLV*' + - name: M + sequence: 'MADSNGTITVEELKKLLEQWNLVIGFLFLTWICLLQFAYANRNRFLYIIKLIFLWLLWPVTLACFVLAAVYRINWITGGIAIAMACLVGLMWLSYFIASFRLFARTRSMWSFNPETNILLNVPLHGTILTRPLLESELVIGAVILRGHLRIAGHHLGRCDIKDLPKEITVATSRTLSYYKLGASQRVAGDSGFAAYSRYRIGNYKLNTDHSSSSDNIALLVQ*' + - name: N + sequence: 'MSDNGPQNQRNAPRITFGGPSDSTGSNQNGERSGARSKQRRPQGLPNNTASWFTALTQHGKEDLKFPRGQGVPINTNSSPDDQIGYYRRATRRIRGGDGKMKDLSPRWYFYYLGTGPEAGLPYGANKDGIIWVATEGALNTPKDHIGTRNPANNAAIVLQLPQGTTLPKGFYAEGSRGGSQASSRSSSRSRNSSRNSTPGSSRGTSPARMAGNGGDAALALLLLDRLNQLESKMSGKGQQQQGQTVTKKSAAEASKKPRQKRTATKAYNVTQAFGRRGPEQTQGNFGDQELIRQGTDYKHWPQIAQFAPSASAFFGMSRIGMEVTPSGTWLTYTGAIKLDDKDPNFKDQVILLNKHIDAYKTFPPTEPKKDKKKKADETQALPQRQKKQQTVTLLPAADLDDFSKQLQQSMSSADSTQA*' + - name: ORF1a + sequence: 'MESLVPGFNEKTHVQLSLPVLQVRDVLVRGFGDSVEEVLSEARQHLKDGTCGLVEVEKGVLPQLEQPYVFIKRSDARTAPHGHVMVELVAELEGIQYGRSGETLGVLVPHVGEIPVAYRKVLLRKNGNKGAGGHSYGADLKSFDLGDELGTDPYEDFQENWNTKHSSGVTRELMRELNGGAYTRYVDNNFCGPDGYPLECIKDLLARAGKASCTLSEQLDFIDTKRGVYCCREHEHEIAWYTERSEKSYELQTPFEIKLAKKFDTFNGECPNFVFPLNSIIKTIQPRVEKKKLDGFMGRIRSVYPVASPNECNQMCLSTLMKCDHCGETSWQTGDFVKATCEFCGTENLTKEGATTCGYLPQNAVVKIYCPACHNSEVGPEHSLAEYHNESGLKTILRKGGRTIAFGGCVFSYVGCHNKCAYWVPRASANIGCNHTGVVGEGSEGLNDNLLEILQKEKVNINIVGDFKLNEEIAIILASFSASTSAFVETVKGLDYKAFKQIVESCGNFKVTKGKAKKGAWNIGEQKSILSPLYAFASEAARVVRSIFSRTLETAQNSVRVLQKAAITILDGISQYSLRLIDAMMFTSDLATNNLVVMAYITGGVVQLTSQWLTNIFGTVYEKLKPVLDWLEEKFKEGVEFLRDGWEIVKFISTCACEIVGGQIVTCAKEIKESVQTFFKLVNKFLALCADSIIIGGAKLKALNLGETFVTHSKGLYRKCVKSREETGLLMPLKAPKEIIFLEGETLPTEVLTEEVVLKTGDLQPLEQPTSEAVEAPLVGTPVCINGLMLLEIKDTEKYCALAPNMMVTNNTFTLKGGAPTKVTFGDDTVIEVQGYKSVNITFELDERIDKVLNEKCSAYTVELGTEVNEFACVVADAVIKTLQPVSELLTPLGIDLDEWSMATYYLFDESGEFKLASHMYCSFYPPDEDEEEGDCEEEEFEPSTQYEYGTEDDYQGKPLEFGATSAALQPEEEQEEDWLDDDSQQTVGQQDGSEDNQTTTIQTIVEVQPQLEMELTPVVQTIEVNSFSGYLKLTDNVYIKNADIVEEAKKVKPTVVVNAANVYLKHGGGVAGALNKATNNAMQVESDDYIATNGPLKVGGSCVLSGHNLAKHCLHVVGPNVNKGEDIQLLKSAYENFNQHEVLLAPLLSAGIFGADPIHSLRVCVDTVRTNVYLAVFDKNLYDKLVSSFLEMKSEKQVEQKIAEIPKEEVKPFITESKPSVEQRKQDDKKIKACVEEVTTTLEETKFLTENLLLYIDINGNLHPDSATLVSDIDITFLKKDAPYIVGDVVQEGVLTAVVIPTKKAGGTTEMLAKALRKVPTDNYITTYPGQGLNGYTVEEAKTVLKKCKSAFYILPSIISNEKQEILGTVSWNLREMLAHAEETRKLMPVCVETKAIVSTIQRKYKGIKIQEGVVDYGARFYFYTSKTTVASLINTLNDLNETLVTMPLGYVTHGLNLEEAARYMRSLKVPATVSVSSPDAVTAYNGYLTSSSKTPEEHFIETISLAGSYKDWSYSGQSTQLGIEFLKRGDKSVYYTSNPTTFHLDGEVITFDNLKTLLSLREVRTIKVFTTVDNINLHTQVVDMSMTYGQQFGPTYLDGADVTKIKPHNSHEGKTFYVLPNDDTLRVEAFEYYHTTDPSFLGRYMSALNHTKKWKYPQVNGLTSIKWADNNCYLATALLTLQQIELKFNPPALQDAYYRARAGEAANFCALILAYCNKTVGELGDVRETMSYLFQHANLDSCKRVLNVVCKTCGQQQTTLKGVEAVMYMGTLSYEQFKKGVQIPCTCGKQATKYLVQQESPFVMMSAPPAQYELKHGTFTCASEYTGNYQCGHYKHITSKETLYCIDGALLTKSSEYKGPITDVFYKENSYTTTIKPVTYKLDGVVCTEIDPKLDNYYKKDNSYFTEQPIDLVPNQPYPNASFDNFKFVCDNIKFADDLNQLTGYKKPASRELKVTFFPDLNGDVVAIDYKHYTPSFKKGAKLLHKPIVWHVNNATNKATYKPNTWCIRCLWSTKPVETSNSFDVLKSEDAQGMDNLACEDLKPVSEEVVENPTIQKDVLECNVKTTEVVGDIILKPANNSLKITEEVGHTDLMAAYVDNSSLTIKKPNELSRVLGLKTLATHGLAAVNSVPWDTIANYAKPFLNKVVSTTTNIVTRCLNRVCTNYMPYFFTLLLQLCTFTRSTNSRIKASMPTTIAKNTVKSVGKFCLEASFNYLKSPNFSKLINIIIWFLLLSVCLGSLIYSTAALGVLMSNLGMPSYCTGYREGYLNSTNVTIATYCTGSIPCSVCLSGLDSLDTYPSLETIQITISSFKWDLTAFGLVAEWFLAYILFTRFFYVLGLAAIMQLFFSYFAVHFISNSWLMWLIINLVQMAPISAMVRMYIFFASFYYVWKSYVHVVDGCNSSTCMMCYKRNRATRVECTTIVNGVRRSFYVYANGGKGFCKLHNWNCVNCDTFCAGSTFISDEVARDLSLQFKRPINPTDQSSYIVDSVTVKNGSIHLYFDKAGQKTYERHSLSHFVNLDNLRANNTKGSLPINVIVFDGKSKCEESSAKSASVYYSQLMCQPILLLDQALVSDVGDSAEVAVKMFDAYVNTFSSTFNVPMEKLKTLVATAEAELAKNVSLDNVLSTFISAARQGFVDSDVETKDVVECLKLSHQSDIEVTGDSCNNYMLTYNKVENMTPRDLGACIDCSARHINAQVAKSHNIALIWNVKDFMSLSEQLRKQIRSAAKKNNLPFKLTCATTRQVVNVVTTKIALKGGKIVNNWLKQLIKVTLVFLFVAAIFYLITPVHVMSKHTDFSSEIIGYKAIDGGVTRDIASTDTCFANKHADFDTWFSQRGGSYTNDKACPLIAAVITREVGFVVPGLPGTILRTTNGDFLHFLPRVFSAVGNICYTPSKLIEYTDFATSACVLAAECTIFKDASGKPVPYCYDTNVLEGSVAYESLRPDTRYVLMDGSIIQFPNTYLEGSVRVVTTFDSEYCRHGTCERSEAGVCVSTSGRWVLNNDYYRSLPGVFCGVDAVNLLTNMFTPLIQPIGALDISASIVAGGIVAIVVTCLAYYFMRFRRAFGEYSHVVAFNTLLFLMSFTVLCLTPVYSFLPGVYSVIYLYLTFYLTNDVSFLAHIQWMVMFTPLVPFWITIAYIICISTKHFYWFFSNYLKRRVVFNGVSFSTFEEAALCTFLLNKEMYLKLRSDVLLPLTQYNRYLALYNKYKYFSGAMDTTSYREAACCHLAKALNDFSNSGSDVLYQPPQTSITSAVLQSGFRKMAFPSGKVEGCMVQVTCGTTTLNGLWLDDVVYCPRHVICTSEDMLNPNYEDLLIRKSNHNFLVQAGNVQLRVIGHSMQNCVLKLKVDTANPKTPKYKFVRIQPGQTFSVLACYNGSPSGVYQCAMRPNFTIKGSFLNGSCGSVGFNIDYDCVSFCYMHHMELPTGVHAGTDLEGNFYGPFVDRQTAQAAGTDTTITVNVLAWLYAAVINGDRWFLNRFTTTLNDFNLVAMKYNYEPLTQDHVDILGPLSAQTGIAVLDMCASLKELLQNGMNGRTILGSALLEDEFTPFDVVRQCSGVTFQSAVKRTIKGTHHWLLLTILTSLLVLVQSTQWSLFFFLYENAFLPFAMGIIAMSAFAMMFVKHKHAFLCLFLLPSLATVAYFNMVYMPASWVMRIMTWLDMVDTSLSGFKLKDCVMYASAVVLLILMTARTVYDDGARRVWTLMNVLTLVYKVYYGNALDQAISMWALIISVTSNYSGVVTTVMFLARGIVFMCVEYCPIFFITGNTLQCIMLVYCFLGYFCTCYFGLFCLLNRYFRLTLGVYDYLVSTQEFRYMNSQGLLPPKNSIDAFKLNIKLLGVGGKPCIKVATVQSKMSDVKCTSVVLLSVLQQLRVESSSKLWAQCVQLHNDILLAKDTTEAFEKMVSLLSVLLSMQGAVDINKLCEEMLDNRATLQAIASEFSSLPSYAAFATAQEAYEQAVANGDSEVVLKKLKKSLNVAKSEFDRDAAMQRKLEKMADQAMTQMYKQARSEDKRAKVTSAMQTMLFTMLRKLDNDALNNIINNARDGCVPLNIIPLTTAAKLMVVIPDYNTYKNTCDGTTFTYASALWEIQQVVDADSKIVQLSEISMDNSPNLAWPLIVTALRANSAVKLQNNELSPVALRQMSCAAGTTQTACTDDNALAYYNTTKGGRFVLALLSDLQDLKWARFPKSDGTGTIYTELEPPCRFVTDTPKGPKVKYLYFIKGLNNLNRGMVLGSLAATVRLQAGNATEVPANSTVLSFCAFAVDAAKAYKDYLASGGQPITNCVKMLCTHTGTGQAITVTPEANMDQESFGGASCCLYCRCHIDHPNPKGFCDLKGKYVQIPTTCANDPVGFTLKNTVCTVCGMWKGYGCSCDQLREPMLQSADAQSFLN' + - name: ORF1b + sequence: 'RVCGVSAARLTPCGTGTSTDVVYRAFDIYNDKVAGFAKFLKTNCCRFQEKDEDDNLIDSYFVVKRHTFSNYQHEETIYNLLKDCPAVAKHDFFKFRIDGDMVPHISRQRLTKYTMADLVYALRHFDEGNCDTLKEILVTYNCCDDDYFNKKDWYDFVENPDILRVYANLGERVRQALLKTVQFCDAMRNAGIVGVLTLDNQDLNGNWYDFGDFIQTTPGSGVPVVDSYYSLLMPILTLTRALTAESHVDTDLTKPYIKWDLLKYDFTEERLKLFDRYFKYWDQTYHPNCVNCLDDRCILHCANFNVLFSTVFPPTSFGPLVRKIFVDGVPFVVSTGYHFRELGVVHNQDVNLHSSRLSFKELLVYAADPAMHAASGNLLLDKRTTCFSVAALTNNVAFQTVKPGNFNKDFYDFAVSKGFFKEGSSVELKHFFFAQDGNAAISDYDYYRYNLPTMCDIRQLLFVVEVVDKYFDCYDGGCINANQVIVNNLDKSAGFPFNKWGKARLYYDSMSYEDQDALFAYTKRNVIPTITQMNLKYAISAKNRARTVAGVSICSTMTNRQFHQKLLKSIAATRGATVVIGTSKFYGGWHNMLKTVYSDVENPHLMGWDYPKCDRAMPNMLRIMASLVLARKHTTCCSLSHRFYRLANECAQVLSEMVMCGGSLYVKPGGTSSGDATTAYANSVFNICQAVTANVNALLSTDGNKIADKYVRNLQHRLYECLYRNRDVDTDFVNEFYAYLRKHFSMMILSDDAVVCFNSTYASQGLVASIKNFKSVLYYQNNVFMSEAKCWTETDLTKGPHEFCSQHTMLVKQGDDYVYLPYPDPSRILGAGCFVDDIVKTDGTLMIERFVSLAIDAYPLTKHPNQEYADVFHLYLQYIRKLHDELTGHMLDMYSVMLTNDNTSRYWEPEFYEAMYTPHTVLQAVGACVLCNSQTSLRCGACIRRPFLCCKCCYDHVISTSHKLVLSVNPYVCNAPGCDVTDVTQLYLGGMSYYCKSHKPPISFPLCANGQVFGLYKNTCVGSDNVTDFNAIATCDWTNAGDYILANTCTERLKLFAAETLKATEETFKLSYGIATVREVLSDRELHLSWEVGKPRPPLNRNYVFTGYRVTKNSKVQIGEYTFEKGDYGDAVVYRGTTTYKLNVGDYFVLTSHTVMPLSAPTLVPQEHYVRITGLYPTLNISDEFSSNVANYQKVGMQKYSTLQGPPGTGKSHFAIGLALYYPSARIVYTACSHAAVDALCEKALKYLPIDKCSRIIPARARVECFDKFKVNSTLEQYVFCTVNALPETTADIVVFDEISMATNYDLSVVNARLRAKHYVYIGDPAQLPAPRTLLTKGTLEPEYFNSVCRLMKTIGPDMFLGTCRRCPAEIVDTVSALVYDNKLKAHKDKSAQCFKMFYKGVITHDVSSAINRPQIGVVREFLTRNPAWRKAVFISPYNSQNAVASKILGLPTQTVDSSQGSEYDYVIFTQTTETAHSCNVNRFNVAITRAKVGILCIMSDRDLYDKLQFTSLEIPRRNVATLQAENVTGLFKDCSKVITGLHPTQAPTHLSVDTKFKTEGLCVDIPGIPKDMTYRRLISMMGFKMNYQVNGYPNMFITREEAIRHVRAWIGFDVEGCHATREAVGTNLPLQLGFSTGVNLVAVPTGYVDTPNNTDFSRVSAKPPPGDQFKHLIPLMYKGLPWNVVRIKIVQMLSDTLKNLSDRVVFVLWAHGFELTSMKYFVKIGPERTCCLCDRRATCFSTASDTYACWHHSIGFDYVYNPFMIDVQQWGFTGNLQSNHDLYCQVHGNAHVASCDAIMTRCLAVHECFVKRVDWTIEYPIIGDELKINAACRKVQHMVVKAALLADKFPVLHDIGNPKAIKCVPQADVEWKFYDAQPCSDKAYKIEELFYSYATHSDKFTDGVCLFWNCNVDRYPANSIVCRFDTRVLSNLNLPGCDGGSLYVNKHAFHTPAFDKSAFVNLKQLPFFYYSDSPCESHGKQVVSDIDYVPLKSATCITRCNLGGAVCRHHANEYRLYLDAYNMMISAGFSLWVYKQFDTYNLWNTFTRLQSLENVAFNVVNKGHFDGQQGEVPVSIINNTVYTKVDGVDVELFENKTTLPVNVAFELWAKRNIKPVPEVKILNNLGVDIAANTVIWDYKRDAPAHISTIGVCSMTDIAKKPTETICAPLTVFFDGRVDGQVDLFRNARNGVLITEGSVKGLQPSVGPKQASLNGVTLIGEAVKTQFNYYKKVDGVVQQLPETYFTQSRNLQEFKPRSQMEIDFLELAMDEFIERYKLEGYAFEHIVYGDFSHSQLGGLHLLIGLAKRFKESPFELEDFIPMDSTVKNYFITDAQTGSSKCVCSVIDLLLDDFVEIIKSQDLSVVSKVVKVTIDYTEISFMLWCKDGHVETFYPKLQSSQAWQPGVAMPNLYKMQRMLLEKCDLQNYGDSATLPKGIMMNVAKYTQLCQYLNTLTLAVPYNMRVIHFGAGSDKGVAPGTAVLRQWLPTGTLLVDSDLNDFVSDADSTLIGDCATVHTANKWDLIISDMYDPKTKNVTKENDSKEGFFTYICGFIQQKLALGGSVAIKITEHSWNADLYKLMGHFAWWTAFVTNVNASSSEAFLIGCNYLGKPREQIDGYVMHANYIFWRNTNPIQLSSYSLFDMSKFPLKLRGTAVMSLKEGQINDMILSLLSKGRLIIRENNRVVISSDVLVNN*' + - name: ORF3a + sequence: 'MDLFMRIFTIGTVTLKQGEIKDATPSDFVRATATIPIQASLPFGWLIVGVALLAVFQSASKIITLKKRWQLALSKGVHFVCNLLLLFVTVYSHLLLVAAGLEAPFLYLYALVYFLQSINFVRIIMRLWLCWKCRSKNPLLYDANYFLCWHTNCYDYCIPYNSVTSSIVITSGDGTTSPISEHDYQIGGYTEKWESGVKDCVVLHSYFTSDYYQLYSTQLSTDTGVEHVTFFIYNKIVDEPEEHVQIHTIDGSSGVVNPVMEPIYDEPTTTTSVPL*' + - name: ORF6 + sequence: 'MFHLVDFQVTIAEILLIIMRTFKVSIWNLDYIINLIIKNLSKSLTENKYSQLDEEQPMEID*' + - name: ORF7a + sequence: 'MKIILFLALITLATCELYHYQECVRGTTVLLKEPCSSGTYEGNSPFHPLADNKFALTCFSTQFAFACPDGVKHVYQLRARSVSPKLFIRQEEVQELYSPIFLIVAAIVFITLCFTLKRKTE*' + - name: ORF7b + sequence: 'MIELSLIDFYLCFLAFLLFLVLIMLIIFWFSLELQDHNETCHA*' + - name: ORF8 + sequence: 'MKFLVFLGIITTVAAFHQECSLQSCTQHQPYVVDDPCPIHFYSKWYIRVGARKSAPLIELCVDEAGSKSPIQYIDIGNYTVSCLPFTINCQEPKLGSLVVRCSFYEDFLEYHDVRVVLDFI*' + - name: ORF9b + sequence: 'MDPKISEMHPALRLVDPQIQLAVTRMENAVGRDQNNVGPKVYPIILRLGSPLSLNMARKTLNSLEDKAFQLTPIAVQMTKLATTEELPDEFVVVTVK*' + - name: S + sequence: 'MFVFLVLLPLVSSQCVNLTTRTQLPPAYTNSFTRGVYYPDKVFRSSVLHSTQDLFLPFFSNVTWFHAIHVSGTNGTKRFDNPVLPFNDGVYFASTEKSNIIRGWIFGTTLDSKTQSLLIVNNATNVVIKVCEFQFCNDPFLGVYYHKNNKSWMESEFRVYSSANNCTFEYVSQPFLMDLEGKQGNFKNLREFVFKNIDGYFKIYSKHTPINLVRDLPQGFSALEPLVDLPIGINITRFQTLLALHRSYLTPGDSSSGWTAGAAAYYVGYLQPRTFLLKYNENGTITDAVDCALDPLSETKCTLKSFTVEKGIYQTSNFRVQPTESIVRFPNITNLCPFGEVFNATRFASVYAWNRKRISNCVADYSVLYNSASFSTFKCYGVSPTKLNDLCFTNVYADSFVIRGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAWNSNNLDSKVGGNYNYLYRLFRKSNLKPFERDISTEIYQAGSTPCNGVEGFNCYFPLQSYGFQPTNGVGYQPYRVVVLSFELLHAPATVCGPKKSTNLVKNKCVNFNFNGLTGTGVLTESNKKFLPFQQFGRDIADTTDAVRDPQTLEILDITPCSFGGVSVITPGTNTSNQVAVLYQDVNCTEVPVAIHADQLTPTWRVYSTGSNVFQTRAGCLIGAEHVNNSYECDIPIGAGICASYQTQTNSPRRARSVASQSIIAYTMSLGAENSVAYSNNSIAIPTNFTISVTTEILPVSMTKTSVDCTMYICGDSTECSNLLLQYGSFCTQLNRALTGIAVEQDKNTQEVFAQVKQIYKTPPIKDFGGFNFSQILPDPSKPSKRSFIEDLLFNKVTLADAGFIKQYGDCLGDIAARDLICAQKFNGLTVLPPLLTDEMIAQYTSALLAGTITSGWTFGAGAALQIPFAMQMAYRFNGIGVTQNVLYENQKLIANQFNSAIGKIQDSLSSTASALGKLQDVVNQNAQALNTLVKQLSSNFGAISSVLNDILSRLDKVEAEVQIDRLITGRLQSLQTYVTQQLIRAAEIRASANLAATKMSECVLGQSKRVDFCGKGYHLMSFPQSAPHGVVFLHVTYVPAQEKNFTTAPAICHDGKAHFPREGVFVSNGTHWFVTQRNFYEPQIITTDNTFVSGNCDVVIGIVNNTVYDPLQPELDSFKEELDKYFKNHTSPDVDLGDISGINASVVNIQKEIDRLNEVAKNLNESLIDLQELGKYEQYIKWPWYIWLGFIAGLIAIVMVTIMLCCMTSCCSCLKGCCSCGSCCKFDEDDSEPVLKGVKLHYT*' +referenceGenome: + nucleotideSequences: + - name: main + sequence: 'ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTTACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAGATGGCACTTGTGGCTTAGTAGAAGTTGAAAAAGGCGTTTTGCCTCAACTTGAACAGCCCTATGTGTTCATCAAACGTTCGGATGCTCGAACTGCACCTCATGGTCATGTTATGGTTGAGCTGGTAGCAGAACTCGAAGGCATTCAGTACGGTCGTAGTGGTGAGACACTTGGTGTCCTTGTCCCTCATGTGGGCGAAATACCAGTGGCTTACCGCAAGGTTCTTCTTCGTAAGAACGGTAATAAAGGAGCTGGTGGCCATAGTTACGGCGCCGATCTAAAGTCATTTGACTTAGGCGACGAGCTTGGCACTGATCCTTATGAAGATTTTCAAGAAAACTGGAACACTAAACATAGCAGTGGTGTTACCCGTGAACTCATGCGTGAGCTTAACGGAGGGGCATACACTCGCTATGTCGATAACAACTTCTGTGGCCCTGATGGCTACCCTCTTGAGTGCATTAAAGACCTTCTAGCACGTGCTGGTAAAGCTTCATGCACTTTGTCCGAACAACTGGACTTTATTGACACTAAGAGGGGTGTATACTGCTGCCGTGAACATGAGCATGAAATTGCTTGGTACACGGAACGTTCTGAAAAGAGCTATGAATTGCAGACACCTTTTGAAATTAAATTGGCAAAGAAATTTGACACCTTCAATGGGGAATGTCCAAATTTTGTATTTCCCTTAAATTCCATAATCAAGACTATTCAACCAAGGGTTGAAAAGAAAAAGCTTGATGGCTTTATGGGTAGAATTCGATCTGTCTATCCAGTTGCGTCACCAAATGAATGCAACCAAATGTGCCTTTCAACTCTCATGAAGTGTGATCATTGTGGTGAAACTTCATGGCAGACGGGCGATTTTGTTAAAGCCACTTGCGAATTTTGTGGCACTGAGAATTTGACTAAAGAAGGTGCCACTACTTGTGGTTACTTACCCCAAAATGCTGTTGTTAAAATTTATTGTCCAGCATGTCACAATTCAGAAGTAGGACCTGAGCATAGTCTTGCCGAATACCATAATGAATCTGGCTTGAAAACCATTCTTCGTAAGGGTGGTCGCACTATTGCCTTTGGAGGCTGTGTGTTCTCTTATGTTGGTTGCCATAACAAGTGTGCCTATTGGGTTCCACGTGCTAGCGCTAACATAGGTTGTAACCATACAGGTGTTGTTGGAGAAGGTTCCGAAGGTCTTAATGACAACCTTCTTGAAATACTCCAAAAAGAGAAAGTCAACATCAATATTGTTGGTGACTTTAAACTTAATGAAGAGATCGCCATTATTTTGGCATCTTTTTCTGCTTCCACAAGTGCTTTTGTGGAAACTGTGAAAGGTTTGGATTATAAAGCATTCAAACAAATTGTTGAATCCTGTGGTAATTTTAAAGTTACAAAAGGAAAAGCTAAAAAAGGTGCCTGGAATATTGGTGAACAGAAATCAATACTGAGTCCTCTTTATGCATTTGCATCAGAGGCTGCTCGTGTTGTACGATCAATTTTCTCCCGCACTCTTGAAACTGCTCAAAATTCTGTGCGTGTTTTACAGAAGGCCGCTATAACAATACTAGATGGAATTTCACAGTATTCACTGAGACTCATTGATGCTATGATGTTCACATCTGATTTGGCTACTAACAATCTAGTTGTAATGGCCTACATTACAGGTGGTGTTGTTCAGTTGACTTCGCAGTGGCTAACTAACATCTTTGGCACTGTTTATGAAAAACTCAAACCCGTCCTTGATTGGCTTGAAGAGAAGTTTAAGGAAGGTGTAGAGTTTCTTAGAGACGGTTGGGAAATTGTTAAATTTATCTCAACCTGTGCTTGTGAAATTGTCGGTGGACAAATTGTCACCTGTGCAAAGGAAATTAAGGAGAGTGTTCAGACATTCTTTAAGCTTGTAAATAAATTTTTGGCTTTGTGTGCTGACTCTATCATTATTGGTGGAGCTAAACTTAAAGCCTTGAATTTAGGTGAAACATTTGTCACGCACTCAAAGGGATTGTACAGAAAGTGTGTTAAATCCAGAGAAGAAACTGGCCTACTCATGCCTCTAAAAGCCCCAAAAGAAATTATCTTCTTAGAGGGAGAAACACTTCCCACAGAAGTGTTAACAGAGGAAGTTGTCTTGAAAACTGGTGATTTACAACCATTAGAACAACCTACTAGTGAAGCTGTTGAAGCTCCATTGGTTGGTACACCAGTTTGTATTAACGGGCTTATGTTGCTCGAAATCAAAGACACAGAAAAGTACTGTGCCCTTGCACCTAATATGATGGTAACAAACAATACCTTCACACTCAAAGGCGGTGCACCAACAAAGGTTACTTTTGGTGATGACACTGTGATAGAAGTGCAAGGTTACAAGAGTGTGAATATCACTTTTGAACTTGATGAAAGGATTGATAAAGTACTTAATGAGAAGTGCTCTGCCTATACAGTTGAACTCGGTACAGAAGTAAATGAGTTCGCCTGTGTTGTGGCAGATGCTGTCATAAAAACTTTGCAACCAGTATCTGAATTACTTACACCACTGGGCATTGATTTAGATGAGTGGAGTATGGCTACATACTACTTATTTGATGAGTCTGGTGAGTTTAAATTGGCTTCACATATGTATTGTTCTTTCTACCCTCCAGATGAGGATGAAGAAGAAGGTGATTGTGAAGAAGAAGAGTTTGAGCCATCAACTCAATATGAGTATGGTACTGAAGATGATTACCAAGGTAAACCTTTGGAATTTGGTGCCACTTCTGCTGCTCTTCAACCTGAAGAAGAGCAAGAAGAAGATTGGTTAGATGATGATAGTCAACAAACTGTTGGTCAACAAGACGGCAGTGAGGACAATCAGACAACTACTATTCAAACAATTGTTGAGGTTCAACCTCAATTAGAGATGGAACTTACACCAGTTGTTCAGACTATTGAAGTGAATAGTTTTAGTGGTTATTTAAAACTTACTGACAATGTATACATTAAAAATGCAGACATTGTGGAAGAAGCTAAAAAGGTAAAACCAACAGTGGTTGTTAATGCAGCCAATGTTTACCTTAAACATGGAGGAGGTGTTGCAGGAGCCTTAAATAAGGCTACTAACAATGCCATGCAAGTTGAATCTGATGATTACATAGCTACTAATGGACCACTTAAAGTGGGTGGTAGTTGTGTTTTAAGCGGACACAATCTTGCTAAACACTGTCTTCATGTTGTCGGCCCAAATGTTAACAAAGGTGAAGACATTCAACTTCTTAAGAGTGCTTATGAAAATTTTAATCAGCACGAAGTTCTACTTGCACCATTATTATCAGCTGGTATTTTTGGTGCTGACCCTATACATTCTTTAAGAGTTTGTGTAGATACTGTTCGCACAAATGTCTACTTAGCTGTCTTTGATAAAAATCTCTATGACAAACTTGTTTCAAGCTTTTTGGAAATGAAGAGTGAAAAGCAAGTTGAACAAAAGATCGCTGAGATTCCTAAAGAGGAAGTTAAGCCATTTATAACTGAAAGTAAACCTTCAGTTGAACAGAGAAAACAAGATGATAAGAAAATCAAAGCTTGTGTTGAAGAAGTTACAACAACTCTGGAAGAAACTAAGTTCCTCACAGAAAACTTGTTACTTTATATTGACATTAATGGCAATCTTCATCCAGATTCTGCCACTCTTGTTAGTGACATTGACATCACTTTCTTAAAGAAAGATGCTCCATATATAGTGGGTGATGTTGTTCAAGAGGGTGTTTTAACTGCTGTGGTTATACCTACTAAAAAGGCTGGTGGCACTACTGAAATGCTAGCGAAAGCTTTGAGAAAAGTGCCAACAGACAATTATATAACCACTTACCCGGGTCAGGGTTTAAATGGTTACACTGTAGAGGAGGCAAAGACAGTGCTTAAAAAGTGTAAAAGTGCCTTTTACATTCTACCATCTATTATCTCTAATGAGAAGCAAGAAATTCTTGGAACTGTTTCTTGGAATTTGCGAGAAATGCTTGCACATGCAGAAGAAACACGCAAATTAATGCCTGTCTGTGTGGAAACTAAAGCCATAGTTTCAACTATACAGCGTAAATATAAGGGTATTAAAATACAAGAGGGTGTGGTTGATTATGGTGCTAGATTTTACTTTTACACCAGTAAAACAACTGTAGCGTCACTTATCAACACACTTAACGATCTAAATGAAACTCTTGTTACAATGCCACTTGGCTATGTAACACATGGCTTAAATTTGGAAGAAGCTGCTCGGTATATGAGATCTCTCAAAGTGCCAGCTACAGTTTCTGTTTCTTCACCTGATGCTGTTACAGCGTATAATGGTTATCTTACTTCTTCTTCTAAAACACCTGAAGAACATTTTATTGAAACCATCTCACTTGCTGGTTCCTATAAAGATTGGTCCTATTCTGGACAATCTACACAACTAGGTATAGAATTTCTTAAGAGAGGTGATAAAAGTGTATATTACACTAGTAATCCTACCACATTCCACCTAGATGGTGAAGTTATCACCTTTGACAATCTTAAGACACTTCTTTCTTTGAGAGAAGTGAGGACTATTAAGGTGTTTACAACAGTAGACAACATTAACCTCCACACGCAAGTTGTGGACATGTCAATGACATATGGACAACAGTTTGGTCCAACTTATTTGGATGGAGCTGATGTTACTAAAATAAAACCTCATAATTCACATGAAGGTAAAACATTTTATGTTTTACCTAATGATGACACTCTACGTGTTGAGGCTTTTGAGTACTACCACACAACTGATCCTAGTTTTCTGGGTAGGTACATGTCAGCATTAAATCACACTAAAAAGTGGAAATACCCACAAGTTAATGGTTTAACTTCTATTAAATGGGCAGATAACAACTGTTATCTTGCCACTGCATTGTTAACACTCCAACAAATAGAGTTGAAGTTTAATCCACCTGCTCTACAAGATGCTTATTACAGAGCAAGGGCTGGTGAAGCTGCTAACTTTTGTGCACTTATCTTAGCCTACTGTAATAAGACAGTAGGTGAGTTAGGTGATGTTAGAGAAACAATGAGTTACTTGTTTCAACATGCCAATTTAGATTCTTGCAAAAGAGTCTTGAACGTGGTGTGTAAAACTTGTGGACAACAGCAGACAACCCTTAAGGGTGTAGAAGCTGTTATGTACATGGGCACACTTTCTTATGAACAATTTAAGAAAGGTGTTCAGATACCTTGTACGTGTGGTAAACAAGCTACAAAATATCTAGTACAACAGGAGTCACCTTTTGTTATGATGTCAGCACCACCTGCTCAGTATGAACTTAAGCATGGTACATTTACTTGTGCTAGTGAGTACACTGGTAATTACCAGTGTGGTCACTATAAACATATAACTTCTAAAGAAACTTTGTATTGCATAGACGGTGCTTTACTTACAAAGTCCTCAGAATACAAAGGTCCTATTACGGATGTTTTCTACAAAGAAAACAGTTACACAACAACCATAAAACCAGTTACTTATAAATTGGATGGTGTTGTTTGTACAGAAATTGACCCTAAGTTGGACAATTATTATAAGAAAGACAATTCTTATTTCACAGAGCAACCAATTGATCTTGTACCAAACCAACCATATCCAAACGCAAGCTTCGATAATTTTAAGTTTGTATGTGATAATATCAAATTTGCTGATGATTTAAACCAGTTAACTGGTTATAAGAAACCTGCTTCAAGAGAGCTTAAAGTTACATTTTTCCCTGACTTAAATGGTGATGTGGTGGCTATTGATTATAAACACTACACACCCTCTTTTAAGAAAGGAGCTAAATTGTTACATAAACCTATTGTTTGGCATGTTAACAATGCAACTAATAAAGCCACGTATAAACCAAATACCTGGTGTATACGTTGTCTTTGGAGCACAAAACCAGTTGAAACATCAAATTCGTTTGATGTACTGAAGTCAGAGGACGCGCAGGGAATGGATAATCTTGCCTGCGAAGATCTAAAACCAGTCTCTGAAGAAGTAGTGGAAAATCCTACCATACAGAAAGACGTTCTTGAGTGTAATGTGAAAACTACCGAAGTTGTAGGAGACATTATACTTAAACCAGCAAATAATAGTTTAAAAATTACAGAAGAGGTTGGCCACACAGATCTAATGGCTGCTTATGTAGACAATTCTAGTCTTACTATTAAGAAACCTAATGAATTATCTAGAGTATTAGGTTTGAAAACCCTTGCTACTCATGGTTTAGCTGCTGTTAATAGTGTCCCTTGGGATACTATAGCTAATTATGCTAAGCCTTTTCTTAACAAAGTTGTTAGTACAACTACTAACATAGTTACACGGTGTTTAAACCGTGTTTGTACTAATTATATGCCTTATTTCTTTACTTTATTGCTACAATTGTGTACTTTTACTAGAAGTACAAATTCTAGAATTAAAGCATCTATGCCGACTACTATAGCAAAGAATACTGTTAAGAGTGTCGGTAAATTTTGTCTAGAGGCTTCATTTAATTATTTGAAGTCACCTAATTTTTCTAAACTGATAAATATTATAATTTGGTTTTTACTATTAAGTGTTTGCCTAGGTTCTTTAATCTACTCAACCGCTGCTTTAGGTGTTTTAATGTCTAATTTAGGCATGCCTTCTTACTGTACTGGTTACAGAGAAGGCTATTTGAACTCTACTAATGTCACTATTGCAACCTACTGTACTGGTTCTATACCTTGTAGTGTTTGTCTTAGTGGTTTAGATTCTTTAGACACCTATCCTTCTTTAGAAACTATACAAATTACCATTTCATCTTTTAAATGGGATTTAACTGCTTTTGGCTTAGTTGCAGAGTGGTTTTTGGCATATATTCTTTTCACTAGGTTTTTCTATGTACTTGGATTGGCTGCAATCATGCAATTGTTTTTCAGCTATTTTGCAGTACATTTTATTAGTAATTCTTGGCTTATGTGGTTAATAATTAATCTTGTACAAATGGCCCCGATTTCAGCTATGGTTAGAATGTACATCTTCTTTGCATCATTTTATTATGTATGGAAAAGTTATGTGCATGTTGTAGACGGTTGTAATTCATCAACTTGTATGATGTGTTACAAACGTAATAGAGCAACAAGAGTCGAATGTACAACTATTGTTAATGGTGTTAGAAGGTCCTTTTATGTCTATGCTAATGGAGGTAAAGGCTTTTGCAAACTACACAATTGGAATTGTGTTAATTGTGATACATTCTGTGCTGGTAGTACATTTATTAGTGATGAAGTTGCGAGAGACTTGTCACTACAGTTTAAAAGACCAATAAATCCTACTGACCAGTCTTCTTACATCGTTGATAGTGTTACAGTGAAGAATGGTTCCATCCATCTTTACTTTGATAAAGCTGGTCAAAAGACTTATGAAAGACATTCTCTCTCTCATTTTGTTAACTTAGACAACCTGAGAGCTAATAACACTAAAGGTTCATTGCCTATTAATGTTATAGTTTTTGATGGTAAATCAAAATGTGAAGAATCATCTGCAAAATCAGCGTCTGTTTACTACAGTCAGCTTATGTGTCAACCTATACTGTTACTAGATCAGGCATTAGTGTCTGATGTTGGTGATAGTGCGGAAGTTGCAGTTAAAATGTTTGATGCTTACGTTAATACGTTTTCATCAACTTTTAACGTACCAATGGAAAAACTCAAAACACTAGTTGCAACTGCAGAAGCTGAACTTGCAAAGAATGTGTCCTTAGACAATGTCTTATCTACTTTTATTTCAGCAGCTCGGCAAGGGTTTGTTGATTCAGATGTAGAAACTAAAGATGTTGTTGAATGTCTTAAATTGTCACATCAATCTGACATAGAAGTTACTGGCGATAGTTGTAATAACTATATGCTCACCTATAACAAAGTTGAAAACATGACACCCCGTGACCTTGGTGCTTGTATTGACTGTAGTGCGCGTCATATTAATGCGCAGGTAGCAAAAAGTCACAACATTGCTTTGATATGGAACGTTAAAGATTTCATGTCATTGTCTGAACAACTACGAAAACAAATACGTAGTGCTGCTAAAAAGAATAACTTACCTTTTAAGTTGACATGTGCAACTACTAGACAAGTTGTTAATGTTGTAACAACAAAGATAGCACTTAAGGGTGGTAAAATTGTTAATAATTGGTTGAAGCAGTTAATTAAAGTTACACTTGTGTTCCTTTTTGTTGCTGCTATTTTCTATTTAATAACACCTGTTCATGTCATGTCTAAACATACTGACTTTTCAAGTGAAATCATAGGATACAAGGCTATTGATGGTGGTGTCACTCGTGACATAGCATCTACAGATACTTGTTTTGCTAACAAACATGCTGATTTTGACACATGGTTTAGCCAGCGTGGTGGTAGTTATACTAATGACAAAGCTTGCCCATTGATTGCTGCAGTCATAACAAGAGAAGTGGGTTTTGTCGTGCCTGGTTTGCCTGGCACGATATTACGCACAACTAATGGTGACTTTTTGCATTTCTTACCTAGAGTTTTTAGTGCAGTTGGTAACATCTGTTACACACCATCAAAACTTATAGAGTACACTGACTTTGCAACATCAGCTTGTGTTTTGGCTGCTGAATGTACAATTTTTAAAGATGCTTCTGGTAAGCCAGTACCATATTGTTATGATACCAATGTACTAGAAGGTTCTGTTGCTTATGAAAGTTTACGCCCTGACACACGTTATGTGCTCATGGATGGCTCTATTATTCAATTTCCTAACACCTACCTTGAAGGTTCTGTTAGAGTGGTAACAACTTTTGATTCTGAGTACTGTAGGCACGGCACTTGTGAAAGATCAGAAGCTGGTGTTTGTGTATCTACTAGTGGTAGATGGGTACTTAACAATGATTATTACAGATCTTTACCAGGAGTTTTCTGTGGTGTAGATGCTGTAAATTTACTTACTAATATGTTTACACCACTAATTCAACCTATTGGTGCTTTGGACATATCAGCATCTATAGTAGCTGGTGGTATTGTAGCTATCGTAGTAACATGCCTTGCCTACTATTTTATGAGGTTTAGAAGAGCTTTTGGTGAATACAGTCATGTAGTTGCCTTTAATACTTTACTATTCCTTATGTCATTCACTGTACTCTGTTTAACACCAGTTTACTCATTCTTACCTGGTGTTTATTCTGTTATTTACTTGTACTTGACATTTTATCTTACTAATGATGTTTCTTTTTTAGCACATATTCAGTGGATGGTTATGTTCACACCTTTAGTACCTTTCTGGATAACAATTGCTTATATCATTTGTATTTCCACAAAGCATTTCTATTGGTTCTTTAGTAATTACCTAAAGAGACGTGTAGTCTTTAATGGTGTTTCCTTTAGTACTTTTGAAGAAGCTGCGCTGTGCACCTTTTTGTTAAATAAAGAAATGTATCTAAAGTTGCGTAGTGATGTGCTATTACCTCTTACGCAATATAATAGATACTTAGCTCTTTATAATAAGTACAAGTATTTTAGTGGAGCAATGGATACAACTAGCTACAGAGAAGCTGCTTGTTGTCATCTCGCAAAGGCTCTCAATGACTTCAGTAACTCAGGTTCTGATGTTCTTTACCAACCACCACAAACCTCTATCACCTCAGCTGTTTTGCAGAGTGGTTTTAGAAAAATGGCATTCCCATCTGGTAAAGTTGAGGGTTGTATGGTACAAGTAACTTGTGGTACAACTACACTTAACGGTCTTTGGCTTGATGACGTAGTTTACTGTCCAAGACATGTGATCTGCACCTCTGAAGACATGCTTAACCCTAATTATGAAGATTTACTCATTCGTAAGTCTAATCATAATTTCTTGGTACAGGCTGGTAATGTTCAACTCAGGGTTATTGGACATTCTATGCAAAATTGTGTACTTAAGCTTAAGGTTGATACAGCCAATCCTAAGACACCTAAGTATAAGTTTGTTCGCATTCAACCAGGACAGACTTTTTCAGTGTTAGCTTGTTACAATGGTTCACCATCTGGTGTTTACCAATGTGCTATGAGGCCCAATTTCACTATTAAGGGTTCATTCCTTAATGGTTCATGTGGTAGTGTTGGTTTTAACATAGATTATGACTGTGTCTCTTTTTGTTACATGCACCATATGGAATTACCAACTGGAGTTCATGCTGGCACAGACTTAGAAGGTAACTTTTATGGACCTTTTGTTGACAGGCAAACAGCACAAGCAGCTGGTACGGACACAACTATTACAGTTAATGTTTTAGCTTGGTTGTACGCTGCTGTTATAAATGGAGACAGGTGGTTTCTCAATCGATTTACCACAACTCTTAATGACTTTAACCTTGTGGCTATGAAGTACAATTATGAACCTCTAACACAAGACCATGTTGACATACTAGGACCTCTTTCTGCTCAAACTGGAATTGCCGTTTTAGATATGTGTGCTTCATTAAAAGAATTACTGCAAAATGGTATGAATGGACGTACCATATTGGGTAGTGCTTTATTAGAAGATGAATTTACACCTTTTGATGTTGTTAGACAATGCTCAGGTGTTACTTTCCAAAGTGCAGTGAAAAGAACAATCAAGGGTACACACCACTGGTTGTTACTCACAATTTTGACTTCACTTTTAGTTTTAGTCCAGAGTACTCAATGGTCTTTGTTCTTTTTTTTGTATGAAAATGCCTTTTTACCTTTTGCTATGGGTATTATTGCTATGTCTGCTTTTGCAATGATGTTTGTCAAACATAAGCATGCATTTCTCTGTTTGTTTTTGTTACCTTCTCTTGCCACTGTAGCTTATTTTAATATGGTCTATATGCCTGCTAGTTGGGTGATGCGTATTATGACATGGTTGGATATGGTTGATACTAGTTTGTCTGGTTTTAAGCTAAAAGACTGTGTTATGTATGCATCAGCTGTAGTGTTACTAATCCTTATGACAGCAAGAACTGTGTATGATGATGGTGCTAGGAGAGTGTGGACACTTATGAATGTCTTGACACTCGTTTATAAAGTTTATTATGGTAATGCTTTAGATCAAGCCATTTCCATGTGGGCTCTTATAATCTCTGTTACTTCTAACTACTCAGGTGTAGTTACAACTGTCATGTTTTTGGCCAGAGGTATTGTTTTTATGTGTGTTGAGTATTGCCCTATTTTCTTCATAACTGGTAATACACTTCAGTGTATAATGCTAGTTTATTGTTTCTTAGGCTATTTTTGTACTTGTTACTTTGGCCTCTTTTGTTTACTCAACCGCTACTTTAGACTGACTCTTGGTGTTTATGATTACTTAGTTTCTACACAGGAGTTTAGATATATGAATTCACAGGGACTACTCCCACCCAAGAATAGCATAGATGCCTTCAAACTCAACATTAAATTGTTGGGTGTTGGTGGCAAACCTTGTATCAAAGTAGCCACTGTACAGTCTAAAATGTCAGATGTAAAGTGCACATCAGTAGTCTTACTCTCAGTTTTGCAACAACTCAGAGTAGAATCATCATCTAAATTGTGGGCTCAATGTGTCCAGTTACACAATGACATTCTCTTAGCTAAAGATACTACTGAAGCCTTTGAAAAAATGGTTTCACTACTTTCTGTTTTGCTTTCCATGCAGGGTGCTGTAGACATAAACAAGCTTTGTGAAGAAATGCTGGACAACAGGGCAACCTTACAAGCTATAGCCTCAGAGTTTAGTTCCCTTCCATCATATGCAGCTTTTGCTACTGCTCAAGAAGCTTATGAGCAGGCTGTTGCTAATGGTGATTCTGAAGTTGTTCTTAAAAAGTTGAAGAAGTCTTTGAATGTGGCTAAATCTGAATTTGACCGTGATGCAGCCATGCAACGTAAGTTGGAAAAGATGGCTGATCAAGCTATGACCCAAATGTATAAACAGGCTAGATCTGAGGACAAGAGGGCAAAAGTTACTAGTGCTATGCAGACAATGCTTTTCACTATGCTTAGAAAGTTGGATAATGATGCACTCAACAACATTATCAACAATGCAAGAGATGGTTGTGTTCCCTTGAACATAATACCTCTTACAACAGCAGCCAAACTAATGGTTGTCATACCAGACTATAACACATATAAAAATACGTGTGATGGTACAACATTTACTTATGCATCAGCATTGTGGGAAATCCAACAGGTTGTAGATGCAGATAGTAAAATTGTTCAACTTAGTGAAATTAGTATGGACAATTCACCTAATTTAGCATGGCCTCTTATTGTAACAGCTTTAAGGGCCAATTCTGCTGTCAAATTACAGAATAATGAGCTTAGTCCTGTTGCACTACGACAGATGTCTTGTGCTGCCGGTACTACACAAACTGCTTGCACTGATGACAATGCGTTAGCTTACTACAACACAACAAAGGGAGGTAGGTTTGTACTTGCACTGTTATCCGATTTACAGGATTTGAAATGGGCTAGATTCCCTAAGAGTGATGGAACTGGTACTATCTATACAGAACTGGAACCACCTTGTAGGTTTGTTACAGACACACCTAAAGGTCCTAAAGTGAAGTATTTATACTTTATTAAAGGATTAAACAACCTAAATAGAGGTATGGTACTTGGTAGTTTAGCTGCCACAGTACGTCTACAAGCTGGTAATGCAACAGAAGTGCCTGCCAATTCAACTGTATTATCTTTCTGTGCTTTTGCTGTAGATGCTGCTAAAGCTTACAAAGATTATCTAGCTAGTGGGGGACAACCAATCACTAATTGTGTTAAGATGTTGTGTACACACACTGGTACTGGTCAGGCAATAACAGTTACACCGGAAGCCAATATGGATCAAGAATCCTTTGGTGGTGCATCGTGTTGTCTGTACTGCCGTTGCCACATAGATCATCCAAATCCTAAAGGATTTTGTGACTTAAAAGGTAAGTATGTACAAATACCTACAACTTGTGCTAATGACCCTGTGGGTTTTACACTTAAAAACACAGTCTGTACCGTCTGCGGTATGTGGAAAGGTTATGGCTGTAGTTGTGATCAACTCCGCGAACCCATGCTTCAGTCAGCTGATGCACAATCGTTTTTAAACGGGTTTGCGGTGTAAGTGCAGCCCGTCTTACACCGTGCGGCACAGGCACTAGTACTGATGTCGTATACAGGGCTTTTGACATCTACAATGATAAAGTAGCTGGTTTTGCTAAATTCCTAAAAACTAATTGTTGTCGCTTCCAAGAAAAGGACGAAGATGACAATTTAATTGATTCTTACTTTGTAGTTAAGAGACACACTTTCTCTAACTACCAACATGAAGAAACAATTTATAATTTACTTAAGGATTGTCCAGCTGTTGCTAAACATGACTTCTTTAAGTTTAGAATAGACGGTGACATGGTACCACATATATCACGTCAACGTCTTACTAAATACACAATGGCAGACCTCGTCTATGCTTTAAGGCATTTTGATGAAGGTAATTGTGACACATTAAAAGAAATACTTGTCACATACAATTGTTGTGATGATGATTATTTCAATAAAAAGGACTGGTATGATTTTGTAGAAAACCCAGATATATTACGCGTATACGCCAACTTAGGTGAACGTGTACGCCAAGCTTTGTTAAAAACAGTACAATTCTGTGATGCCATGCGAAATGCTGGTATTGTTGGTGTACTGACATTAGATAATCAAGATCTCAATGGTAACTGGTATGATTTCGGTGATTTCATACAAACCACGCCAGGTAGTGGAGTTCCTGTTGTAGATTCTTATTATTCATTGTTAATGCCTATATTAACCTTGACCAGGGCTTTAACTGCAGAGTCACATGTTGACACTGACTTAACAAAGCCTTACATTAAGTGGGATTTGTTAAAATATGACTTCACGGAAGAGAGGTTAAAACTCTTTGACCGTTATTTTAAATATTGGGATCAGACATACCACCCAAATTGTGTTAACTGTTTGGATGACAGATGCATTCTGCATTGTGCAAACTTTAATGTTTTATTCTCTACAGTGTTCCCACCTACAAGTTTTGGACCACTAGTGAGAAAAATATTTGTTGATGGTGTTCCATTTGTAGTTTCAACTGGATACCACTTCAGAGAGCTAGGTGTTGTACATAATCAGGATGTAAACTTACATAGCTCTAGACTTAGTTTTAAGGAATTACTTGTGTATGCTGCTGACCCTGCTATGCACGCTGCTTCTGGTAATCTATTACTAGATAAACGCACTACGTGCTTTTCAGTAGCTGCACTTACTAACAATGTTGCTTTTCAAACTGTCAAACCCGGTAATTTTAACAAAGACTTCTATGACTTTGCTGTGTCTAAGGGTTTCTTTAAGGAAGGAAGTTCTGTTGAATTAAAACACTTCTTCTTTGCTCAGGATGGTAATGCTGCTATCAGCGATTATGACTACTATCGTTATAATCTACCAACAATGTGTGATATCAGACAACTACTATTTGTAGTTGAAGTTGTTGATAAGTACTTTGATTGTTACGATGGTGGCTGTATTAATGCTAACCAAGTCATCGTCAACAACCTAGACAAATCAGCTGGTTTTCCATTTAATAAATGGGGTAAGGCTAGACTTTATTATGATTCAATGAGTTATGAGGATCAAGATGCACTTTTCGCATATACAAAACGTAATGTCATCCCTACTATAACTCAAATGAATCTTAAGTATGCCATTAGTGCAAAGAATAGAGCTCGCACCGTAGCTGGTGTCTCTATCTGTAGTACTATGACCAATAGACAGTTTCATCAAAAATTATTGAAATCAATAGCCGCCACTAGAGGAGCTACTGTAGTAATTGGAACAAGCAAATTCTATGGTGGTTGGCACAACATGTTAAAAACTGTTTATAGTGATGTAGAAAACCCTCACCTTATGGGTTGGGATTATCCTAAATGTGATAGAGCCATGCCTAACATGCTTAGAATTATGGCCTCACTTGTTCTTGCTCGCAAACATACAACGTGTTGTAGCTTGTCACACCGTTTCTATAGATTAGCTAATGAGTGTGCTCAAGTATTGAGTGAAATGGTCATGTGTGGCGGTTCACTATATGTTAAACCAGGTGGAACCTCATCAGGAGATGCCACAACTGCTTATGCTAATAGTGTTTTTAACATTTGTCAAGCTGTCACGGCCAATGTTAATGCACTTTTATCTACTGATGGTAACAAAATTGCCGATAAGTATGTCCGCAATTTACAACACAGACTTTATGAGTGTCTCTATAGAAATAGAGATGTTGACACAGACTTTGTGAATGAGTTTTACGCATATTTGCGTAAACATTTCTCAATGATGATACTCTCTGACGATGCTGTTGTGTGTTTCAATAGCACTTATGCATCTCAAGGTCTAGTGGCTAGCATAAAGAACTTTAAGTCAGTTCTTTATTATCAAAACAATGTTTTTATGTCTGAAGCAAAATGTTGGACTGAGACTGACCTTACTAAAGGACCTCATGAATTTTGCTCTCAACATACAATGCTAGTTAAACAGGGTGATGATTATGTGTACCTTCCTTACCCAGATCCATCAAGAATCCTAGGGGCCGGCTGTTTTGTAGATGATATCGTAAAAACAGATGGTACACTTATGATTGAACGGTTCGTGTCTTTAGCTATAGATGCTTACCCACTTACTAAACATCCTAATCAGGAGTATGCTGATGTCTTTCATTTGTACTTACAATACATAAGAAAGCTACATGATGAGTTAACAGGACACATGTTAGACATGTATTCTGTTATGCTTACTAATGATAACACTTCAAGGTATTGGGAACCTGAGTTTTATGAGGCTATGTACACACCGCATACAGTCTTACAGGCTGTTGGGGCTTGTGTTCTTTGCAATTCACAGACTTCATTAAGATGTGGTGCTTGCATACGTAGACCATTCTTATGTTGTAAATGCTGTTACGACCATGTCATATCAACATCACATAAATTAGTCTTGTCTGTTAATCCGTATGTTTGCAATGCTCCAGGTTGTGATGTCACAGATGTGACTCAACTTTACTTAGGAGGTATGAGCTATTATTGTAAATCACATAAACCACCCATTAGTTTTCCATTGTGTGCTAATGGACAAGTTTTTGGTTTATATAAAAATACATGTGTTGGTAGCGATAATGTTACTGACTTTAATGCAATTGCAACATGTGACTGGACAAATGCTGGTGATTACATTTTAGCTAACACCTGTACTGAAAGACTCAAGCTTTTTGCAGCAGAAACGCTCAAAGCTACTGAGGAGACATTTAAACTGTCTTATGGTATTGCTACTGTACGTGAAGTGCTGTCTGACAGAGAATTACATCTTTCATGGGAAGTTGGTAAACCTAGACCACCACTTAACCGAAATTATGTCTTTACTGGTTATCGTGTAACTAAAAACAGTAAAGTACAAATAGGAGAGTACACCTTTGAAAAAGGTGACTATGGTGATGCTGTTGTTTACCGAGGTACAACAACTTACAAATTAAATGTTGGTGATTATTTTGTGCTGACATCACATACAGTAATGCCATTAAGTGCACCTACACTAGTGCCACAAGAGCACTATGTTAGAATTACTGGCTTATACCCAACACTCAATATCTCAGATGAGTTTTCTAGCAATGTTGCAAATTATCAAAAGGTTGGTATGCAAAAGTATTCTACACTCCAGGGACCACCTGGTACTGGTAAGAGTCATTTTGCTATTGGCCTAGCTCTCTACTACCCTTCTGCTCGCATAGTGTATACAGCTTGCTCTCATGCCGCTGTTGATGCACTATGTGAGAAGGCATTAAAATATTTGCCTATAGATAAATGTAGTAGAATTATACCTGCACGTGCTCGTGTAGAGTGTTTTGATAAATTCAAAGTGAATTCAACATTAGAACAGTATGTCTTTTGTACTGTAAATGCATTGCCTGAGACGACAGCAGATATAGTTGTCTTTGATGAAATTTCAATGGCCACAAATTATGATTTGAGTGTTGTCAATGCCAGATTACGTGCTAAGCACTATGTGTACATTGGCGACCCTGCTCAATTACCTGCACCACGCACATTGCTAACTAAGGGCACACTAGAACCAGAATATTTCAATTCAGTGTGTAGACTTATGAAAACTATAGGTCCAGACATGTTCCTCGGAACTTGTCGGCGTTGTCCTGCTGAAATTGTTGACACTGTGAGTGCTTTGGTTTATGATAATAAGCTTAAAGCACATAAAGACAAATCAGCTCAATGCTTTAAAATGTTTTATAAGGGTGTTATCACGCATGATGTTTCATCTGCAATTAACAGGCCACAAATAGGCGTGGTAAGAGAATTCCTTACACGTAACCCTGCTTGGAGAAAAGCTGTCTTTATTTCACCTTATAATTCACAGAATGCTGTAGCCTCAAAGATTTTGGGACTACCAACTCAAACTGTTGATTCATCACAGGGCTCAGAATATGACTATGTCATATTCACTCAAACCACTGAAACAGCTCACTCTTGTAATGTAAACAGATTTAATGTTGCTATTACCAGAGCAAAAGTAGGCATACTTTGCATAATGTCTGATAGAGACCTTTATGACAAGTTGCAATTTACAAGTCTTGAAATTCCACGTAGGAATGTGGCAACTTTACAAGCTGAAAATGTAACAGGACTCTTTAAAGATTGTAGTAAGGTAATCACTGGGTTACATCCTACACAGGCACCTACACACCTCAGTGTTGACACTAAATTCAAAACTGAAGGTTTATGTGTTGACATACCTGGCATACCTAAGGACATGACCTATAGAAGACTCATCTCTATGATGGGTTTTAAAATGAATTATCAAGTTAATGGTTACCCTAACATGTTTATCACCCGCGAAGAAGCTATAAGACATGTACGTGCATGGATTGGCTTCGATGTCGAGGGGTGTCATGCTACTAGAGAAGCTGTTGGTACCAATTTACCTTTACAGCTAGGTTTTTCTACAGGTGTTAACCTAGTTGCTGTACCTACAGGTTATGTTGATACACCTAATAATACAGATTTTTCCAGAGTTAGTGCTAAACCACCGCCTGGAGATCAATTTAAACACCTCATACCACTTATGTACAAAGGACTTCCTTGGAATGTAGTGCGTATAAAGATTGTACAAATGTTAAGTGACACACTTAAAAATCTCTCTGACAGAGTCGTATTTGTCTTATGGGCACATGGCTTTGAGTTGACATCTATGAAGTATTTTGTGAAAATAGGACCTGAGCGCACCTGTTGTCTATGTGATAGACGTGCCACATGCTTTTCCACTGCTTCAGACACTTATGCCTGTTGGCATCATTCTATTGGATTTGATTACGTCTATAATCCGTTTATGATTGATGTTCAACAATGGGGTTTTACAGGTAACCTACAAAGCAACCATGATCTGTATTGTCAAGTCCATGGTAATGCACATGTAGCTAGTTGTGATGCAATCATGACTAGGTGTCTAGCTGTCCACGAGTGCTTTGTTAAGCGTGTTGACTGGACTATTGAATATCCTATAATTGGTGATGAACTGAAGATTAATGCGGCTTGTAGAAAGGTTCAACACATGGTTGTTAAAGCTGCATTATTAGCAGACAAATTCCCAGTTCTTCACGACATTGGTAACCCTAAAGCTATTAAGTGTGTACCTCAAGCTGATGTAGAATGGAAGTTCTATGATGCACAGCCTTGTAGTGACAAAGCTTATAAAATAGAAGAATTATTCTATTCTTATGCCACACATTCTGACAAATTCACAGATGGTGTATGCCTATTTTGGAATTGCAATGTCGATAGATATCCTGCTAATTCCATTGTTTGTAGATTTGACACTAGAGTGCTATCTAACCTTAACTTGCCTGGTTGTGATGGTGGCAGTTTGTATGTAAATAAACATGCATTCCACACACCAGCTTTTGATAAAAGTGCTTTTGTTAATTTAAAACAATTACCATTTTTCTATTACTCTGACAGTCCATGTGAGTCTCATGGAAAACAAGTAGTGTCAGATATAGATTATGTACCACTAAAGTCTGCTACGTGTATAACACGTTGCAATTTAGGTGGTGCTGTCTGTAGACATCATGCTAATGAGTACAGATTGTATCTCGATGCTTATAACATGATGATCTCAGCTGGCTTTAGCTTGTGGGTTTACAAACAATTTGATACTTATAACCTCTGGAACACTTTTACAAGACTTCAGAGTTTAGAAAATGTGGCTTTTAATGTTGTAAATAAGGGACACTTTGATGGACAACAGGGTGAAGTACCAGTTTCTATCATTAATAACACTGTTTACACAAAAGTTGATGGTGTTGATGTAGAATTGTTTGAAAATAAAACAACATTACCTGTTAATGTAGCATTTGAGCTTTGGGCTAAGCGCAACATTAAACCAGTACCAGAGGTGAAAATACTCAATAATTTGGGTGTGGACATTGCTGCTAATACTGTGATCTGGGACTACAAAAGAGATGCTCCAGCACATATATCTACTATTGGTGTTTGTTCTATGACTGACATAGCCAAGAAACCAACTGAAACGATTTGTGCACCACTCACTGTCTTTTTTGATGGTAGAGTTGATGGTCAAGTAGACTTATTTAGAAATGCCCGTAATGGTGTTCTTATTACAGAAGGTAGTGTTAAAGGTTTACAACCATCTGTAGGTCCCAAACAAGCTAGTCTTAATGGAGTCACATTAATTGGAGAAGCCGTAAAAACACAGTTCAATTATTATAAGAAAGTTGATGGTGTTGTCCAACAATTACCTGAAACTTACTTTACTCAGAGTAGAAATTTACAAGAATTTAAACCCAGGAGTCAAATGGAAATTGATTTCTTAGAATTAGCTATGGATGAATTCATTGAACGGTATAAATTAGAAGGCTATGCCTTCGAACATATCGTTTATGGAGATTTTAGTCATAGTCAGTTAGGTGGTTTACATCTACTGATTGGACTAGCTAAACGTTTTAAGGAATCACCTTTTGAATTAGAAGATTTTATTCCTATGGACAGTACAGTTAAAAACTATTTCATAACAGATGCGCAAACAGGTTCATCTAAGTGTGTGTGTTCTGTTATTGATTTATTACTTGATGATTTTGTTGAAATAATAAAATCCCAAGATTTATCTGTAGTTTCTAAGGTTGTCAAAGTGACTATTGACTATACAGAAATTTCATTTATGCTTTGGTGTAAAGATGGCCATGTAGAAACATTTTACCCAAAATTACAATCTAGTCAAGCGTGGCAACCGGGTGTTGCTATGCCTAATCTTTACAAAATGCAAAGAATGCTATTAGAAAAGTGTGACCTTCAAAATTATGGTGATAGTGCAACATTACCTAAAGGCATAATGATGAATGTCGCAAAATATACTCAACTGTGTCAATATTTAAACACATTAACATTAGCTGTACCCTATAATATGAGAGTTATACATTTTGGTGCTGGTTCTGATAAAGGAGTTGCACCAGGTACAGCTGTTTTAAGACAGTGGTTGCCTACGGGTACGCTGCTTGTCGATTCAGATCTTAATGACTTTGTCTCTGATGCAGATTCAACTTTGATTGGTGATTGTGCAACTGTACATACAGCTAATAAATGGGATCTCATTATTAGTGATATGTACGACCCTAAGACTAAAAATGTTACAAAAGAAAATGACTCTAAAGAGGGTTTTTTCACTTACATTTGTGGGTTTATACAACAAAAGCTAGCTCTTGGAGGTTCCGTGGCTATAAAGATAACAGAACATTCTTGGAATGCTGATCTTTATAAGCTCATGGGACACTTCGCATGGTGGACAGCCTTTGTTACTAATGTGAATGCGTCATCATCTGAAGCATTTTTAATTGGATGTAATTATCTTGGCAAACCACGCGAACAAATAGATGGTTATGTCATGCATGCAAATTACATATTTTGGAGGAATACAAATCCAATTCAGTTGTCTTCCTATTCTTTATTTGACATGAGTAAATTTCCCCTTAAATTAAGGGGTACTGCTGTTATGTCTTTAAAAGAAGGTCAAATCAATGATATGATTTTATCTCTTCTTAGTAAAGGTAGACTTATAATTAGAGAAAACAACAGAGTTGTTATTTCTAGTGATGTTCTTGTTAACAACTAAACGAACAATGTTTGTTTTTCTTGTTTTATTGCCACTAGTCTCTAGTCAGTGTGTTAATCTTACAACCAGAACTCAATTACCCCCTGCATACACTAATTCTTTCACACGTGGTGTTTATTACCCTGACAAAGTTTTCAGATCCTCAGTTTTACATTCAACTCAGGACTTGTTCTTACCTTTCTTTTCCAATGTTACTTGGTTCCATGCTATACATGTCTCTGGGACCAATGGTACTAAGAGGTTTGATAACCCTGTCCTACCATTTAATGATGGTGTTTATTTTGCTTCCACTGAGAAGTCTAACATAATAAGAGGCTGGATTTTTGGTACTACTTTAGATTCGAAGACCCAGTCCCTACTTATTGTTAATAACGCTACTAATGTTGTTATTAAAGTCTGTGAATTTCAATTTTGTAATGATCCATTTTTGGGTGTTTATTACCACAAAAACAACAAAAGTTGGATGGAAAGTGAGTTCAGAGTTTATTCTAGTGCGAATAATTGCACTTTTGAATATGTCTCTCAGCCTTTTCTTATGGACCTTGAAGGAAAACAGGGTAATTTCAAAAATCTTAGGGAATTTGTGTTTAAGAATATTGATGGTTATTTTAAAATATATTCTAAGCACACGCCTATTAATTTAGTGCGTGATCTCCCTCAGGGTTTTTCGGCTTTAGAACCATTGGTAGATTTGCCAATAGGTATTAACATCACTAGGTTTCAAACTTTACTTGCTTTACATAGAAGTTATTTGACTCCTGGTGATTCTTCTTCAGGTTGGACAGCTGGTGCTGCAGCTTATTATGTGGGTTATCTTCAACCTAGGACTTTTCTATTAAAATATAATGAAAATGGAACCATTACAGATGCTGTAGACTGTGCACTTGACCCTCTCTCAGAAACAAAGTGTACGTTGAAATCCTTCACTGTAGAAAAAGGAATCTATCAAACTTCTAACTTTAGAGTCCAACCAACAGAATCTATTGTTAGATTTCCTAATATTACAAACTTGTGCCCTTTTGGTGAAGTTTTTAACGCCACCAGATTTGCATCTGTTTATGCTTGGAACAGGAAGAGAATCAGCAACTGTGTTGCTGATTATTCTGTCCTATATAATTCCGCATCATTTTCCACTTTTAAGTGTTATGGAGTGTCTCCTACTAAATTAAATGATCTCTGCTTTACTAATGTCTATGCAGATTCATTTGTAATTAGAGGTGATGAAGTCAGACAAATCGCTCCAGGGCAAACTGGAAAGATTGCTGATTATAATTATAAATTACCAGATGATTTTACAGGCTGCGTTATAGCTTGGAATTCTAACAATCTTGATTCTAAGGTTGGTGGTAATTATAATTACCTGTATAGATTGTTTAGGAAGTCTAATCTCAAACCTTTTGAGAGAGATATTTCAACTGAAATCTATCAGGCCGGTAGCACACCTTGTAATGGTGTTGAAGGTTTTAATTGTTACTTTCCTTTACAATCATATGGTTTCCAACCCACTAATGGTGTTGGTTACCAACCATACAGAGTAGTAGTACTTTCTTTTGAACTTCTACATGCACCAGCAACTGTTTGTGGACCTAAAAAGTCTACTAATTTGGTTAAAAACAAATGTGTCAATTTCAACTTCAATGGTTTAACAGGCACAGGTGTTCTTACTGAGTCTAACAAAAAGTTTCTGCCTTTCCAACAATTTGGCAGAGACATTGCTGACACTACTGATGCTGTCCGTGATCCACAGACACTTGAGATTCTTGACATTACACCATGTTCTTTTGGTGGTGTCAGTGTTATAACACCAGGAACAAATACTTCTAACCAGGTTGCTGTTCTTTATCAGGATGTTAACTGCACAGAAGTCCCTGTTGCTATTCATGCAGATCAACTTACTCCTACTTGGCGTGTTTATTCTACAGGTTCTAATGTTTTTCAAACACGTGCAGGCTGTTTAATAGGGGCTGAACATGTCAACAACTCATATGAGTGTGACATACCCATTGGTGCAGGTATATGCGCTAGTTATCAGACTCAGACTAATTCTCCTCGGCGGGCACGTAGTGTAGCTAGTCAATCCATCATTGCCTACACTATGTCACTTGGTGCAGAAAATTCAGTTGCTTACTCTAATAACTCTATTGCCATACCCACAAATTTTACTATTAGTGTTACCACAGAAATTCTACCAGTGTCTATGACCAAGACATCAGTAGATTGTACAATGTACATTTGTGGTGATTCAACTGAATGCAGCAATCTTTTGTTGCAATATGGCAGTTTTTGTACACAATTAAACCGTGCTTTAACTGGAATAGCTGTTGAACAAGACAAAAACACCCAAGAAGTTTTTGCACAAGTCAAACAAATTTACAAAACACCACCAATTAAAGATTTTGGTGGTTTTAATTTTTCACAAATATTACCAGATCCATCAAAACCAAGCAAGAGGTCATTTATTGAAGATCTACTTTTCAACAAAGTGACACTTGCAGATGCTGGCTTCATCAAACAATATGGTGATTGCCTTGGTGATATTGCTGCTAGAGACCTCATTTGTGCACAAAAGTTTAACGGCCTTACTGTTTTGCCACCTTTGCTCACAGATGAAATGATTGCTCAATACACTTCTGCACTGTTAGCGGGTACAATCACTTCTGGTTGGACCTTTGGTGCAGGTGCTGCATTACAAATACCATTTGCTATGCAAATGGCTTATAGGTTTAATGGTATTGGAGTTACACAGAATGTTCTCTATGAGAACCAAAAATTGATTGCCAACCAATTTAATAGTGCTATTGGCAAAATTCAAGACTCACTTTCTTCCACAGCAAGTGCACTTGGAAAACTTCAAGATGTGGTCAACCAAAATGCACAAGCTTTAAACACGCTTGTTAAACAACTTAGCTCCAATTTTGGTGCAATTTCAAGTGTTTTAAATGATATCCTTTCACGTCTTGACAAAGTTGAGGCTGAAGTGCAAATTGATAGGTTGATCACAGGCAGACTTCAAAGTTTGCAGACATATGTGACTCAACAATTAATTAGAGCTGCAGAAATCAGAGCTTCTGCTAATCTTGCTGCTACTAAAATGTCAGAGTGTGTACTTGGACAATCAAAAAGAGTTGATTTTTGTGGAAAGGGCTATCATCTTATGTCCTTCCCTCAGTCAGCACCTCATGGTGTAGTCTTCTTGCATGTGACTTATGTCCCTGCACAAGAAAAGAACTTCACAACTGCTCCTGCCATTTGTCATGATGGAAAAGCACACTTTCCTCGTGAAGGTGTCTTTGTTTCAAATGGCACACACTGGTTTGTAACACAAAGGAATTTTTATGAACCACAAATCATTACTACAGACAACACATTTGTGTCTGGTAACTGTGATGTTGTAATAGGAATTGTCAACAACACAGTTTATGATCCTTTGCAACCTGAATTAGACTCATTCAAGGAGGAGTTAGATAAATATTTTAAGAATCATACATCACCAGATGTTGATTTAGGTGACATCTCTGGCATTAATGCTTCAGTTGTAAACATTCAAAAAGAAATTGACCGCCTCAATGAGGTTGCCAAGAATTTAAATGAATCTCTCATCGATCTCCAAGAACTTGGAAAGTATGAGCAGTATATAAAATGGCCATGGTACATTTGGCTAGGTTTTATAGCTGGCTTGATTGCCATAGTAATGGTGACAATTATGCTTTGCTGTATGACCAGTTGCTGTAGTTGTCTCAAGGGCTGTTGTTCTTGTGGATCCTGCTGCAAATTTGATGAAGACGACTCTGAGCCAGTGCTCAAAGGAGTCAAATTACATTACACATAAACGAACTTATGGATTTGTTTATGAGAATCTTCACAATTGGAACTGTAACTTTGAAGCAAGGTGAAATCAAGGATGCTACTCCTTCAGATTTTGTTCGCGCTACTGCAACGATACCGATACAAGCCTCACTCCCTTTCGGATGGCTTATTGTTGGCGTTGCACTTCTTGCTGTTTTTCAGAGCGCTTCCAAAATCATAACCCTCAAAAAGAGATGGCAACTAGCACTCTCCAAGGGTGTTCACTTTGTTTGCAACTTGCTGTTGTTGTTTGTAACAGTTTACTCACACCTTTTGCTCGTTGCTGCTGGCCTTGAAGCCCCTTTTCTCTATCTTTATGCTTTAGTCTACTTCTTGCAGAGTATAAACTTTGTAAGAATAATAATGAGGCTTTGGCTTTGCTGGAAATGCCGTTCCAAAAACCCATTACTTTATGATGCCAACTATTTTCTTTGCTGGCATACTAATTGTTACGACTATTGTATACCTTACAATAGTGTAACTTCTTCAATTGTCATTACTTCAGGTGATGGCACAACAAGTCCTATTTCTGAACATGACTACCAGATTGGTGGTTATACTGAAAAATGGGAATCTGGAGTAAAAGACTGTGTTGTATTACACAGTTACTTCACTTCAGACTATTACCAGCTGTACTCAACTCAATTGAGTACAGACACTGGTGTTGAACATGTTACCTTCTTCATCTACAATAAAATTGTTGATGAGCCTGAAGAACATGTCCAAATTCACACAATCGACGGTTCATCCGGAGTTGTTAATCCAGTAATGGAACCAATTTATGATGAACCGACGACGACTACTAGCGTGCCTTTGTAAGCACAAGCTGATGAGTACGAACTTATGTACTCATTCGTTTCGGAAGAGACAGGTACGTTAATAGTTAATAGCGTACTTCTTTTTCTTGCTTTCGTGGTATTCTTGCTAGTTACACTAGCCATCCTTACTGCGCTTCGATTGTGTGCGTACTGCTGCAATATTGTTAACGTGAGTCTTGTAAAACCTTCTTTTTACGTTTACTCTCGTGTTAAAAATCTGAATTCTTCTAGAGTTCCTGATCTTCTGGTCTAAACGAACTAAATATTATATTAGTTTTTCTGTTTGGAACTTTAATTTTAGCCATGGCAGATTCCAACGGTACTATTACCGTTGAAGAGCTTAAAAAGCTCCTTGAACAATGGAACCTAGTAATAGGTTTCCTATTCCTTACATGGATTTGTCTTCTACAATTTGCCTATGCCAACAGGAATAGGTTTTTGTATATAATTAAGTTAATTTTCCTCTGGCTGTTATGGCCAGTAACTTTAGCTTGTTTTGTGCTTGCTGCTGTTTACAGAATAAATTGGATCACCGGTGGAATTGCTATCGCAATGGCTTGTCTTGTAGGCTTGATGTGGCTCAGCTACTTCATTGCTTCTTTCAGACTGTTTGCGCGTACGCGTTCCATGTGGTCATTCAATCCAGAAACTAACATTCTTCTCAACGTGCCACTCCATGGCACTATTCTGACCAGACCGCTTCTAGAAAGTGAACTCGTAATCGGAGCTGTGATCCTTCGTGGACATCTTCGTATTGCTGGACACCATCTAGGACGCTGTGACATCAAGGACCTGCCTAAAGAAATCACTGTTGCTACATCACGAACGCTTTCTTATTACAAATTGGGAGCTTCGCAGCGTGTAGCAGGTGACTCAGGTTTTGCTGCATACAGTCGCTACAGGATTGGCAACTATAAATTAAACACAGACCATTCCAGTAGCAGTGACAATATTGCTTTGCTTGTACAGTAAGTGACAACAGATGTTTCATCTCGTTGACTTTCAGGTTACTATAGCAGAGATATTACTAATTATTATGAGGACTTTTAAAGTTTCCATTTGGAATCTTGATTACATCATAAACCTCATAATTAAAAATTTATCTAAGTCACTAACTGAGAATAAATATTCTCAATTAGATGAAGAGCAACCAATGGAGATTGATTAAACGAACATGAAAATTATTCTTTTCTTGGCACTGATAACACTCGCTACTTGTGAGCTTTATCACTACCAAGAGTGTGTTAGAGGTACAACAGTACTTTTAAAAGAACCTTGCTCTTCTGGAACATACGAGGGCAATTCACCATTTCATCCTCTAGCTGATAACAAATTTGCACTGACTTGCTTTAGCACTCAATTTGCTTTTGCTTGTCCTGACGGCGTAAAACACGTCTATCAGTTACGTGCCAGATCAGTTTCACCTAAACTGTTCATCAGACAAGAGGAAGTTCAAGAACTTTACTCTCCAATTTTTCTTATTGTTGCGGCAATAGTGTTTATAACACTTTGCTTCACACTCAAAAGAAAGACAGAATGATTGAACTTTCATTAATTGACTTCTATTTGTGCTTTTTAGCCTTTCTGCTATTCCTTGTTTTAATTATGCTTATTATCTTTTGGTTCTCACTTGAACTGCAAGATCATAATGAAACTTGTCACGCCTAAACGAACATGAAATTTCTTGTTTTCTTAGGAATCATCACAACTGTAGCTGCATTTCACCAAGAATGTAGTTTACAGTCATGTACTCAACATCAACCATATGTAGTTGATGACCCGTGTCCTATTCACTTCTATTCTAAATGGTATATTAGAGTAGGAGCTAGAAAATCAGCACCTTTAATTGAATTGTGCGTGGATGAGGCTGGTTCTAAATCACCCATTCAGTACATCGATATCGGTAATTATACAGTTTCCTGTTTACCTTTTACAATTAATTGCCAGGAACCTAAATTGGGTAGTCTTGTAGTGCGTTGTTCGTTCTATGAAGACTTTTTAGAGTATCATGACGTTCGTGTTGTTTTAGATTTCATCTAAACGAACAAACTAAAATGTCTGATAATGGACCCCAAAATCAGCGAAATGCACCCCGCATTACGTTTGGTGGACCCTCAGATTCAACTGGCAGTAACCAGAATGGAGAACGCAGTGGGGCGCGATCAAAACAACGTCGGCCCCAAGGTTTACCCAATAATACTGCGTCTTGGTTCACCGCTCTCACTCAACATGGCAAGGAAGACCTTAAATTCCCTCGAGGACAAGGCGTTCCAATTAACACCAATAGCAGTCCAGATGACCAAATTGGCTACTACCGAAGAGCTACCAGACGAATTCGTGGTGGTGACGGTAAAATGAAAGATCTCAGTCCAAGATGGTATTTCTACTACCTAGGAACTGGGCCAGAAGCTGGACTTCCCTATGGTGCTAACAAAGACGGCATCATATGGGTTGCAACTGAGGGAGCCTTGAATACACCAAAAGATCACATTGGCACCCGCAATCCTGCTAACAATGCTGCAATCGTGCTACAACTTCCTCAAGGAACAACATTGCCAAAAGGCTTCTACGCAGAAGGGAGCAGAGGCGGCAGTCAAGCCTCTTCTCGTTCCTCATCACGTAGTCGCAACAGTTCAAGAAATTCAACTCCAGGCAGCAGTAGGGGAACTTCTCCTGCTAGAATGGCTGGCAATGGCGGTGATGCTGCTCTTGCTTTGCTGCTGCTTGACAGATTGAACCAGCTTGAGAGCAAAATGTCTGGTAAAGGCCAACAACAACAAGGCCAAACTGTCACTAAGAAATCTGCTGCTGAGGCTTCTAAGAAGCCTCGGCAAAAACGTACTGCCACTAAAGCATACAATGTAACACAAGCTTTCGGCAGACGTGGTCCAGAACAAACCCAAGGAAATTTTGGGGACCAGGAACTAATCAGACAAGGAACTGATTACAAACATTGGCCGCAAATTGCACAATTTGCCCCCAGCGCTTCAGCGTTCTTCGGAATGTCGCGCATTGGCATGGAAGTCACACCTTCGGGAACGTGGTTGACCTACACAGGTGCCATCAAATTGGATGACAAAGATCCAAATTTCAAAGATCAAGTCATTTTGCTGAATAAGCATATTGACGCATACAAAACATTCCCACCAACAGAGCCTAAAAAGGACAAAAAGAAGAAGGCTGATGAAACTCAAGCCTTACCGCAGAGACAGAAGAAACAGCAAACTGTGACTCTTCTTCCTGCTGCAGATTTGGATGATTTCTCCAAACAATTGCAACAATCCATGAGCAGTGCTGACTCAACTCAGGCCTAAACTCATGCAGACCACACAAGGCAGATGGGCTATATAAACGTTTTCGCTTTTCCGTTTACGATATATAGTCTACTCTTGTGCAGAATGAATTCTCGTAACTACATAGCACAAGTAGATGTAGTTAACTTTAATCTCACATAGCAATCTTTAATCAGTGTGTAACATTAGGGAGGACTTGAAAGAGCCACCACATTTTCACCGAGGCCACGCGGAGTACGATCGAGTGTACAGTGAACAATGCTAGGGAGAGCTGCCTATATGGAAGAGCCCTAATGTGTAAAATTAATTTTAGTAGTGCTATCCCCATGTGATTTTAATAGCTTCTTAGGAGAATGACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + genes: + - name: E + sequence: 'MYSFVSEETGTLIVNSVLLFLAFVVFLLVTLAILTALRLCAYCCNIVNVSLVKPSFYVYSRVKNLNSSRVPDLLV*' + - name: M + sequence: 'MADSNGTITVEELKKLLEQWNLVIGFLFLTWICLLQFAYANRNRFLYIIKLIFLWLLWPVTLACFVLAAVYRINWITGGIAIAMACLVGLMWLSYFIASFRLFARTRSMWSFNPETNILLNVPLHGTILTRPLLESELVIGAVILRGHLRIAGHHLGRCDIKDLPKEITVATSRTLSYYKLGASQRVAGDSGFAAYSRYRIGNYKLNTDHSSSSDNIALLVQ*' + - name: N + sequence: 'MSDNGPQNQRNAPRITFGGPSDSTGSNQNGERSGARSKQRRPQGLPNNTASWFTALTQHGKEDLKFPRGQGVPINTNSSPDDQIGYYRRATRRIRGGDGKMKDLSPRWYFYYLGTGPEAGLPYGANKDGIIWVATEGALNTPKDHIGTRNPANNAAIVLQLPQGTTLPKGFYAEGSRGGSQASSRSSSRSRNSSRNSTPGSSRGTSPARMAGNGGDAALALLLLDRLNQLESKMSGKGQQQQGQTVTKKSAAEASKKPRQKRTATKAYNVTQAFGRRGPEQTQGNFGDQELIRQGTDYKHWPQIAQFAPSASAFFGMSRIGMEVTPSGTWLTYTGAIKLDDKDPNFKDQVILLNKHIDAYKTFPPTEPKKDKKKKADETQALPQRQKKQQTVTLLPAADLDDFSKQLQQSMSSADSTQA*' + - name: ORF1a + sequence: 'MESLVPGFNEKTHVQLSLPVLQVRDVLVRGFGDSVEEVLSEARQHLKDGTCGLVEVEKGVLPQLEQPYVFIKRSDARTAPHGHVMVELVAELEGIQYGRSGETLGVLVPHVGEIPVAYRKVLLRKNGNKGAGGHSYGADLKSFDLGDELGTDPYEDFQENWNTKHSSGVTRELMRELNGGAYTRYVDNNFCGPDGYPLECIKDLLARAGKASCTLSEQLDFIDTKRGVYCCREHEHEIAWYTERSEKSYELQTPFEIKLAKKFDTFNGECPNFVFPLNSIIKTIQPRVEKKKLDGFMGRIRSVYPVASPNECNQMCLSTLMKCDHCGETSWQTGDFVKATCEFCGTENLTKEGATTCGYLPQNAVVKIYCPACHNSEVGPEHSLAEYHNESGLKTILRKGGRTIAFGGCVFSYVGCHNKCAYWVPRASANIGCNHTGVVGEGSEGLNDNLLEILQKEKVNINIVGDFKLNEEIAIILASFSASTSAFVETVKGLDYKAFKQIVESCGNFKVTKGKAKKGAWNIGEQKSILSPLYAFASEAARVVRSIFSRTLETAQNSVRVLQKAAITILDGISQYSLRLIDAMMFTSDLATNNLVVMAYITGGVVQLTSQWLTNIFGTVYEKLKPVLDWLEEKFKEGVEFLRDGWEIVKFISTCACEIVGGQIVTCAKEIKESVQTFFKLVNKFLALCADSIIIGGAKLKALNLGETFVTHSKGLYRKCVKSREETGLLMPLKAPKEIIFLEGETLPTEVLTEEVVLKTGDLQPLEQPTSEAVEAPLVGTPVCINGLMLLEIKDTEKYCALAPNMMVTNNTFTLKGGAPTKVTFGDDTVIEVQGYKSVNITFELDERIDKVLNEKCSAYTVELGTEVNEFACVVADAVIKTLQPVSELLTPLGIDLDEWSMATYYLFDESGEFKLASHMYCSFYPPDEDEEEGDCEEEEFEPSTQYEYGTEDDYQGKPLEFGATSAALQPEEEQEEDWLDDDSQQTVGQQDGSEDNQTTTIQTIVEVQPQLEMELTPVVQTIEVNSFSGYLKLTDNVYIKNADIVEEAKKVKPTVVVNAANVYLKHGGGVAGALNKATNNAMQVESDDYIATNGPLKVGGSCVLSGHNLAKHCLHVVGPNVNKGEDIQLLKSAYENFNQHEVLLAPLLSAGIFGADPIHSLRVCVDTVRTNVYLAVFDKNLYDKLVSSFLEMKSEKQVEQKIAEIPKEEVKPFITESKPSVEQRKQDDKKIKACVEEVTTTLEETKFLTENLLLYIDINGNLHPDSATLVSDIDITFLKKDAPYIVGDVVQEGVLTAVVIPTKKAGGTTEMLAKALRKVPTDNYITTYPGQGLNGYTVEEAKTVLKKCKSAFYILPSIISNEKQEILGTVSWNLREMLAHAEETRKLMPVCVETKAIVSTIQRKYKGIKIQEGVVDYGARFYFYTSKTTVASLINTLNDLNETLVTMPLGYVTHGLNLEEAARYMRSLKVPATVSVSSPDAVTAYNGYLTSSSKTPEEHFIETISLAGSYKDWSYSGQSTQLGIEFLKRGDKSVYYTSNPTTFHLDGEVITFDNLKTLLSLREVRTIKVFTTVDNINLHTQVVDMSMTYGQQFGPTYLDGADVTKIKPHNSHEGKTFYVLPNDDTLRVEAFEYYHTTDPSFLGRYMSALNHTKKWKYPQVNGLTSIKWADNNCYLATALLTLQQIELKFNPPALQDAYYRARAGEAANFCALILAYCNKTVGELGDVRETMSYLFQHANLDSCKRVLNVVCKTCGQQQTTLKGVEAVMYMGTLSYEQFKKGVQIPCTCGKQATKYLVQQESPFVMMSAPPAQYELKHGTFTCASEYTGNYQCGHYKHITSKETLYCIDGALLTKSSEYKGPITDVFYKENSYTTTIKPVTYKLDGVVCTEIDPKLDNYYKKDNSYFTEQPIDLVPNQPYPNASFDNFKFVCDNIKFADDLNQLTGYKKPASRELKVTFFPDLNGDVVAIDYKHYTPSFKKGAKLLHKPIVWHVNNATNKATYKPNTWCIRCLWSTKPVETSNSFDVLKSEDAQGMDNLACEDLKPVSEEVVENPTIQKDVLECNVKTTEVVGDIILKPANNSLKITEEVGHTDLMAAYVDNSSLTIKKPNELSRVLGLKTLATHGLAAVNSVPWDTIANYAKPFLNKVVSTTTNIVTRCLNRVCTNYMPYFFTLLLQLCTFTRSTNSRIKASMPTTIAKNTVKSVGKFCLEASFNYLKSPNFSKLINIIIWFLLLSVCLGSLIYSTAALGVLMSNLGMPSYCTGYREGYLNSTNVTIATYCTGSIPCSVCLSGLDSLDTYPSLETIQITISSFKWDLTAFGLVAEWFLAYILFTRFFYVLGLAAIMQLFFSYFAVHFISNSWLMWLIINLVQMAPISAMVRMYIFFASFYYVWKSYVHVVDGCNSSTCMMCYKRNRATRVECTTIVNGVRRSFYVYANGGKGFCKLHNWNCVNCDTFCAGSTFISDEVARDLSLQFKRPINPTDQSSYIVDSVTVKNGSIHLYFDKAGQKTYERHSLSHFVNLDNLRANNTKGSLPINVIVFDGKSKCEESSAKSASVYYSQLMCQPILLLDQALVSDVGDSAEVAVKMFDAYVNTFSSTFNVPMEKLKTLVATAEAELAKNVSLDNVLSTFISAARQGFVDSDVETKDVVECLKLSHQSDIEVTGDSCNNYMLTYNKVENMTPRDLGACIDCSARHINAQVAKSHNIALIWNVKDFMSLSEQLRKQIRSAAKKNNLPFKLTCATTRQVVNVVTTKIALKGGKIVNNWLKQLIKVTLVFLFVAAIFYLITPVHVMSKHTDFSSEIIGYKAIDGGVTRDIASTDTCFANKHADFDTWFSQRGGSYTNDKACPLIAAVITREVGFVVPGLPGTILRTTNGDFLHFLPRVFSAVGNICYTPSKLIEYTDFATSACVLAAECTIFKDASGKPVPYCYDTNVLEGSVAYESLRPDTRYVLMDGSIIQFPNTYLEGSVRVVTTFDSEYCRHGTCERSEAGVCVSTSGRWVLNNDYYRSLPGVFCGVDAVNLLTNMFTPLIQPIGALDISASIVAGGIVAIVVTCLAYYFMRFRRAFGEYSHVVAFNTLLFLMSFTVLCLTPVYSFLPGVYSVIYLYLTFYLTNDVSFLAHIQWMVMFTPLVPFWITIAYIICISTKHFYWFFSNYLKRRVVFNGVSFSTFEEAALCTFLLNKEMYLKLRSDVLLPLTQYNRYLALYNKYKYFSGAMDTTSYREAACCHLAKALNDFSNSGSDVLYQPPQTSITSAVLQSGFRKMAFPSGKVEGCMVQVTCGTTTLNGLWLDDVVYCPRHVICTSEDMLNPNYEDLLIRKSNHNFLVQAGNVQLRVIGHSMQNCVLKLKVDTANPKTPKYKFVRIQPGQTFSVLACYNGSPSGVYQCAMRPNFTIKGSFLNGSCGSVGFNIDYDCVSFCYMHHMELPTGVHAGTDLEGNFYGPFVDRQTAQAAGTDTTITVNVLAWLYAAVINGDRWFLNRFTTTLNDFNLVAMKYNYEPLTQDHVDILGPLSAQTGIAVLDMCASLKELLQNGMNGRTILGSALLEDEFTPFDVVRQCSGVTFQSAVKRTIKGTHHWLLLTILTSLLVLVQSTQWSLFFFLYENAFLPFAMGIIAMSAFAMMFVKHKHAFLCLFLLPSLATVAYFNMVYMPASWVMRIMTWLDMVDTSLSGFKLKDCVMYASAVVLLILMTARTVYDDGARRVWTLMNVLTLVYKVYYGNALDQAISMWALIISVTSNYSGVVTTVMFLARGIVFMCVEYCPIFFITGNTLQCIMLVYCFLGYFCTCYFGLFCLLNRYFRLTLGVYDYLVSTQEFRYMNSQGLLPPKNSIDAFKLNIKLLGVGGKPCIKVATVQSKMSDVKCTSVVLLSVLQQLRVESSSKLWAQCVQLHNDILLAKDTTEAFEKMVSLLSVLLSMQGAVDINKLCEEMLDNRATLQAIASEFSSLPSYAAFATAQEAYEQAVANGDSEVVLKKLKKSLNVAKSEFDRDAAMQRKLEKMADQAMTQMYKQARSEDKRAKVTSAMQTMLFTMLRKLDNDALNNIINNARDGCVPLNIIPLTTAAKLMVVIPDYNTYKNTCDGTTFTYASALWEIQQVVDADSKIVQLSEISMDNSPNLAWPLIVTALRANSAVKLQNNELSPVALRQMSCAAGTTQTACTDDNALAYYNTTKGGRFVLALLSDLQDLKWARFPKSDGTGTIYTELEPPCRFVTDTPKGPKVKYLYFIKGLNNLNRGMVLGSLAATVRLQAGNATEVPANSTVLSFCAFAVDAAKAYKDYLASGGQPITNCVKMLCTHTGTGQAITVTPEANMDQESFGGASCCLYCRCHIDHPNPKGFCDLKGKYVQIPTTCANDPVGFTLKNTVCTVCGMWKGYGCSCDQLREPMLQSADAQSFLN' + - name: ORF1b + sequence: 'RVCGVSAARLTPCGTGTSTDVVYRAFDIYNDKVAGFAKFLKTNCCRFQEKDEDDNLIDSYFVVKRHTFSNYQHEETIYNLLKDCPAVAKHDFFKFRIDGDMVPHISRQRLTKYTMADLVYALRHFDEGNCDTLKEILVTYNCCDDDYFNKKDWYDFVENPDILRVYANLGERVRQALLKTVQFCDAMRNAGIVGVLTLDNQDLNGNWYDFGDFIQTTPGSGVPVVDSYYSLLMPILTLTRALTAESHVDTDLTKPYIKWDLLKYDFTEERLKLFDRYFKYWDQTYHPNCVNCLDDRCILHCANFNVLFSTVFPPTSFGPLVRKIFVDGVPFVVSTGYHFRELGVVHNQDVNLHSSRLSFKELLVYAADPAMHAASGNLLLDKRTTCFSVAALTNNVAFQTVKPGNFNKDFYDFAVSKGFFKEGSSVELKHFFFAQDGNAAISDYDYYRYNLPTMCDIRQLLFVVEVVDKYFDCYDGGCINANQVIVNNLDKSAGFPFNKWGKARLYYDSMSYEDQDALFAYTKRNVIPTITQMNLKYAISAKNRARTVAGVSICSTMTNRQFHQKLLKSIAATRGATVVIGTSKFYGGWHNMLKTVYSDVENPHLMGWDYPKCDRAMPNMLRIMASLVLARKHTTCCSLSHRFYRLANECAQVLSEMVMCGGSLYVKPGGTSSGDATTAYANSVFNICQAVTANVNALLSTDGNKIADKYVRNLQHRLYECLYRNRDVDTDFVNEFYAYLRKHFSMMILSDDAVVCFNSTYASQGLVASIKNFKSVLYYQNNVFMSEAKCWTETDLTKGPHEFCSQHTMLVKQGDDYVYLPYPDPSRILGAGCFVDDIVKTDGTLMIERFVSLAIDAYPLTKHPNQEYADVFHLYLQYIRKLHDELTGHMLDMYSVMLTNDNTSRYWEPEFYEAMYTPHTVLQAVGACVLCNSQTSLRCGACIRRPFLCCKCCYDHVISTSHKLVLSVNPYVCNAPGCDVTDVTQLYLGGMSYYCKSHKPPISFPLCANGQVFGLYKNTCVGSDNVTDFNAIATCDWTNAGDYILANTCTERLKLFAAETLKATEETFKLSYGIATVREVLSDRELHLSWEVGKPRPPLNRNYVFTGYRVTKNSKVQIGEYTFEKGDYGDAVVYRGTTTYKLNVGDYFVLTSHTVMPLSAPTLVPQEHYVRITGLYPTLNISDEFSSNVANYQKVGMQKYSTLQGPPGTGKSHFAIGLALYYPSARIVYTACSHAAVDALCEKALKYLPIDKCSRIIPARARVECFDKFKVNSTLEQYVFCTVNALPETTADIVVFDEISMATNYDLSVVNARLRAKHYVYIGDPAQLPAPRTLLTKGTLEPEYFNSVCRLMKTIGPDMFLGTCRRCPAEIVDTVSALVYDNKLKAHKDKSAQCFKMFYKGVITHDVSSAINRPQIGVVREFLTRNPAWRKAVFISPYNSQNAVASKILGLPTQTVDSSQGSEYDYVIFTQTTETAHSCNVNRFNVAITRAKVGILCIMSDRDLYDKLQFTSLEIPRRNVATLQAENVTGLFKDCSKVITGLHPTQAPTHLSVDTKFKTEGLCVDIPGIPKDMTYRRLISMMGFKMNYQVNGYPNMFITREEAIRHVRAWIGFDVEGCHATREAVGTNLPLQLGFSTGVNLVAVPTGYVDTPNNTDFSRVSAKPPPGDQFKHLIPLMYKGLPWNVVRIKIVQMLSDTLKNLSDRVVFVLWAHGFELTSMKYFVKIGPERTCCLCDRRATCFSTASDTYACWHHSIGFDYVYNPFMIDVQQWGFTGNLQSNHDLYCQVHGNAHVASCDAIMTRCLAVHECFVKRVDWTIEYPIIGDELKINAACRKVQHMVVKAALLADKFPVLHDIGNPKAIKCVPQADVEWKFYDAQPCSDKAYKIEELFYSYATHSDKFTDGVCLFWNCNVDRYPANSIVCRFDTRVLSNLNLPGCDGGSLYVNKHAFHTPAFDKSAFVNLKQLPFFYYSDSPCESHGKQVVSDIDYVPLKSATCITRCNLGGAVCRHHANEYRLYLDAYNMMISAGFSLWVYKQFDTYNLWNTFTRLQSLENVAFNVVNKGHFDGQQGEVPVSIINNTVYTKVDGVDVELFENKTTLPVNVAFELWAKRNIKPVPEVKILNNLGVDIAANTVIWDYKRDAPAHISTIGVCSMTDIAKKPTETICAPLTVFFDGRVDGQVDLFRNARNGVLITEGSVKGLQPSVGPKQASLNGVTLIGEAVKTQFNYYKKVDGVVQQLPETYFTQSRNLQEFKPRSQMEIDFLELAMDEFIERYKLEGYAFEHIVYGDFSHSQLGGLHLLIGLAKRFKESPFELEDFIPMDSTVKNYFITDAQTGSSKCVCSVIDLLLDDFVEIIKSQDLSVVSKVVKVTIDYTEISFMLWCKDGHVETFYPKLQSSQAWQPGVAMPNLYKMQRMLLEKCDLQNYGDSATLPKGIMMNVAKYTQLCQYLNTLTLAVPYNMRVIHFGAGSDKGVAPGTAVLRQWLPTGTLLVDSDLNDFVSDADSTLIGDCATVHTANKWDLIISDMYDPKTKNVTKENDSKEGFFTYICGFIQQKLALGGSVAIKITEHSWNADLYKLMGHFAWWTAFVTNVNASSSEAFLIGCNYLGKPREQIDGYVMHANYIFWRNTNPIQLSSYSLFDMSKFPLKLRGTAVMSLKEGQINDMILSLLSKGRLIIRENNRVVISSDVLVNN*' + - name: ORF3a + sequence: 'MDLFMRIFTIGTVTLKQGEIKDATPSDFVRATATIPIQASLPFGWLIVGVALLAVFQSASKIITLKKRWQLALSKGVHFVCNLLLLFVTVYSHLLLVAAGLEAPFLYLYALVYFLQSINFVRIIMRLWLCWKCRSKNPLLYDANYFLCWHTNCYDYCIPYNSVTSSIVITSGDGTTSPISEHDYQIGGYTEKWESGVKDCVVLHSYFTSDYYQLYSTQLSTDTGVEHVTFFIYNKIVDEPEEHVQIHTIDGSSGVVNPVMEPIYDEPTTTTSVPL*' + - name: ORF6 + sequence: 'MFHLVDFQVTIAEILLIIMRTFKVSIWNLDYIINLIIKNLSKSLTENKYSQLDEEQPMEID*' + - name: ORF7a + sequence: 'MKIILFLALITLATCELYHYQECVRGTTVLLKEPCSSGTYEGNSPFHPLADNKFALTCFSTQFAFACPDGVKHVYQLRARSVSPKLFIRQEEVQELYSPIFLIVAAIVFITLCFTLKRKTE*' + - name: ORF7b + sequence: 'MIELSLIDFYLCFLAFLLFLVLIMLIIFWFSLELQDHNETCHA*' + - name: ORF8 + sequence: 'MKFLVFLGIITTVAAFHQECSLQSCTQHQPYVVDDPCPIHFYSKWYIRVGARKSAPLIELCVDEAGSKSPIQYIDIGNYTVSCLPFTINCQEPKLGSLVVRCSFYEDFLEYHDVRVVLDFI*' + - name: ORF9b + sequence: 'MDPKISEMHPALRLVDPQIQLAVTRMENAVGRDQNNVGPKVYPIILRLGSPLSLNMARKTLNSLEDKAFQLTPIAVQMTKLATTEELPDEFVVVTVK*' + - name: S + sequence: 'MFVFLVLLPLVSSQCVNLTTRTQLPPAYTNSFTRGVYYPDKVFRSSVLHSTQDLFLPFFSNVTWFHAIHVSGTNGTKRFDNPVLPFNDGVYFASTEKSNIIRGWIFGTTLDSKTQSLLIVNNATNVVIKVCEFQFCNDPFLGVYYHKNNKSWMESEFRVYSSANNCTFEYVSQPFLMDLEGKQGNFKNLREFVFKNIDGYFKIYSKHTPINLVRDLPQGFSALEPLVDLPIGINITRFQTLLALHRSYLTPGDSSSGWTAGAAAYYVGYLQPRTFLLKYNENGTITDAVDCALDPLSETKCTLKSFTVEKGIYQTSNFRVQPTESIVRFPNITNLCPFGEVFNATRFASVYAWNRKRISNCVADYSVLYNSASFSTFKCYGVSPTKLNDLCFTNVYADSFVIRGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAWNSNNLDSKVGGNYNYLYRLFRKSNLKPFERDISTEIYQAGSTPCNGVEGFNCYFPLQSYGFQPTNGVGYQPYRVVVLSFELLHAPATVCGPKKSTNLVKNKCVNFNFNGLTGTGVLTESNKKFLPFQQFGRDIADTTDAVRDPQTLEILDITPCSFGGVSVITPGTNTSNQVAVLYQDVNCTEVPVAIHADQLTPTWRVYSTGSNVFQTRAGCLIGAEHVNNSYECDIPIGAGICASYQTQTNSPRRARSVASQSIIAYTMSLGAENSVAYSNNSIAIPTNFTISVTTEILPVSMTKTSVDCTMYICGDSTECSNLLLQYGSFCTQLNRALTGIAVEQDKNTQEVFAQVKQIYKTPPIKDFGGFNFSQILPDPSKPSKRSFIEDLLFNKVTLADAGFIKQYGDCLGDIAARDLICAQKFNGLTVLPPLLTDEMIAQYTSALLAGTITSGWTFGAGAALQIPFAMQMAYRFNGIGVTQNVLYENQKLIANQFNSAIGKIQDSLSSTASALGKLQDVVNQNAQALNTLVKQLSSNFGAISSVLNDILSRLDKVEAEVQIDRLITGRLQSLQTYVTQQLIRAAEIRASANLAATKMSECVLGQSKRVDFCGKGYHLMSFPQSAPHGVVFLHVTYVPAQEKNFTTAPAICHDGKAHFPREGVFVSNGTHWFVTQRNFYEPQIITTDNTFVSGNCDVVIGIVNNTVYDPLQPELDSFKEELDKYFKNHTSPDVDLGDISGINASVVNIQKEIDRLNEVAKNLNESLIDLQELGKYEQYIKWPWYIWLGFIAGLIAIVMVTIMLCCMTSCCSCLKGCCSCGSCCKFDEDDSEPVLKGVKLHYT*' +displayName: Test Dummy Organism + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-dummy-organism:8080" diff --git a/kubernetes/loculus/fixtures/organisms/ebola-sudan.yaml b/kubernetes/loculus/fixtures/organisms/ebola-sudan.yaml new file mode 100644 index 0000000000..ab8be7eaaf --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/ebola-sudan.yaml @@ -0,0 +1,1561 @@ +schema: + submissionDataTypes: + consensusSequences: true + maxSequencesPerEntry: 1 + loadSequencesAutomatically: true + tableColumns: + - sampleCollectionDate + - ncbiReleaseDate + - authors + - authorAffiliations + - geoLocCountry + - geoLocAdmin1 + - length + defaultOrderBy: sampleCollectionDate + defaultOrder: descending + earliestReleaseDate: + enabled: true + externalFields: + - ncbiReleaseDate + richFastaHeaderFields: + - displayName + files: + - name: annotations + displayName: Annotations + metadata: + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + header: Sample details + ingest: ncbiCollectionDate + order: 10 + orderOnDetailsPage: 200 + includeInDownloadsByDefault: true + notSearchable: true + columnWidth: 100 + type: string + - name: sampleCollectionDateRangeLower + displayName: Collection date (lower bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: lower + noInput: true + - name: sampleCollectionDateRangeUpper + displayName: Collection date (upper bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: upper + noInput: true + - name: displayName + displayName: Display name + definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{accessionVersion}/{sampleCollectionDate}`. + noInput: true + type: string + - name: ncbiReleaseDate + displayName: NCBI release date + definition: Date the viral nucleotide accession was first released on the INSDC. + type: date + header: INSDC + orderOnDetailsPage: 1000 + noInput: true + columnWidth: 100 + - name: earliestReleaseDate + displayName: Earliest release date + definition: The earliest release date for the accession, across all versions in both Loculus and the INSDC. + header: Sample details + type: date + rangeSearch: true + noInput: true + includeInDownloadsByDefault: true + orderOnDetailsPage: 300 + - name: ncbiUpdateDate + type: date + displayName: NCBI update date + definition: Date the viral nucleotide accession was last updated on the INSDC. + header: INSDC + orderOnDetailsPage: 1020 + noInput: true + perSegment: true + oneHeader: true + columnWidth: 100 + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 420 + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 440 + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 20 + orderOnDetailsPage: 400 + ingest: country + options: &id001 + - name: Afghanistan + - name: Albania + - name: Algeria + - name: American Samoa + - name: Andorra + - name: Angola + - name: Anguilla + - name: Antarctica + - name: Antigua and Barbuda + - name: Arctic Ocean + - name: Argentina + - name: Armenia + - name: Aruba + - name: Ashmore and Cartier Islands + - name: Atlantic Ocean + - name: Australia + - name: Austria + - name: Azerbaijan + - name: Bahamas + - name: Bahrain + - name: Baltic Sea + - name: Baker Island + - name: Bangladesh + - name: Barbados + - name: Bassas da India + - name: Belarus + - name: Belgium + - name: Belize + - name: Benin + - name: Bermuda + - name: Bhutan + - name: Bolivia + - name: Borneo + - name: Bosnia and Herzegovina + - name: Botswana + - name: Bouvet Island + - name: Brazil + - name: British Virgin Islands + - name: Brunei + - name: Bulgaria + - name: Burkina Faso + - name: Burundi + - name: Cambodia + - name: Cameroon + - name: Canada + - name: Cape Verde + - name: Cayman Islands + - name: Central African Republic + - name: Chad + - name: Chile + - name: China + - name: Christmas Island + - name: Clipperton Island + - name: Cocos Islands + - name: Colombia + - name: Comoros + - name: Cook Islands + - name: Coral Sea Islands + - name: Costa Rica + - name: Cote d'Ivoire + - name: Croatia + - name: Cuba + - name: Curacao + - name: Cyprus + - name: Czechia + - name: Democratic Republic of the Congo + - name: Denmark + - name: Djibouti + - name: Dominica + - name: Dominican Republic + - name: Ecuador + - name: Egypt + - name: El Salvador + - name: Equatorial Guinea + - name: Eritrea + - name: Estonia + - name: Eswatini + - name: Ethiopia + - name: Europa Island + - name: Falkland Islands (Islas Malvinas) + - name: Faroe Islands + - name: Fiji + - name: Finland + - name: France + - name: French Guiana + - name: French Polynesia + - name: French Southern and Antarctic Lands + - name: Gabon + - name: Gambia + - name: Gaza Strip + - name: Georgia + - name: Germany + - name: Ghana + - name: Gibraltar + - name: Glorioso Islands + - name: Greece + - name: Greenland + - name: Grenada + - name: Guadeloupe + - name: Guam + - name: Guatemala + - name: Guernsey + - name: Guinea + - name: Guinea-Bissau + - name: Guyana + - name: Haiti + - name: Heard Island and McDonald Islands + - name: Honduras + - name: Hong Kong + - name: Howland Island + - name: Hungary + - name: Iceland + - name: India + - name: Indian Ocean + - name: Indonesia + - name: Iran + - name: Iraq + - name: Ireland + - name: Isle of Man + - name: Israel + - name: Italy + - name: Jamaica + - name: Jan Mayen + - name: Japan + - name: Jarvis Island + - name: Jersey + - name: Johnston Atoll + - name: Jordan + - name: Juan de Nova Island + - name: Kazakhstan + - name: Kenya + - name: Kerguelen Archipelago + - name: Kingman Reef + - name: Kiribati + - name: Kosovo + - name: Kuwait + - name: Kyrgyzstan + - name: Laos + - name: Latvia + - name: Lebanon + - name: Lesotho + - name: Liberia + - name: Libya + - name: Liechtenstein + - name: Line Islands + - name: Lithuania + - name: Luxembourg + - name: Macau + - name: Madagascar + - name: Malawi + - name: Malaysia + - name: Maldives + - name: Mali + - name: Malta + - name: Marshall Islands + - name: Martinique + - name: Mauritania + - name: Mauritius + - name: Mayotte + - name: Mediterranean Sea + - name: Mexico + - name: Micronesia, Federated States of + - name: Midway Islands + - name: Moldova + - name: Monaco + - name: Mongolia + - name: Montenegro + - name: Montserrat + - name: Morocco + - name: Mozambique + - name: Myanmar + - name: Namibia + - name: Nauru + - name: Navassa Island + - name: Nepal + - name: Netherlands + - name: New Caledonia + - name: New Zealand + - name: Nicaragua + - name: Niger + - name: Nigeria + - name: Niue + - name: Norfolk Island + - name: North Korea + - name: North Macedonia + - name: North Sea + - name: Northern Mariana Islands + - name: Norway + - name: Oman + - name: Pacific Ocean + - name: Pakistan + - name: Palau + - name: Palmyra Atoll + - name: Panama + - name: Papua New Guinea + - name: Paracel Islands + - name: Paraguay + - name: Peru + - name: Philippines + - name: Pitcairn Islands + - name: Poland + - name: Portugal + - name: Puerto Rico + - name: Qatar + - name: Republic of the Congo + - name: Reunion + - name: Romania + - name: Ross Sea + - name: Russia + - name: Rwanda + - name: Saint Barthelemy + - name: Saint Helena + - name: Saint Kitts and Nevis + - name: Saint Lucia + - name: Saint Martin + - name: Saint Pierre and Miquelon + - name: Saint Vincent and the Grenadines + - name: Samoa + - name: San Marino + - name: Sao Tome and Principe + - name: Saudi Arabia + - name: Senegal + - name: Serbia + - name: Seychelles + - name: Sierra Leone + - name: Singapore + - name: Sint Maarten + - name: Slovakia + - name: Slovenia + - name: Solomon Islands + - name: Somalia + - name: South Africa + - name: South Georgia and the South Sandwich Islands + - name: South Korea + - name: South Sudan + - name: Southern Ocean + - name: Spain + - name: Spratly Islands + - name: Sri Lanka + - name: State of Palestine + - name: Sudan + - name: Suriname + - name: Svalbard + - name: Sweden + - name: Switzerland + - name: Syria + - name: Taiwan + - name: Tajikistan + - name: Tanzania + - name: Tasman Sea + - name: Thailand + - name: Timor-Leste + - name: Togo + - name: Tokelau + - name: Tonga + - name: Trinidad and Tobago + - name: Tromelin Island + - name: Tunisia + - name: Turkey + - name: Turkmenistan + - name: Turks and Caicos Islands + - name: Tuvalu + - name: Uganda + - name: Ukraine + - name: United Arab Emirates + - name: United Kingdom + - name: Uruguay + - name: USA + - name: Uzbekistan + - name: Vanuatu + - name: Venezuela + - name: Viet Nam + - name: Virgin Islands + - name: Wake Island + - name: Wallis and Futuna + - name: West Bank + - name: Western Sahara + - name: Yemen + - name: Zambia + - name: Zimbabwe + - name: Belgian Congo + - name: British Guiana + - name: Burma + - name: Czechoslovakia + - name: Czech Republic + - name: East Timor + - name: Korea + - name: Macedonia + - name: Micronesia + - name: Netherlands Antilles + - name: Serbia and Montenegro + - name: Siam + - name: Swaziland + - name: The former Yugoslav Republic of Macedonia + - name: USSR + - name: Yugoslavia + - name: Zaire + type: string + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + generateIndex: true + autocomplete: true + initiallyVisible: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 30 + orderOnDetailsPage: 460 + ingest: division + type: string + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + orderOnDetailsPage: 480 + type: string + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + generateIndex: true + autocomplete: true + header: Sample details + orderOnDetailsPage: 500 + type: string + - name: geoLocSite + ontology_id: GENEPIO:0100436 + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + header: Sample details + orderOnDetailsPage: 520 + type: string + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + header: Sample details + ingest: ncbiIsolateName + substringSearch: true + orderOnDetailsPage: 600 + type: string + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + type: authors + header: Authors + substringSearch: true + order: 40 + orderOnDetailsPage: 800 + includeInDownloadsByDefault: true + ingest: ncbiSubmitterNames + columnWidth: 140 + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + substringSearch: true + header: Authors + ingest: ncbiSubmitterAffiliation + includeInDownloadsByDefault: true + orderOnDetailsPage: 820 + type: string + - name: ncbiSubmitterCountry + displayName: NCBI submitter country + definition: The country representing the submitter's affilation. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: insdcAccessionBase + displayName: INSDC accession base + definition: The base accession assigned by the INSDC (GenBank/ENA/DDBJ) for this sequence, without the version number. + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + type: string + - name: insdcVersion + displayName: INSDC version + definition: The version number of the INSDC record, incremented each time the sequence record is updated. + type: int + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + - name: insdcAccessionFull + displayName: INSDC accession + definition: The full versioned INSDC accession (base accession plus version number), searchable across GenBank, ENA, and DDBJ. + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/nuccore/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://getentry.ddbj.nig.ac.jp/getentry/na/__value__ + header: INSDC + orderOnDetailsPage: 1040 + ingest: genbankAccession + noInput: true + perSegment: true + oneHeader: true + type: string + - name: bioprojectAccession + displayName: BioProject accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/bioproject/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/bioproject/__value__ + header: INSDC + orderOnDetailsPage: 1060 + ingest: bioprojects + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + type: string + - name: gcaAccession + displayName: GCA accession + definition: The GenBank genome assembly accession number for the sequence. + customDisplay: + type: link + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + header: INSDC + orderOnDetailsPage: 1100 + noInput: true + oneHeader: true + type: string + - name: biosampleAccession + displayName: BioSample accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/biosample/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/biosample/__value__ + header: INSDC + orderOnDetailsPage: 1080 + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + oneHeader: true + type: string + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + header: Sample details + orderOnDetailsPage: 620 + type: string + - name: sampleReceivedDate + ontology_id: GENEPIO:0001177 + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + type: date + orderOnDetailsPage: 260 + header: Sample details + - name: sampleType + displayName: Sample type + definition: Method of sampling. + header: Sampling + orderOnDetailsPage: 1200 + type: string + - name: purposeOfSampling + displayName: Purpose of sampling + ontology_id: GENEPIO:0001198 + definition: The reason that the sample was collected. + header: Sampling + orderOnDetailsPage: 1220 + ingest: ncbiPurposeOfSampling + type: string + - name: presamplingActivity + ontology_id: GENEPIO:0100433 + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + header: Sampling + orderOnDetailsPage: 1240 + type: string + - name: anatomicalMaterial + ontology_id: GENEPIO:0001211 + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + header: Sampling + orderOnDetailsPage: 1260 + type: string + - name: anatomicalPart + ontology_id: GENEPIO:0001214 + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + header: Sampling + orderOnDetailsPage: 1280 + type: string + - name: bodyProduct + ontology_id: GENEPIO:0001216 + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + header: Sampling + orderOnDetailsPage: 1300 + type: string + - name: environmentalMaterial + ontology_id: GENEPIO:0001223 + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + header: Sampling + orderOnDetailsPage: 1320 + type: string + - name: environmentalSite + ontology_id: GENEPIO:0001232 + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + header: Sampling + orderOnDetailsPage: 1340 + type: string + - name: collectionDevice + ontology_id: GENEPIO:0001234 + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + header: Sampling + orderOnDetailsPage: 1360 + type: string + - name: collectionMethod + ontology_id: GENEPIO:0001241 + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + header: Sampling + orderOnDetailsPage: 1380 + type: string + - name: foodProduct + ontology_id: GENEPIO:0100444 + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + header: Sampling + orderOnDetailsPage: 1400 + type: string + - name: foodProductProperties + ontology_id: GENEPIO:0100445 + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + header: Sampling + orderOnDetailsPage: 1420 + type: string + - name: specimenProcessing + ontology_id: GENEPIO:0100435 + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + header: Specimen processing + orderOnDetailsPage: 1500 + type: string + - name: specimenProcessingDetails + ontology_id: GENEPIO:0100311 + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + header: Specimen processing + orderOnDetailsPage: 1520 + type: string + - name: experimentalSpecimenRoleType + ontology_id: GENEPIO:0100921 + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + header: Specimen processing + type: string + - name: hostAge + ontology_id: GENEPIO:0001392 + definition: Age of host at the time of sampling. + displayName: Host age + type: int + header: Host + rangeSearch: true + - name: hostAgeBin + ontology_id: GENEPIO:0001394 + definition: The age category of the host at the time of sampling. + displayName: Host age bin + header: Host + type: string + - name: hostGender + ontology_id: GENEPIO:0001395 + definition: The gender of the host at the time of sample collection. + displayName: Host gender + header: Host + ingest: ncbiHostSex + orderOnDetailsPage: 1640 + type: string + - name: hostOriginCountry + ontology_id: GENEPIO:0100438 + definition: The country of origin of the host. + displayName: Host origin country + header: Host + type: string + - name: hostDisease + ontology_id: GENEPIO:0001391 + definition: The name of the disease experienced by the host. + displayName: Host disease + header: Host + type: string + - name: signsAndSymptoms + ontology_id: GENEPIO:0001400 + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + header: Host + type: string + - name: hostHealthState + ontology_id: GENEPIO:0001388 + definition: Health status of the host at the time of sample collection. + displayName: Host health state + header: Host + type: string + - name: hostHealthOutcome + ontology_id: GENEPIO:0001390 + definition: Disease outcome in the host. + displayName: Host health outcome + header: Host + orderOnDetailsPage: 1740 + type: string + - name: travelHistory + ontology_id: GENEPIO:0001416 + definition: Travel history in last six months. + displayName: Travel history + header: Host + orderOnDetailsPage: 1760 + type: string + - name: exposureEvent + ontology_id: GENEPIO:0001417 + definition: Event leading to exposure. + displayName: Exposure event + header: Host + orderOnDetailsPage: 1780 + type: string + - name: hostRole + ontology_id: GENEPIO:0001419 + definition: The role of the host in relation to the exposure setting. + displayName: Host role + header: Host + orderOnDetailsPage: 1800 + type: string + - name: exposureSetting + ontology_id: GENEPIO:0001428 + definition: The setting leading to exposure. + displayName: Exposure setting + header: Host + orderOnDetailsPage: 1820 + type: string + - name: exposureDetails + ontology_id: GENEPIO:0001431 + definition: Additional host exposure information. + displayName: Exposure details + header: Host + orderOnDetailsPage: 1840 + type: string + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + header: Host + orderOnDetailsPage: 1860 + type: string + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + header: Host + orderOnDetailsPage: 1880 + type: string + - name: hostVaccinationStatus + ontology_id: GENEPIO:0001404 + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + header: Host + orderOnDetailsPage: 1900 + type: string + - name: purposeOfSequencing + ontology_id: GENEPIO:0001445 + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + header: Sequencing + orderOnDetailsPage: 2000 + type: string + - name: sequencingDate + ontology_id: GENEPIO:0001447 + definition: The date the sample was sequenced. + displayName: Sequencing date + type: date + orderOnDetailsPage: 2020 + header: Sequencing + - name: ampliconPcrPrimerScheme + ontology_id: GENEPIO:0001456 + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + header: Sequencing + orderOnDetailsPage: 2040 + type: string + - name: ampliconSize + ontology_id: GENEPIO:0001449 + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + header: Sequencing + orderOnDetailsPage: 2060 + type: string + - name: sequencingInstrument + ontology_id: GENEPIO:0001452 + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + header: Sequencing + orderOnDetailsPage: 2080 + type: string + - name: sequencingProtocol + ontology_id: GENEPIO:0001454 + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + header: Sequencing + orderOnDetailsPage: 2100 + type: string + - name: sequencingAssayType + ontology_id: GENEPIO:0100997 + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + header: Sequencing + orderOnDetailsPage: 2120 + type: string + - name: sequencedByOrganization + ontology_id: GENEPIO:0100416 + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + header: Sequencing + orderOnDetailsPage: 2140 + type: string + - name: sequencedByContactName + ontology_id: GENEPIO:0100471 + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + header: Sequencing + orderOnDetailsPage: 2160 + type: string + - name: sequencedByContactEmail + ontology_id: GENEPIO:0100422 + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + header: Sequencing + orderOnDetailsPage: 2180 + type: string + - name: rawSequenceDataProcessingMethod + ontology_id: GENEPIO:0001458 + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + header: Sequencing + orderOnDetailsPage: 2200 + type: string + - name: dehostingMethod + ontology_id: GENEPIO:0001459 + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + header: Sequencing + orderOnDetailsPage: 2220 + type: string + - name: referenceGenomeAccession + ontology_id: GENEPIO:0001485 + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + header: Sequencing + orderOnDetailsPage: 2240 + type: string + - name: consensusSequenceSoftwareName + ontology_id: GENEPIO:0001463 + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + header: Sequencing + orderOnDetailsPage: 2260 + type: string + - name: consensusSequenceSoftwareVersion + ontology_id: GENEPIO:0001469 + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + header: Sequencing + orderOnDetailsPage: 2280 + type: string + - name: depthOfCoverage + ontology_id: GENEPIO:0001474 + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2300 + - name: breadthOfCoverage + ontology_id: GENEPIO:0001475 + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2320 + - name: qualityControlMethodName + ontology_id: GENEPIO:0100557 + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + header: Sequencing + orderOnDetailsPage: 2340 + type: string + - name: qualityControlMethodVersion + ontology_id: GENEPIO:0100558 + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + header: Sequencing + orderOnDetailsPage: 2360 + type: string + - name: qualityControlDetermination + ontology_id: GENEPIO:0100559 + definition: The determination of a quality control assessment. + displayName: Quality control determination + header: Sequencing + orderOnDetailsPage: 2380 + type: string + - name: qualityControlIssues + ontology_id: GENEPIO:0100560 + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + header: Sequencing + orderOnDetailsPage: 2400 + type: string + - name: qualityControlDetails + ontology_id: GENEPIO:0100561 + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + header: Diagnostics + orderOnDetailsPage: 2500 + type: string + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + header: Diagnostics + orderOnDetailsPage: 2520 + type: string + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + header: Diagnostics + orderOnDetailsPage: 2540 + type: string + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + header: Diagnostics + orderOnDetailsPage: 2560 + type: string + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + header: Diagnostics + orderOnDetailsPage: 2580 + type: string + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + orderOnDetailsPage: 2600 + header: Diagnostics + type: string + - name: length + type: int + header: Alignment and QC metrics + isSequenceFilter: true + noInput: true + rangeSearch: true + initiallyVisible: true + perSegment: true + displayName: Length + definition: The length of the sequence. + orderOnDetailsPage: 2700 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: hostTaxonId + type: string + autocomplete: true + displayName: Host species + definition: Taxon ID for the host. + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostTaxId + noInput: true + orderOnDetailsPage: 1540 + - name: hostNameScientific + displayName: Host name - scientific + definition: The scientific name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostName + noInput: true + orderOnDetailsPage: 1560 + type: string + - name: hostNameCommon + displayName: Host name - common + definition: The common name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostCommonName + noInput: true + orderOnDetailsPage: 1580 + type: string + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + type: boolean + autocomplete: true + header: Host + ingest: ncbiIsLabHost + orderOnDetailsPage: 1920 + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + generateIndex: true + orderOnDetailsPage: 1940 + autocomplete: true + header: Host + type: string + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + type: int + header: Host + orderOnDetailsPage: 1960 + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + generateIndex: true + autocomplete: true + header: Host + orderOnDetailsPage: 1980 + type: string + - name: ncbiSourceDb + displayName: NCBI source DB + definition: Indicates if the source of the viral nucleotide record is from a GenBank submitter or from NCBI-derived curation (RefSeq). + generateIndex: true + autocomplete: true + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + type: string + - name: ncbiVirusName + displayName: NCBI Virus name + definition: Scientific name for the organism from the NCBI Taxonomy. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: ncbiVirusTaxId + displayName: NCBI Virus tax ID + definition: Identifier for the organism from the NCBI Taxonomy. + type: int + autocomplete: true + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/labs/virus/vssi/#/virus?SeqType_s=Nucleotide&VirusLineage_ss=taxid:__value__ + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/sra/?term=__value__ + header: INSDC + orderOnDetailsPage: 1120 + ingest: ncbiSraAccessions + type: string + - name: totalSnps + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total SNPs + definition: Total count of nucleotide substitutions. + orderOnDetailsPage: 2720 + - name: totalInsertedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total inserted nucs + definition: Total count of inserted nucleotide positions. + orderOnDetailsPage: 2740 + - name: totalDeletedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + orderOnDetailsPage: 2760 + displayName: Total deleted nucs + definition: Total count of deleted nucleotide positions. + - name: totalAmbiguousNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total ambiguous nucs + definition: Total count of ambiguous nucleotides (not A, C, G, T, or N). + orderOnDetailsPage: 2780 + - name: totalUnknownNucs + orderOnDetailsPage: 2800 + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total unknown nucs + definition: Total count of missing (N) nucleotides. + - name: totalFrameShifts + isSequenceFilter: true + type: int + rangeSearch: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Total frame shifts + definition: Total count of detected frame shifts. + orderOnDetailsPage: 2820 + - name: frameShifts + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Frame shifts + definition: Frame-shifting insertions or deletions detected in CDS regions. + orderOnDetailsPage: 2840 + type: string + - name: totalStopCodons + isSequenceFilter: true + perSegment: true + displayName: Total stop codons + definition: Number of penalized premature stop codons. + type: int + header: Alignment and QC metrics + noInput: true + - name: stopCodons + isSequenceFilter: true + perSegment: true + displayName: Stop codons + definition: Premature stop codons not in the ignored list (penalized). + header: Alignment and QC metrics + noInput: true + type: string + - name: completeness + isSequenceFilter: true + type: float + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Completeness + definition: Fraction of reference positions covered by the sequence (0.0 to 1.0). + rangeSearch: true + percentage: true + orderOnDetailsPage: 2860 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: versionComment + type: string + extraInputFields: + - name: host + type: string + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + desired: true + position: last + - name: id + displayName: ID + definition: Unique identifier for your Ebola Sudan sample. + guidance: Use the sample ID assigned by your sequencing lab. + example: EBOV-SD-2024-001 + required: true + noEdit: true + position: first + organismName: Ebola Sudan + image: /images/organisms/ebolasudan_small.jpg + linkOuts: + - name: Nextclade + maxNumberOfRecommendedEntries: 5 + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences+rich|fasta]}}&dataset-name=nextstrain/ebola/sudan&dataset-server=https://raw.githubusercontent.com/nextstrain/nextclade_data/ebola/data_output + multiFieldSearches: + - name: identifier + displayName: Sample Identifier + orderInSearchDisplay: -10000 + fields: + - accessionVersion + - submissionId + - insdcAccessionFull + - bioprojectAccession + - gcaAccession + - biosampleAccession + - insdcRawReadsAccession + - name: contributor + displayName: Contributor + orderInSearchDisplay: -9999 + fields: + - authors + - authorAffiliations + - sequencedByOrganization + - sequencedByContactName + - submitter + - groupName + inputFields: + - name: id + displayName: ID + definition: Unique identifier for your Ebola Sudan sample. + guidance: Use the sample ID assigned by your sequencing lab. + example: EBOV-SD-2024-001 + required: true + noEdit: true + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + required: true + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + required: true + options: *id001 + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + - name: geoLocSite + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + - name: bioprojectAccession + displayName: BioProject accession + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + - name: biosampleAccession + displayName: BioSample accession + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + - name: sampleReceivedDate + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + - name: sampleType + displayName: Sample type + definition: Method of sampling. + - name: purposeOfSampling + displayName: Purpose of sampling + definition: The reason that the sample was collected. + - name: presamplingActivity + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + - name: anatomicalMaterial + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + - name: anatomicalPart + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + - name: bodyProduct + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + - name: environmentalMaterial + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + - name: environmentalSite + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + - name: collectionDevice + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + - name: collectionMethod + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + - name: foodProduct + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + - name: foodProductProperties + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + - name: specimenProcessing + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + - name: specimenProcessingDetails + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + - name: experimentalSpecimenRoleType + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + - name: hostAge + definition: Age of host at the time of sampling. + displayName: Host age + - name: hostAgeBin + definition: The age category of the host at the time of sampling. + displayName: Host age bin + - name: hostGender + definition: The gender of the host at the time of sample collection. + displayName: Host gender + - name: hostOriginCountry + definition: The country of origin of the host. + displayName: Host origin country + - name: hostDisease + definition: The name of the disease experienced by the host. + displayName: Host disease + - name: signsAndSymptoms + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + - name: hostHealthState + definition: Health status of the host at the time of sample collection. + displayName: Host health state + - name: hostHealthOutcome + definition: Disease outcome in the host. + displayName: Host health outcome + - name: travelHistory + definition: Travel history in last six months. + displayName: Travel history + - name: exposureEvent + definition: Event leading to exposure. + displayName: Exposure event + - name: hostRole + definition: The role of the host in relation to the exposure setting. + displayName: Host role + - name: exposureSetting + definition: The setting leading to exposure. + displayName: Exposure setting + - name: exposureDetails + definition: Additional host exposure information. + displayName: Exposure details + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + - name: hostVaccinationStatus + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + - name: purposeOfSequencing + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + - name: sequencingDate + definition: The date the sample was sequenced. + displayName: Sequencing date + - name: ampliconPcrPrimerScheme + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + - name: ampliconSize + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + - name: sequencingInstrument + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + - name: sequencingProtocol + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + - name: sequencingAssayType + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + - name: sequencedByOrganization + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + - name: sequencedByContactName + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + - name: sequencedByContactEmail + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + - name: rawSequenceDataProcessingMethod + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + - name: dehostingMethod + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + - name: referenceGenomeAccession + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + - name: consensusSequenceSoftwareName + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + - name: consensusSequenceSoftwareVersion + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + - name: depthOfCoverage + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + - name: breadthOfCoverage + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + - name: qualityControlMethodName + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + - name: qualityControlMethodVersion + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + - name: qualityControlDetermination + definition: The determination of a quality control assessment. + displayName: Quality control determination + - name: qualityControlIssues + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + - name: qualityControlDetails + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + - name: host + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + desired: true +referenceGenomes: + - name: main + references: + - name: singleReference + sequence: 'CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAAACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGGGGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAACCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTATCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGTGGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTCATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGATTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAAGCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAGGCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGCACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAGACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTGTCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTGGACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATGGTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAAGACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGAATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAATCTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGGGAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCCAACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAGAAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGCCATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATGATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCGTATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTTGGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCCTCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATCACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGAATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGAGCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAAAATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACTGGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGCCCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGTGAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACAGGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAATTGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCTGTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGTAATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGGAGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTTTAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACACTGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAATTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTATTAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAGTCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACAACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGTTGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAACATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTTCCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACATTTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTATCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCCCCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTCTCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCTGTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGCCAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATTTCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTTCCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAAAAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCACCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACAGAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAAGTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATGAAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTATGCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATACCGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCGCAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCCGAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGGGAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATCGTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAATGAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACCTGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCACAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTCCATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATCCAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCATACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCCCTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAATGAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCTTTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATGGGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCAAAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGGATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCTGCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTGCTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGTGCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTCTATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAAACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCACATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTTGTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAATACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAAAAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGGCATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCTGGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGTGAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCGGGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACCCACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGCACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAATAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACGGGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTTTGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTGCAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAGGCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTGGACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATGATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTTCTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATTATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTTTGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAAAGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTTGCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCCAAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCACGTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGTAGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGCACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAAGCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCTCCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACTAGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGAGTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGACAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATGGCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCTCCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATATACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATTTTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAATAGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTTAAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGTAGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAGTTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGTATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGAGAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTGCGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAAGACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTGACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCATAACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCATTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGTACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACTGTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAAAGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCCAAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCGCTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGTAAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTCATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAATATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTTACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTTCTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCATTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATACTGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGACCCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAACAATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTAGATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTCAATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAATGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAAACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACTACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGATGAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAATTACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATCTTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGACATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGCATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCGAGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGATCCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGGATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAACGAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAAAAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATTATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAGTTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGATAGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATTCCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCATTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAACGTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTATTGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAAAAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTTGCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCAAGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAAAATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTCTTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTTAGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGTTATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGGGATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAATGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTACACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGGATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAATCTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATCATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTATCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCCTGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAAAAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACATCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTCTGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTTTCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATGATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTACCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAGAATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACCACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAAAAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAAGAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACCTCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTTGAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCCCTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCCGGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAGGTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTACCGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCAGATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGAGGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTAGCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGGCTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGAATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACTCAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTATCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAAGGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAAAATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAATCTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAAGAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCCTAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGAACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGGTTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGCATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGTAATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATCAATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTTCAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAGAGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGTCATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAAATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACGATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTGCTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTGAGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTCGTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATTGCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAAATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAGCTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCTAAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAGGTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGAATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTCAGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAAACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTTCTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATAACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAAGTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAACACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACATAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTACGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGATCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA' + insdcAccessionFull: NC_002549.1 + genes: + - name: NP + sequence: 'MDKRVRGSWALGGQSEVDLDYHKILTAGLSVQQGIVRQRVIPVYVVSDLEGICQHIIQAFEAGVDFQDNADSFLLLLCLHHAYQGDHRLFLKSDAVQYLEGHGFRFEVREKENVHRLDELLPNVTGGKNLRRTLAAMPEEETTEANAGQFLSFASLFLPKLVVGEKACLEKVQRQIQVHAEQGLIQYPTSWQSVGHMMVIFRLMRTNFLIKFLLIHQGMHMVAGHDANDTVISNSVAQARFSGLLIVKTVLDHILQKTDLGVRLHPLARTAKVKNEVSSFKAALGSLAKHGEYAPFARLLNLSGVNNLEHGLYPQLSAIALGVATAHGSTLAGVNVGEQYQQLREAATEAEKQLQQYAETRELDNLGLDEQEKKILMSFHQKKNEISFQQTNAMVTLRKERLAKLTEAITTASKIKVGDRYPDDNDIPFPGPIYDETHPNPSDDNPDDSRDTTIPGGVVDPYDDESNNYPDYEDSAEGTTGDLDLFNLDDDDDDSQPGPPDRGQSKERAARTHGLQDPTLDGAKKVPELTPGSHQPGNLHITKPGSNTNQPQGNMSSTLQSMTPIQEESEPDDQKDDDDESLTSLDSEGDEDVESVSGENNPTVAPPAPVYKDTGVDTNQQNGPSNAVDGQGSESEALPINPEKGSALEETYYHLLKTQGPFEAINYYHLMSDEPIAFSTESGKEYIFPDSLEEAYPPWLSEKEALEKENRYLVIDGQQFLWPVMSLQDKFLAVLQHD*' + - name: VP35 + sequence: 'MQQDRTYRHHGPEVSGWFSEQLMTGKIPLTEVFVDVENKPSPAPITIISKNPKTTRKSDKQVQTDDASSLLTEEVKAAINSVISAVRRQTNAIESLEGRVTTLEASLKPVQDMAKTISSLNRSCAEMVAKYDLLVMTTGRATATAAATEAYWNEHGQAPPGPSLYEDDAIKAKLKDPNGKVPESVKQAYINLDSTSALNEENFGRPYISAKDLKEIIYDHLPGFGTAFHQLVQVICKIGKDNNILDIIHAEFQASLAEGDSPQCALIQITKRIPAFQDASPPIVHIKSRGDIPKACQKSLRPVPPSPKIDRGWVCIFQFQDGKALGLKI*' + - name: VP40 + sequence: 'MRRVTVPTAPPAYADIGYPMSMLPIKSSRAVSGIQQKQEVLPGMDTPSNSMRPVADDNIDHTSHTPNGVASAFILEATVNVISGPKVLMKQIPIWLPLGIADQKTYSFDSTTAAIMLASYTITHFGKANNPLVRVNRLGQGIPDHPLRLLRMGNQAFLQEFVLPPVQLPQYFTFDLTALKLVTQPLPAATWTDETPSNLSGALRPGLSFHPKLRPVLLPGKTGKKGHVSDLTAPDKIQTIVNLMQDFKIVPIDPAKSIIGIEVPELLVHKLTGKKMSQKNGQPIIPVLLPKYIGLDPISPGDLTMVITPDYDDCHSPASCSYLSEK*' + - name: GP + sequence: 'MGGLSLLQLPRDKFRKSSFFVWVIILFQKAFSMPLGVVTNSTLEVTEIDQLVCKDHLASTDQLKSVGLNLEGSGVSTDIPSATKRWGFRSGVPPKVVSYEAGEWAENCYNLEIKKPDGSECLPPPPDGVRGFPRCRYVHKAQGTGPCPGDYAFHKDGAFFLYDRLASTVIYRGVNFAEGVIAFLILAKPKETFLQSPPIREAVNYTENTSSYYATSYLEYEIENFGAQHSTTLFKIDNNTFVRLDRPHTPQFLFQLNDTIHLHQQLSNTTGRLIWTLDANINADIGEWAFWENKKNLSEQLRGEELSFEALSLNETEDDDAASSRITKGRISDRATRKYSDLVPKNSPGMVPLHIPEGETTLPSQNSTEGRRVGVNTQETITETAATIIGTNGNHMQISTIGIRPSSSQIPSSSPTTAPSPEAQTPTTHTSGPSVMATEEPTTPPGSSPGPTTEAPTLTTPENITTAVKTVLPQESTSNGLITSTVTGILGSLGLRKRSRRQTNTKATGKCNPNLHYWTAQEQHNAAGIAWIPYFGPGAEGIYTEGLMHNQNALVCGLRQLANETTQALQLFLRATTELRTYTILNRKAIDFLLRRWGGTCRILGPDCCIEPHDWTKNITDKINQIIHDFIDNPLPNQDNDDNWWTGWRQWIPAGIGITGIIIAIIALLCVCKLLC*' + - name: ssGP + sequence: 'MGGLSLLQLPRDKFRKSSFFVWVIILFQKAFSMPLGVVTNSTLEVTEIDQLVCKDHLASTDQLKSVGLNLEGSGVSTDIPSATKRWGFRSGVPPKVVSYEAGEWAENCYNLEIKKPDGSECLPPPPDGVRGFPRCRYVHKAQGTGPCPGDYAFHKDGAFFLYDRLASTVIYRGVNFAEGVIAFLILAKPKETFLQSPPIREAVNYTENTSSYYATSYLEYEIENFGAQHSTTLFKIDNNTFVRLDRPHTPQFLFQLNDTIHLHQQLSNTTGRLIWTLDANINADIGEWAFWENKKSLRTTTWRRAVFRSFIAQRDRRR*' + - name: sGP + sequence: 'MGGLSLLQLPRDKFRKSSFFVWVIILFQKAFSMPLGVVTNSTLEVTEIDQLVCKDHLASTDQLKSVGLNLEGSGVSTDIPSATKRWGFRSGVPPKVVSYEAGEWAENCYNLEIKKPDGSECLPPPPDGVRGFPRCRYVHKAQGTGPCPGDYAFHKDGAFFLYDRLASTVIYRGVNFAEGVIAFLILAKPKETFLQSPPIREAVNYTENTSSYYATSYLEYEIENFGAQHSTTLFKIDNNTFVRLDRPHTPQFLFQLNDTIHLHQQLSNTTGRLIWTLDANINADIGEWAFWENKKISPNNYVEKSCLSKLYRSTRQKTMMRHRRELQREESPTGPPGSIRTWFQRIPLGWFHCTYQKGKQHCRLRIRQKVEE*' + - name: VP30 + sequence: 'MERGRERGRSRNSRADQQNSTGPQFRTRSISRDKTTTDYRSSRSTSQVRVPTVFHKKGTGTLTVPPAPKDVCPTLRKGFLCDSNFCKKDHQLESLTDRELLLLIARKTCGSTDSSLNIAAPKDLRLANPTADDFKQDGSPKLTLKLLVETAEFWANQNINEVDDAKLRALLTLSAVLVRKFSKSQLSQLCESHLRRENLGQDQAESVLEVYQRLHSDKGGAFEAALWQQWDRQSLTMFISAFLHVALQLSCESSTVVISGLRLLAPPSVNEGLPPAPGEYTWSEDSTT*' + - name: VP24 + sequence: 'MAKATGRYNLVTPKRELEQGVVFSDLCNFLVTPTVQGWKVYWAGLEFDVNQKGITLLNRLKVNDFAPAWAMTRNLFPHLFKNQQSEVQTPIWALRVILAAGILDQLMDHSLIEPLSGALNLIADWLLTTSTNHFNMRTQRVKDQLSMRMLSLIRSNIINFINKLETLHVVNYKGLLSSVEIGTPSYAIIITRTNMGYLVEVQEPDKSAMDIRHPGPVKFSLLHESTLKPVATPKPSSITSLIMEFNSSLAI*' + - name: L + sequence: 'MMATQHTQYPDARLSSPIVLDQCDLVTRACGLYSEYSLNPKLKTCRLPKHIYRLKYDTIVLRFISDVPVATIPIDYIAPMLINVLADSKNVPLEPPCLSFLDEIVNYTVQDAAFLNYYMNQIKTQEGVITDQLKQNIRRVIHKNRYLSALFFWHDLAILTRRGRMNRGNVRSTWFVTNEVVDILGYGDYIFWKIPIALLPMNTANVPHASTDWYQPNIFKEAIQGHTHIISVSTAEVLIMCKDLVTSRFNTLLIAELARLEDPVSADYPLVDNIQSLYNAGDYLLSILGSEGYKIIKYLEPLCLAKIQLCSQYTERKGRFLTQMHLAVIQTLRELLLNRGLKKSQLSKIREFHQLLLRLRSTPQQLCELFSIQKHWGHPVLHSEKAIQKVKNHATVLKALRPIIIFETYCVFKYSVAKHFFDSQGTWYSVISDRCLTPGLNSYIRRNQFPPLPMIKDLLWEFYHLDHPPLFSTKIISDLSIFIKDRATAVEQTCWDAVFEPNVLGYSPPYRFNTKRVPEQFLEQEDFSIESVLQYAQELRYLLPQNRNFSFSLKEKELNVGRTFGKLPYLTRNVQTLCEALLADGLAKAFPSNMMVVTEREQKESLLHQASWHHTSDDFGEHATVRGSSFVTDLEKYNLAFRYEFTAPFIKYCNQCYGVRNVFDWMHFLIPQCYMHVSDYYNPPHNVTLENREYPPEGPSAYRGHLGGIEGLQQKLWTSISCAQISLVEIKTGFKLRSAVMGDNQCITVLSVFPLESSPNEQERCAEDNAARVAASLAKVTSACGIFLKPDETFVHSGFIYFGKKQYLNGIQLPQSLKTAARMAPLSDAIFDDLQGTLASIGTAFERSISETRHILPCRVAAAFHTYFSVRILQHHHLGFHKGSDLGQLAINKPLDFGTIALSLAVPQVLGGLSFLNPEKCLYRNLGDPVTSGLFQLKHYLSMVGMSDIFHALIAKSPGNCSAIDFVLNPGGLNVPGSQDLTSFLRQIVRRSITLSARNKLINTLFHASADLEDELVCKWLLSSTPVMSRFAADIFSRTPSGKRLQILGYLEGTRTLLASKMISNNAETPILERLRKITLQRWNLWFSYLDHCDPALMEAIQPIKCTVDIAQILREYSWAHILDGRQLIGATLPCIPEQFQTTWLKPYEQCVECSSTNNSSPYVSVALKRNVVSAWPDASRLGWTIGDGIPYIGSRTEDKIGQPAIKPRCPSAALREAIELTSRLTWVTQGSANSDQLIRPFLEARVNLSVQEILQMTPSHYSGNIVHRYNDQYSPHSFMANRMSNTATRLMVSTNTLGEFSGGGQAARDSNIIFQNVINFAVALYDIRFRNTCTSSIQYHRAHIHLTNCCTREVPAQYLTYTTTLNLDLSKYRNNELIYDSDPLRGGLNCNLSIDSPLMKGPRLNIIEDDLIRLPHLSGWELAKTVLQSIISDSSNSSTDPISSGETRSFTTHFLTYPKIGLLYSFGALISFYLGNTILCTKKIGLTEFLYYLQNQIHNLSHRSLRIFKPTFRHSSVMSRLMDIDPNFSIYIGGTAGDRGLSDAARLFLRIAISTFLSFVEEWVIFRKANIPLWVIYPLEGQRSDPPGEFLNRVKSLIVGTEDDKNKGSILSRSGEKCSSNLVYNCKSTASNFFHASLAYWRGRHRPKKTIGATNATTAPHIILPLGNSDRPPGLDLNRNNDTFIPTRIKQIVQGDSRNDRTTTTRFPPKSRSTPTSATEPPTKMYEGSTTHQGKLTDTHLDEDHNAKEFPSNPHRLVVPFFKLTKDGEYSIEPSPEESRSNIKGLLQHLRTMVDTTIYCRFTGIVSSMHYKLDEVLWEYNKFESAVTLAEGEGSGALLLIQKYGVKKLFLNTLATEHSIESEVISGYTTPRMLLPIMPKTHRGELEVILNNSASQITDITHRDWFSNQKNRIPNDADIITMDAETTENLDRSRLYEAVYTIICNHINPKTLKVVILKVFLSDLDGMCWINNYLAPMFGSGYLIKPITSSAKSSEWYLCLSNLLSTLRTTQHQTQANCLHVVQCALQQQVQRGSYWLSHLTKYTTSRLHNSYIAFGFPSLEKVLYHRYNLVDSRNGPLVSITRHLALLQTEIRELVTDYNQLRQSRTQTYHFIKTSKGRITKLVNDYLRFELVIRALKNNSTWHHELYLLPELIGVCHRFNHTRNCTCSERFLVQTLYLHRMSDAEIKLMDRLTSLVNMFPEGFRSSSV*' +referenceGenome: + nucleotideSequences: + - name: main + sequence: 'CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAAACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGGGGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAACCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTATCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGTGGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTCATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGATTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAAGCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAGGCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGCACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAGACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTGTCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTGGACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATGGTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAAGACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGAATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAATCTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGGGAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCCAACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAGAAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGCCATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATGATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCGTATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTTGGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCCTCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATCACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGAATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGAGCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAAAATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACTGGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGCCCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGTGAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACAGGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAATTGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCTGTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGTAATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGGAGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTTTAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACACTGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAATTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTATTAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAGTCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACAACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGTTGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAACATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTTCCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACATTTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTATCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCCCCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTCTCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCTGTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGCCAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATTTCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTTCCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAAAAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCACCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACAGAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAAGTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATGAAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTATGCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATACCGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCGCAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCCGAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGGGAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATCGTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAATGAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACCTGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCACAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTCCATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATCCAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCATACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCCCTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAATGAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCTTTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATGGGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCAAAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGGATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCTGCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTGCTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGTGCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTCTATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAAACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCACATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTTGTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAATACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAAAAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGGCATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCTGGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGTGAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCGGGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACCCACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGCACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAATAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACGGGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTTTGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTGCAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAGGCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTGGACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATGATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTTCTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATTATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTTTGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAAAGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTTGCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCCAAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCACGTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGTAGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGCACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAAGCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCTCCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACTAGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGAGTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGACAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATGGCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCTCCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATATACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATTTTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAATAGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTTAAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGTAGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAGTTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGTATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGAGAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTGCGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAAGACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTGACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCATAACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCATTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGTACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACTGTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAAAGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCCAAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCGCTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGTAAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTCATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAATATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTTACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTTCTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCATTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATACTGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGACCCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAACAATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTAGATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTCAATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAATGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAAACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACTACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGATGAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAATTACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATCTTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGACATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGCATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCGAGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGATCCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGGATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAACGAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAAAAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATTATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAGTTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGATAGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATTCCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCATTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAACGTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTATTGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAAAAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTTGCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCAAGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAAAATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTCTTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTTAGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGTTATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGGGATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAATGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTACACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGGATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAATCTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATCATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTATCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCCTGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAAAAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACATCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTCTGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTTTCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATGATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTACCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAGAATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACCACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAAAAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAAGAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACCTCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTTGAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCCCTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCCGGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAGGTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTACCGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCAGATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGAGGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTAGCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGGCTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGAATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACTCAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTATCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAAGGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAAAATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAATCTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAAGAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCCTAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGAACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGGTTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGCATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGTAATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATCAATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTTCAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAGAGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGTCATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAAATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACGATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTGCTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTGAGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTCGTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATTGCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAAATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAGCTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCTAAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAGGTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGAATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTCAGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAAACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTTCTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATAACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAAGTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAACACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACATAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTACGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGATCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA' + genes: + - name: NP + sequence: 'MDKRVRGSWALGGQSEVDLDYHKILTAGLSVQQGIVRQRVIPVYVVSDLEGICQHIIQAFEAGVDFQDNADSFLLLLCLHHAYQGDHRLFLKSDAVQYLEGHGFRFEVREKENVHRLDELLPNVTGGKNLRRTLAAMPEEETTEANAGQFLSFASLFLPKLVVGEKACLEKVQRQIQVHAEQGLIQYPTSWQSVGHMMVIFRLMRTNFLIKFLLIHQGMHMVAGHDANDTVISNSVAQARFSGLLIVKTVLDHILQKTDLGVRLHPLARTAKVKNEVSSFKAALGSLAKHGEYAPFARLLNLSGVNNLEHGLYPQLSAIALGVATAHGSTLAGVNVGEQYQQLREAATEAEKQLQQYAETRELDNLGLDEQEKKILMSFHQKKNEISFQQTNAMVTLRKERLAKLTEAITTASKIKVGDRYPDDNDIPFPGPIYDETHPNPSDDNPDDSRDTTIPGGVVDPYDDESNNYPDYEDSAEGTTGDLDLFNLDDDDDDSQPGPPDRGQSKERAARTHGLQDPTLDGAKKVPELTPGSHQPGNLHITKPGSNTNQPQGNMSSTLQSMTPIQEESEPDDQKDDDDESLTSLDSEGDEDVESVSGENNPTVAPPAPVYKDTGVDTNQQNGPSNAVDGQGSESEALPINPEKGSALEETYYHLLKTQGPFEAINYYHLMSDEPIAFSTESGKEYIFPDSLEEAYPPWLSEKEALEKENRYLVIDGQQFLWPVMSLQDKFLAVLQHD*' + - name: VP35 + sequence: 'MQQDRTYRHHGPEVSGWFSEQLMTGKIPLTEVFVDVENKPSPAPITIISKNPKTTRKSDKQVQTDDASSLLTEEVKAAINSVISAVRRQTNAIESLEGRVTTLEASLKPVQDMAKTISSLNRSCAEMVAKYDLLVMTTGRATATAAATEAYWNEHGQAPPGPSLYEDDAIKAKLKDPNGKVPESVKQAYINLDSTSALNEENFGRPYISAKDLKEIIYDHLPGFGTAFHQLVQVICKIGKDNNILDIIHAEFQASLAEGDSPQCALIQITKRIPAFQDASPPIVHIKSRGDIPKACQKSLRPVPPSPKIDRGWVCIFQFQDGKALGLKI*' + - name: VP40 + sequence: 'MRRVTVPTAPPAYADIGYPMSMLPIKSSRAVSGIQQKQEVLPGMDTPSNSMRPVADDNIDHTSHTPNGVASAFILEATVNVISGPKVLMKQIPIWLPLGIADQKTYSFDSTTAAIMLASYTITHFGKANNPLVRVNRLGQGIPDHPLRLLRMGNQAFLQEFVLPPVQLPQYFTFDLTALKLVTQPLPAATWTDETPSNLSGALRPGLSFHPKLRPVLLPGKTGKKGHVSDLTAPDKIQTIVNLMQDFKIVPIDPAKSIIGIEVPELLVHKLTGKKMSQKNGQPIIPVLLPKYIGLDPISPGDLTMVITPDYDDCHSPASCSYLSEK*' + - name: GP + sequence: 'MGGLSLLQLPRDKFRKSSFFVWVIILFQKAFSMPLGVVTNSTLEVTEIDQLVCKDHLASTDQLKSVGLNLEGSGVSTDIPSATKRWGFRSGVPPKVVSYEAGEWAENCYNLEIKKPDGSECLPPPPDGVRGFPRCRYVHKAQGTGPCPGDYAFHKDGAFFLYDRLASTVIYRGVNFAEGVIAFLILAKPKETFLQSPPIREAVNYTENTSSYYATSYLEYEIENFGAQHSTTLFKIDNNTFVRLDRPHTPQFLFQLNDTIHLHQQLSNTTGRLIWTLDANINADIGEWAFWENKKNLSEQLRGEELSFEALSLNETEDDDAASSRITKGRISDRATRKYSDLVPKNSPGMVPLHIPEGETTLPSQNSTEGRRVGVNTQETITETAATIIGTNGNHMQISTIGIRPSSSQIPSSSPTTAPSPEAQTPTTHTSGPSVMATEEPTTPPGSSPGPTTEAPTLTTPENITTAVKTVLPQESTSNGLITSTVTGILGSLGLRKRSRRQTNTKATGKCNPNLHYWTAQEQHNAAGIAWIPYFGPGAEGIYTEGLMHNQNALVCGLRQLANETTQALQLFLRATTELRTYTILNRKAIDFLLRRWGGTCRILGPDCCIEPHDWTKNITDKINQIIHDFIDNPLPNQDNDDNWWTGWRQWIPAGIGITGIIIAIIALLCVCKLLC*' + - name: ssGP + sequence: 'MGGLSLLQLPRDKFRKSSFFVWVIILFQKAFSMPLGVVTNSTLEVTEIDQLVCKDHLASTDQLKSVGLNLEGSGVSTDIPSATKRWGFRSGVPPKVVSYEAGEWAENCYNLEIKKPDGSECLPPPPDGVRGFPRCRYVHKAQGTGPCPGDYAFHKDGAFFLYDRLASTVIYRGVNFAEGVIAFLILAKPKETFLQSPPIREAVNYTENTSSYYATSYLEYEIENFGAQHSTTLFKIDNNTFVRLDRPHTPQFLFQLNDTIHLHQQLSNTTGRLIWTLDANINADIGEWAFWENKKSLRTTTWRRAVFRSFIAQRDRRR*' + - name: sGP + sequence: 'MGGLSLLQLPRDKFRKSSFFVWVIILFQKAFSMPLGVVTNSTLEVTEIDQLVCKDHLASTDQLKSVGLNLEGSGVSTDIPSATKRWGFRSGVPPKVVSYEAGEWAENCYNLEIKKPDGSECLPPPPDGVRGFPRCRYVHKAQGTGPCPGDYAFHKDGAFFLYDRLASTVIYRGVNFAEGVIAFLILAKPKETFLQSPPIREAVNYTENTSSYYATSYLEYEIENFGAQHSTTLFKIDNNTFVRLDRPHTPQFLFQLNDTIHLHQQLSNTTGRLIWTLDANINADIGEWAFWENKKISPNNYVEKSCLSKLYRSTRQKTMMRHRRELQREESPTGPPGSIRTWFQRIPLGWFHCTYQKGKQHCRLRIRQKVEE*' + - name: VP30 + sequence: 'MERGRERGRSRNSRADQQNSTGPQFRTRSISRDKTTTDYRSSRSTSQVRVPTVFHKKGTGTLTVPPAPKDVCPTLRKGFLCDSNFCKKDHQLESLTDRELLLLIARKTCGSTDSSLNIAAPKDLRLANPTADDFKQDGSPKLTLKLLVETAEFWANQNINEVDDAKLRALLTLSAVLVRKFSKSQLSQLCESHLRRENLGQDQAESVLEVYQRLHSDKGGAFEAALWQQWDRQSLTMFISAFLHVALQLSCESSTVVISGLRLLAPPSVNEGLPPAPGEYTWSEDSTT*' + - name: VP24 + sequence: 'MAKATGRYNLVTPKRELEQGVVFSDLCNFLVTPTVQGWKVYWAGLEFDVNQKGITLLNRLKVNDFAPAWAMTRNLFPHLFKNQQSEVQTPIWALRVILAAGILDQLMDHSLIEPLSGALNLIADWLLTTSTNHFNMRTQRVKDQLSMRMLSLIRSNIINFINKLETLHVVNYKGLLSSVEIGTPSYAIIITRTNMGYLVEVQEPDKSAMDIRHPGPVKFSLLHESTLKPVATPKPSSITSLIMEFNSSLAI*' + - name: L + sequence: 'MMATQHTQYPDARLSSPIVLDQCDLVTRACGLYSEYSLNPKLKTCRLPKHIYRLKYDTIVLRFISDVPVATIPIDYIAPMLINVLADSKNVPLEPPCLSFLDEIVNYTVQDAAFLNYYMNQIKTQEGVITDQLKQNIRRVIHKNRYLSALFFWHDLAILTRRGRMNRGNVRSTWFVTNEVVDILGYGDYIFWKIPIALLPMNTANVPHASTDWYQPNIFKEAIQGHTHIISVSTAEVLIMCKDLVTSRFNTLLIAELARLEDPVSADYPLVDNIQSLYNAGDYLLSILGSEGYKIIKYLEPLCLAKIQLCSQYTERKGRFLTQMHLAVIQTLRELLLNRGLKKSQLSKIREFHQLLLRLRSTPQQLCELFSIQKHWGHPVLHSEKAIQKVKNHATVLKALRPIIIFETYCVFKYSVAKHFFDSQGTWYSVISDRCLTPGLNSYIRRNQFPPLPMIKDLLWEFYHLDHPPLFSTKIISDLSIFIKDRATAVEQTCWDAVFEPNVLGYSPPYRFNTKRVPEQFLEQEDFSIESVLQYAQELRYLLPQNRNFSFSLKEKELNVGRTFGKLPYLTRNVQTLCEALLADGLAKAFPSNMMVVTEREQKESLLHQASWHHTSDDFGEHATVRGSSFVTDLEKYNLAFRYEFTAPFIKYCNQCYGVRNVFDWMHFLIPQCYMHVSDYYNPPHNVTLENREYPPEGPSAYRGHLGGIEGLQQKLWTSISCAQISLVEIKTGFKLRSAVMGDNQCITVLSVFPLESSPNEQERCAEDNAARVAASLAKVTSACGIFLKPDETFVHSGFIYFGKKQYLNGIQLPQSLKTAARMAPLSDAIFDDLQGTLASIGTAFERSISETRHILPCRVAAAFHTYFSVRILQHHHLGFHKGSDLGQLAINKPLDFGTIALSLAVPQVLGGLSFLNPEKCLYRNLGDPVTSGLFQLKHYLSMVGMSDIFHALIAKSPGNCSAIDFVLNPGGLNVPGSQDLTSFLRQIVRRSITLSARNKLINTLFHASADLEDELVCKWLLSSTPVMSRFAADIFSRTPSGKRLQILGYLEGTRTLLASKMISNNAETPILERLRKITLQRWNLWFSYLDHCDPALMEAIQPIKCTVDIAQILREYSWAHILDGRQLIGATLPCIPEQFQTTWLKPYEQCVECSSTNNSSPYVSVALKRNVVSAWPDASRLGWTIGDGIPYIGSRTEDKIGQPAIKPRCPSAALREAIELTSRLTWVTQGSANSDQLIRPFLEARVNLSVQEILQMTPSHYSGNIVHRYNDQYSPHSFMANRMSNTATRLMVSTNTLGEFSGGGQAARDSNIIFQNVINFAVALYDIRFRNTCTSSIQYHRAHIHLTNCCTREVPAQYLTYTTTLNLDLSKYRNNELIYDSDPLRGGLNCNLSIDSPLMKGPRLNIIEDDLIRLPHLSGWELAKTVLQSIISDSSNSSTDPISSGETRSFTTHFLTYPKIGLLYSFGALISFYLGNTILCTKKIGLTEFLYYLQNQIHNLSHRSLRIFKPTFRHSSVMSRLMDIDPNFSIYIGGTAGDRGLSDAARLFLRIAISTFLSFVEEWVIFRKANIPLWVIYPLEGQRSDPPGEFLNRVKSLIVGTEDDKNKGSILSRSGEKCSSNLVYNCKSTASNFFHASLAYWRGRHRPKKTIGATNATTAPHIILPLGNSDRPPGLDLNRNNDTFIPTRIKQIVQGDSRNDRTTTTRFPPKSRSTPTSATEPPTKMYEGSTTHQGKLTDTHLDEDHNAKEFPSNPHRLVVPFFKLTKDGEYSIEPSPEESRSNIKGLLQHLRTMVDTTIYCRFTGIVSSMHYKLDEVLWEYNKFESAVTLAEGEGSGALLLIQKYGVKKLFLNTLATEHSIESEVISGYTTPRMLLPIMPKTHRGELEVILNNSASQITDITHRDWFSNQKNRIPNDADIITMDAETTENLDRSRLYEAVYTIICNHINPKTLKVVILKVFLSDLDGMCWINNYLAPMFGSGYLIKPITSSAKSSEWYLCLSNLLSTLRTTQHQTQANCLHVVQCALQQQVQRGSYWLSHLTKYTTSRLHNSYIAFGFPSLEKVLYHRYNLVDSRNGPLVSITRHLALLQTEIRELVTDYNQLRQSRTQTYHFIKTSKGRITKLVNDYLRFELVIRALKNNSTWHHELYLLPELIGVCHRFNHTRNCTCSERFLVQTLYLHRMSDAEIKLMDRLTSLVNMFPEGFRSSSV*' +displayName: Ebola Sudan + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-ebola-sudan:8080" diff --git a/kubernetes/loculus/fixtures/organisms/enteroviruses.yaml b/kubernetes/loculus/fixtures/organisms/enteroviruses.yaml new file mode 100644 index 0000000000..ab0c4daa34 --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/enteroviruses.yaml @@ -0,0 +1,1793 @@ +schema: + referenceIdentifierField: genotype + submissionDataTypes: + consensusSequences: true + maxSequencesPerEntry: 1 + loadSequencesAutomatically: true + earliestReleaseDate: + enabled: true + externalFields: + - ncbiReleaseDate + richFastaHeaderFields: + - displayName + files: + - name: annotations + displayName: Annotations + metadata: + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + header: Sample details + ingest: ncbiCollectionDate + order: 10 + orderOnDetailsPage: 200 + includeInDownloadsByDefault: true + notSearchable: true + columnWidth: 100 + type: string + - name: sampleCollectionDateRangeLower + displayName: Collection date (lower bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: lower + noInput: true + - name: sampleCollectionDateRangeUpper + displayName: Collection date (upper bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: upper + noInput: true + - name: displayName + displayName: Display name + definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{accessionVersion}/{sampleCollectionDate}`. + noInput: true + type: string + - name: ncbiReleaseDate + displayName: NCBI release date + definition: Date the viral nucleotide accession was first released on the INSDC. + type: date + header: INSDC + orderOnDetailsPage: 1000 + noInput: true + columnWidth: 100 + - name: earliestReleaseDate + displayName: Earliest release date + definition: The earliest release date for the accession, across all versions in both Loculus and the INSDC. + header: Sample details + type: date + rangeSearch: true + noInput: true + includeInDownloadsByDefault: true + orderOnDetailsPage: 300 + - name: ncbiUpdateDate + type: date + displayName: NCBI update date + definition: Date the viral nucleotide accession was last updated on the INSDC. + header: INSDC + orderOnDetailsPage: 1020 + noInput: true + perSegment: true + oneHeader: true + columnWidth: 100 + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 420 + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 440 + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 20 + orderOnDetailsPage: 400 + ingest: country + options: &id001 + - name: Afghanistan + - name: Albania + - name: Algeria + - name: American Samoa + - name: Andorra + - name: Angola + - name: Anguilla + - name: Antarctica + - name: Antigua and Barbuda + - name: Arctic Ocean + - name: Argentina + - name: Armenia + - name: Aruba + - name: Ashmore and Cartier Islands + - name: Atlantic Ocean + - name: Australia + - name: Austria + - name: Azerbaijan + - name: Bahamas + - name: Bahrain + - name: Baltic Sea + - name: Baker Island + - name: Bangladesh + - name: Barbados + - name: Bassas da India + - name: Belarus + - name: Belgium + - name: Belize + - name: Benin + - name: Bermuda + - name: Bhutan + - name: Bolivia + - name: Borneo + - name: Bosnia and Herzegovina + - name: Botswana + - name: Bouvet Island + - name: Brazil + - name: British Virgin Islands + - name: Brunei + - name: Bulgaria + - name: Burkina Faso + - name: Burundi + - name: Cambodia + - name: Cameroon + - name: Canada + - name: Cape Verde + - name: Cayman Islands + - name: Central African Republic + - name: Chad + - name: Chile + - name: China + - name: Christmas Island + - name: Clipperton Island + - name: Cocos Islands + - name: Colombia + - name: Comoros + - name: Cook Islands + - name: Coral Sea Islands + - name: Costa Rica + - name: Cote d'Ivoire + - name: Croatia + - name: Cuba + - name: Curacao + - name: Cyprus + - name: Czechia + - name: Democratic Republic of the Congo + - name: Denmark + - name: Djibouti + - name: Dominica + - name: Dominican Republic + - name: Ecuador + - name: Egypt + - name: El Salvador + - name: Equatorial Guinea + - name: Eritrea + - name: Estonia + - name: Eswatini + - name: Ethiopia + - name: Europa Island + - name: Falkland Islands (Islas Malvinas) + - name: Faroe Islands + - name: Fiji + - name: Finland + - name: France + - name: French Guiana + - name: French Polynesia + - name: French Southern and Antarctic Lands + - name: Gabon + - name: Gambia + - name: Gaza Strip + - name: Georgia + - name: Germany + - name: Ghana + - name: Gibraltar + - name: Glorioso Islands + - name: Greece + - name: Greenland + - name: Grenada + - name: Guadeloupe + - name: Guam + - name: Guatemala + - name: Guernsey + - name: Guinea + - name: Guinea-Bissau + - name: Guyana + - name: Haiti + - name: Heard Island and McDonald Islands + - name: Honduras + - name: Hong Kong + - name: Howland Island + - name: Hungary + - name: Iceland + - name: India + - name: Indian Ocean + - name: Indonesia + - name: Iran + - name: Iraq + - name: Ireland + - name: Isle of Man + - name: Israel + - name: Italy + - name: Jamaica + - name: Jan Mayen + - name: Japan + - name: Jarvis Island + - name: Jersey + - name: Johnston Atoll + - name: Jordan + - name: Juan de Nova Island + - name: Kazakhstan + - name: Kenya + - name: Kerguelen Archipelago + - name: Kingman Reef + - name: Kiribati + - name: Kosovo + - name: Kuwait + - name: Kyrgyzstan + - name: Laos + - name: Latvia + - name: Lebanon + - name: Lesotho + - name: Liberia + - name: Libya + - name: Liechtenstein + - name: Line Islands + - name: Lithuania + - name: Luxembourg + - name: Macau + - name: Madagascar + - name: Malawi + - name: Malaysia + - name: Maldives + - name: Mali + - name: Malta + - name: Marshall Islands + - name: Martinique + - name: Mauritania + - name: Mauritius + - name: Mayotte + - name: Mediterranean Sea + - name: Mexico + - name: Micronesia, Federated States of + - name: Midway Islands + - name: Moldova + - name: Monaco + - name: Mongolia + - name: Montenegro + - name: Montserrat + - name: Morocco + - name: Mozambique + - name: Myanmar + - name: Namibia + - name: Nauru + - name: Navassa Island + - name: Nepal + - name: Netherlands + - name: New Caledonia + - name: New Zealand + - name: Nicaragua + - name: Niger + - name: Nigeria + - name: Niue + - name: Norfolk Island + - name: North Korea + - name: North Macedonia + - name: North Sea + - name: Northern Mariana Islands + - name: Norway + - name: Oman + - name: Pacific Ocean + - name: Pakistan + - name: Palau + - name: Palmyra Atoll + - name: Panama + - name: Papua New Guinea + - name: Paracel Islands + - name: Paraguay + - name: Peru + - name: Philippines + - name: Pitcairn Islands + - name: Poland + - name: Portugal + - name: Puerto Rico + - name: Qatar + - name: Republic of the Congo + - name: Reunion + - name: Romania + - name: Ross Sea + - name: Russia + - name: Rwanda + - name: Saint Barthelemy + - name: Saint Helena + - name: Saint Kitts and Nevis + - name: Saint Lucia + - name: Saint Martin + - name: Saint Pierre and Miquelon + - name: Saint Vincent and the Grenadines + - name: Samoa + - name: San Marino + - name: Sao Tome and Principe + - name: Saudi Arabia + - name: Senegal + - name: Serbia + - name: Seychelles + - name: Sierra Leone + - name: Singapore + - name: Sint Maarten + - name: Slovakia + - name: Slovenia + - name: Solomon Islands + - name: Somalia + - name: South Africa + - name: South Georgia and the South Sandwich Islands + - name: South Korea + - name: South Sudan + - name: Southern Ocean + - name: Spain + - name: Spratly Islands + - name: Sri Lanka + - name: State of Palestine + - name: Sudan + - name: Suriname + - name: Svalbard + - name: Sweden + - name: Switzerland + - name: Syria + - name: Taiwan + - name: Tajikistan + - name: Tanzania + - name: Tasman Sea + - name: Thailand + - name: Timor-Leste + - name: Togo + - name: Tokelau + - name: Tonga + - name: Trinidad and Tobago + - name: Tromelin Island + - name: Tunisia + - name: Turkey + - name: Turkmenistan + - name: Turks and Caicos Islands + - name: Tuvalu + - name: Uganda + - name: Ukraine + - name: United Arab Emirates + - name: United Kingdom + - name: Uruguay + - name: USA + - name: Uzbekistan + - name: Vanuatu + - name: Venezuela + - name: Viet Nam + - name: Virgin Islands + - name: Wake Island + - name: Wallis and Futuna + - name: West Bank + - name: Western Sahara + - name: Yemen + - name: Zambia + - name: Zimbabwe + - name: Belgian Congo + - name: British Guiana + - name: Burma + - name: Czechoslovakia + - name: Czech Republic + - name: East Timor + - name: Korea + - name: Macedonia + - name: Micronesia + - name: Netherlands Antilles + - name: Serbia and Montenegro + - name: Siam + - name: Swaziland + - name: The former Yugoslav Republic of Macedonia + - name: USSR + - name: Yugoslavia + - name: Zaire + type: string + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + generateIndex: true + autocomplete: true + initiallyVisible: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 30 + orderOnDetailsPage: 460 + ingest: division + type: string + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + orderOnDetailsPage: 480 + type: string + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + generateIndex: true + autocomplete: true + header: Sample details + orderOnDetailsPage: 500 + type: string + - name: geoLocSite + ontology_id: GENEPIO:0100436 + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + header: Sample details + orderOnDetailsPage: 520 + type: string + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + header: Sample details + ingest: ncbiIsolateName + substringSearch: true + orderOnDetailsPage: 600 + type: string + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + type: authors + header: Authors + substringSearch: true + order: 40 + orderOnDetailsPage: 800 + includeInDownloadsByDefault: true + ingest: ncbiSubmitterNames + columnWidth: 140 + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + substringSearch: true + header: Authors + ingest: ncbiSubmitterAffiliation + includeInDownloadsByDefault: true + orderOnDetailsPage: 820 + type: string + - name: ncbiSubmitterCountry + displayName: NCBI submitter country + definition: The country representing the submitter's affilation. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: insdcAccessionBase + displayName: INSDC accession base + definition: The base accession assigned by the INSDC (GenBank/ENA/DDBJ) for this sequence, without the version number. + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + type: string + - name: insdcVersion + displayName: INSDC version + definition: The version number of the INSDC record, incremented each time the sequence record is updated. + type: int + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + - name: insdcAccessionFull + displayName: INSDC accession + definition: The full versioned INSDC accession (base accession plus version number), searchable across GenBank, ENA, and DDBJ. + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/nuccore/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://getentry.ddbj.nig.ac.jp/getentry/na/__value__ + header: INSDC + orderOnDetailsPage: 1040 + ingest: genbankAccession + noInput: true + perSegment: true + oneHeader: true + type: string + - name: bioprojectAccession + displayName: BioProject accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/bioproject/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/bioproject/__value__ + header: INSDC + orderOnDetailsPage: 1060 + ingest: bioprojects + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + type: string + - name: gcaAccession + displayName: GCA accession + definition: The GenBank genome assembly accession number for the sequence. + customDisplay: + type: link + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + header: INSDC + orderOnDetailsPage: 1100 + noInput: true + oneHeader: true + type: string + - name: biosampleAccession + displayName: BioSample accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/biosample/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/biosample/__value__ + header: INSDC + orderOnDetailsPage: 1080 + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + oneHeader: true + type: string + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + header: Sample details + orderOnDetailsPage: 620 + type: string + - name: sampleReceivedDate + ontology_id: GENEPIO:0001177 + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + type: date + orderOnDetailsPage: 260 + header: Sample details + - name: sampleType + displayName: Sample type + definition: Method of sampling. + header: Sampling + orderOnDetailsPage: 1200 + type: string + - name: purposeOfSampling + displayName: Purpose of sampling + ontology_id: GENEPIO:0001198 + definition: The reason that the sample was collected. + header: Sampling + orderOnDetailsPage: 1220 + ingest: ncbiPurposeOfSampling + type: string + - name: presamplingActivity + ontology_id: GENEPIO:0100433 + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + header: Sampling + orderOnDetailsPage: 1240 + type: string + - name: anatomicalMaterial + ontology_id: GENEPIO:0001211 + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + header: Sampling + orderOnDetailsPage: 1260 + type: string + - name: anatomicalPart + ontology_id: GENEPIO:0001214 + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + header: Sampling + orderOnDetailsPage: 1280 + type: string + - name: bodyProduct + ontology_id: GENEPIO:0001216 + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + header: Sampling + orderOnDetailsPage: 1300 + type: string + - name: environmentalMaterial + ontology_id: GENEPIO:0001223 + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + header: Sampling + orderOnDetailsPage: 1320 + type: string + - name: environmentalSite + ontology_id: GENEPIO:0001232 + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + header: Sampling + orderOnDetailsPage: 1340 + type: string + - name: collectionDevice + ontology_id: GENEPIO:0001234 + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + header: Sampling + orderOnDetailsPage: 1360 + type: string + - name: collectionMethod + ontology_id: GENEPIO:0001241 + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + header: Sampling + orderOnDetailsPage: 1380 + type: string + - name: foodProduct + ontology_id: GENEPIO:0100444 + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + header: Sampling + orderOnDetailsPage: 1400 + type: string + - name: foodProductProperties + ontology_id: GENEPIO:0100445 + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + header: Sampling + orderOnDetailsPage: 1420 + type: string + - name: specimenProcessing + ontology_id: GENEPIO:0100435 + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + header: Specimen processing + orderOnDetailsPage: 1500 + type: string + - name: specimenProcessingDetails + ontology_id: GENEPIO:0100311 + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + header: Specimen processing + orderOnDetailsPage: 1520 + type: string + - name: experimentalSpecimenRoleType + ontology_id: GENEPIO:0100921 + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + header: Specimen processing + type: string + - name: hostAge + ontology_id: GENEPIO:0001392 + definition: Age of host at the time of sampling. + displayName: Host age + type: int + header: Host + rangeSearch: true + - name: hostAgeBin + ontology_id: GENEPIO:0001394 + definition: The age category of the host at the time of sampling. + displayName: Host age bin + header: Host + type: string + - name: hostGender + ontology_id: GENEPIO:0001395 + definition: The gender of the host at the time of sample collection. + displayName: Host gender + header: Host + ingest: ncbiHostSex + orderOnDetailsPage: 1640 + type: string + - name: hostOriginCountry + ontology_id: GENEPIO:0100438 + definition: The country of origin of the host. + displayName: Host origin country + header: Host + type: string + - name: hostDisease + ontology_id: GENEPIO:0001391 + definition: The name of the disease experienced by the host. + displayName: Host disease + header: Host + type: string + - name: signsAndSymptoms + ontology_id: GENEPIO:0001400 + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + header: Host + type: string + - name: hostHealthState + ontology_id: GENEPIO:0001388 + definition: Health status of the host at the time of sample collection. + displayName: Host health state + header: Host + type: string + - name: hostHealthOutcome + ontology_id: GENEPIO:0001390 + definition: Disease outcome in the host. + displayName: Host health outcome + header: Host + orderOnDetailsPage: 1740 + type: string + - name: travelHistory + ontology_id: GENEPIO:0001416 + definition: Travel history in last six months. + displayName: Travel history + header: Host + orderOnDetailsPage: 1760 + type: string + - name: exposureEvent + ontology_id: GENEPIO:0001417 + definition: Event leading to exposure. + displayName: Exposure event + header: Host + orderOnDetailsPage: 1780 + type: string + - name: hostRole + ontology_id: GENEPIO:0001419 + definition: The role of the host in relation to the exposure setting. + displayName: Host role + header: Host + orderOnDetailsPage: 1800 + type: string + - name: exposureSetting + ontology_id: GENEPIO:0001428 + definition: The setting leading to exposure. + displayName: Exposure setting + header: Host + orderOnDetailsPage: 1820 + type: string + - name: exposureDetails + ontology_id: GENEPIO:0001431 + definition: Additional host exposure information. + displayName: Exposure details + header: Host + orderOnDetailsPage: 1840 + type: string + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + header: Host + orderOnDetailsPage: 1860 + type: string + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + header: Host + orderOnDetailsPage: 1880 + type: string + - name: hostVaccinationStatus + ontology_id: GENEPIO:0001404 + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + header: Host + orderOnDetailsPage: 1900 + type: string + - name: purposeOfSequencing + ontology_id: GENEPIO:0001445 + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + header: Sequencing + orderOnDetailsPage: 2000 + type: string + - name: sequencingDate + ontology_id: GENEPIO:0001447 + definition: The date the sample was sequenced. + displayName: Sequencing date + type: date + orderOnDetailsPage: 2020 + header: Sequencing + - name: ampliconPcrPrimerScheme + ontology_id: GENEPIO:0001456 + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + header: Sequencing + orderOnDetailsPage: 2040 + type: string + - name: ampliconSize + ontology_id: GENEPIO:0001449 + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + header: Sequencing + orderOnDetailsPage: 2060 + type: string + - name: sequencingInstrument + ontology_id: GENEPIO:0001452 + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + header: Sequencing + orderOnDetailsPage: 2080 + type: string + - name: sequencingProtocol + ontology_id: GENEPIO:0001454 + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + header: Sequencing + orderOnDetailsPage: 2100 + type: string + - name: sequencingAssayType + ontology_id: GENEPIO:0100997 + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + header: Sequencing + orderOnDetailsPage: 2120 + type: string + - name: sequencedByOrganization + ontology_id: GENEPIO:0100416 + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + header: Sequencing + orderOnDetailsPage: 2140 + type: string + - name: sequencedByContactName + ontology_id: GENEPIO:0100471 + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + header: Sequencing + orderOnDetailsPage: 2160 + type: string + - name: sequencedByContactEmail + ontology_id: GENEPIO:0100422 + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + header: Sequencing + orderOnDetailsPage: 2180 + type: string + - name: rawSequenceDataProcessingMethod + ontology_id: GENEPIO:0001458 + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + header: Sequencing + orderOnDetailsPage: 2200 + type: string + - name: dehostingMethod + ontology_id: GENEPIO:0001459 + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + header: Sequencing + orderOnDetailsPage: 2220 + type: string + - name: referenceGenomeAccession + ontology_id: GENEPIO:0001485 + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + header: Sequencing + orderOnDetailsPage: 2240 + type: string + - name: consensusSequenceSoftwareName + ontology_id: GENEPIO:0001463 + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + header: Sequencing + orderOnDetailsPage: 2260 + type: string + - name: consensusSequenceSoftwareVersion + ontology_id: GENEPIO:0001469 + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + header: Sequencing + orderOnDetailsPage: 2280 + type: string + - name: depthOfCoverage + ontology_id: GENEPIO:0001474 + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2300 + - name: breadthOfCoverage + ontology_id: GENEPIO:0001475 + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2320 + - name: qualityControlMethodName + ontology_id: GENEPIO:0100557 + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + header: Sequencing + orderOnDetailsPage: 2340 + type: string + - name: qualityControlMethodVersion + ontology_id: GENEPIO:0100558 + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + header: Sequencing + orderOnDetailsPage: 2360 + type: string + - name: qualityControlDetermination + ontology_id: GENEPIO:0100559 + definition: The determination of a quality control assessment. + displayName: Quality control determination + header: Sequencing + orderOnDetailsPage: 2380 + type: string + - name: qualityControlIssues + ontology_id: GENEPIO:0100560 + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + header: Sequencing + orderOnDetailsPage: 2400 + type: string + - name: qualityControlDetails + ontology_id: GENEPIO:0100561 + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + header: Diagnostics + orderOnDetailsPage: 2500 + type: string + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + header: Diagnostics + orderOnDetailsPage: 2520 + type: string + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + header: Diagnostics + orderOnDetailsPage: 2540 + type: string + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + header: Diagnostics + orderOnDetailsPage: 2560 + type: string + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + header: Diagnostics + orderOnDetailsPage: 2580 + type: string + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + orderOnDetailsPage: 2600 + header: Diagnostics + type: string + - name: length + type: int + header: Alignment and QC metrics + isSequenceFilter: true + noInput: true + rangeSearch: true + initiallyVisible: true + perSegment: true + displayName: Length + definition: The length of the sequence. + orderOnDetailsPage: 2700 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: hostTaxonId + type: string + autocomplete: true + displayName: Host species + definition: Taxon ID for the host. + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostTaxId + noInput: true + orderOnDetailsPage: 1540 + - name: hostNameScientific + displayName: Host name - scientific + definition: The scientific name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostName + noInput: true + orderOnDetailsPage: 1560 + type: string + - name: hostNameCommon + displayName: Host name - common + definition: The common name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostCommonName + noInput: true + orderOnDetailsPage: 1580 + type: string + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + type: boolean + autocomplete: true + header: Host + ingest: ncbiIsLabHost + orderOnDetailsPage: 1920 + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + generateIndex: true + orderOnDetailsPage: 1940 + autocomplete: true + header: Host + type: string + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + type: int + header: Host + orderOnDetailsPage: 1960 + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + generateIndex: true + autocomplete: true + header: Host + orderOnDetailsPage: 1980 + type: string + - name: ncbiSourceDb + displayName: NCBI source DB + definition: Indicates if the source of the viral nucleotide record is from a GenBank submitter or from NCBI-derived curation (RefSeq). + generateIndex: true + autocomplete: true + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + type: string + - name: ncbiVirusName + displayName: NCBI Virus name + definition: Scientific name for the organism from the NCBI Taxonomy. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: ncbiVirusTaxId + displayName: NCBI Virus tax ID + definition: Identifier for the organism from the NCBI Taxonomy. + type: int + autocomplete: true + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/labs/virus/vssi/#/virus?SeqType_s=Nucleotide&VirusLineage_ss=taxid:__value__ + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/sra/?term=__value__ + header: INSDC + orderOnDetailsPage: 1120 + ingest: ncbiSraAccessions + type: string + - name: totalSnps + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total SNPs + definition: Total count of nucleotide substitutions. + orderOnDetailsPage: 2720 + - name: totalInsertedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total inserted nucs + definition: Total count of inserted nucleotide positions. + orderOnDetailsPage: 2740 + - name: totalDeletedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + orderOnDetailsPage: 2760 + displayName: Total deleted nucs + definition: Total count of deleted nucleotide positions. + - name: totalAmbiguousNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total ambiguous nucs + definition: Total count of ambiguous nucleotides (not A, C, G, T, or N). + orderOnDetailsPage: 2780 + - name: totalUnknownNucs + orderOnDetailsPage: 2800 + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total unknown nucs + definition: Total count of missing (N) nucleotides. + - name: totalFrameShifts + isSequenceFilter: true + type: int + rangeSearch: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Total frame shifts + definition: Total count of detected frame shifts. + orderOnDetailsPage: 2820 + - name: frameShifts + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Frame shifts + definition: Frame-shifting insertions or deletions detected in CDS regions. + orderOnDetailsPage: 2840 + type: string + - name: totalStopCodons + isSequenceFilter: true + perSegment: true + displayName: Total stop codons + definition: Number of penalized premature stop codons. + type: int + header: Alignment and QC metrics + noInput: true + - name: stopCodons + isSequenceFilter: true + perSegment: true + displayName: Stop codons + definition: Premature stop codons not in the ignored list (penalized). + header: Alignment and QC metrics + noInput: true + type: string + - name: completeness + isSequenceFilter: true + type: float + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Completeness + definition: Fraction of reference positions covered by the sequence (0.0 to 1.0). + rangeSearch: true + percentage: true + orderOnDetailsPage: 2860 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: clade_cv_a10 + type: string + - name: clade_cv_a16 + type: string + - name: clade_ev_a71 + type: string + - name: clade_ev_d68 + type: string + - name: genotype + type: string + - name: versionComment + type: string + extraInputFields: + - name: host + type: string + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + desired: true + position: last + organismName: Enterovirus + image: /images/organisms/enterovirus.jpg + linkOuts: + - name: Nextclade (CV-A16) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:main+rich|fasta]}}&dataset-name=enpen/enterovirus/cv-a16&dataset-server=https://raw.githubusercontent.com/nextstrain/nextclade_data/evs-datasets/data_output + onlyForReferences: + main: CV-A16 + - name: Nextclade (CV-A10) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:main+rich|fasta]}}&dataset-name=enpen/enterovirus/cv-a10&dataset-server=https://raw.githubusercontent.com/nextstrain/nextclade_data/evs-datasets/data_output + onlyForReferences: + main: CV-A10 + - name: Nextclade (EV-A71) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:main+rich|fasta]}}&dataset-name=enpen/enterovirus/ev-a71&dataset-server=https://raw.githubusercontent.com/nextstrain/nextclade_data/evs-datasets/data_output + onlyForReferences: + main: EV-A71 + - name: Nextclade (EV-D68) + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences:main+rich|fasta]}}&dataset-name=enpen/enterovirus/ev-d68 + onlyForReferences: + main: EV-D68 + metadataAdd: + - name: clade_cv_a16 + isSequenceFilter: true + relatesToSegment: main + displayName: Clade CV-A16 + definition: The clade assigned by Nextclade, only populated for sequences of genotype CV-A16. + onlyForReference: CV-A16 + header: Clade + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + - name: clade_cv_a10 + isSequenceFilter: true + relatesToSegment: main + displayName: Clade CV-A10 + definition: The clade assigned by Nextclade, only populated for sequences of genotype CV-A10. + onlyForReference: CV-A10 + header: Clade + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + - name: clade_ev_a71 + isSequenceFilter: true + relatesToSegment: main + displayName: Clade EV-A71 + definition: The clade assigned by Nextclade, only populated for sequences of genotype EV-A71. + onlyForReference: EV-A71 + header: Clade + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + - name: clade_ev_d68 + isSequenceFilter: true + relatesToSegment: main + displayName: Clade EV-D68 + definition: The clade assigned by Nextclade, only populated for sequences of genotype EV-D68. + onlyForReference: EV-D68 + header: Clade + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + - name: genotype + isSequenceFilter: true + displayName: Genotype + definition: The genotype of this sequence, determined by which Nextclade reference dataset the sequence was assigned to during processing. + header: Genotype + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + tableColumns: + - sampleCollectionDate + - geoLocCountry + - authors + - authorAffiliations + - ncbiReleaseDate + - hostNameScientific + - genotype + - segment + defaultOrderBy: sampleCollectionDate + defaultOrder: descending + multiFieldSearches: + - name: identifier + displayName: Sample Identifier + orderInSearchDisplay: -10000 + fields: + - accessionVersion + - submissionId + - insdcAccessionFull + - bioprojectAccession + - gcaAccession + - biosampleAccession + - insdcRawReadsAccession + - name: contributor + displayName: Contributor + orderInSearchDisplay: -9999 + fields: + - authors + - authorAffiliations + - sequencedByOrganization + - sequencedByContactName + - submitter + - groupName + inputFields: + - name: id + displayName: ID + example: GJP123 + noEdit: true + required: true + definition: Your sequence identifier; should match the sequence's id in the FASTA file - this is used to link the metadata to the FASTA sequence. + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + required: true + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + required: true + options: *id001 + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + - name: geoLocSite + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + - name: bioprojectAccession + displayName: BioProject accession + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + - name: biosampleAccession + displayName: BioSample accession + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + - name: sampleReceivedDate + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + - name: sampleType + displayName: Sample type + definition: Method of sampling. + - name: purposeOfSampling + displayName: Purpose of sampling + definition: The reason that the sample was collected. + - name: presamplingActivity + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + - name: anatomicalMaterial + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + - name: anatomicalPart + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + - name: bodyProduct + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + - name: environmentalMaterial + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + - name: environmentalSite + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + - name: collectionDevice + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + - name: collectionMethod + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + - name: foodProduct + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + - name: foodProductProperties + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + - name: specimenProcessing + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + - name: specimenProcessingDetails + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + - name: experimentalSpecimenRoleType + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + - name: hostAge + definition: Age of host at the time of sampling. + displayName: Host age + - name: hostAgeBin + definition: The age category of the host at the time of sampling. + displayName: Host age bin + - name: hostGender + definition: The gender of the host at the time of sample collection. + displayName: Host gender + - name: hostOriginCountry + definition: The country of origin of the host. + displayName: Host origin country + - name: hostDisease + definition: The name of the disease experienced by the host. + displayName: Host disease + - name: signsAndSymptoms + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + - name: hostHealthState + definition: Health status of the host at the time of sample collection. + displayName: Host health state + - name: hostHealthOutcome + definition: Disease outcome in the host. + displayName: Host health outcome + - name: travelHistory + definition: Travel history in last six months. + displayName: Travel history + - name: exposureEvent + definition: Event leading to exposure. + displayName: Exposure event + - name: hostRole + definition: The role of the host in relation to the exposure setting. + displayName: Host role + - name: exposureSetting + definition: The setting leading to exposure. + displayName: Exposure setting + - name: exposureDetails + definition: Additional host exposure information. + displayName: Exposure details + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + - name: hostVaccinationStatus + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + - name: purposeOfSequencing + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + - name: sequencingDate + definition: The date the sample was sequenced. + displayName: Sequencing date + - name: ampliconPcrPrimerScheme + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + - name: ampliconSize + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + - name: sequencingInstrument + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + - name: sequencingProtocol + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + - name: sequencingAssayType + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + - name: sequencedByOrganization + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + - name: sequencedByContactName + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + - name: sequencedByContactEmail + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + - name: rawSequenceDataProcessingMethod + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + - name: dehostingMethod + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + - name: referenceGenomeAccession + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + - name: consensusSequenceSoftwareName + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + - name: consensusSequenceSoftwareVersion + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + - name: depthOfCoverage + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + - name: breadthOfCoverage + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + - name: qualityControlMethodName + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + - name: qualityControlMethodVersion + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + - name: qualityControlDetermination + definition: The determination of a quality control assessment. + displayName: Quality control determination + - name: qualityControlIssues + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + - name: qualityControlDetails + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + - name: host + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + desired: true +referenceGenomes: + - name: main + references: + - name: CV-A16 + sequence: 'TTAAAACAGCCTGTGGGTTGTACCCACCCACAGGGCCCACTGGGCGCTAGCACTCTGATTCTACGGAATCCTTGTGCGCCTGTTTTATGTCCCTTCCCCCAATCAGTAACTTAGAAGCATTGCACCTCTTTCGACCGTTAGCAGGCGTGGCGCACCAGCCATGTCTTGGTCAAGCACTTCTGTTTCCCCGGACCGAGTATCAATAGACTGCTCACGCGGTTGAGGGAGAAAACGTCCGTTACCCGGCTAACTACTTCGAGAAGCCTAGTAGCACCATGAAAGTTGCAGAGTGTTTCGCTCAGCACTTCCCCCGTGTAGATCAGGTCGATGAGTCACTGCGATCCCCACGGGCGACCGTGGCAGTGGCTGCGTTGGCGGCCTGCCTGTGGGGTAACCCACAGGACGCTCTAATATGGACATGGTGCAAAGAGTCTATTGAGCTAGTTAGTAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACTGCGGAGCACATACCCTCGACCCAGGGGGCAGTGTGTCGTAACGGGCAACTCTGCAGCGGAACCGACTACTTTGGGTGTCCGTGTTTCCTTTTATTCTTATACTGGCTGCTTATGGTGACAATTGAAAGATTGTTACCATATAGCTATTGGATTGGCCATCCGGTGTGCAACAGAGCTATTATTTACCTATTTGTTGGGTATATACCACTCACATCCAGAAAAACCCTCGACACACTAGTATACATTCTTTACTTGAATTCTAGAAAATGGGGTCACAAGTCTCAACCCAACGATCGGGTTCCCACGAAAATTCGAACTCAGCATCAGAAGGATCTACTATAAACTACACCACCATCAACTATTACAAGGATGCATATGCTGCCAGCGCGGGTCGCCAAGATATGTCTCAGGACCCTAAGAAATTTACAGACCCTGTGATGGATGTCATACACGAGATGGCTCCTCCCTTGAAATCACCCAGTGCTGAAGCTTGTGGTTATAGTGATCGAGTTGCCCAACTCACAATTGGAAACTCCACAATCACTACACAAGAAGCTGCAAACATTATAATAGCATATGGGGAATGGCCCGAGTATTGCAAGGACGCTGATGCCACAGCTGTTGACAAGCCCACCAGACCCGATGTGTCGGTAAATAGGTTCTTCACCCTTGATACTAAATCGTGGGCTAAAGACTCGAAGGGATGGTACTGGAAATTCCCGGACGTTTTGACGGAGGTGGGCGTGTTTGGGCAGAATGCGCAATTCCATTATCTGTATAGATCCGGATTCTGTGTGCACGTGCAGTGCAATGCCAGCAAATTCCACCAGGGTGCTCTCTTGGTTGCCATACTGCCTGAGTACGTGCTGGGCACCATTGCCGGGGGCGATGGTAACGAGAACTCACATCCCCCGTACGTCACCACCCAGCCAGGACAGGTGGGTGCTGTACTTACAAATCCTTATGTTTTGGATGCTGGGGTACCCCTTAGTCAATTGACGGTGTGTCCGCATCAGTGGATTAACCTACGAACCAACAACTGTGCGACCATCATAGTACCATACATGAATACTGTACCATTCGATTCGGCCCTAAACCATTGCAACTTTGGCTTAATTGTAGTGCCCGTAGTACCACTCGACTTTAACGCTGGAGCTACATCAGAAATACCAATAACTGTCACCATCGCACCCATGTGCGCTGAATTTGCAGGTTTGCGACAGGCAATCAAACAGGGGATACCTACCGAGTTGAAGCCCGGTACTAATCAGTTTCTCACTACTGATGATGGTGTCTCAGCTCCCATTTTGCCCGGATTCCACCCTACTCCAGCCATACACATACCTGGTGAAGTGCGCAATCTGTTGGAGATTTGCAGGGTAGAGACCATATTGGAGGTGAACAATCTACAGAGTAATGAGACAACCCCCATGCAACGACTATGCTTCCCTGTCTCGGTGCAGAGTAAGACGGGGGAATTGTGTGCTGTTTTTAGGGCCGACCCTGGTAGGAATGGACCGTGGCAGTCGACTATTTTAGGACAGTTGTGTAGGTACTATACTCAATGGTCTGGATCTCTGGAAGTCACTTTCATGTTTGCTGGATCGTTCATGGCAACAGGAAAGATGCTAATCGCATACACACCTCCGGGGGGTGGGGTCCCAGCAGATCGGCTCACTGCAATGCTGGGAACCCATGTGATATGGGATTTTGGCCTCCAATCCTCAGTCACGCTAGTTATACCATGGATAAGCAACACACACTATAGGGCGCACGCCAAGGACGGTTATTTTGATTATTACACCACTGGCACGATCACTATATGGTATCAGACAAATTATGTCGTACCTATTGGAGCCCCCACAACAGCCTATATTGTGGCCCTCGCAGCCGCTCAGGACAACTTTACCATGAAACTGTGCAAAGACACTGAGGATATTGAGCAATCTGCAAACATCCAGGGTGATGGAATTGCAGACATGATTGACCAGGCTGTCACTTCCCGAGTTGGTCGTGCGCTGACATCCTTACAGGTAGAACCTACCGCCGCCAACACCAATGCTAGTGAGCACAGATTGGGCACCGGGCTCGTCCCCGCCTTGCAGGCTGCAGAGACCGGCGCCTCTTCTAATGCACAGGATGAGAATCTTATAGAAACCCGGTGTGTGTTGAACCATCACTCCACTCAAGAGACCACGATTGGCAACTTTTTCAGTCGAGCAGGACTAGTGAGTATTATTACCATGCCCACCACAGGTACCCAAAACACCGATGGGTATGTGAACTGGGATATTGACTTGATGGGTTATGCTCAAATGAGGCGTAAGTGTGAGCTATTCACATACATGCGCTTTGATGCAGAGTTTACATTTGTAGCTGCCAAACCAAACGGTGAGCTAGTACCACAATTGTTGCAGTACATGTATGTGCCTCCCGGAGCTCCAAAACCTACGTCCCGGGATTCCTTTGCCTGGCAGACTGCTACCAATCCTTCCATCTTCGTCAAGTTGACTGACCCCCCGGCACAAGTGTCAGTACCCTTCATGTCTCCCGCCAGCGCCTACCAGTGGTTTTACGATGGCTATCCAACGTTTGGAGCCCATCCACAATCGAATGACGCAGACTATGGCCAATGTCCAAACAACATGATGGGCACCTTTAGCATCAGAACTGTAGGCACTGAGAAATCCCCCCATTCTATCACCCTTCGGGTGTATATGAGAATCAAGCATGTCAGGGCCTGGATACCCCGGCCACTCAGAAACCAACCGTACCTTTTTAAGACAAATCCAAATTATAAAGGCAACGACATCAAATGCACCAGCACAAGTAGGGACAAAATAACAACACTAGGAAAATTTGGCCAGCAATCTGGTGCTATTTACGTGGGTAACTATAGAGTGGTCAATCGTCACTTGGCCACGCACAATGACTGGGCCAATTTAGTATGGGAAGACAGCTCAAGGGATCTTTTAGTCTCCTCCACCACTGCACAGGGATGTGACACTATTGCTCGATGCGATTGTCAAACGGGGGTGTATTATTGCAGCTCAAGAAGAAAGCACTACCCAGTCAGTTTCTCAAAGCCCAGTCTAATCTTTGTGGAAGCTAGTGAGTACTACCCAGCCAGGTACCAATCGCACCTCATGCTCGCTGTAGGGCATTCCGAGCCTGGAGATTGCGGAGGTATTCTCAGGTGCCAGCATGGTGTTGTTGGTATAGTGTCTACAGGTGGAAATGGGCTTGTTGGTTTTGCTGATGTTAGAGACCTCCTGTGGCTGGATGAAGAGGCTATGGAACAAGGAGTGTCTGATTACATCAAAGGGCTTGGTGATGCGTTTGGGACAGGTTTCACTGACGCTGTTTCCAGAGAGGTTGAGGCTCTTAAAAATCACCTCATAGGATCTGAGGGTGCAGTTGAGAAGATTCTTAAAAACTTAATTAAGCTCATTTCTGCACTAGTGATTGTGATCAGGAGTGATTACGATATGGTCACTCTCACAGCAACTCTGGCTCTAATTGGGTGTCATGGCAGCCCTTGGGCTTGGATCAAGGCTAAGACAGCATCTATTCTGGGCATCCCCATTGCTCAGAAACAGAGCGCATCATGGCTAAAAAAATTCAACGACATGGCTAATGCTGCCAAAGGGCTAGAATGGATATCTAACAAAATTAGTAAGTTCATTGATTGGCTCAAGGAGAAGATTATACCAGCAGCTAAGGAAAAGGTTGAATTCTTGAACAACCTGAAACAGCTACCATTATTAGAGAACCAGATCTCAAACCTAGAACAATCTGCTGCTTCACAAGAGGACCTTGAAGCAATGTTTGGAAATGTGTCATACCTTGCCCATTTCTGCCGCAAATTTCAGCCACTGTACGCTACAGAAGCCAAGAGGGTCTACGCCCTGGAGAAAAGGATGAATAACTACATGCAGTTCAAGAGCAAACACCGTATTGAACCTGTATGTCTCATCATCAGGGGCTCACCAGGTACTGGGAAATCTTTGGCAACCGGTATCATTGCCCGGGCAATAGCCGACAAGTACCACTCCAGTGTGTATTCACTTCCCCCAGACCCAGATCACTTTGATGGGTACAAACAACAAGTAGTTACAGTTATGGATGATTTGTGTCAAAATCCTGATGGTAAGGATATGTCATTATTTTGTCAAATGGTATCCACTGTGGACTTTATCCCACCAATGGCCTCCCTAGAAGAAAAAGGAGTTTCTTTCACATCCAAATTCGTCATTGCGTCCACCAATGCCAGCAACATCATAGTGCCAACGGTGTCCGACTCTGATGCCATCCGCCGCAGATTCTATATGGACTGTGACATTGAGGTGACAGACTCATACAAGACAGATTTGGGCAGATTGGATGCAGGACGGGCCGCCAGATTATGTTCTGAAAACAACACTGCAAACTTCAAGCGCTGCAGCCCACTGGTGTGTGGGAAAGCCATTCAACTCAGAGATAGGAAATCCAAGGTCAGATACAGTGTGGACACGGTGGTCTCCGAACTCATTAGAGAGTACAATAACAGATATGCTATTGGTAATACAATCGAAGCCTTGTTTCAGGGTCCACCTAAGTTTAGGCCCATTAGAATCAGTCTTGAGGAAAAGCCAGCCCCAGATGCCATTAGTGATCTCCTCGCTAGTGTGGACAGTGAGGAAGTGCGTCAATACTGCAGAGACCAAGGCTGGATCATCCCAGAAACTCCCACCAATGTCGAGCGGCACCTTAATAGGGCGGTGTTGATCATGCAGTCCATTGCCACTGTGGTAGCAGTTGTTTCACTGGTGTATGTTATCTACAAATTGTTTGCAGGGTTCCAAGGTGCATACTCTGGTGCCCCTAAGCAAACACTCAAGAAACCTATACTCCGCACAGCGACAGTGCAAGGCCCAAGTCTTGATTTTGCTCTCTCATTGCTTAGGAGGAACATCAGACAAGTCCAGACAGACCAAGGCCACTTCACTATGCTAGGTGTTAGAGATCGCTTGGCTGTCCTCCCGCGACACTCGCAGCCTGGGAAGACAATATGGGTAGAACACAAGCTTATAAACATCTTAGATGCTGTTGAGTTGGTGGATGAACAAGGGGTCAACTTAGAACTGACCCTGGTTACTCTTGATACCAATGAGAAGTTCAGAGACATCACCAAGTTCATCCCAGAAAACATTAGCGCTGCCAGTGATGCCACCCTAGTGATCAACACAGAGCACATGCCCTCCATGTTCGTGCCAGTGGGTGACGTTGTGCAATACGGTTTCCTAAACCTCAGCGGAAAACCCACCCATCGTACCATGATGTACAACTTTCCCACTAAGGCTGGACAATGTGGTGGAGTGGTGACATCTGTTGGAAAGGTTATCGGCATACATATTGGTGGCAATGGCAGGCAGGGCTTTTGTGCAGGACTCAAGAGGAGTTATTTTGCCAGTGAGCAAGGAGAGATCCAGTGGGTCAAGCCCAATAAAGAGACAGGCAGACTCAACATCAATGGGCCGACTCGCACTAAGCTCGAACCCAGTGTGTTCCATGATGTCTTTGAGGGGAACAAAGAACCAGCAGTCTTGCATAGCAGAGACCCACGCCTTGAAGTGGACTTTGAGCAGGCTTTATTCTCCAAGTATGTAGGGAACACACTGCATGAGCCTGACGAGTACATCAAAGAGGCGGCCCTCCACTATGCAAATCAGTTGAAGCAACTGGACATCAACACTTCTCAAATGAGTATGGAGGAAGCTTGCTATGGTACTGAGAACCTTGAGGCCATTGATCTCCACACCAGCGCGGGCTACCCCTACAGTGCTCTGGGAATAAAGAAGAGAGATATTTTAGATCCCACTACCAGAGATGTGAGTAAGATGAAATTCTATATGGACAAGTATGGCCTTGACCTTCCTTACTCTACCTATGTCAAGGATGAGCTCCGCTCGATAGATAAGATTAAGAAAGGAAAATCCCGCCTGATCGAGGCTAGTAGTCTAAATGACTCAGTGTACCTCAGAATGGCCTTTGGTCACTTGTACGAGACTTTCCACGCAAATCCAGGAACCATAACTGGCTCAGCTGTGGGATGCAACCCGGATACATTCTGGAGTAAACTACCAATCCTGCTTCCAGGTTCACTCTTTGCATTTGATTACTCAGGCTATGATGCTAGTCTCAGTCCAGTCTGGTTTAGAGCACTGGAGCTGGTCCTCAGGGAGGTGGGCTATAGTGAGGAGGCAGTCTCCCTTATAGAGGGGATTAACCATACACACCATGTATACCGCAATAAAACTTACTGTGTACTTGGTGGAATGCCCTCAGGCTGTTCAGGAACATCCATTTTCAACTCAATGATCAACAACATCATAATTAGGACATTGCTCATAAAAACATTCAAGGGCATTGATTTGGACGAGCTTAACATGGTCGCCTACGGGGATGATGTGCTTGCTAGTTATCCTTTCCCAATCGATTGCCTGGAATTGGCAAGAACAGGCAAAGAGTATGGTTTGACTATGACTCCTGCAGACAAATCTCCTTGCTTCAATGAGGTAAATTGGGGCAATGCAACCTTTCTCAAAAGAGGCTTCTTGCCCGATGAGCAATTCCCTTTCTTGATCCATCCTACCATGCCAATGAAGGAGATTCACGAATCTATTCGATGGACCAAGGACGCACGAAACACTCAAGATCACGTACGATCCCTGTGTCTTTTGGCGTGGCACAATGGTAAGCAGGAGTATGAAAAATTTGTGAGCACAATTAGGTCTGTCCCAGTAGGAAAAGCTTTGGCTATACCGAATTATGAAAATCTGAGACGCAATTGGCTCGAATTATTTTAGAGGTCAGATATACCTCAACCCCACCAGGGATCTGGTCGTGAATATGACTGGTGGGGGTAAATTTGTTATAACCAGAATAGC' + insdcAccessionFull: U05876.1 + genes: + - name: VP4 + sequence: 'MGSQVSTQRSGSHENSNSASEGSTINYTTINYYKDAYAASAGRQDMSQDPKKFTDPVMDVIHEMAPPLK' + - name: VP2 + sequence: 'SPSAEACGYSDRVAQLTIGNSTITTQEAANIIIAYGEWPEYCKDADATAVDKPTRPDVSVNRFFTLDTKSWAKDSKGWYWKFPDVLTEVGVFGQNAQFHYLYRSGFCVHVQCNASKFHQGALLVAILPEYVLGTIAGGDGNENSHPPYVTTQPGQVGAVLTNPYVLDAGVPLSQLTVCPHQWINLRTNNCATIIVPYMNTVPFDSALNHCNFGLIVVPVVPLDFNAGATSEIPITVTIAPMCAEFAGLRQAIKQ' + - name: VP3 + sequence: 'GIPTELKPGTNQFLTTDDGVSAPILPGFHPTPAIHIPGEVRNLLEICRVETILEVNNLQSNETTPMQRLCFPVSVQSKTGELCAVFRADPGRNGPWQSTILGQLCRYYTQWSGSLEVTFMFAGSFMATGKMLIAYTPPGGGVPADRLTAMLGTHVIWDFGLQSSVTLVIPWISNTHYRAHAKDGYFDYYTTGTITIWYQTNYVVPIGAPTTAYIVALAAAQDNFTMKLCKDTEDIEQSANIQ' + - name: VP1 + sequence: 'GDGIADMIDQAVTSRVGRALTSLQVEPTAANTNASEHRLGTGLVPALQAAETGASSNAQDENLIETRCVLNHHSTQETTIGNFFSRAGLVSIITMPTTGTQNTDGYVNWDIDLMGYAQMRRKCELFTYMRFDAEFTFVAAKPNGELVPQLLQYMYVPPGAPKPTSRDSFAWQTATNPSIFVKLTDPPAQVSVPFMSPASAYQWFYDGYPTFGAHPQSNDADYGQCPNNMMGTFSIRTVGTEKSPHSITLRVYMRIKHVRAWIPRPLRNQPYLFKTNPNYKGNDIKCTSTSRDKITTL' + - name: 2A + sequence: 'GKFGQQSGAIYVGNYRVVNRHLATHNDWANLVWEDSSRDLLVSSTTAQGCDTIARCDCQTGVYYCSSRRKHYPVSFSKPSLIFVEASEYYPARYQSHLMLAVGHSEPGDCGGILRCQHGVVGIVSTGGNGLVGFADVRDLLWLDEEAMEQ' + - name: 2B + sequence: 'GVSDYIKGLGDAFGTGFTDAVSREVEALKNHLIGSEGAVEKILKNLIKLISALVIVIRSDYDMVTLTATLALIGCHGSPWAWIKAKTASILGIPIAQKQ' + - name: 2C + sequence: 'SASWLKKFNDMANAAKGLEWISNKISKFIDWLKEKIIPAAKEKVEFLNNLKQLPLLENQISNLEQSAASQEDLEAMFGNVSYLAHFCRKFQPLYATEAKRVYALEKRMNNYMQFKSKHRIEPVCLIIRGSPGTGKSLATGIIARAIADKYHSSVYSLPPDPDHFDGYKQQVVTVMDDLCQNPDGKDMSLFCQMVSTVDFIPPMASLEEKGVSFTSKFVIASTNASNIIVPTVSDSDAIRRRFYMDCDIEVTDSYKTDLGRLDAGRAARLCSENNTANFKRCSPLVCGKAIQLRDRKSKVRYSVDTVVSELIREYNNRYAIGNTIEALFQ' + - name: 3A + sequence: 'GPPKFRPIRISLEEKPAPDAISDLLASVDSEEVRQYCRDQGWIIPETPTNVERHLNRAVLIMQSIATVVAVVSLVYVIYKLFAGFQ' + - name: 3B + sequence: 'GAYSGAPKQTLKKPILRTATVQ' + - name: 3C + sequence: 'GPSLDFALSLLRRNIRQVQTDQGHFTMLGVRDRLAVLPRHSQPGKTIWVEHKLINILDAVELVDEQGVNLELTLVTLDTNEKFRDITKFIPENISAASDATLVINTEHMPSMFVPVGDVVQYGFLNLSGKPTHRTMMYNFPTKAGQCGGVVTSVGKVIGIHIGGNGRQGFCAGLKRSYFASEQ' + - name: 3D + sequence: 'GEIQWVKPNKETGRLNINGPTRTKLEPSVFHDVFEGNKEPAVLHSRDPRLEVDFEQALFSKYVGNTLHEPDEYIKEAALHYANQLKQLDINTSQMSMEEACYGTENLEAIDLHTSAGYPYSALGIKKRDILDPTTRDVSKMKFYMDKYGLDLPYSTYVKDELRSIDKIKKGKSRLIEASSLNDSVYLRMAFGHLYETFHANPGTITGSAVGCNPDTFWSKLPILLPGSLFAFDYSGYDASLSPVWFRALELVLREVGYSEEAVSLIEGINHTHHVYRNKTYCVLGGMPSGCSGTSIFNSMINNIIIRTLLIKTFKGIDLDELNMVAYGDDVLASYPFPIDCLELARTGKEYGLTMTPADKSPCFNEVNWGNATFLKRGFLPDEQFPFLIHPTMPMKEIHESIRWTKDARNTQDHVRSLCLLAWHNGKQEYEKFVSTIRSVPVGKALAIPNYENLRRNWLELF' + - name: CV-A10 + sequence: 'TTAAAACAGCCTGTGGGTTGCACCCACTCACAGGGCCCACTGGGCGCTAGCACTCTGGTACCGTGGTACCTTTGTGCGCCTGTTTTATACCCCCTTCCCCGTTTGAACATTAGAAGTAACGCACCTCGATCAGTAGCAGGCGCGGCGCACCAGCCGTGTCTTGATCAAGCACTTCTGTTTCCCCGGACCGAGTATCAATAGACTGCTCACGCGGTTGAAGGAGAAAACGTTCGTTATCCGGCTAACTACTTCGAGAAACTTAGTAGCACCATGGAAGTTGCAGAGTGTTTCGCTCAGCACTCCCCCCCCAGTGTAGATCAGGCTGATGGATCACCGCGTTCCTCACGGGTGACCGTGGCGGTGGTCGCGTTGGCGGCCTGCCCATGGGGCAACCCATGGGACGCTCTAATATGGACATGGTGTGAAGAGTCTATTGAGCTAGTTAGTAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACTGCGGAGCACGTGTCCCCAACCCAGGGGATAGCGTGTCGTAACGGGTAACTCTGCAGCGGAACCGACTACTTTGGGTGTCCGTGTTTCCTTTATTCTTATCCTGGCTGCTTATGGTGACAATTGAGAGATTGTTACCATATAGCTATTGGATTGGCCATCCGGTGTCTAACAGAGCTATAATATATCTTTTTGTTGGGTTCGTACCCCTCAATTTTGAGGTTGTTCTCACATTAAAGTACATCTTGGTGTTAAATATCAGAAGATGGGAGCACAAGTTTCATCTCAGAGGTCAGGATCCCACGAGACTGGGAATGTGGCTACTGGAGGATCGACCATCAACTTCACCAATATAAATTATTACAAGGATTCATATGCAGCTTCTGCTAGTCGTCAGGACTTCACACAAGATCCAAAGAAGTTCACTCAACCGGTTTTGGATTCCATCAGAGAGCTGTCTGCCCCCTTGAATTCACCATCAGTTGAAGCGTGTGGATATAGTGACCGAGTGGCACAACTAACGGTGGGTAACTCCTCCATTACGACACAAGAGGCTGCCAATATAGTTTTGGCATACGGTGAGTGGCCTGAGTACTGCCCTGATACTGACGCAACAGCGGTGGACAAGCCTACTCGTCCGGATGTGTCAGTTAATAGATTCTACACTTTAGATTCAAAGATGTGGCAAGAGAACTCTACTGGATGGTATTGGAAGTTCCCAGATGTTTTGAACAAGACCGGGGTGTTTGGTCAGAATGCACAGTTCCACTACTTGTATCGTTCAGGGTTCTGTCTTCATGTTCAATGTAACGCTAGCAAATTTCACCAGGGGGCTCTTCTTGTGGCAGTTATCCCGGAGTTCGTTCTAGCAGGCAGAGGTTCAAACACGAAGCCCAATGAAGCCCCTCACCCGGGATTTAATACCACCTTTCCTGGCACTGCTGGCGCTTCCTTCAACGACCCGTACGTGCTTGACTCTGGGGTACCTCTTAGCCAATCCTTAATATACCCACATCAGTGGATCAACTTAAGGACCAACAATTGTGCAACAATAATAGTACCTTATATCAATGCTGTCCCCTTTGATTCAGCCATCAATCATAGCAACTTTGGATTAATAGTTGTGCCTGTGAGTCCGCTGAAATACTCTTCAGGGGCCACCACTGCAATCCCAATCACAGTGACCATAGCGCCCCTCAATTCCGAATTTGGTGGGCTGCGCCAAGCTGTCAGCCAGGGACTCCCTACCGAGTTAAGACCAGGCACCAATCAGTTCCTAACAACGGAAGATGATACTGCCGCACCCATACTCCCAGGTTTCTCCCCCACCCCAAGCATCCACATTCCAGGAGAAGTACGCTCATTACTAGAACTGTGTAGGGTGGAAACCATACTGGAAGTGAACAACACGACCGATGCAACCGGTCTGAACAGACTCCTAATTCCAGTCTCCGCCCAAAACAAGGCAGATGAACTATGTGCTGCATTCATGGTTGATCCTGGTCGTATCGGGCCCTGGCAATCGACTTTGGTTGGTCAAATATGTAGGTACTACACACAATGGTCAGGCTCGTTAAAGGTCACATTCATGTTTACAGGCTCTTTTATGGCAACAGGTAAGATGCTGATAGCGTACTCACCACCAGGCAGTGCTCAACCAGCCAATAGGGAGACCGCAATGCTCGGGACCCACGTCATATGGGATTTTGGATTACAATCTTCAGTTTCACTGGTGATACCATGGATCAGCAACACACACTTCAGAACAGCCAAAACTGGTGGTAACTATGACTATTACACAGCTGGTGTGGTGACATTATGGTATCAAACAAATTATGTAGTCCCGCCTGAGACGCCCGGAGAGGCTTACATTATCGCCATGGGGGCTGCTCAAGACAACTTCACCTTGAAGATATGTAAAGACACGGATGAGGTTACCCAACAAGCTGTCTTACAAGGCGACCCCGTGGAGGACATCATCCACGACGCTTTGAGCAGCACTGTGCGGCGGGCCATAACTAGTGGTCAAGATGTCAACACAGCGGCCGGTACCGCTCCTAGCTCTCACAGGTTGGAGACTGGTCGTGTTCCCGCCCTACAAGCAGCAGAAACTGGAGCCACTTCTAACGCTACAGATGAGAACATGATAGAAACGCGGTGTGTCATGAACAGAAATGGAGTGTTGGAGGCGACTATAAGTCATTTCTTCTCACGCTCAGGTTTGGTGGGTGTTGTCAATCTAACTGACGGAGGCACCGATACAACGGGATATGCAGTGTGGGACATTGACATCATGGGTTTTGTGCAACTGCGGCGGAAATGTGAGATGTTCACATACATGAGATTCAACGCTGAGTTCACATTCGTCACTACAACAGAAAATGGCGAGGCAAGGCCATTTATGTTACAGTATATGTATGTACCTCCAGGTGCCCCTAAGCCAACGGGTAGAGATGCTTTTCAGTGGCAAACAGCGACAAATCCATCCGTTTTCGTTAAGCTCACAGATCCACCTGCTCAGGTATCAGTCCCCTTCATGTCACCTGCTAGTGCCTACCAATGGTTCTATGACGGGTATCCAACATTTGGACAACACCCGGAAACATCTAATACAACATATGGACAGTGCCCTAACAACATGATGGGGACCTTTGCTGTGAGAGTAGTGAGTAGAGTGGCTAGCCAGCTCAAACTACAGACACGAGTGTATATGAAGCTTAAGCATGTGAGAGCATGGATCCCTAGGCCAATAAGATCCCAGCCTTACCTCCTAAAGAATTTTCCAAATTATGATAGTAGTAAGATCACATACAGCGCAAGAGATCGTGCCAGCATAAAACAAGCTAATATGGGAAAGTTTGGGCAACAGTCTGGGGCAATATATGTGGGTAATTACAGAGTGGTGAATAGACACTTGGCCACACATAACGATTGGGTAAATCTGGTGTGGGAGGATAGTTCTAGAGACTTGCTGGTCTCATCTACTACAGCACAGGGGTGTGATACCATAGCTCGGTGCAACTGTCAGACAGGTGTGTATTACTGTAACTCTCGTAGGAAACACTACCCGGTCAGCTTTTCCAAACCCAGCCTGATATTTGTTGAAGCTAGCGAATATTACCCAGCTAGGTACCAGTCACACCTCATGCTTGCCGAGGGTCACTCAGAACCTGGTGACTGTGGTGGCATCCTCAGGTGTCAACACGGTGTGGTTGGGTTAGTTTCCACCGGTGGAAATGGCCTCGTGGGGTTTGCTGATGTGAGGGACTTACTCTGGCTTGATGAGGAGGCTATGGAACAAGGAGTGTCTGACTACATCAAAGGTCTTGGTGATGCTTTTGGCACGGGCTTCACTGACGCAGTGTCCAGGGAAGTGGAAGCATTGAAAAATCACTTGATTGGCTCTGAGGGGGCTGTCGAGAAAATCTTGAAAAACCTGGTGAAGCTAATTTCAGCATTAGTTATAGTCATCAGGAGTGACTACGATATGGTCACTCTTACAGCTACGCTTGCCCTGATCGGGTGCCATGGGAGTCCTTGGGCGTGGATCAAATCAAAGACAGCTTCCATCTTAGGCATATCCATGGCACAAAAGCAAAGCGCCTCATGGCTGAAGAAGTTCAATGATATGGCAAATGCCGCAAAAGGGCTCGAGTGGATCTCCAATAAGATCAGCAAGTTTATTGATTGGCTTAAGGAGAAGATCATTCCAGCTGCTAAGGAGAAAGTCGAGTTCTTGAATAACTTAAAGCAACTCCCTCTGTTGGAAAATCAAATTTCCAATCTCGAGCAATCTGCCGCCTCACAGGAAGATCTAGAAGCTATATTTGGCAATGTGTCGTACCTAGCTCATTTTTGCCGCAAGTTCCAACCACTCTACGCAACTGAGGCCAAGAGAGTCTATGCCCTGGAGAAAAGAGTGAACAACTACATGCAGTTCAAGAGCAAACACCGTATTGAACCTGTATGCTTGATCATTAGAGGCTCTCCAGGTACGGGAAAATCACTTGCCACAGGTATTATAGCTAGAGCCATTGCTGATAAGTACCATTCCAGTGTCTATTCGCTCCCTCCAGACCCAGATCACTTCGACGGGTATAAACAACAGGTGGTCACAGTCATGGACGATCTCTGCCAGAATCCAGACGGGAAAGATATGTCCTTGTTCTGCCAAATGGTCTCCACAGTGGACTTTATACCACCCATGGCGTCACTGGAAGAAAAAGGCGTATCCTTCACCTCTAAGTTTGTCATTGCATCGACCAACGCTAGCAACATCATAGTCCCCACAGTCTCAGACTCAGATGCAATCCGCAGGAGATTCTATATGGACTGTGATATAGAAGTAACTGACTCTTACAAGACAGATCTCGGTCGATTGGACGCGGGTAGAGCTGCCAAGCTTTGTTCAGAGAATAACACTGCTAACTTCAAAAGATGCAGTCCACTTGTGTGTGGCAAAGCCATCCAATTGAAAGATAGGAAGTCTAAGGTCAGATATAGTGTTGACACTATGGTATCAGAGCTAATCAGAGAGTACAACAATAGATCTGCTGTTGGAAACACCATAGAAGCACTCTTCCAAGGGCCCCCCAAGTTCAGACCTATAAGAATCAGCCTCGAGGAGAAGCCAACACCAGATGCTATCAGTGACCTTCTCGCTAGTGTTGATAGCGAAGAGGTCCGACAGTATTGCAGAGAGCAAGGGTGGATAATCCCAGAAACACCAACCAACGTGGAACGACACCTCAATAGAGCAGTTCTGGTAATGCAGTCCATTGCTACCGTAGTTGCGGTTGTGTCCCTTGTGTATGTCATTTATAAACTGTTTGCCGGATTTCAAGGTGCCTACTCTGGAGCACCCAAGCAGGCGCTCAAGAAGCCTGTGCTAAGAACAGCTACTGTCCAAGGACCTAGCTTGGACTTCGCTTTGTCTCTTCTGAGGCGCAACATCAGACAAGCGCAGACCGACCAGGGACACTTCACCATGCTAGGCATACGGGACCGTCTAGCCATCTTGCCACGCCACTCACAACCAGGGAAGACCATCTGGATAGAGCACAAATTGGTCAACGTACTAGATGCAGTTGAGTTGGTGGATGAGCAAGGTGTTAATTTGGAACTCACGCTGGTGACCTTGGACACTAATGAGAAGTTTAGGGACATTACCAAGTTCATCCCAGAGACAATAGCTGGTGCTAGTGATGCAACTCTAGTTATCAACACTGAGCATATGCCCTCGATGTTTGTGCCAATAGGTGACGTTGTGCAGTATGGGTTTTTGAATCTCAGTGGCAAACCCACACACAGAACTATGATGTACAATTTCCCCACGAAAGCAGGACAGTGTGGGGGGGTAGTCACTTCAGTTGGCAAGATCATTGGAATCCACATTGGCGGGAATGGACGCCAGGGCTTCTGCGCTGGTTTAAAGAGGAGCTACTTTGCCAGCGAGCAAGGAGAGATCCAGTGGATGAAGCCCAACAAAGAGACTGGGAGGCTGAACATCAATGGTCCAACCCGAACCAAACTGGAACCTAGCGTGTTCCACAATGTGTTCGAGGGTAATAAAGAGCCAGCAGTTCTGACCAGTAAAGACCCCAGGCTTGAGGTTGATTTTGAACAAGCCTTGTTCTCCAAATATGTGGGCAACACTCTGCATGAGCCTGATGAGTATGTGACACAAGCTGCCCTTCATTACGCAAATCAATTAAAACAACTAGACATAAACACCAGCAAGATGAGCATGGAGGAGGCGTGCTATGGTACAGAAAATTTAGAAGCTATAGACCTACACACCAGTGCTGGATATCCTTATAGTGCCTTGGGTATTAAAAAGAGGGATATTCTTGATCCGGTCACCAGGGACACCTCCAAGATGAAACTATACATGGACAAGTATGGACTAGATTTACCCTATTCAACCTATGTGAAGGATGAGCTTAGGTCTCTAGATAAAATCAAGAAGGGGAAATCTCGCTTAATTGAGGCCAGCAGCTTGAATGATTCTGTCTACCTTAGAATGACTTTTGGTCATCTTTATGAGGTGTTTCACGCCAACCCGGGAACTATAACCGGGTCTGCAGTGGGGTGTAATCCTGATGTGTTCTGGAGCAAGTTGCCAATTCTACTACCGGGTTCGCTCTTTGCGTTTGACTACTCAAGCTATGATGCAAGTCTTAGTCCTGTATGGTTCAGAGCTTTAGAATTGGTTTTACGAGAGATTGGTTACTCAGAGGAGGCTGTGTCACTCATAGAGGGGATTAACCACACTCACCATGTGTATCGGAATAAGACATACTGTGTCCTTGGTGGGATGCCTTCAGGTTGCTCTGGCACTTCCATTTTCAATTCCATGATTAACAACATAATCATTAGAACTCTCTTGATCAAGACGTTCAAAGGGATAGACTTGGATGAACTAAACATGGTGGCCTACGGAGATGATGTACTGGCTAGCTACCCATTTCCCATCGACTGTTTGGAGTTGGCGAGAACTGGCAAAGAGTATGGACTGACTATGACTCCCGCCGATAAGTCACCCTGTTTTAATGAAGTCACCTGGGAGAACGCAACCTTTTTGAAGAGGGGTTTCCTACCAGACCATCAGTTCCCTTTTCTAATCCACCCTACCATGCCCATGAGGGAAATCCACGAGTCCATTCGTTGGACCAAGGATGCACGTAACACTCAAGACCACGTGCGTTCCCTTTGCTTGTTGGCGTGGCACAATGGAAAGGAGGAATATGAAAAATTTGTGAGCACAATCAGATCAGTTCCTATTGGAAAAGCCTTGGCGATACCAAATTTTGAGAACTTGAGGAGAAATTGGCTCGAATTGTTTTAAACTTACAGCTTAAAGCTGAACCCCACCAGAAACCTGGTCGTGCAAATGACTGGTGGGGGTAAATTTGTTATAACCAGAATAGC' + insdcAccessionFull: AY421767.1 + genes: + - name: VP4 + sequence: 'MGAQVSSQRSGSHETGNVATGGSTINFTNINYYKDSYAASASRQDFTQDPKKFTQPVLDSIRELSAPLN' + - name: VP2 + sequence: 'SPSVEACGYSDRVAQLTVGNSSITTQEAANIVLAYGEWPEYCPDTDATAVDKPTRPDVSVNRFYTLDSKMWQENSTGWYWKFPDVLNKTGVFGQNAQFHYLYRSGFCLHVQCNASKFHQGALLVAVIPEFVLAGRGSNTKPNEAPHPGFNTTFPGTAGASFNDPYVLDSGVPLSQSLIYPHQWINLRTNNCATIIVPYINAVPFDSAINHSNFGLIVVPVSPLKYSSGATTAIPITVTIAPLNSEFGGLRQAVSQ' + - name: VP3 + sequence: 'GLPTELRPGTNQFLTTEDDTAAPILPGFSPTPSIHIPGEVRSLLELCRVETILEVNNTTDATGLNRLLIPVSAQNKADELCAAFMVDPGRIGPWQSTLVGQICRYYTQWSGSLKVTFMFTGSFMATGKMLIAYSPPGSAQPANRETAMLGTHVIWDFGLQSSVSLVIPWISNTHFRTAKTGGNYDYYTAGVVTLWYQTNYVVPPETPGEAYIIAMGAAQDNFTLKICKDTDEVTQQAVLQ' + - name: VP1 + sequence: 'GDPVEDIIHDALSSTVRRAITSGQDVNTAAGTAPSSHRLETGRVPALQAAETGATSNATDENMIETRCVMNRNGVLEATISHFFSRSGLVGVVNLTDGGTDTTGYAVWDIDIMGFVQLRRKCEMFTYMRFNAEFTFVTTTENGEARPFMLQYMYVPPGAPKPTGRDAFQWQTATNPSVFVKLTDPPAQVSVPFMSPASAYQWFYDGYPTFGQHPETSNTTYGQCPNNMMGTFAVRVVSRVASQLKLQTRVYMKLKHVRAWIPRPIRSQPYLLKNFPNYDSSKITYSARDRASIKQANM' + - name: 2A + sequence: 'GKFGQQSGAIYVGNYRVVNRHLATHNDWVNLVWEDSSRDLLVSSTTAQGCDTIARCNCQTGVYYCNSRRKHYPVSFSKPSLIFVEASEYYPARYQSHLMLAEGHSEPGDCGGILRCQHGVVGLVSTGGNGLVGFADVRDLLWLDEEAMEQ' + - name: 2B + sequence: 'GVSDYIKGLGDAFGTGFTDAVSREVEALKNHLIGSEGAVEKILKNLVKLISALVIVIRSDYDMVTLTATLALIGCHGSPWAWIKSKTASILGISMAQKQ' + - name: 2C + sequence: 'SASWLKKFNDMANAAKGLEWISNKISKFIDWLKEKIIPAAKEKVEFLNNLKQLPLLENQISNLEQSAASQEDLEAIFGNVSYLAHFCRKFQPLYATEAKRVYALEKRVNNYMQFKSKHRIEPVCLIIRGSPGTGKSLATGIIARAIADKYHSSVYSLPPDPDHFDGYKQQVVTVMDDLCQNPDGKDMSLFCQMVSTVDFIPPMASLEEKGVSFTSKFVIASTNASNIIVPTVSDSDAIRRRFYMDCDIEVTDSYKTDLGRLDAGRAAKLCSENNTANFKRCSPLVCGKAIQLKDRKSKVRYSVDTMVSELIREYNNRSAVGNTIEALFQ' + - name: 3A + sequence: 'GPPKFRPIRISLEEKPTPDAISDLLASVDSEEVRQYCREQGWIIPETPTNVERHLNRAVLVMQSIATVVAVVSLVYVIYKLFAGFQ' + - name: 3B + sequence: 'GAYSGAPKQALKKPVLRTATVQ' + - name: 3C + sequence: 'GPSLDFALSLLRRNIRQAQTDQGHFTMLGIRDRLAILPRHSQPGKTIWIEHKLVNVLDAVELVDEQGVNLELTLVTLDTNEKFRDITKFIPETIAGASDATLVINTEHMPSMFVPIGDVVQYGFLNLSGKPTHRTMMYNFPTKAGQCGGVVTSVGKIIGIHIGGNGRQGFCAGLKRSYFASEQ' + - name: 3D + sequence: 'GEIQWMKPNKETGRLNINGPTRTKLEPSVFHNVFEGNKEPAVLTSKDPRLEVDFEQALFSKYVGNTLHEPDEYVTQAALHYANQLKQLDINTSKMSMEEACYGTENLEAIDLHTSAGYPYSALGIKKRDILDPVTRDTSKMKLYMDKYGLDLPYSTYVKDELRSLDKIKKGKSRLIEASSLNDSVYLRMTFGHLYEVFHANPGTITGSAVGCNPDVFWSKLPILLPGSLFAFDYSSYDASLSPVWFRALELVLREIGYSEEAVSLIEGINHTHHVYRNKTYCVLGGMPSGCSGTSIFNSMINNIIIRTLLIKTFKGIDLDELNMVAYGDDVLASYPFPIDCLELARTGKEYGLTMTPADKSPCFNEVTWENATFLKRGFLPDHQFPFLIHPTMPMREIHESIRWTKDARNTQDHVRSLCLLAWHNGKEEYEKFVSTIRSVPIGKALAIPNFENLRRNWLELF' + - name: EV-A71 + sequence: 'TTAAAACAGCTGTGGGTTGTCACCCACCCACAGGGTCCACTGGGCGCTAGTACACTGGTATCTCGGTACCTTTGTACGCCTGTTTTATACCCCCTCCCTGATTTGCAACTTAGAAGCAACGCAAACCAGATCAATAGTAGGTGTGACATACCAGTCGCATCTTGATCAAGCACTTCTGTATCCCCGGACCGAGTATCAATAGACTGTGCACACGGTTGAAGGAGAAAACGTCCGTTACCCGGCTAACTACTTCGAGAAGCCTAGTAACGCCATTGAAGTTGCAGAGTGTTTCGCTCAGCACTCCCCCCGTGTAGATCAGGTCGATGAGTCACCGCATTCCCCACGGGCGACCGTGGCGGTGGCTGCGTTGGCGGCCTGCCTATGGGGTAACCCATAGGACGCTCTAATACGGACATGGCGTGAAGAGTCTATTGAGCTAGTTAGTAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACTGCGGAGCACATACCCTTAATCCAAAGGGCAGTGTGTCGTAACGGGCAACTCTGCAGCGGAACCGACTACTTTGGGTGTCCGTGTTTCTTTTTATTCTTGTATTGGCTGCTTATGGTGACAATTAAAGAATTGTTACCATATAGCTATTGGATTGGCCATCCAGTGTCAAACAGAGCTATTGTATATCTCTTTGTTGGATTCACACCTCTCACTCTTGAAACGTTACACACCCTCAATTACATTATACTGCTGAACACGAAGCGATGGGCTCCCAGGTCTCCACACAGCGATCCGGCTCGCATGAGAATTCCAACTCAGCCACGGAAGGCTCCACTATAAATTACACAACCATTAATTACTACAAAGACTCGTATGCTGCCACTGCTGGAAAGCAAAGTCTCAAACAAGATCCTGACAAGTTTGCGAACCCTGTGAAGGACATCTTTACTGAAATGGCAGCGCCCTTAAAGTCTCCCTCTGCTGAAGCATGTGGCTATAGCGACCGAGTGGCACAGCTTACCATTGGAAATTCCACCATTACTACACAAGAAGCAGCAAACATAATAGTTGGGTATGGTGAGTGGCCTTCATACTGCTCTGATAATGATGCAACAGCGGTAGACAAACCTACACGGCCTGATGTCTCAGTAAATAGATTTTACACGCTAGACACTAAGCTATGGGAGAAATCATCCAAGGGGTGGTACTGGAAGTTCCCAGATGTACTGACTGAAACCGGAGTTTTTGGTCCAAATGCACAATTTCACTACTTATACCGTTCAGGGTTCTGCATCCACGTTCAATGTAACGCTAGCAAATTTCACCAAGGGGCGCTACTCGTTGCGGTATTGCCCGAGTATGTCATTGGAACAGTGGCAGGCGGCACAGGCACAGAGAACAGTCACCCTCCTTATAAACAAACCCAACCCGGCGCTGATGGATTTGAATTACAACATCCATATGTTCTTGATGCTGGAATTCCAATATCTCAGTTGACAGTGTGCCCTCACCAGTGGATCAATTTACGAACCAACAATTGTGCCACCATAATAGTGCCATACATGAACACACTACCTTTTGATTCCGCATTGAACCACTGTAATTTCGGACTATTGGTGGTGCCTATCAGCCCGCTGGATTTCGACCAAGGGGCGACACCGGTAATTCCTATCACTATCACGTTGGCTCCGATGTGTTCTGAGTTTGCGGGTCTCAGGCAGGCAGTTACGCAGGGTTTTCCCACTGAATTGAAACCTGGCACTAATCAGTTCTTAACCACGGATGATGGTGTGTCAGCACCTATATTGCCAAATTTCCACCCCACCCCGTGCATTCACATACCTGGCGAGGTTAGAAACTTACTAGAACTGTGCCAGGTAGAAACCATTTTAGAAGTCAACAATGTGCCCACCAACGCAACCAGTTTGATGGAAAGGCTACGGTTTCCAGTGTCAGCCCAAGCAGGGAAAGGTGAGTTGTGTGCAGTGTTCAGGGCCGACCCTGGGAGGGATGGTCCTTGGCAATCCACCATGCTAGGCCAGTTGTGTGGATATTACACCCAATGGTCAGGGTCTTTGGAAGTCACTTTTATGTTCACCGGATCCTTTATGGCAACTGGTAAAATGCTTATAGCTTACACACCCCCAGGGGGCCCTTTGCCTAAAGATAGAGCCACAGCTATGCTGGGGACGCACGTCATCTGGGACTTTGGCTTGCAATCGTCCGTCACCCTCGTCATACCATGGATCAGTAACACTCACTATAGGGCGCATGCTCGAGATGGGGTGTTTGATTACTACACCACAGGTTTGGTTAGTATATGGTACCAAACAAATTATGTAGTCCCTATTGGAGCACCTAATACTGCCTATATAATAGCGTTGGCAGCAGCCCAAAAGAATTTCACTATGAAATTGTGCAAGGACACCAGTGACATTTTGGAAACGGCCACTATTCAAGGGGACAGAGTGGCAGATGTGATTGAGAGCTCTATAGGAGATAGTGTGAGTAAGGCCCTCACCCCAGCTTTACCTGCACCCACAGGCCCAGACACCCAAGTGAGCAGTCATCGCTTAGACACTGGAAAAGTACCAGCACTTCAAGCCGCCGAAATCGGAGCTTCGTCGAATGCTAGTGATGAGAGTATGATTGAGACTCGGTGTGTTCTTAACTCACATAGCACAGCTGAAACCACCCTTGATAGTTTCTTCAGCAGAGCAGGCTTAGTTGGGGAGATAGATCTTCCTCTAAAGGGCACCACCAATCCGAACGGGTATGCCAACTGGGACATAGACATAACCGGTTATGCGCAGATGCGCAGAAAAGTGGAACTATTCACCTATATGCGCTTTGACGCAGAGTTCACTTTTGTCGCGTGCACACCTACCGGAAGGGTCGTTCCACAGCTGCTTCAATACATGTTTGTTCCACCCGGGGCCCCCAAACCAGACTCCAGAGACTCTTTGGCTTGGCCAACGGCCACGAACCCCTCAGTTTTTGTCAAATCATCCGACCCACCAGCACAAGTCTCAGTGCCATTTATGTCACCTGCAAGCGCATACCAATGGTTTTATGACGGATACCCTACATTTGGAGAGCACAAGCAAGAGAAGGATCTCGAGTATGGGGCATGCCCGAATAACATGATGGGCACATTCTCAGTGCGGACTGTGGGATCGTCAAAGTCAGAATATTCCTTAGTCATCAGAATATACATGAGAATGAAGCACGTCAGAGCGTGGATACCTCGGCCGATGCGCAATCAGAACTATTTGTTCAAATCCAACCCAAACTATGCTGGTGATTCCATTAAACCAACTGGTACCAGCCGAACGGCAATCACTACGCTCGGGAAATTCGGTCAGCAGTCTGGGGCTATTTATGTGGGCAACTTTAGGGTAGTAAACAGACACCTAGCCACCCATACTGACTGGGCCAACTTGGTGTGGGAAGACAGCTCTAGAGACCTCCTAGTTTCTTCAACTACCGCTCAAGGGTGTGACACCATTGCTCGATGTAACTGCCAAACCGGAGTGTATTACTGTAACTCTCGCAGAAAACACTATCCAGTCAGTTTTTCGAAACCTAGTTTGGTGTTTGTAGAAGCTAGTGAGTATTATCCAGCTAGATATCAGTCCCATCTTATGCTTGCTGAGGGCCATTCAGAACCTGGTGATTGTGGCGGTATTCTTAGATGCCAACACGGTGTGGTGGGAATTGTCTCCACTGGCGGAAGTGGCCTTGTGGGATTTGCTGACGTTAGAGATCTTCTGTGGCTAGATGAGGAAGCGATGGAGCAGGGGGTATCTGATTACATCAAAGGTCTCGGTCGAGCCTTCGGCACAGGTTTCACTGACGCAGTGTCTAGGGAAGTGGAAGCGTTGAAGAACCACTTAATCGGCTCCGAAGGGGCTGTTGAGAAGATCTTGAAGAACTTGGTGAAGCTAATTTCAGCCTTAGTTATAGTCATCAGAAGTGATTATGATATGGTCACCCTCACAGCCACACTAGCTCTGATCGGGTGCCACGGGAGTCCTTGGGCGTGGATCAAATCAAAGACAGCTTCCATACTGGGCATTCCCATGGCACAAAAACAGAGTGCCTCATGGCTAAAGAAGTTCAATGACATGGCAAATGCTGCAAAAGGGCTTGAGTGGATTTTCAACAAGATCAGTAAGTTCATTGACTGGCTTAAAGAGAAGATCATTCCAGCTGCCAAAGAGAAAGTTGAGTTTTTGAACAACCTAAAACAGCTCCCCTTGTTGGAGAACCAAGTCTCCAATCTTGAACAGTCTGCTGCCTCACAAGAAGACTTAGAAGCTATGTTTGGTAATGTGATATATCTGGCTCACTTTTGCCGCAAATTCCAACCACTCTACGCAACTGAGGCCAAGAGAGTCTACGCTTTAGAGAAAAGGATGAATAACTACATGCAGTTCAAGAGCAAACACCGTATTGAACCTGTATGCTTGATCATCAGAGGTTCCCCCGGAACGGGCAAATCGCTCGCCACAGGCATTATAGCTAGAGCCATTGCTGACAAGTATCGCTCTAGTGTATACTCACTCCCCCCAGACCCAGATCACTTTGATGGGTATAAGCAACAGGTGGTCGCGGTCATGGATGATCTCTGCCAGAACCCGGACGGAAAAGACATGTCCCTATTTTGTCAAATGGTTTCTACAGTAGATTTTGTACCACCCATGGCATCACTAGAGGAGAAAGGAGTGTCCTTCACCTCTAAGTTTGTCATTGCATCGACCAATGCTAGTAACATCATAGTCCCCACAGTTTCAGATTCAGATGCAATTCGCAGGCGATTCTATATGGACTGCGATATAGAAGTGACAGATTCTTACAAGACAGACCTCGGTCGGCTGGACGCAGGTAGAGCTGCCAAGCTTTGTACAGAAAATAACACTGCTAATTTTAAGAGATGCAGCCCACTGGTGTGTGGTAAGGCTATTCAGCTGAGAGACAGGAAGTCCAAAGTGAGATATAGCGTCGACACCGTGGTATCGGAACTGATCAGAGAGTACAACAATAGATCTGCTATTGGGAATACTATAGAAGCACTCTTTCAAGGACCCCCTAAATTCAGGCCTATAAGAATTAGTCTCGAAGAAAAGCCAGCCCCAGATGCCATTAGCGATCTCCTCGCTAGTGTAGATAGCGAGGAGGTGCGTCAGTACTGCAGGGAACAAGGCTGGATCATCCCTGAAACTCCCACCAATGTTGAGCGTCACCTCAATAGAGCAGTATTGGTAATGCAGTCCATCGCCACTGTGGTTGCAGTTGTGTCTCTTGTTTATGTCATTTATAAGCTGTTTGCCGGGTTCCAGGGTGCTTACTCTGGAGCGCCCAAGCCCATTCTCAAGAAGCCCGTGTTAAGAACAGCCACGGTCCAAGGGCCCAGCTTAGACTTCGCCTTGTCTCTTTTGAGGCGCAACATTAGACAAGCGCAAACTGACCAAGGACACTTCACCATGCTAGGAGTGCGAGATCGCCTAGCCATCCTGCCGCGCCACTCGCAACCAGGGAAGACCATCTGGGTAGAGCATAAATTAATCAATGTACTAGATGCAGTTGAGTTGGTGGATGAGCAAGGTGTAAACTTGGAACTCACACTGGTAACTTTGGACACCAATGAAAAATTTAGGGATATCACCAAGTGTATCCCAGAAGTGATCACCGGGGCGAGTGACGCAACTCTAGTCATCAACACTGAGCACATTCCCTCAATGTTTGTGCCGGTGGGTGACGTTGTGCAGTACGGTTTCTTGAACCTTAGTGGTAAACCCACACACAGAACCATGATGTATAACTTCCCCACGAAGCCAGGACAGTGTGGGGGGGTGGTTACCTCAGTTGGTAAGATCATTGGAATCCACATTGGCGGGAATGGACGCCAGGCCTTTTGCGCTGGCCTAAAGAGGAGTTATTTTGCCAGCGAGCAAGGAGAGATCCAGTGGATGAAGCCTAACAGAGAAACCGGGAGGTTGAATATTAATGGTCCAACCCGAACTAAGCTGGAACCCAGTGTATTCCATGATGTGTTCGAGGGCAACAAGGAACCAGCGGTCCTGACTAGTAAGGACCCCAGACTTGAGGTTGATTTTGAGCAAGCTTTGTTCTCCAAGTATGTGGGTAACACCCTGCATGAACCTGATGAGTACGTGACACAGGCTGCTCTCCACTACGCAAATCAGCTGAAGCAACTGGACATCAACACCAGCAAGATGAGCATGGAAGAAGCGTGCTATGGCACAGAATATTTAGAAGCTATAGACTTGCACACCAGTGCTGGATACCCTTATAGTGCTTTGGGCATCAAGAAAAGAGACATCCTCGACCCAGTTACCAGAGACACCTCCAGGATGAAGTTATATATGGATAAGTATGGGTTGGACTTGCCTAATTCCACTTATGTAAAGGATGAGCTTAGTTCTCTAGATAAGATCAGAAAAGGGGAGTCTCGCCTGATTGAGGCTAGCAGCTTAAATGATCCTGTCTACCCTAGATTGACTTTTGGACACCTTTATGAAGTGTTTCACGCCAACCCAGGGACTGTAACAGGATCTGCAGTTGGGTGCAACCCTGATGTATTTTGGAGCAAGTTACCAATTTTGTTACCGGGTTCACTCTTTGCATTTGACTACTCAGGATATGATGCAAGCCTTAGTCCTGTGTGGTTCAGAGCTCTAGAGTTGGTTCTGAGAGAGATCGGTTACTCGGAGGAGGCTGTGTCACTCATAGAAGGGATCAATCACACCCACCACGTGTACCGAAACAAGACATATTGTGTACTTGGTGGAATGCCCTCAGGCTGCTCCGGTACTTCCATTTTCAATTCCATGATTAACAACATAATCATCAGAACCCTCCTGATTAAAACATTCAAAGGTATAGACTTAGATGAGCTGAAAATGGTAGCTTATGGAGATGACGTGTTGGCCAGCTACCCGTTTCCTATTGATTGCTTGGAATGGGGTAAAACAGGCAAAGAATATGGGCTGACTATGACTCCTGCTGATAAATCACCTTGTTTCAATGAGGTTACCTGGGAGAATGCAACCTTCTTAAAACGGGGTTTTCTACCGGACCATCAGTTCCCTTTTCTGATCCATCCCACTATGCCCATGAGGGAAATCCATGAGTCCATCCGCTGGACCAAGGACGCGCGCAATACTCAAGATCATGTGCGCTCCCTTTGTCTCCTGGCATGGCATAATGGAAAAGAGGAGTATGAGAAATTTGTGAGTACAATTAGATCAGTCCCCATTGGAAGGGCTTTAGCAATTCCAAATTTGGAGAACTTGAGAAGAAATTGGCTCGAGTTATTTTAAACTTACAGCTCAATGCTGAACCCCACCAGAAATCTGGTCGTGTCAATGACTGGTGGGGGTAAATTTGTTATAACCAGAATAGC' + insdcAccessionFull: U22521.1 + genes: + - name: VP4 + sequence: 'MGSQVSTQRSGSHENSNSATEGSTINYTTINYYKDSYAATAGKQSLKQDPDKFANPVKDIFTEMAAPLK' + - name: VP2 + sequence: 'SPSAEACGYSDRVAQLTIGNSTITTQEAANIIVGYGEWPSYCSDNDATAVDKPTRPDVSVNRFYTLDTKLWEKSSKGWYWKFPDVLTETGVFGPNAQFHYLYRSGFCIHVQCNASKFHQGALLVAVLPEYVIGTVAGGTGTENSHPPYKQTQPGADGFELQHPYVLDAGIPISQLTVCPHQWINLRTNNCATIIVPYMNTLPFDSALNHCNFGLLVVPISPLDFDQGATPVIPITITLAPMCSEFAGLRQAVTQ' + - name: VP3 + sequence: 'GFPTELKPGTNQFLTTDDGVSAPILPNFHPTPCIHIPGEVRNLLELCQVETILEVNNVPTNATSLMERLRFPVSAQAGKGELCAVFRADPGRDGPWQSTMLGQLCGYYTQWSGSLEVTFMFTGSFMATGKMLIAYTPPGGPLPKDRATAMLGTHVIWDFGLQSSVTLVIPWISNTHYRAHARDGVFDYYTTGLVSIWYQTNYVVPIGAPNTAYIIALAAAQKNFTMKLCKDTSDILETATIQ' + - name: VP1 + sequence: 'GDRVADVIESSIGDSVSKALTPALPAPTGPDTQVSSHRLDTGKVPALQAAEIGASSNASDESMIETRCVLNSHSTAETTLDSFFSRAGLVGEIDLPLKGTTNPNGYANWDIDITGYAQMRRKVELFTYMRFDAEFTFVACTPTGRVVPQLLQYMFVPPGAPKPDSRDSLAWPTATNPSVFVKSSDPPAQVSVPFMSPASAYQWFYDGYPTFGEHKQEKDLEYGACPNNMMGTFSVRTVGSSKSEYSLVIRIYMRMKHVRAWIPRPMRNQNYLFKSNPNYAGDSIKPTGTSRTAITTL' + - name: 2A + sequence: 'GKFGQQSGAIYVGNFRVVNRHLATHTDWANLVWEDSSRDLLVSSTTAQGCDTIARCNCQTGVYYCNSRRKHYPVSFSKPSLVFVEASEYYPARYQSHLMLAEGHSEPGDCGGILRCQHGVVGIVSTGGSGLVGFADVRDLLWLDEEAMEQ' + - name: 2B + sequence: 'GVSDYIKGLGRAFGTGFTDAVSREVEALKNHLIGSEGAVEKILKNLVKLISALVIVIRSDYDMVTLTATLALIGCHGSPWAWIKSKTASILGIPMAQKQ' + - name: 2C + sequence: 'SASWLKKFNDMANAAKGLEWIFNKISKFIDWLKEKIIPAAKEKVEFLNNLKQLPLLENQVSNLEQSAASQEDLEAMFGNVIYLAHFCRKFQPLYATEAKRVYALEKRMNNYMQFKSKHRIEPVCLIIRGSPGTGKSLATGIIARAIADKYRSSVYSLPPDPDHFDGYKQQVVAVMDDLCQNPDGKDMSLFCQMVSTVDFVPPMASLEEKGVSFTSKFVIASTNASNIIVPTVSDSDAIRRRFYMDCDIEVTDSYKTDLGRLDAGRAAKLCTENNTANFKRCSPLVCGKAIQLRDRKSKVRYSVDTVVSELIREYNNRSAIGNTIEALFQ' + - name: 3A + sequence: 'GPPKFRPIRISLEEKPAPDAISDLLASVDSEEVRQYCREQGWIIPETPTNVERHLNRAVLVMQSIATVVAVVSLVYVIYKLFAGFQ' + - name: 3B + sequence: 'GAYSGAPKPILKKPVLRTATVQ' + - name: 3C + sequence: 'GPSLDFALSLLRRNIRQAQTDQGHFTMLGVRDRLAILPRHSQPGKTIWVEHKLINVLDAVELVDEQGVNLELTLVTLDTNEKFRDITKCIPEVITGASDATLVINTEHIPSMFVPVGDVVQYGFLNLSGKPTHRTMMYNFPTKPGQCGGVVTSVGKIIGIHIGGNGRQAFCAGLKRSYFASEQ' + - name: 3D + sequence: 'GEIQWMKPNRETGRLNINGPTRTKLEPSVFHDVFEGNKEPAVLTSKDPRLEVDFEQALFSKYVGNTLHEPDEYVTQAALHYANQLKQLDINTSKMSMEEACYGTEYLEAIDLHTSAGYPYSALGIKKRDILDPVTRDTSRMKLYMDKYGLDLPNSTYVKDELSSLDKIRKGESRLIEASSLNDPVYPRLTFGHLYEVFHANPGTVTGSAVGCNPDVFWSKLPILLPGSLFAFDYSGYDASLSPVWFRALELVLREIGYSEEAVSLIEGINHTHHVYRNKTYCVLGGMPSGCSGTSIFNSMINNIIIRTLLIKTFKGIDLDELKMVAYGDDVLASYPFPIDCLEWGKTGKEYGLTMTPADKSPCFNEVTWENATFLKRGFLPDHQFPFLIHPTMPMREIHESIRWTKDARNTQDHVRSLCLLAWHNGKEEYEKFVSTIRSVPIGRALAIPNLENLRRNWLELF' + - name: EV-D68 + sequence: 'TTAAAACAGCTCTGGGGTTGTTCCCACTCAAGGGCCCACGTGGCGGCTAGTACTCTGGTATCTCGGTACCTTTGTACGCCTGTTTTAATTCCCTCCCCAACGTAACTTAGAAGCTTTTAAACCAAAGCTCAATAGGTGGAGCGCAAACCAGCGCTCTTATGAGCAAGCACTTCTGTCTCCCCGGTGTGGTTGTATAGACTGTCCCCACGGTTGAAAACAACTTATCCGTTAACCGCTATAGTACTTCGAGAAACCTAGTATTGCCTTCGGAGTGTTGATGCGTTGCGCTCAGCACACTAACCCGTGTGTAGCTTGGGTCGATGAGTCTGGACGTACCCCACTGGCGACAGTGGTCCAGGCTGCGTTGGCGGCCTACTCATGGTGAAAACCATGAGACGCTAGACATGAACAAGGTGTGAAGAGTCTATTGAGCTGCTATAGAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACCATGGAGCAAGTGCTCACAAACCAGTGAGTTACTTGTCGTAACGCGCAAGTCCGTGGCGGAACCGACTACTTTGGGTGTCCGTGTTTCACTTTTTACTTTTATGACTGCTAATGGTGACAATTTAATATTGTTACCATTTGGCTTGTCGAATTGATCACATAAGATCTATAGTTTTGTTCACTGATTTGCTTTGAAATAATCTCACCTCAAAACCTCCAGTACATAACATTTAAAGAGTTTAAACTTATTTATAACAATGGGAGCTCAAGTTACTAGACAGCAAACTGGAACTCATGAGAATGCTAACATTGCTACAAATGGATCCCATATTACATACAATCAGATAAATTTTTACAAGGATAGTTATGCAGCTTCAGCCAGCAAGCAAGACTTTTCACAGGATCCATCAAAATTCACTGAACCAGTGGTGGAAGGCTTAAAAGCAGGGGCACCAGTTTTGAAATCTCCTAGCGCTGAGGCTTGTGGCTACAGTGATAGAGTACTACAACTCAAATTGGGCAACTCAGCTATTGTCACTCAAGAAGCAGCAAACTACTGTTGCGCTTATGGTGAATGGCCTAATTATTTACCAGATCATGAAGCAGTAGCTATTGATAAACCTACACAACCAGAAACTTCTACAGACAGATTTTATACTTTAAGATCAGTCAAATGGGAGAGTAATAGCACAGGATGGTGGTGGAAACTACCTGATGCATTAAACAACATAGGCATGTTTGGACAAAATGTACAATATCACTACCTATACAGATCTGGCTTCTTAATTCATGTGCAGTGCAACGCTACAAAGTTCCATCAAGGCGCGCTGTTGGTGGTAGCAATACCAGAGCATCAGAGAGGGGCACACGACACCACCACTAGTCCAGGGTTTAATGATATCATGAAAGGTGAAAGAGGAGGGACGTTTAACCACCCATATGTTCTTGATGATGGAACATCAATAGCTTGTGCGACAATATTTCCACATCAATGGATAAATCTAAGAACCAACAATTCAGCAACGATTGTTCTTCCATGGATGAATGTTGCACCAATGGACTTTCCACTTAGACACAATCAGTGGACACTAGCAGTAATACCAGTGGTTCCATTGGGTACACGCACAATGTCAAGTGTTGTCCCAATAACAGTTTCAATTGCCCCTATGTGTTGTGAGTTTAACGGACTCAGGCACGCCATCACCCAGGGTGTTCCAACATATCTTCTACCAGGTTCAGGACAATTTCTGACTACTGATGACCATAGCTCAGCACCAGTCCTCCCGTGTTTCAACCCAACTCCAGAAATGCACATTCCAGGGCAAATCCGCAACATGTTGGAAATGATTCAAGTGGAATCAATGATGGAGATTAACAATACAGACGGCGCAAATGGCATGGAGCGTCTCAGAGTTGACATATCAGTACAAGCAGATCTTGATCAGTTATTATTTAACATTCCACTAGATATACAACTGGATGGACCACTTAGAAACACTTTAGTAGGGAACATATCCAGATATTATACTCATTGGTCTGGATCTCTAGAAATGACATTCATGTTTTGCGGTAGTTTTATGGCAACAGGGAAATTGATCCTATGTTACACACCTCCAGGTGGATCATGCCCAACAACTAGAGAAACTGCCATGTTAGGCACTCACATCGTCTGGGACTTTGGATTACAATCTAGTATCACCTTAATAATACCTTGGATTAGTGGATCCCACTACAGGATGTTCAATAGCGACGCTAAGTCAACCAATGCTAATGTTGGCTATGTAACCTGTTTCATGCAGACCAACCTGATAGTCCCCAGTGAGTCCTCTGATACTTGTTCTTTAATAGGATTCATAGCAGCAAAAGATGACTTTTCCCTCAGGTTAATGAGAGATAGTCCAGATATTGGACAATCAAACCACTTACATGGAGCAGAGGCAGCCTATCAGGTGGAGAGTATCATCAAAACAGCAACTGATACTGTGAAGAGTGAGATTAACGCCGAACTTGGTGTGGTCCCTAGTCTAAATGCAGTTGAAACTGGTGCAACTTCCAACACTGAACCAGAAGAAGCCATACAAACTCGCACAGTAATAAATCAGCATGGTGTGTCGGAGACGTTAGTGGAGAATTTTCTTGGTAGGGCAGCCCTAGTGTCAAAGAAAAGTTTTGAATACAAGAATCATGCCTCATCCAGCGCAGGGACACACAAAAACTTTTTTAAATGGACAATTAATACTAAGTCTTTTGTCCAGTTAAGAAGAAAGCTGGAATTATTCACATACCTTAGGTTTGATGCTGAAATCACCATACTCACAACTGTGGCAGTAAATGGTAATAATGACAGCACATACATGGGTCTCCCTGACTTGACACTCCAAGCAATGTTTGTACCAACTGGTGCTCTTACTCCAAAGGAGCAGGATTCATTTCATTGGCAATCAGGCAGTAATGCTAGTGTGTTCTTTAAAATTTCTGATCCCCCAGCTAGAATGACTATACCTTTTATGTGCATCAACTCAGCATATTCAGTTTTTTATGATGGCTTTGCTGGATTTGAGAAAAATGGTCTATATGGAATAAACCCAGCTGACACTATTGGCAACTTGTGTGTCAGAATAGTGAATGAACATCAACCAGTTGGTTTTACAGTGACCGTTAGGGTTTACATGAAGCCTAAACATATAAAAGCATGGGCTCCACGACCACCGCGAACCATGCCATACATGAGCATTGCTAATGCAAATTACAAAGGTAGAGATACAGCACCAAACACACTTAATGCCATAATTGGTAATAGAGCGAGTGTCACAACTATGCCTCACAACATAGTAACCACCGGTCCAGGTTTTGGGGGAGTCTTTGTAGGGTCCTTTAAGATAATTAACTATCACTTAGCCACAATAGAAGAGAGACAATCAGCCATCTATGTGGACTGGCAATCAGATGTTCTAGTCACCCCCATTGCCGCTCATGGTAGACATCAAATAGCAAGATGCAAGTGTAATACAGGGGTTTACTATTGCCGGCACAGAGATAGAAGCTACCCAATTTGCTTTGAAGGTCCAGGAATTCAATGGATTGAACAAAATGAATACTACCCGGCAAGATACCAGACTAATGTACTTCTAGCAGCTGGCCCTGCAGAAGCAGGAGATTGTGGTGGTCTATTAGTCTGCCCACACGGGGTGATTGGCCTCCTTACAGCAGGAGGGGGTGGGATTGTAGCCTTCACAGACATCAGAAATTTACTGTGGTTAGATACTGATGTTATGGAACAAGGTATTACTGATTACATACAAAATCTTGGTAATGCTTTTGGAGCAGGATTCACAGAAACAATCTCCAATAAGGCTAAGGAAGTGCAAGACATGTTAATTGGAGAGAGTTCATTATTGGAAAAATTACTAAAAGCTCTAATCAAAATTATATCAGCACTGGTGATTGTCATTAGAAATTCAGAAGATTTGATAACAGTTACAGCTACACTAGCATTGTTAGGGTGTCATGACTCACCATGGAGCTACTTGAAGCAAAAAGTATGCTCATACTTGGGTATTCCCTATGTGCCTAGACAAAGTGAATCATGGCTTAAGAAGTTCACAGAGGCGTGCAATGCTCTTAGGGGTTTAGATTGGCTATCACAAAAGATAGACAAGTTCATTAATTGGCTCAAAACTAAAATATTACCAGAGGCTAGGGAGAAATATGAATTCGTGCAAAGACTCAAGCAGCTACCAGTAATAGAAAAACAAGTTAGCACTATTGAGCATAGTTGCCCAACAACAGAACGACAACAGGCCTTATTCAACAATGTTCAGTATTACTCACATTACTGTAGGAAGTACGCACCACTTTACGCAGTGGAATCAAAAAGAGTGGCCGCTCTTGAAAAGAAAATAAACAATTACATCCAGTTCAAGTCCAAATCTCGCATTGAACCGGTTTGTTTAATAATACACGGCTCCCCAGGAACTGGCAAATCAGTAGCCTCAAATTTAATTGCTAGAGCTATCACAGAAAAATTAGGCGGGGACATTTACTCCCTACCCCCAGACCCTAAATACTTTGATGGATATAAACAGCAAACAGTAGTCCTTATGGATGATTTAATGCAAAATCCAGATGGGAATGATATATCTATGTTTTGCCAAATGGTTTCAACTGTGGATTTTATTCCTCCAATGGCTAGTTTGGAAGAGAAAGGAACTCTATATACCAGTCCATTCTTAATAGCCACTACTAATGCTGGTTCAATACATGCACCAACAGTCTCAGACTCAAAGGCTTTGTCACGCAGATTCAAATTTGATGTGGACATTGAAGTTACAGATTCATACAAAGACTCAAACAAACTAGACATGTCCAGAGCAGTGGAAATGTGCAAACCAGATAACTGTACCCCTACTAATTATAAAAGATGCTGCCCACTGATTTGCGGAAAAGCTATTCAATTTAGAGATCGTAGAACCAATGCAAGATCCACGGTTGATATGCTAGTGACTGACATTATTAAGGAATACAGAACCAGGAATAGCACACAAGACAAATTAGAAGCCCTATTTCAAGGACCTCCACAATTCAAGGAGATTAAAATCTCAGTCGCTCCAGACACACCAGCCCCTGATGCCATAAATGACCTTCTTAGGTCAGTGGACTCTCAAGAAGTTAGAGATTATTGTCAAAAGAAGGGGTGGATTGTAATACACCCGTCAAATGAGCTAGTTGTAGAAAAACATATTAGTAGAGCTTTCATCACTCTGCAAGCTATTGCCACCTTTGTATCAATAGCTGGTGTGGTCTATGTTATATACAAACTTTTTGCTGGTATTCAGGGCCCATATACAGGAATTCCTAATCCTAAACCCAAAGTGCCCTCTCTTAGAACAGCCAAAGTGCAAGGACCAGGATTTGATTTTGCGCAAGCCATAATGAAGAAAAATACTGTTATTGCTAGAACTGAAAAAGGCGAGTTCACAATGCTTGGTGTGTATGATAGAGTGGCAGTCATTCCAACACATGCATCTGTTGGAGAAATCATTTACATCAACGATGTAGAAACCAGAGTTCTAGATGCATGTGCACTTAGAGACTTGACAGACACAAACCTAGAAATAACTATAGTCAAATTGGATCGCAATCAAAAATTTAGAGACATCAGACACTTTTTACCCAGATGTGAGGATGATTACAATGATGCTGTGCTTAGTGTACATACATCAAAATTCCCTAACATGTACATTCCAGTTGGACAAGTCACTAACTACGGCTTCTTGAACCTGGGCGGCACACCAACACATCGGATTTTAATGTATAATTTTCCAACAAGAGCTGGTCAGTGTGGTGGTGTGGTGACAACCACAGGTAAAGTGATAGGAATACACGTGGGCGGGAATGGAGCTCAGGGATTCGCAGCAATGTTGCTCCACTCTTACTTTACTGATACACAAGGTGAGATAGTTAGCAATGAGAAGAGTGGGATGTGTATTAATGCACCAGCAAAAACAAAACTCCAACCCAGTGTCTTCCATCAAGTTTTTGAAGGTTCAAAGGAACCAGCAGTACTCAATTCAAAAGATCCTAGACTCAAGACTGATTTCGAGGAGGCTATTTTCTCAAAATATACAGGTAACAAAATTATGTTAATGGATGAGTACATGGAAGAAGCAGTGGATCATTATGTGGGATGTTTAGAACCACTGGATATTAGTGTAGATCCCATACCCCTTGAAAATGCTATGTATGGAATGGAGGGTCTTGAAGCATTGGACTTAACCACCAGTGCGGGCTTCCCTTATTTGTTACAAGGGAAAAAGAAAAGGGACATATTCAACAGACAAACCAGAGATACTAGTGAGATGACAAAGATGTTGGAAAAATACGGAGTTGACCTACCTTTCGTGACTTTCGTGAAAGACGAGCTTAGATCAAGAGAGAAAGTCGAAAAGGGGAAGTCACGCCTGATTGAAGCCAGTTCCTTGAACGACTCAGTTGCCATGAGGGTTGCCTTTGGAAATCTCTACGCTACATTTCATAACAATCCAGGCACAGCAACTGGTAGTGCAGTTGGTTGTGATCCAGATATATTCTGGTCAAAAATCCCTATTTTGTTAGATGGAGAGATTTTTGCTTTTGATTACACTGGTTATGACGCTAGTTTATCACCAGTGTGGTTTGCCTGTTTGAAAAAGGTTCTGATTAAATTAGGTTACACCCACCAAACATCTTTTATAGATTATCTATGCCACTCAGTGCATTTGTACAAGGATAGAAAATATGTAATTAATGGTGGAATGCCCTCTGGTTCTTCAGGTACTAGTATATTTAACACTATGATTAATAACATAATTATAAGAACTTTATTAATTAAGGTTTACAAAGGCATAGATCTGGACCAGTTTAAAATGATAGCATACGGAGATGATGTTATTGCTAGTTACCCACACAAAATTGATCCAGGTTTATTAGCAGAAGCAGGCAAGCACTATGGATTGGTAATGACACCAGCGGACAAAGGTACCAGTTTCATTGACACTAATTGGGAAAATGTAACTTTTTTGAAAAGATACTTCAGAGCAGATGATCAATACCCCTTTCTTATACATCCAGTGATGCCAATGAAAGAGATACATGAATCTATTAGATGGACTAAAGATCCCAGAAACACACAAGATCATGTTAGATCCTTGTGTTACCTGGCGTGGCATAATGGAGAAGAGGCTTACAATGAATTTTGTAGAAAAATTAGAAGTGTGCCCGTGGGGAGGGCATTGACACTACCTGCATATTCTAGTCTTAGACGGAAATGGTTAGATTCGTTTTAGATAACTCTAATTGAAACCCAAGTTACAGTTACTTTCACTTAGAGGTAAATTTTGGTCACTTGGGGACC' + insdcAccessionFull: AY426531.1 + genes: + - name: VP4 + sequence: 'MGAQVTRQQTGTHENANIATNGSHITYNQINFYKDSYAASASKQDFSQDPSKFTEPVVEGLKAGAPVLK' + - name: VP2 + sequence: 'SPSAEACGYSDRVLQLKLGNSAIVTQEAANYCCAYGEWPNYLPDHEAVAIDKPTQPETSTDRFYTLRSVKWESNSTGWWWKLPDALNNIGMFGQNVQYHYLYRSGFLIHVQCNATKFHQGALLVVAIPEHQRGAHDTTTSPGFNDIMKGERGGTFNHPYVLDDGTSIACATIFPHQWINLRTNNSATIVLPWMNVAPMDFPLRHNQWTLAVIPVVPLGTRTMSSVVPITVSIAPMCCEFNGLRHAITQ' + - name: VP3 + sequence: 'GVPTYLLPGSGQFLTTDDHSSAPVLPCFNPTPEMHIPGQIRNMLEMIQVESMMEINNTDGANGMERLRVDISVQADLDQLLFNIPLDIQLDGPLRNTLVGNISRYYTHWSGSLEMTFMFCGSFMATGKLILCYTPPGGSCPTTRETAMLGTHIVWDFGLQSSITLIIPWISGSHYRMFNSDAKSTNANVGYVTCFMQTNLIVPSESSDTCSLIGFIAAKDDFSLRLMRDSPDIGQ' + - name: VP1 + sequence: 'SNHLHGAEAAYQVESIIKTATDTVKSEINAELGVVPSLNAVETGATSNTEPEEAIQTRTVINQHGVSETLVENFLGRAALVSKKSFEYKNHASSSAGTHKNFFKWTINTKSFVQLRRKLELFTYLRFDAEITILTTVAVNGNNDSTYMGLPDLTLQAMFVPTGALTPKEQDSFHWQSGSNASVFFKISDPPARMTIPFMCINSAYSVFYDGFAGFEKNGLYGINPADTIGNLCVRIVNEHQPVGFTVTVRVYMKPKHIKAWAPRPPRTMPYMSIANANYKGRDTAPNTLNAIIGNRASVTTMPHNIVTT' + - name: 2A + sequence: 'GPGFGGVFVGSFKIINYHLATIEERQSAIYVDWQSDVLVTPIAAHGRHQIARCKCNTGVYYCRHRDRSYPICFEGPGIQWIEQNEYYPARYQTNVLLAAGPAEAGDCGGLLVCPHGVIGLLTAGGGGIVAFTDIRNLLWLDTDVMEQ' + - name: 2B + sequence: 'GITDYIQNLGNAFGAGFTETISNKAKEVQDMLIGESSLLEKLLKALIKIISALVIVIRNSEDLITVTATLALLGCHDSPWSYLKQKVCSYLGIPYVPRQ' + - name: 2C + sequence: 'SESWLKKFTEACNALRGLDWLSQKIDKFINWLKTKILPEAREKYEFVQRLKQLPVIEKQVSTIEHSCPTTERQQALFNNVQYYSHYCRKYAPLYAVESKRVAALEKKINNYIQFKSKSRIEPVCLIIHGSPGTGKSVASNLIARAITEKLGGDIYSLPPDPKYFDGYKQQTVVLMDDLMQNPDGNDISMFCQMVSTVDFIPPMASLEEKGTLYTSPFLIATTNAGSIHAPTVSDSKALSRRFKFDVDIEVTDSYKDSNKLDMSRAVEMCKPDNCTPTNYKRCCPLICGKAIQFRDRRTNARSTVDMLVTDIIKEYRTRNSTQDKLEALFQ' + - name: 3A + sequence: 'GPPQFKEIKISVAPDTPAPDAINDLLRSVDSQEVRDYCQKKGWIVIHPSNELVVEKHISRAFITLQAIATFVSIAGVVYVIYKLFAGIQ' + - name: 3B + sequence: 'GPYTGIPNPKPKVPSLRTAKVQ' + - name: 3C + sequence: 'GPGFDFAQAIMKKNTVIARTEKGEFTMLGVYDRVAVIPTHASVGEIIYINDVETRVLDACALRDLTDTNLEITIVKLDRNQKFRDIRHFLPRCEDDYNDAVLSVHTSKFPNMYIPVGQVTNYGFLNLGGTPTHRILMYNFPTRAGQCGGVVTTTGKVIGIHVGGNGAQGFAAMLLHSYFTDTQ' + - name: 3D + sequence: 'GEIVSNEKSGMCINAPAKTKLQPSVFHQVFEGSKEPAVLNSKDPRLKTDFEEAIFSKYTGNKIMLMDEYMEEAVDHYVGCLEPLDISVDPIPLENAMYGMEGLEALDLTTSAGFPYLLQGKKKRDIFNRQTRDTSEMTKMLEKYGVDLPFVTFVKDELRSREKVEKGKSRLIEASSLNDSVAMRVAFGNLYATFHNNPGTATGSAVGCDPDIFWSKIPILLDGEIFAFDYTGYDASLSPVWFACLKKVLIKLGYTHQTSFIDYLCHSVHLYKDRKYVINGGMPSGSSGTSIFNTMINNIIIRTLLIKVYKGIDLDQFKMIAYGDDVIASYPHKIDPGLLAEAGKHYGLVMTPADKGTSFIDTNWENVTFLKRYFRADDQYPFLIHPVMPMKEIHESIRWTKDPRNTQDHVRSLCYLAWHNGEEAYNEFCRKIRSVPVGRALTLPAYSSLRRKWLDSF' +referenceGenome: + nucleotideSequences: + - name: CV-A16 + sequence: 'TTAAAACAGCCTGTGGGTTGTACCCACCCACAGGGCCCACTGGGCGCTAGCACTCTGATTCTACGGAATCCTTGTGCGCCTGTTTTATGTCCCTTCCCCCAATCAGTAACTTAGAAGCATTGCACCTCTTTCGACCGTTAGCAGGCGTGGCGCACCAGCCATGTCTTGGTCAAGCACTTCTGTTTCCCCGGACCGAGTATCAATAGACTGCTCACGCGGTTGAGGGAGAAAACGTCCGTTACCCGGCTAACTACTTCGAGAAGCCTAGTAGCACCATGAAAGTTGCAGAGTGTTTCGCTCAGCACTTCCCCCGTGTAGATCAGGTCGATGAGTCACTGCGATCCCCACGGGCGACCGTGGCAGTGGCTGCGTTGGCGGCCTGCCTGTGGGGTAACCCACAGGACGCTCTAATATGGACATGGTGCAAAGAGTCTATTGAGCTAGTTAGTAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACTGCGGAGCACATACCCTCGACCCAGGGGGCAGTGTGTCGTAACGGGCAACTCTGCAGCGGAACCGACTACTTTGGGTGTCCGTGTTTCCTTTTATTCTTATACTGGCTGCTTATGGTGACAATTGAAAGATTGTTACCATATAGCTATTGGATTGGCCATCCGGTGTGCAACAGAGCTATTATTTACCTATTTGTTGGGTATATACCACTCACATCCAGAAAAACCCTCGACACACTAGTATACATTCTTTACTTGAATTCTAGAAAATGGGGTCACAAGTCTCAACCCAACGATCGGGTTCCCACGAAAATTCGAACTCAGCATCAGAAGGATCTACTATAAACTACACCACCATCAACTATTACAAGGATGCATATGCTGCCAGCGCGGGTCGCCAAGATATGTCTCAGGACCCTAAGAAATTTACAGACCCTGTGATGGATGTCATACACGAGATGGCTCCTCCCTTGAAATCACCCAGTGCTGAAGCTTGTGGTTATAGTGATCGAGTTGCCCAACTCACAATTGGAAACTCCACAATCACTACACAAGAAGCTGCAAACATTATAATAGCATATGGGGAATGGCCCGAGTATTGCAAGGACGCTGATGCCACAGCTGTTGACAAGCCCACCAGACCCGATGTGTCGGTAAATAGGTTCTTCACCCTTGATACTAAATCGTGGGCTAAAGACTCGAAGGGATGGTACTGGAAATTCCCGGACGTTTTGACGGAGGTGGGCGTGTTTGGGCAGAATGCGCAATTCCATTATCTGTATAGATCCGGATTCTGTGTGCACGTGCAGTGCAATGCCAGCAAATTCCACCAGGGTGCTCTCTTGGTTGCCATACTGCCTGAGTACGTGCTGGGCACCATTGCCGGGGGCGATGGTAACGAGAACTCACATCCCCCGTACGTCACCACCCAGCCAGGACAGGTGGGTGCTGTACTTACAAATCCTTATGTTTTGGATGCTGGGGTACCCCTTAGTCAATTGACGGTGTGTCCGCATCAGTGGATTAACCTACGAACCAACAACTGTGCGACCATCATAGTACCATACATGAATACTGTACCATTCGATTCGGCCCTAAACCATTGCAACTTTGGCTTAATTGTAGTGCCCGTAGTACCACTCGACTTTAACGCTGGAGCTACATCAGAAATACCAATAACTGTCACCATCGCACCCATGTGCGCTGAATTTGCAGGTTTGCGACAGGCAATCAAACAGGGGATACCTACCGAGTTGAAGCCCGGTACTAATCAGTTTCTCACTACTGATGATGGTGTCTCAGCTCCCATTTTGCCCGGATTCCACCCTACTCCAGCCATACACATACCTGGTGAAGTGCGCAATCTGTTGGAGATTTGCAGGGTAGAGACCATATTGGAGGTGAACAATCTACAGAGTAATGAGACAACCCCCATGCAACGACTATGCTTCCCTGTCTCGGTGCAGAGTAAGACGGGGGAATTGTGTGCTGTTTTTAGGGCCGACCCTGGTAGGAATGGACCGTGGCAGTCGACTATTTTAGGACAGTTGTGTAGGTACTATACTCAATGGTCTGGATCTCTGGAAGTCACTTTCATGTTTGCTGGATCGTTCATGGCAACAGGAAAGATGCTAATCGCATACACACCTCCGGGGGGTGGGGTCCCAGCAGATCGGCTCACTGCAATGCTGGGAACCCATGTGATATGGGATTTTGGCCTCCAATCCTCAGTCACGCTAGTTATACCATGGATAAGCAACACACACTATAGGGCGCACGCCAAGGACGGTTATTTTGATTATTACACCACTGGCACGATCACTATATGGTATCAGACAAATTATGTCGTACCTATTGGAGCCCCCACAACAGCCTATATTGTGGCCCTCGCAGCCGCTCAGGACAACTTTACCATGAAACTGTGCAAAGACACTGAGGATATTGAGCAATCTGCAAACATCCAGGGTGATGGAATTGCAGACATGATTGACCAGGCTGTCACTTCCCGAGTTGGTCGTGCGCTGACATCCTTACAGGTAGAACCTACCGCCGCCAACACCAATGCTAGTGAGCACAGATTGGGCACCGGGCTCGTCCCCGCCTTGCAGGCTGCAGAGACCGGCGCCTCTTCTAATGCACAGGATGAGAATCTTATAGAAACCCGGTGTGTGTTGAACCATCACTCCACTCAAGAGACCACGATTGGCAACTTTTTCAGTCGAGCAGGACTAGTGAGTATTATTACCATGCCCACCACAGGTACCCAAAACACCGATGGGTATGTGAACTGGGATATTGACTTGATGGGTTATGCTCAAATGAGGCGTAAGTGTGAGCTATTCACATACATGCGCTTTGATGCAGAGTTTACATTTGTAGCTGCCAAACCAAACGGTGAGCTAGTACCACAATTGTTGCAGTACATGTATGTGCCTCCCGGAGCTCCAAAACCTACGTCCCGGGATTCCTTTGCCTGGCAGACTGCTACCAATCCTTCCATCTTCGTCAAGTTGACTGACCCCCCGGCACAAGTGTCAGTACCCTTCATGTCTCCCGCCAGCGCCTACCAGTGGTTTTACGATGGCTATCCAACGTTTGGAGCCCATCCACAATCGAATGACGCAGACTATGGCCAATGTCCAAACAACATGATGGGCACCTTTAGCATCAGAACTGTAGGCACTGAGAAATCCCCCCATTCTATCACCCTTCGGGTGTATATGAGAATCAAGCATGTCAGGGCCTGGATACCCCGGCCACTCAGAAACCAACCGTACCTTTTTAAGACAAATCCAAATTATAAAGGCAACGACATCAAATGCACCAGCACAAGTAGGGACAAAATAACAACACTAGGAAAATTTGGCCAGCAATCTGGTGCTATTTACGTGGGTAACTATAGAGTGGTCAATCGTCACTTGGCCACGCACAATGACTGGGCCAATTTAGTATGGGAAGACAGCTCAAGGGATCTTTTAGTCTCCTCCACCACTGCACAGGGATGTGACACTATTGCTCGATGCGATTGTCAAACGGGGGTGTATTATTGCAGCTCAAGAAGAAAGCACTACCCAGTCAGTTTCTCAAAGCCCAGTCTAATCTTTGTGGAAGCTAGTGAGTACTACCCAGCCAGGTACCAATCGCACCTCATGCTCGCTGTAGGGCATTCCGAGCCTGGAGATTGCGGAGGTATTCTCAGGTGCCAGCATGGTGTTGTTGGTATAGTGTCTACAGGTGGAAATGGGCTTGTTGGTTTTGCTGATGTTAGAGACCTCCTGTGGCTGGATGAAGAGGCTATGGAACAAGGAGTGTCTGATTACATCAAAGGGCTTGGTGATGCGTTTGGGACAGGTTTCACTGACGCTGTTTCCAGAGAGGTTGAGGCTCTTAAAAATCACCTCATAGGATCTGAGGGTGCAGTTGAGAAGATTCTTAAAAACTTAATTAAGCTCATTTCTGCACTAGTGATTGTGATCAGGAGTGATTACGATATGGTCACTCTCACAGCAACTCTGGCTCTAATTGGGTGTCATGGCAGCCCTTGGGCTTGGATCAAGGCTAAGACAGCATCTATTCTGGGCATCCCCATTGCTCAGAAACAGAGCGCATCATGGCTAAAAAAATTCAACGACATGGCTAATGCTGCCAAAGGGCTAGAATGGATATCTAACAAAATTAGTAAGTTCATTGATTGGCTCAAGGAGAAGATTATACCAGCAGCTAAGGAAAAGGTTGAATTCTTGAACAACCTGAAACAGCTACCATTATTAGAGAACCAGATCTCAAACCTAGAACAATCTGCTGCTTCACAAGAGGACCTTGAAGCAATGTTTGGAAATGTGTCATACCTTGCCCATTTCTGCCGCAAATTTCAGCCACTGTACGCTACAGAAGCCAAGAGGGTCTACGCCCTGGAGAAAAGGATGAATAACTACATGCAGTTCAAGAGCAAACACCGTATTGAACCTGTATGTCTCATCATCAGGGGCTCACCAGGTACTGGGAAATCTTTGGCAACCGGTATCATTGCCCGGGCAATAGCCGACAAGTACCACTCCAGTGTGTATTCACTTCCCCCAGACCCAGATCACTTTGATGGGTACAAACAACAAGTAGTTACAGTTATGGATGATTTGTGTCAAAATCCTGATGGTAAGGATATGTCATTATTTTGTCAAATGGTATCCACTGTGGACTTTATCCCACCAATGGCCTCCCTAGAAGAAAAAGGAGTTTCTTTCACATCCAAATTCGTCATTGCGTCCACCAATGCCAGCAACATCATAGTGCCAACGGTGTCCGACTCTGATGCCATCCGCCGCAGATTCTATATGGACTGTGACATTGAGGTGACAGACTCATACAAGACAGATTTGGGCAGATTGGATGCAGGACGGGCCGCCAGATTATGTTCTGAAAACAACACTGCAAACTTCAAGCGCTGCAGCCCACTGGTGTGTGGGAAAGCCATTCAACTCAGAGATAGGAAATCCAAGGTCAGATACAGTGTGGACACGGTGGTCTCCGAACTCATTAGAGAGTACAATAACAGATATGCTATTGGTAATACAATCGAAGCCTTGTTTCAGGGTCCACCTAAGTTTAGGCCCATTAGAATCAGTCTTGAGGAAAAGCCAGCCCCAGATGCCATTAGTGATCTCCTCGCTAGTGTGGACAGTGAGGAAGTGCGTCAATACTGCAGAGACCAAGGCTGGATCATCCCAGAAACTCCCACCAATGTCGAGCGGCACCTTAATAGGGCGGTGTTGATCATGCAGTCCATTGCCACTGTGGTAGCAGTTGTTTCACTGGTGTATGTTATCTACAAATTGTTTGCAGGGTTCCAAGGTGCATACTCTGGTGCCCCTAAGCAAACACTCAAGAAACCTATACTCCGCACAGCGACAGTGCAAGGCCCAAGTCTTGATTTTGCTCTCTCATTGCTTAGGAGGAACATCAGACAAGTCCAGACAGACCAAGGCCACTTCACTATGCTAGGTGTTAGAGATCGCTTGGCTGTCCTCCCGCGACACTCGCAGCCTGGGAAGACAATATGGGTAGAACACAAGCTTATAAACATCTTAGATGCTGTTGAGTTGGTGGATGAACAAGGGGTCAACTTAGAACTGACCCTGGTTACTCTTGATACCAATGAGAAGTTCAGAGACATCACCAAGTTCATCCCAGAAAACATTAGCGCTGCCAGTGATGCCACCCTAGTGATCAACACAGAGCACATGCCCTCCATGTTCGTGCCAGTGGGTGACGTTGTGCAATACGGTTTCCTAAACCTCAGCGGAAAACCCACCCATCGTACCATGATGTACAACTTTCCCACTAAGGCTGGACAATGTGGTGGAGTGGTGACATCTGTTGGAAAGGTTATCGGCATACATATTGGTGGCAATGGCAGGCAGGGCTTTTGTGCAGGACTCAAGAGGAGTTATTTTGCCAGTGAGCAAGGAGAGATCCAGTGGGTCAAGCCCAATAAAGAGACAGGCAGACTCAACATCAATGGGCCGACTCGCACTAAGCTCGAACCCAGTGTGTTCCATGATGTCTTTGAGGGGAACAAAGAACCAGCAGTCTTGCATAGCAGAGACCCACGCCTTGAAGTGGACTTTGAGCAGGCTTTATTCTCCAAGTATGTAGGGAACACACTGCATGAGCCTGACGAGTACATCAAAGAGGCGGCCCTCCACTATGCAAATCAGTTGAAGCAACTGGACATCAACACTTCTCAAATGAGTATGGAGGAAGCTTGCTATGGTACTGAGAACCTTGAGGCCATTGATCTCCACACCAGCGCGGGCTACCCCTACAGTGCTCTGGGAATAAAGAAGAGAGATATTTTAGATCCCACTACCAGAGATGTGAGTAAGATGAAATTCTATATGGACAAGTATGGCCTTGACCTTCCTTACTCTACCTATGTCAAGGATGAGCTCCGCTCGATAGATAAGATTAAGAAAGGAAAATCCCGCCTGATCGAGGCTAGTAGTCTAAATGACTCAGTGTACCTCAGAATGGCCTTTGGTCACTTGTACGAGACTTTCCACGCAAATCCAGGAACCATAACTGGCTCAGCTGTGGGATGCAACCCGGATACATTCTGGAGTAAACTACCAATCCTGCTTCCAGGTTCACTCTTTGCATTTGATTACTCAGGCTATGATGCTAGTCTCAGTCCAGTCTGGTTTAGAGCACTGGAGCTGGTCCTCAGGGAGGTGGGCTATAGTGAGGAGGCAGTCTCCCTTATAGAGGGGATTAACCATACACACCATGTATACCGCAATAAAACTTACTGTGTACTTGGTGGAATGCCCTCAGGCTGTTCAGGAACATCCATTTTCAACTCAATGATCAACAACATCATAATTAGGACATTGCTCATAAAAACATTCAAGGGCATTGATTTGGACGAGCTTAACATGGTCGCCTACGGGGATGATGTGCTTGCTAGTTATCCTTTCCCAATCGATTGCCTGGAATTGGCAAGAACAGGCAAAGAGTATGGTTTGACTATGACTCCTGCAGACAAATCTCCTTGCTTCAATGAGGTAAATTGGGGCAATGCAACCTTTCTCAAAAGAGGCTTCTTGCCCGATGAGCAATTCCCTTTCTTGATCCATCCTACCATGCCAATGAAGGAGATTCACGAATCTATTCGATGGACCAAGGACGCACGAAACACTCAAGATCACGTACGATCCCTGTGTCTTTTGGCGTGGCACAATGGTAAGCAGGAGTATGAAAAATTTGTGAGCACAATTAGGTCTGTCCCAGTAGGAAAAGCTTTGGCTATACCGAATTATGAAAATCTGAGACGCAATTGGCTCGAATTATTTTAGAGGTCAGATATACCTCAACCCCACCAGGGATCTGGTCGTGAATATGACTGGTGGGGGTAAATTTGTTATAACCAGAATAGC' + - name: CV-A10 + sequence: 'TTAAAACAGCCTGTGGGTTGCACCCACTCACAGGGCCCACTGGGCGCTAGCACTCTGGTACCGTGGTACCTTTGTGCGCCTGTTTTATACCCCCTTCCCCGTTTGAACATTAGAAGTAACGCACCTCGATCAGTAGCAGGCGCGGCGCACCAGCCGTGTCTTGATCAAGCACTTCTGTTTCCCCGGACCGAGTATCAATAGACTGCTCACGCGGTTGAAGGAGAAAACGTTCGTTATCCGGCTAACTACTTCGAGAAACTTAGTAGCACCATGGAAGTTGCAGAGTGTTTCGCTCAGCACTCCCCCCCCAGTGTAGATCAGGCTGATGGATCACCGCGTTCCTCACGGGTGACCGTGGCGGTGGTCGCGTTGGCGGCCTGCCCATGGGGCAACCCATGGGACGCTCTAATATGGACATGGTGTGAAGAGTCTATTGAGCTAGTTAGTAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACTGCGGAGCACGTGTCCCCAACCCAGGGGATAGCGTGTCGTAACGGGTAACTCTGCAGCGGAACCGACTACTTTGGGTGTCCGTGTTTCCTTTATTCTTATCCTGGCTGCTTATGGTGACAATTGAGAGATTGTTACCATATAGCTATTGGATTGGCCATCCGGTGTCTAACAGAGCTATAATATATCTTTTTGTTGGGTTCGTACCCCTCAATTTTGAGGTTGTTCTCACATTAAAGTACATCTTGGTGTTAAATATCAGAAGATGGGAGCACAAGTTTCATCTCAGAGGTCAGGATCCCACGAGACTGGGAATGTGGCTACTGGAGGATCGACCATCAACTTCACCAATATAAATTATTACAAGGATTCATATGCAGCTTCTGCTAGTCGTCAGGACTTCACACAAGATCCAAAGAAGTTCACTCAACCGGTTTTGGATTCCATCAGAGAGCTGTCTGCCCCCTTGAATTCACCATCAGTTGAAGCGTGTGGATATAGTGACCGAGTGGCACAACTAACGGTGGGTAACTCCTCCATTACGACACAAGAGGCTGCCAATATAGTTTTGGCATACGGTGAGTGGCCTGAGTACTGCCCTGATACTGACGCAACAGCGGTGGACAAGCCTACTCGTCCGGATGTGTCAGTTAATAGATTCTACACTTTAGATTCAAAGATGTGGCAAGAGAACTCTACTGGATGGTATTGGAAGTTCCCAGATGTTTTGAACAAGACCGGGGTGTTTGGTCAGAATGCACAGTTCCACTACTTGTATCGTTCAGGGTTCTGTCTTCATGTTCAATGTAACGCTAGCAAATTTCACCAGGGGGCTCTTCTTGTGGCAGTTATCCCGGAGTTCGTTCTAGCAGGCAGAGGTTCAAACACGAAGCCCAATGAAGCCCCTCACCCGGGATTTAATACCACCTTTCCTGGCACTGCTGGCGCTTCCTTCAACGACCCGTACGTGCTTGACTCTGGGGTACCTCTTAGCCAATCCTTAATATACCCACATCAGTGGATCAACTTAAGGACCAACAATTGTGCAACAATAATAGTACCTTATATCAATGCTGTCCCCTTTGATTCAGCCATCAATCATAGCAACTTTGGATTAATAGTTGTGCCTGTGAGTCCGCTGAAATACTCTTCAGGGGCCACCACTGCAATCCCAATCACAGTGACCATAGCGCCCCTCAATTCCGAATTTGGTGGGCTGCGCCAAGCTGTCAGCCAGGGACTCCCTACCGAGTTAAGACCAGGCACCAATCAGTTCCTAACAACGGAAGATGATACTGCCGCACCCATACTCCCAGGTTTCTCCCCCACCCCAAGCATCCACATTCCAGGAGAAGTACGCTCATTACTAGAACTGTGTAGGGTGGAAACCATACTGGAAGTGAACAACACGACCGATGCAACCGGTCTGAACAGACTCCTAATTCCAGTCTCCGCCCAAAACAAGGCAGATGAACTATGTGCTGCATTCATGGTTGATCCTGGTCGTATCGGGCCCTGGCAATCGACTTTGGTTGGTCAAATATGTAGGTACTACACACAATGGTCAGGCTCGTTAAAGGTCACATTCATGTTTACAGGCTCTTTTATGGCAACAGGTAAGATGCTGATAGCGTACTCACCACCAGGCAGTGCTCAACCAGCCAATAGGGAGACCGCAATGCTCGGGACCCACGTCATATGGGATTTTGGATTACAATCTTCAGTTTCACTGGTGATACCATGGATCAGCAACACACACTTCAGAACAGCCAAAACTGGTGGTAACTATGACTATTACACAGCTGGTGTGGTGACATTATGGTATCAAACAAATTATGTAGTCCCGCCTGAGACGCCCGGAGAGGCTTACATTATCGCCATGGGGGCTGCTCAAGACAACTTCACCTTGAAGATATGTAAAGACACGGATGAGGTTACCCAACAAGCTGTCTTACAAGGCGACCCCGTGGAGGACATCATCCACGACGCTTTGAGCAGCACTGTGCGGCGGGCCATAACTAGTGGTCAAGATGTCAACACAGCGGCCGGTACCGCTCCTAGCTCTCACAGGTTGGAGACTGGTCGTGTTCCCGCCCTACAAGCAGCAGAAACTGGAGCCACTTCTAACGCTACAGATGAGAACATGATAGAAACGCGGTGTGTCATGAACAGAAATGGAGTGTTGGAGGCGACTATAAGTCATTTCTTCTCACGCTCAGGTTTGGTGGGTGTTGTCAATCTAACTGACGGAGGCACCGATACAACGGGATATGCAGTGTGGGACATTGACATCATGGGTTTTGTGCAACTGCGGCGGAAATGTGAGATGTTCACATACATGAGATTCAACGCTGAGTTCACATTCGTCACTACAACAGAAAATGGCGAGGCAAGGCCATTTATGTTACAGTATATGTATGTACCTCCAGGTGCCCCTAAGCCAACGGGTAGAGATGCTTTTCAGTGGCAAACAGCGACAAATCCATCCGTTTTCGTTAAGCTCACAGATCCACCTGCTCAGGTATCAGTCCCCTTCATGTCACCTGCTAGTGCCTACCAATGGTTCTATGACGGGTATCCAACATTTGGACAACACCCGGAAACATCTAATACAACATATGGACAGTGCCCTAACAACATGATGGGGACCTTTGCTGTGAGAGTAGTGAGTAGAGTGGCTAGCCAGCTCAAACTACAGACACGAGTGTATATGAAGCTTAAGCATGTGAGAGCATGGATCCCTAGGCCAATAAGATCCCAGCCTTACCTCCTAAAGAATTTTCCAAATTATGATAGTAGTAAGATCACATACAGCGCAAGAGATCGTGCCAGCATAAAACAAGCTAATATGGGAAAGTTTGGGCAACAGTCTGGGGCAATATATGTGGGTAATTACAGAGTGGTGAATAGACACTTGGCCACACATAACGATTGGGTAAATCTGGTGTGGGAGGATAGTTCTAGAGACTTGCTGGTCTCATCTACTACAGCACAGGGGTGTGATACCATAGCTCGGTGCAACTGTCAGACAGGTGTGTATTACTGTAACTCTCGTAGGAAACACTACCCGGTCAGCTTTTCCAAACCCAGCCTGATATTTGTTGAAGCTAGCGAATATTACCCAGCTAGGTACCAGTCACACCTCATGCTTGCCGAGGGTCACTCAGAACCTGGTGACTGTGGTGGCATCCTCAGGTGTCAACACGGTGTGGTTGGGTTAGTTTCCACCGGTGGAAATGGCCTCGTGGGGTTTGCTGATGTGAGGGACTTACTCTGGCTTGATGAGGAGGCTATGGAACAAGGAGTGTCTGACTACATCAAAGGTCTTGGTGATGCTTTTGGCACGGGCTTCACTGACGCAGTGTCCAGGGAAGTGGAAGCATTGAAAAATCACTTGATTGGCTCTGAGGGGGCTGTCGAGAAAATCTTGAAAAACCTGGTGAAGCTAATTTCAGCATTAGTTATAGTCATCAGGAGTGACTACGATATGGTCACTCTTACAGCTACGCTTGCCCTGATCGGGTGCCATGGGAGTCCTTGGGCGTGGATCAAATCAAAGACAGCTTCCATCTTAGGCATATCCATGGCACAAAAGCAAAGCGCCTCATGGCTGAAGAAGTTCAATGATATGGCAAATGCCGCAAAAGGGCTCGAGTGGATCTCCAATAAGATCAGCAAGTTTATTGATTGGCTTAAGGAGAAGATCATTCCAGCTGCTAAGGAGAAAGTCGAGTTCTTGAATAACTTAAAGCAACTCCCTCTGTTGGAAAATCAAATTTCCAATCTCGAGCAATCTGCCGCCTCACAGGAAGATCTAGAAGCTATATTTGGCAATGTGTCGTACCTAGCTCATTTTTGCCGCAAGTTCCAACCACTCTACGCAACTGAGGCCAAGAGAGTCTATGCCCTGGAGAAAAGAGTGAACAACTACATGCAGTTCAAGAGCAAACACCGTATTGAACCTGTATGCTTGATCATTAGAGGCTCTCCAGGTACGGGAAAATCACTTGCCACAGGTATTATAGCTAGAGCCATTGCTGATAAGTACCATTCCAGTGTCTATTCGCTCCCTCCAGACCCAGATCACTTCGACGGGTATAAACAACAGGTGGTCACAGTCATGGACGATCTCTGCCAGAATCCAGACGGGAAAGATATGTCCTTGTTCTGCCAAATGGTCTCCACAGTGGACTTTATACCACCCATGGCGTCACTGGAAGAAAAAGGCGTATCCTTCACCTCTAAGTTTGTCATTGCATCGACCAACGCTAGCAACATCATAGTCCCCACAGTCTCAGACTCAGATGCAATCCGCAGGAGATTCTATATGGACTGTGATATAGAAGTAACTGACTCTTACAAGACAGATCTCGGTCGATTGGACGCGGGTAGAGCTGCCAAGCTTTGTTCAGAGAATAACACTGCTAACTTCAAAAGATGCAGTCCACTTGTGTGTGGCAAAGCCATCCAATTGAAAGATAGGAAGTCTAAGGTCAGATATAGTGTTGACACTATGGTATCAGAGCTAATCAGAGAGTACAACAATAGATCTGCTGTTGGAAACACCATAGAAGCACTCTTCCAAGGGCCCCCCAAGTTCAGACCTATAAGAATCAGCCTCGAGGAGAAGCCAACACCAGATGCTATCAGTGACCTTCTCGCTAGTGTTGATAGCGAAGAGGTCCGACAGTATTGCAGAGAGCAAGGGTGGATAATCCCAGAAACACCAACCAACGTGGAACGACACCTCAATAGAGCAGTTCTGGTAATGCAGTCCATTGCTACCGTAGTTGCGGTTGTGTCCCTTGTGTATGTCATTTATAAACTGTTTGCCGGATTTCAAGGTGCCTACTCTGGAGCACCCAAGCAGGCGCTCAAGAAGCCTGTGCTAAGAACAGCTACTGTCCAAGGACCTAGCTTGGACTTCGCTTTGTCTCTTCTGAGGCGCAACATCAGACAAGCGCAGACCGACCAGGGACACTTCACCATGCTAGGCATACGGGACCGTCTAGCCATCTTGCCACGCCACTCACAACCAGGGAAGACCATCTGGATAGAGCACAAATTGGTCAACGTACTAGATGCAGTTGAGTTGGTGGATGAGCAAGGTGTTAATTTGGAACTCACGCTGGTGACCTTGGACACTAATGAGAAGTTTAGGGACATTACCAAGTTCATCCCAGAGACAATAGCTGGTGCTAGTGATGCAACTCTAGTTATCAACACTGAGCATATGCCCTCGATGTTTGTGCCAATAGGTGACGTTGTGCAGTATGGGTTTTTGAATCTCAGTGGCAAACCCACACACAGAACTATGATGTACAATTTCCCCACGAAAGCAGGACAGTGTGGGGGGGTAGTCACTTCAGTTGGCAAGATCATTGGAATCCACATTGGCGGGAATGGACGCCAGGGCTTCTGCGCTGGTTTAAAGAGGAGCTACTTTGCCAGCGAGCAAGGAGAGATCCAGTGGATGAAGCCCAACAAAGAGACTGGGAGGCTGAACATCAATGGTCCAACCCGAACCAAACTGGAACCTAGCGTGTTCCACAATGTGTTCGAGGGTAATAAAGAGCCAGCAGTTCTGACCAGTAAAGACCCCAGGCTTGAGGTTGATTTTGAACAAGCCTTGTTCTCCAAATATGTGGGCAACACTCTGCATGAGCCTGATGAGTATGTGACACAAGCTGCCCTTCATTACGCAAATCAATTAAAACAACTAGACATAAACACCAGCAAGATGAGCATGGAGGAGGCGTGCTATGGTACAGAAAATTTAGAAGCTATAGACCTACACACCAGTGCTGGATATCCTTATAGTGCCTTGGGTATTAAAAAGAGGGATATTCTTGATCCGGTCACCAGGGACACCTCCAAGATGAAACTATACATGGACAAGTATGGACTAGATTTACCCTATTCAACCTATGTGAAGGATGAGCTTAGGTCTCTAGATAAAATCAAGAAGGGGAAATCTCGCTTAATTGAGGCCAGCAGCTTGAATGATTCTGTCTACCTTAGAATGACTTTTGGTCATCTTTATGAGGTGTTTCACGCCAACCCGGGAACTATAACCGGGTCTGCAGTGGGGTGTAATCCTGATGTGTTCTGGAGCAAGTTGCCAATTCTACTACCGGGTTCGCTCTTTGCGTTTGACTACTCAAGCTATGATGCAAGTCTTAGTCCTGTATGGTTCAGAGCTTTAGAATTGGTTTTACGAGAGATTGGTTACTCAGAGGAGGCTGTGTCACTCATAGAGGGGATTAACCACACTCACCATGTGTATCGGAATAAGACATACTGTGTCCTTGGTGGGATGCCTTCAGGTTGCTCTGGCACTTCCATTTTCAATTCCATGATTAACAACATAATCATTAGAACTCTCTTGATCAAGACGTTCAAAGGGATAGACTTGGATGAACTAAACATGGTGGCCTACGGAGATGATGTACTGGCTAGCTACCCATTTCCCATCGACTGTTTGGAGTTGGCGAGAACTGGCAAAGAGTATGGACTGACTATGACTCCCGCCGATAAGTCACCCTGTTTTAATGAAGTCACCTGGGAGAACGCAACCTTTTTGAAGAGGGGTTTCCTACCAGACCATCAGTTCCCTTTTCTAATCCACCCTACCATGCCCATGAGGGAAATCCACGAGTCCATTCGTTGGACCAAGGATGCACGTAACACTCAAGACCACGTGCGTTCCCTTTGCTTGTTGGCGTGGCACAATGGAAAGGAGGAATATGAAAAATTTGTGAGCACAATCAGATCAGTTCCTATTGGAAAAGCCTTGGCGATACCAAATTTTGAGAACTTGAGGAGAAATTGGCTCGAATTGTTTTAAACTTACAGCTTAAAGCTGAACCCCACCAGAAACCTGGTCGTGCAAATGACTGGTGGGGGTAAATTTGTTATAACCAGAATAGC' + - name: EV-A71 + sequence: 'TTAAAACAGCTGTGGGTTGTCACCCACCCACAGGGTCCACTGGGCGCTAGTACACTGGTATCTCGGTACCTTTGTACGCCTGTTTTATACCCCCTCCCTGATTTGCAACTTAGAAGCAACGCAAACCAGATCAATAGTAGGTGTGACATACCAGTCGCATCTTGATCAAGCACTTCTGTATCCCCGGACCGAGTATCAATAGACTGTGCACACGGTTGAAGGAGAAAACGTCCGTTACCCGGCTAACTACTTCGAGAAGCCTAGTAACGCCATTGAAGTTGCAGAGTGTTTCGCTCAGCACTCCCCCCGTGTAGATCAGGTCGATGAGTCACCGCATTCCCCACGGGCGACCGTGGCGGTGGCTGCGTTGGCGGCCTGCCTATGGGGTAACCCATAGGACGCTCTAATACGGACATGGCGTGAAGAGTCTATTGAGCTAGTTAGTAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACTGCGGAGCACATACCCTTAATCCAAAGGGCAGTGTGTCGTAACGGGCAACTCTGCAGCGGAACCGACTACTTTGGGTGTCCGTGTTTCTTTTTATTCTTGTATTGGCTGCTTATGGTGACAATTAAAGAATTGTTACCATATAGCTATTGGATTGGCCATCCAGTGTCAAACAGAGCTATTGTATATCTCTTTGTTGGATTCACACCTCTCACTCTTGAAACGTTACACACCCTCAATTACATTATACTGCTGAACACGAAGCGATGGGCTCCCAGGTCTCCACACAGCGATCCGGCTCGCATGAGAATTCCAACTCAGCCACGGAAGGCTCCACTATAAATTACACAACCATTAATTACTACAAAGACTCGTATGCTGCCACTGCTGGAAAGCAAAGTCTCAAACAAGATCCTGACAAGTTTGCGAACCCTGTGAAGGACATCTTTACTGAAATGGCAGCGCCCTTAAAGTCTCCCTCTGCTGAAGCATGTGGCTATAGCGACCGAGTGGCACAGCTTACCATTGGAAATTCCACCATTACTACACAAGAAGCAGCAAACATAATAGTTGGGTATGGTGAGTGGCCTTCATACTGCTCTGATAATGATGCAACAGCGGTAGACAAACCTACACGGCCTGATGTCTCAGTAAATAGATTTTACACGCTAGACACTAAGCTATGGGAGAAATCATCCAAGGGGTGGTACTGGAAGTTCCCAGATGTACTGACTGAAACCGGAGTTTTTGGTCCAAATGCACAATTTCACTACTTATACCGTTCAGGGTTCTGCATCCACGTTCAATGTAACGCTAGCAAATTTCACCAAGGGGCGCTACTCGTTGCGGTATTGCCCGAGTATGTCATTGGAACAGTGGCAGGCGGCACAGGCACAGAGAACAGTCACCCTCCTTATAAACAAACCCAACCCGGCGCTGATGGATTTGAATTACAACATCCATATGTTCTTGATGCTGGAATTCCAATATCTCAGTTGACAGTGTGCCCTCACCAGTGGATCAATTTACGAACCAACAATTGTGCCACCATAATAGTGCCATACATGAACACACTACCTTTTGATTCCGCATTGAACCACTGTAATTTCGGACTATTGGTGGTGCCTATCAGCCCGCTGGATTTCGACCAAGGGGCGACACCGGTAATTCCTATCACTATCACGTTGGCTCCGATGTGTTCTGAGTTTGCGGGTCTCAGGCAGGCAGTTACGCAGGGTTTTCCCACTGAATTGAAACCTGGCACTAATCAGTTCTTAACCACGGATGATGGTGTGTCAGCACCTATATTGCCAAATTTCCACCCCACCCCGTGCATTCACATACCTGGCGAGGTTAGAAACTTACTAGAACTGTGCCAGGTAGAAACCATTTTAGAAGTCAACAATGTGCCCACCAACGCAACCAGTTTGATGGAAAGGCTACGGTTTCCAGTGTCAGCCCAAGCAGGGAAAGGTGAGTTGTGTGCAGTGTTCAGGGCCGACCCTGGGAGGGATGGTCCTTGGCAATCCACCATGCTAGGCCAGTTGTGTGGATATTACACCCAATGGTCAGGGTCTTTGGAAGTCACTTTTATGTTCACCGGATCCTTTATGGCAACTGGTAAAATGCTTATAGCTTACACACCCCCAGGGGGCCCTTTGCCTAAAGATAGAGCCACAGCTATGCTGGGGACGCACGTCATCTGGGACTTTGGCTTGCAATCGTCCGTCACCCTCGTCATACCATGGATCAGTAACACTCACTATAGGGCGCATGCTCGAGATGGGGTGTTTGATTACTACACCACAGGTTTGGTTAGTATATGGTACCAAACAAATTATGTAGTCCCTATTGGAGCACCTAATACTGCCTATATAATAGCGTTGGCAGCAGCCCAAAAGAATTTCACTATGAAATTGTGCAAGGACACCAGTGACATTTTGGAAACGGCCACTATTCAAGGGGACAGAGTGGCAGATGTGATTGAGAGCTCTATAGGAGATAGTGTGAGTAAGGCCCTCACCCCAGCTTTACCTGCACCCACAGGCCCAGACACCCAAGTGAGCAGTCATCGCTTAGACACTGGAAAAGTACCAGCACTTCAAGCCGCCGAAATCGGAGCTTCGTCGAATGCTAGTGATGAGAGTATGATTGAGACTCGGTGTGTTCTTAACTCACATAGCACAGCTGAAACCACCCTTGATAGTTTCTTCAGCAGAGCAGGCTTAGTTGGGGAGATAGATCTTCCTCTAAAGGGCACCACCAATCCGAACGGGTATGCCAACTGGGACATAGACATAACCGGTTATGCGCAGATGCGCAGAAAAGTGGAACTATTCACCTATATGCGCTTTGACGCAGAGTTCACTTTTGTCGCGTGCACACCTACCGGAAGGGTCGTTCCACAGCTGCTTCAATACATGTTTGTTCCACCCGGGGCCCCCAAACCAGACTCCAGAGACTCTTTGGCTTGGCCAACGGCCACGAACCCCTCAGTTTTTGTCAAATCATCCGACCCACCAGCACAAGTCTCAGTGCCATTTATGTCACCTGCAAGCGCATACCAATGGTTTTATGACGGATACCCTACATTTGGAGAGCACAAGCAAGAGAAGGATCTCGAGTATGGGGCATGCCCGAATAACATGATGGGCACATTCTCAGTGCGGACTGTGGGATCGTCAAAGTCAGAATATTCCTTAGTCATCAGAATATACATGAGAATGAAGCACGTCAGAGCGTGGATACCTCGGCCGATGCGCAATCAGAACTATTTGTTCAAATCCAACCCAAACTATGCTGGTGATTCCATTAAACCAACTGGTACCAGCCGAACGGCAATCACTACGCTCGGGAAATTCGGTCAGCAGTCTGGGGCTATTTATGTGGGCAACTTTAGGGTAGTAAACAGACACCTAGCCACCCATACTGACTGGGCCAACTTGGTGTGGGAAGACAGCTCTAGAGACCTCCTAGTTTCTTCAACTACCGCTCAAGGGTGTGACACCATTGCTCGATGTAACTGCCAAACCGGAGTGTATTACTGTAACTCTCGCAGAAAACACTATCCAGTCAGTTTTTCGAAACCTAGTTTGGTGTTTGTAGAAGCTAGTGAGTATTATCCAGCTAGATATCAGTCCCATCTTATGCTTGCTGAGGGCCATTCAGAACCTGGTGATTGTGGCGGTATTCTTAGATGCCAACACGGTGTGGTGGGAATTGTCTCCACTGGCGGAAGTGGCCTTGTGGGATTTGCTGACGTTAGAGATCTTCTGTGGCTAGATGAGGAAGCGATGGAGCAGGGGGTATCTGATTACATCAAAGGTCTCGGTCGAGCCTTCGGCACAGGTTTCACTGACGCAGTGTCTAGGGAAGTGGAAGCGTTGAAGAACCACTTAATCGGCTCCGAAGGGGCTGTTGAGAAGATCTTGAAGAACTTGGTGAAGCTAATTTCAGCCTTAGTTATAGTCATCAGAAGTGATTATGATATGGTCACCCTCACAGCCACACTAGCTCTGATCGGGTGCCACGGGAGTCCTTGGGCGTGGATCAAATCAAAGACAGCTTCCATACTGGGCATTCCCATGGCACAAAAACAGAGTGCCTCATGGCTAAAGAAGTTCAATGACATGGCAAATGCTGCAAAAGGGCTTGAGTGGATTTTCAACAAGATCAGTAAGTTCATTGACTGGCTTAAAGAGAAGATCATTCCAGCTGCCAAAGAGAAAGTTGAGTTTTTGAACAACCTAAAACAGCTCCCCTTGTTGGAGAACCAAGTCTCCAATCTTGAACAGTCTGCTGCCTCACAAGAAGACTTAGAAGCTATGTTTGGTAATGTGATATATCTGGCTCACTTTTGCCGCAAATTCCAACCACTCTACGCAACTGAGGCCAAGAGAGTCTACGCTTTAGAGAAAAGGATGAATAACTACATGCAGTTCAAGAGCAAACACCGTATTGAACCTGTATGCTTGATCATCAGAGGTTCCCCCGGAACGGGCAAATCGCTCGCCACAGGCATTATAGCTAGAGCCATTGCTGACAAGTATCGCTCTAGTGTATACTCACTCCCCCCAGACCCAGATCACTTTGATGGGTATAAGCAACAGGTGGTCGCGGTCATGGATGATCTCTGCCAGAACCCGGACGGAAAAGACATGTCCCTATTTTGTCAAATGGTTTCTACAGTAGATTTTGTACCACCCATGGCATCACTAGAGGAGAAAGGAGTGTCCTTCACCTCTAAGTTTGTCATTGCATCGACCAATGCTAGTAACATCATAGTCCCCACAGTTTCAGATTCAGATGCAATTCGCAGGCGATTCTATATGGACTGCGATATAGAAGTGACAGATTCTTACAAGACAGACCTCGGTCGGCTGGACGCAGGTAGAGCTGCCAAGCTTTGTACAGAAAATAACACTGCTAATTTTAAGAGATGCAGCCCACTGGTGTGTGGTAAGGCTATTCAGCTGAGAGACAGGAAGTCCAAAGTGAGATATAGCGTCGACACCGTGGTATCGGAACTGATCAGAGAGTACAACAATAGATCTGCTATTGGGAATACTATAGAAGCACTCTTTCAAGGACCCCCTAAATTCAGGCCTATAAGAATTAGTCTCGAAGAAAAGCCAGCCCCAGATGCCATTAGCGATCTCCTCGCTAGTGTAGATAGCGAGGAGGTGCGTCAGTACTGCAGGGAACAAGGCTGGATCATCCCTGAAACTCCCACCAATGTTGAGCGTCACCTCAATAGAGCAGTATTGGTAATGCAGTCCATCGCCACTGTGGTTGCAGTTGTGTCTCTTGTTTATGTCATTTATAAGCTGTTTGCCGGGTTCCAGGGTGCTTACTCTGGAGCGCCCAAGCCCATTCTCAAGAAGCCCGTGTTAAGAACAGCCACGGTCCAAGGGCCCAGCTTAGACTTCGCCTTGTCTCTTTTGAGGCGCAACATTAGACAAGCGCAAACTGACCAAGGACACTTCACCATGCTAGGAGTGCGAGATCGCCTAGCCATCCTGCCGCGCCACTCGCAACCAGGGAAGACCATCTGGGTAGAGCATAAATTAATCAATGTACTAGATGCAGTTGAGTTGGTGGATGAGCAAGGTGTAAACTTGGAACTCACACTGGTAACTTTGGACACCAATGAAAAATTTAGGGATATCACCAAGTGTATCCCAGAAGTGATCACCGGGGCGAGTGACGCAACTCTAGTCATCAACACTGAGCACATTCCCTCAATGTTTGTGCCGGTGGGTGACGTTGTGCAGTACGGTTTCTTGAACCTTAGTGGTAAACCCACACACAGAACCATGATGTATAACTTCCCCACGAAGCCAGGACAGTGTGGGGGGGTGGTTACCTCAGTTGGTAAGATCATTGGAATCCACATTGGCGGGAATGGACGCCAGGCCTTTTGCGCTGGCCTAAAGAGGAGTTATTTTGCCAGCGAGCAAGGAGAGATCCAGTGGATGAAGCCTAACAGAGAAACCGGGAGGTTGAATATTAATGGTCCAACCCGAACTAAGCTGGAACCCAGTGTATTCCATGATGTGTTCGAGGGCAACAAGGAACCAGCGGTCCTGACTAGTAAGGACCCCAGACTTGAGGTTGATTTTGAGCAAGCTTTGTTCTCCAAGTATGTGGGTAACACCCTGCATGAACCTGATGAGTACGTGACACAGGCTGCTCTCCACTACGCAAATCAGCTGAAGCAACTGGACATCAACACCAGCAAGATGAGCATGGAAGAAGCGTGCTATGGCACAGAATATTTAGAAGCTATAGACTTGCACACCAGTGCTGGATACCCTTATAGTGCTTTGGGCATCAAGAAAAGAGACATCCTCGACCCAGTTACCAGAGACACCTCCAGGATGAAGTTATATATGGATAAGTATGGGTTGGACTTGCCTAATTCCACTTATGTAAAGGATGAGCTTAGTTCTCTAGATAAGATCAGAAAAGGGGAGTCTCGCCTGATTGAGGCTAGCAGCTTAAATGATCCTGTCTACCCTAGATTGACTTTTGGACACCTTTATGAAGTGTTTCACGCCAACCCAGGGACTGTAACAGGATCTGCAGTTGGGTGCAACCCTGATGTATTTTGGAGCAAGTTACCAATTTTGTTACCGGGTTCACTCTTTGCATTTGACTACTCAGGATATGATGCAAGCCTTAGTCCTGTGTGGTTCAGAGCTCTAGAGTTGGTTCTGAGAGAGATCGGTTACTCGGAGGAGGCTGTGTCACTCATAGAAGGGATCAATCACACCCACCACGTGTACCGAAACAAGACATATTGTGTACTTGGTGGAATGCCCTCAGGCTGCTCCGGTACTTCCATTTTCAATTCCATGATTAACAACATAATCATCAGAACCCTCCTGATTAAAACATTCAAAGGTATAGACTTAGATGAGCTGAAAATGGTAGCTTATGGAGATGACGTGTTGGCCAGCTACCCGTTTCCTATTGATTGCTTGGAATGGGGTAAAACAGGCAAAGAATATGGGCTGACTATGACTCCTGCTGATAAATCACCTTGTTTCAATGAGGTTACCTGGGAGAATGCAACCTTCTTAAAACGGGGTTTTCTACCGGACCATCAGTTCCCTTTTCTGATCCATCCCACTATGCCCATGAGGGAAATCCATGAGTCCATCCGCTGGACCAAGGACGCGCGCAATACTCAAGATCATGTGCGCTCCCTTTGTCTCCTGGCATGGCATAATGGAAAAGAGGAGTATGAGAAATTTGTGAGTACAATTAGATCAGTCCCCATTGGAAGGGCTTTAGCAATTCCAAATTTGGAGAACTTGAGAAGAAATTGGCTCGAGTTATTTTAAACTTACAGCTCAATGCTGAACCCCACCAGAAATCTGGTCGTGTCAATGACTGGTGGGGGTAAATTTGTTATAACCAGAATAGC' + - name: EV-D68 + sequence: 'TTAAAACAGCTCTGGGGTTGTTCCCACTCAAGGGCCCACGTGGCGGCTAGTACTCTGGTATCTCGGTACCTTTGTACGCCTGTTTTAATTCCCTCCCCAACGTAACTTAGAAGCTTTTAAACCAAAGCTCAATAGGTGGAGCGCAAACCAGCGCTCTTATGAGCAAGCACTTCTGTCTCCCCGGTGTGGTTGTATAGACTGTCCCCACGGTTGAAAACAACTTATCCGTTAACCGCTATAGTACTTCGAGAAACCTAGTATTGCCTTCGGAGTGTTGATGCGTTGCGCTCAGCACACTAACCCGTGTGTAGCTTGGGTCGATGAGTCTGGACGTACCCCACTGGCGACAGTGGTCCAGGCTGCGTTGGCGGCCTACTCATGGTGAAAACCATGAGACGCTAGACATGAACAAGGTGTGAAGAGTCTATTGAGCTGCTATAGAGTCCTCCGGCCCCTGAATGCGGCTAATCCTAACCATGGAGCAAGTGCTCACAAACCAGTGAGTTACTTGTCGTAACGCGCAAGTCCGTGGCGGAACCGACTACTTTGGGTGTCCGTGTTTCACTTTTTACTTTTATGACTGCTAATGGTGACAATTTAATATTGTTACCATTTGGCTTGTCGAATTGATCACATAAGATCTATAGTTTTGTTCACTGATTTGCTTTGAAATAATCTCACCTCAAAACCTCCAGTACATAACATTTAAAGAGTTTAAACTTATTTATAACAATGGGAGCTCAAGTTACTAGACAGCAAACTGGAACTCATGAGAATGCTAACATTGCTACAAATGGATCCCATATTACATACAATCAGATAAATTTTTACAAGGATAGTTATGCAGCTTCAGCCAGCAAGCAAGACTTTTCACAGGATCCATCAAAATTCACTGAACCAGTGGTGGAAGGCTTAAAAGCAGGGGCACCAGTTTTGAAATCTCCTAGCGCTGAGGCTTGTGGCTACAGTGATAGAGTACTACAACTCAAATTGGGCAACTCAGCTATTGTCACTCAAGAAGCAGCAAACTACTGTTGCGCTTATGGTGAATGGCCTAATTATTTACCAGATCATGAAGCAGTAGCTATTGATAAACCTACACAACCAGAAACTTCTACAGACAGATTTTATACTTTAAGATCAGTCAAATGGGAGAGTAATAGCACAGGATGGTGGTGGAAACTACCTGATGCATTAAACAACATAGGCATGTTTGGACAAAATGTACAATATCACTACCTATACAGATCTGGCTTCTTAATTCATGTGCAGTGCAACGCTACAAAGTTCCATCAAGGCGCGCTGTTGGTGGTAGCAATACCAGAGCATCAGAGAGGGGCACACGACACCACCACTAGTCCAGGGTTTAATGATATCATGAAAGGTGAAAGAGGAGGGACGTTTAACCACCCATATGTTCTTGATGATGGAACATCAATAGCTTGTGCGACAATATTTCCACATCAATGGATAAATCTAAGAACCAACAATTCAGCAACGATTGTTCTTCCATGGATGAATGTTGCACCAATGGACTTTCCACTTAGACACAATCAGTGGACACTAGCAGTAATACCAGTGGTTCCATTGGGTACACGCACAATGTCAAGTGTTGTCCCAATAACAGTTTCAATTGCCCCTATGTGTTGTGAGTTTAACGGACTCAGGCACGCCATCACCCAGGGTGTTCCAACATATCTTCTACCAGGTTCAGGACAATTTCTGACTACTGATGACCATAGCTCAGCACCAGTCCTCCCGTGTTTCAACCCAACTCCAGAAATGCACATTCCAGGGCAAATCCGCAACATGTTGGAAATGATTCAAGTGGAATCAATGATGGAGATTAACAATACAGACGGCGCAAATGGCATGGAGCGTCTCAGAGTTGACATATCAGTACAAGCAGATCTTGATCAGTTATTATTTAACATTCCACTAGATATACAACTGGATGGACCACTTAGAAACACTTTAGTAGGGAACATATCCAGATATTATACTCATTGGTCTGGATCTCTAGAAATGACATTCATGTTTTGCGGTAGTTTTATGGCAACAGGGAAATTGATCCTATGTTACACACCTCCAGGTGGATCATGCCCAACAACTAGAGAAACTGCCATGTTAGGCACTCACATCGTCTGGGACTTTGGATTACAATCTAGTATCACCTTAATAATACCTTGGATTAGTGGATCCCACTACAGGATGTTCAATAGCGACGCTAAGTCAACCAATGCTAATGTTGGCTATGTAACCTGTTTCATGCAGACCAACCTGATAGTCCCCAGTGAGTCCTCTGATACTTGTTCTTTAATAGGATTCATAGCAGCAAAAGATGACTTTTCCCTCAGGTTAATGAGAGATAGTCCAGATATTGGACAATCAAACCACTTACATGGAGCAGAGGCAGCCTATCAGGTGGAGAGTATCATCAAAACAGCAACTGATACTGTGAAGAGTGAGATTAACGCCGAACTTGGTGTGGTCCCTAGTCTAAATGCAGTTGAAACTGGTGCAACTTCCAACACTGAACCAGAAGAAGCCATACAAACTCGCACAGTAATAAATCAGCATGGTGTGTCGGAGACGTTAGTGGAGAATTTTCTTGGTAGGGCAGCCCTAGTGTCAAAGAAAAGTTTTGAATACAAGAATCATGCCTCATCCAGCGCAGGGACACACAAAAACTTTTTTAAATGGACAATTAATACTAAGTCTTTTGTCCAGTTAAGAAGAAAGCTGGAATTATTCACATACCTTAGGTTTGATGCTGAAATCACCATACTCACAACTGTGGCAGTAAATGGTAATAATGACAGCACATACATGGGTCTCCCTGACTTGACACTCCAAGCAATGTTTGTACCAACTGGTGCTCTTACTCCAAAGGAGCAGGATTCATTTCATTGGCAATCAGGCAGTAATGCTAGTGTGTTCTTTAAAATTTCTGATCCCCCAGCTAGAATGACTATACCTTTTATGTGCATCAACTCAGCATATTCAGTTTTTTATGATGGCTTTGCTGGATTTGAGAAAAATGGTCTATATGGAATAAACCCAGCTGACACTATTGGCAACTTGTGTGTCAGAATAGTGAATGAACATCAACCAGTTGGTTTTACAGTGACCGTTAGGGTTTACATGAAGCCTAAACATATAAAAGCATGGGCTCCACGACCACCGCGAACCATGCCATACATGAGCATTGCTAATGCAAATTACAAAGGTAGAGATACAGCACCAAACACACTTAATGCCATAATTGGTAATAGAGCGAGTGTCACAACTATGCCTCACAACATAGTAACCACCGGTCCAGGTTTTGGGGGAGTCTTTGTAGGGTCCTTTAAGATAATTAACTATCACTTAGCCACAATAGAAGAGAGACAATCAGCCATCTATGTGGACTGGCAATCAGATGTTCTAGTCACCCCCATTGCCGCTCATGGTAGACATCAAATAGCAAGATGCAAGTGTAATACAGGGGTTTACTATTGCCGGCACAGAGATAGAAGCTACCCAATTTGCTTTGAAGGTCCAGGAATTCAATGGATTGAACAAAATGAATACTACCCGGCAAGATACCAGACTAATGTACTTCTAGCAGCTGGCCCTGCAGAAGCAGGAGATTGTGGTGGTCTATTAGTCTGCCCACACGGGGTGATTGGCCTCCTTACAGCAGGAGGGGGTGGGATTGTAGCCTTCACAGACATCAGAAATTTACTGTGGTTAGATACTGATGTTATGGAACAAGGTATTACTGATTACATACAAAATCTTGGTAATGCTTTTGGAGCAGGATTCACAGAAACAATCTCCAATAAGGCTAAGGAAGTGCAAGACATGTTAATTGGAGAGAGTTCATTATTGGAAAAATTACTAAAAGCTCTAATCAAAATTATATCAGCACTGGTGATTGTCATTAGAAATTCAGAAGATTTGATAACAGTTACAGCTACACTAGCATTGTTAGGGTGTCATGACTCACCATGGAGCTACTTGAAGCAAAAAGTATGCTCATACTTGGGTATTCCCTATGTGCCTAGACAAAGTGAATCATGGCTTAAGAAGTTCACAGAGGCGTGCAATGCTCTTAGGGGTTTAGATTGGCTATCACAAAAGATAGACAAGTTCATTAATTGGCTCAAAACTAAAATATTACCAGAGGCTAGGGAGAAATATGAATTCGTGCAAAGACTCAAGCAGCTACCAGTAATAGAAAAACAAGTTAGCACTATTGAGCATAGTTGCCCAACAACAGAACGACAACAGGCCTTATTCAACAATGTTCAGTATTACTCACATTACTGTAGGAAGTACGCACCACTTTACGCAGTGGAATCAAAAAGAGTGGCCGCTCTTGAAAAGAAAATAAACAATTACATCCAGTTCAAGTCCAAATCTCGCATTGAACCGGTTTGTTTAATAATACACGGCTCCCCAGGAACTGGCAAATCAGTAGCCTCAAATTTAATTGCTAGAGCTATCACAGAAAAATTAGGCGGGGACATTTACTCCCTACCCCCAGACCCTAAATACTTTGATGGATATAAACAGCAAACAGTAGTCCTTATGGATGATTTAATGCAAAATCCAGATGGGAATGATATATCTATGTTTTGCCAAATGGTTTCAACTGTGGATTTTATTCCTCCAATGGCTAGTTTGGAAGAGAAAGGAACTCTATATACCAGTCCATTCTTAATAGCCACTACTAATGCTGGTTCAATACATGCACCAACAGTCTCAGACTCAAAGGCTTTGTCACGCAGATTCAAATTTGATGTGGACATTGAAGTTACAGATTCATACAAAGACTCAAACAAACTAGACATGTCCAGAGCAGTGGAAATGTGCAAACCAGATAACTGTACCCCTACTAATTATAAAAGATGCTGCCCACTGATTTGCGGAAAAGCTATTCAATTTAGAGATCGTAGAACCAATGCAAGATCCACGGTTGATATGCTAGTGACTGACATTATTAAGGAATACAGAACCAGGAATAGCACACAAGACAAATTAGAAGCCCTATTTCAAGGACCTCCACAATTCAAGGAGATTAAAATCTCAGTCGCTCCAGACACACCAGCCCCTGATGCCATAAATGACCTTCTTAGGTCAGTGGACTCTCAAGAAGTTAGAGATTATTGTCAAAAGAAGGGGTGGATTGTAATACACCCGTCAAATGAGCTAGTTGTAGAAAAACATATTAGTAGAGCTTTCATCACTCTGCAAGCTATTGCCACCTTTGTATCAATAGCTGGTGTGGTCTATGTTATATACAAACTTTTTGCTGGTATTCAGGGCCCATATACAGGAATTCCTAATCCTAAACCCAAAGTGCCCTCTCTTAGAACAGCCAAAGTGCAAGGACCAGGATTTGATTTTGCGCAAGCCATAATGAAGAAAAATACTGTTATTGCTAGAACTGAAAAAGGCGAGTTCACAATGCTTGGTGTGTATGATAGAGTGGCAGTCATTCCAACACATGCATCTGTTGGAGAAATCATTTACATCAACGATGTAGAAACCAGAGTTCTAGATGCATGTGCACTTAGAGACTTGACAGACACAAACCTAGAAATAACTATAGTCAAATTGGATCGCAATCAAAAATTTAGAGACATCAGACACTTTTTACCCAGATGTGAGGATGATTACAATGATGCTGTGCTTAGTGTACATACATCAAAATTCCCTAACATGTACATTCCAGTTGGACAAGTCACTAACTACGGCTTCTTGAACCTGGGCGGCACACCAACACATCGGATTTTAATGTATAATTTTCCAACAAGAGCTGGTCAGTGTGGTGGTGTGGTGACAACCACAGGTAAAGTGATAGGAATACACGTGGGCGGGAATGGAGCTCAGGGATTCGCAGCAATGTTGCTCCACTCTTACTTTACTGATACACAAGGTGAGATAGTTAGCAATGAGAAGAGTGGGATGTGTATTAATGCACCAGCAAAAACAAAACTCCAACCCAGTGTCTTCCATCAAGTTTTTGAAGGTTCAAAGGAACCAGCAGTACTCAATTCAAAAGATCCTAGACTCAAGACTGATTTCGAGGAGGCTATTTTCTCAAAATATACAGGTAACAAAATTATGTTAATGGATGAGTACATGGAAGAAGCAGTGGATCATTATGTGGGATGTTTAGAACCACTGGATATTAGTGTAGATCCCATACCCCTTGAAAATGCTATGTATGGAATGGAGGGTCTTGAAGCATTGGACTTAACCACCAGTGCGGGCTTCCCTTATTTGTTACAAGGGAAAAAGAAAAGGGACATATTCAACAGACAAACCAGAGATACTAGTGAGATGACAAAGATGTTGGAAAAATACGGAGTTGACCTACCTTTCGTGACTTTCGTGAAAGACGAGCTTAGATCAAGAGAGAAAGTCGAAAAGGGGAAGTCACGCCTGATTGAAGCCAGTTCCTTGAACGACTCAGTTGCCATGAGGGTTGCCTTTGGAAATCTCTACGCTACATTTCATAACAATCCAGGCACAGCAACTGGTAGTGCAGTTGGTTGTGATCCAGATATATTCTGGTCAAAAATCCCTATTTTGTTAGATGGAGAGATTTTTGCTTTTGATTACACTGGTTATGACGCTAGTTTATCACCAGTGTGGTTTGCCTGTTTGAAAAAGGTTCTGATTAAATTAGGTTACACCCACCAAACATCTTTTATAGATTATCTATGCCACTCAGTGCATTTGTACAAGGATAGAAAATATGTAATTAATGGTGGAATGCCCTCTGGTTCTTCAGGTACTAGTATATTTAACACTATGATTAATAACATAATTATAAGAACTTTATTAATTAAGGTTTACAAAGGCATAGATCTGGACCAGTTTAAAATGATAGCATACGGAGATGATGTTATTGCTAGTTACCCACACAAAATTGATCCAGGTTTATTAGCAGAAGCAGGCAAGCACTATGGATTGGTAATGACACCAGCGGACAAAGGTACCAGTTTCATTGACACTAATTGGGAAAATGTAACTTTTTTGAAAAGATACTTCAGAGCAGATGATCAATACCCCTTTCTTATACATCCAGTGATGCCAATGAAAGAGATACATGAATCTATTAGATGGACTAAAGATCCCAGAAACACACAAGATCATGTTAGATCCTTGTGTTACCTGGCGTGGCATAATGGAGAAGAGGCTTACAATGAATTTTGTAGAAAAATTAGAAGTGTGCCCGTGGGGAGGGCATTGACACTACCTGCATATTCTAGTCTTAGACGGAAATGGTTAGATTCGTTTTAGATAACTCTAATTGAAACCCAAGTTACAGTTACTTTCACTTAGAGGTAAATTTTGGTCACTTGGGGACC' + genes: + - name: VP4-CV-A16 + sequence: 'MGSQVSTQRSGSHENSNSASEGSTINYTTINYYKDAYAASAGRQDMSQDPKKFTDPVMDVIHEMAPPLK' + - name: VP2-CV-A16 + sequence: 'SPSAEACGYSDRVAQLTIGNSTITTQEAANIIIAYGEWPEYCKDADATAVDKPTRPDVSVNRFFTLDTKSWAKDSKGWYWKFPDVLTEVGVFGQNAQFHYLYRSGFCVHVQCNASKFHQGALLVAILPEYVLGTIAGGDGNENSHPPYVTTQPGQVGAVLTNPYVLDAGVPLSQLTVCPHQWINLRTNNCATIIVPYMNTVPFDSALNHCNFGLIVVPVVPLDFNAGATSEIPITVTIAPMCAEFAGLRQAIKQ' + - name: VP3-CV-A16 + sequence: 'GIPTELKPGTNQFLTTDDGVSAPILPGFHPTPAIHIPGEVRNLLEICRVETILEVNNLQSNETTPMQRLCFPVSVQSKTGELCAVFRADPGRNGPWQSTILGQLCRYYTQWSGSLEVTFMFAGSFMATGKMLIAYTPPGGGVPADRLTAMLGTHVIWDFGLQSSVTLVIPWISNTHYRAHAKDGYFDYYTTGTITIWYQTNYVVPIGAPTTAYIVALAAAQDNFTMKLCKDTEDIEQSANIQ' + - name: VP1-CV-A16 + sequence: 'GDGIADMIDQAVTSRVGRALTSLQVEPTAANTNASEHRLGTGLVPALQAAETGASSNAQDENLIETRCVLNHHSTQETTIGNFFSRAGLVSIITMPTTGTQNTDGYVNWDIDLMGYAQMRRKCELFTYMRFDAEFTFVAAKPNGELVPQLLQYMYVPPGAPKPTSRDSFAWQTATNPSIFVKLTDPPAQVSVPFMSPASAYQWFYDGYPTFGAHPQSNDADYGQCPNNMMGTFSIRTVGTEKSPHSITLRVYMRIKHVRAWIPRPLRNQPYLFKTNPNYKGNDIKCTSTSRDKITTL' + - name: 2A-CV-A16 + sequence: 'GKFGQQSGAIYVGNYRVVNRHLATHNDWANLVWEDSSRDLLVSSTTAQGCDTIARCDCQTGVYYCSSRRKHYPVSFSKPSLIFVEASEYYPARYQSHLMLAVGHSEPGDCGGILRCQHGVVGIVSTGGNGLVGFADVRDLLWLDEEAMEQ' + - name: 2B-CV-A16 + sequence: 'GVSDYIKGLGDAFGTGFTDAVSREVEALKNHLIGSEGAVEKILKNLIKLISALVIVIRSDYDMVTLTATLALIGCHGSPWAWIKAKTASILGIPIAQKQ' + - name: 2C-CV-A16 + sequence: 'SASWLKKFNDMANAAKGLEWISNKISKFIDWLKEKIIPAAKEKVEFLNNLKQLPLLENQISNLEQSAASQEDLEAMFGNVSYLAHFCRKFQPLYATEAKRVYALEKRMNNYMQFKSKHRIEPVCLIIRGSPGTGKSLATGIIARAIADKYHSSVYSLPPDPDHFDGYKQQVVTVMDDLCQNPDGKDMSLFCQMVSTVDFIPPMASLEEKGVSFTSKFVIASTNASNIIVPTVSDSDAIRRRFYMDCDIEVTDSYKTDLGRLDAGRAARLCSENNTANFKRCSPLVCGKAIQLRDRKSKVRYSVDTVVSELIREYNNRYAIGNTIEALFQ' + - name: 3A-CV-A16 + sequence: 'GPPKFRPIRISLEEKPAPDAISDLLASVDSEEVRQYCRDQGWIIPETPTNVERHLNRAVLIMQSIATVVAVVSLVYVIYKLFAGFQ' + - name: 3B-CV-A16 + sequence: 'GAYSGAPKQTLKKPILRTATVQ' + - name: 3C-CV-A16 + sequence: 'GPSLDFALSLLRRNIRQVQTDQGHFTMLGVRDRLAVLPRHSQPGKTIWVEHKLINILDAVELVDEQGVNLELTLVTLDTNEKFRDITKFIPENISAASDATLVINTEHMPSMFVPVGDVVQYGFLNLSGKPTHRTMMYNFPTKAGQCGGVVTSVGKVIGIHIGGNGRQGFCAGLKRSYFASEQ' + - name: 3D-CV-A16 + sequence: 'GEIQWVKPNKETGRLNINGPTRTKLEPSVFHDVFEGNKEPAVLHSRDPRLEVDFEQALFSKYVGNTLHEPDEYIKEAALHYANQLKQLDINTSQMSMEEACYGTENLEAIDLHTSAGYPYSALGIKKRDILDPTTRDVSKMKFYMDKYGLDLPYSTYVKDELRSIDKIKKGKSRLIEASSLNDSVYLRMAFGHLYETFHANPGTITGSAVGCNPDTFWSKLPILLPGSLFAFDYSGYDASLSPVWFRALELVLREVGYSEEAVSLIEGINHTHHVYRNKTYCVLGGMPSGCSGTSIFNSMINNIIIRTLLIKTFKGIDLDELNMVAYGDDVLASYPFPIDCLELARTGKEYGLTMTPADKSPCFNEVNWGNATFLKRGFLPDEQFPFLIHPTMPMKEIHESIRWTKDARNTQDHVRSLCLLAWHNGKQEYEKFVSTIRSVPVGKALAIPNYENLRRNWLELF' + - name: VP4-CV-A10 + sequence: 'MGAQVSSQRSGSHETGNVATGGSTINFTNINYYKDSYAASASRQDFTQDPKKFTQPVLDSIRELSAPLN' + - name: VP2-CV-A10 + sequence: 'SPSVEACGYSDRVAQLTVGNSSITTQEAANIVLAYGEWPEYCPDTDATAVDKPTRPDVSVNRFYTLDSKMWQENSTGWYWKFPDVLNKTGVFGQNAQFHYLYRSGFCLHVQCNASKFHQGALLVAVIPEFVLAGRGSNTKPNEAPHPGFNTTFPGTAGASFNDPYVLDSGVPLSQSLIYPHQWINLRTNNCATIIVPYINAVPFDSAINHSNFGLIVVPVSPLKYSSGATTAIPITVTIAPLNSEFGGLRQAVSQ' + - name: VP3-CV-A10 + sequence: 'GLPTELRPGTNQFLTTEDDTAAPILPGFSPTPSIHIPGEVRSLLELCRVETILEVNNTTDATGLNRLLIPVSAQNKADELCAAFMVDPGRIGPWQSTLVGQICRYYTQWSGSLKVTFMFTGSFMATGKMLIAYSPPGSAQPANRETAMLGTHVIWDFGLQSSVSLVIPWISNTHFRTAKTGGNYDYYTAGVVTLWYQTNYVVPPETPGEAYIIAMGAAQDNFTLKICKDTDEVTQQAVLQ' + - name: VP1-CV-A10 + sequence: 'GDPVEDIIHDALSSTVRRAITSGQDVNTAAGTAPSSHRLETGRVPALQAAETGATSNATDENMIETRCVMNRNGVLEATISHFFSRSGLVGVVNLTDGGTDTTGYAVWDIDIMGFVQLRRKCEMFTYMRFNAEFTFVTTTENGEARPFMLQYMYVPPGAPKPTGRDAFQWQTATNPSVFVKLTDPPAQVSVPFMSPASAYQWFYDGYPTFGQHPETSNTTYGQCPNNMMGTFAVRVVSRVASQLKLQTRVYMKLKHVRAWIPRPIRSQPYLLKNFPNYDSSKITYSARDRASIKQANM' + - name: 2A-CV-A10 + sequence: 'GKFGQQSGAIYVGNYRVVNRHLATHNDWVNLVWEDSSRDLLVSSTTAQGCDTIARCNCQTGVYYCNSRRKHYPVSFSKPSLIFVEASEYYPARYQSHLMLAEGHSEPGDCGGILRCQHGVVGLVSTGGNGLVGFADVRDLLWLDEEAMEQ' + - name: 2B-CV-A10 + sequence: 'GVSDYIKGLGDAFGTGFTDAVSREVEALKNHLIGSEGAVEKILKNLVKLISALVIVIRSDYDMVTLTATLALIGCHGSPWAWIKSKTASILGISMAQKQ' + - name: 2C-CV-A10 + sequence: 'SASWLKKFNDMANAAKGLEWISNKISKFIDWLKEKIIPAAKEKVEFLNNLKQLPLLENQISNLEQSAASQEDLEAIFGNVSYLAHFCRKFQPLYATEAKRVYALEKRVNNYMQFKSKHRIEPVCLIIRGSPGTGKSLATGIIARAIADKYHSSVYSLPPDPDHFDGYKQQVVTVMDDLCQNPDGKDMSLFCQMVSTVDFIPPMASLEEKGVSFTSKFVIASTNASNIIVPTVSDSDAIRRRFYMDCDIEVTDSYKTDLGRLDAGRAAKLCSENNTANFKRCSPLVCGKAIQLKDRKSKVRYSVDTMVSELIREYNNRSAVGNTIEALFQ' + - name: 3A-CV-A10 + sequence: 'GPPKFRPIRISLEEKPTPDAISDLLASVDSEEVRQYCREQGWIIPETPTNVERHLNRAVLVMQSIATVVAVVSLVYVIYKLFAGFQ' + - name: 3B-CV-A10 + sequence: 'GAYSGAPKQALKKPVLRTATVQ' + - name: 3C-CV-A10 + sequence: 'GPSLDFALSLLRRNIRQAQTDQGHFTMLGIRDRLAILPRHSQPGKTIWIEHKLVNVLDAVELVDEQGVNLELTLVTLDTNEKFRDITKFIPETIAGASDATLVINTEHMPSMFVPIGDVVQYGFLNLSGKPTHRTMMYNFPTKAGQCGGVVTSVGKIIGIHIGGNGRQGFCAGLKRSYFASEQ' + - name: 3D-CV-A10 + sequence: 'GEIQWMKPNKETGRLNINGPTRTKLEPSVFHNVFEGNKEPAVLTSKDPRLEVDFEQALFSKYVGNTLHEPDEYVTQAALHYANQLKQLDINTSKMSMEEACYGTENLEAIDLHTSAGYPYSALGIKKRDILDPVTRDTSKMKLYMDKYGLDLPYSTYVKDELRSLDKIKKGKSRLIEASSLNDSVYLRMTFGHLYEVFHANPGTITGSAVGCNPDVFWSKLPILLPGSLFAFDYSSYDASLSPVWFRALELVLREIGYSEEAVSLIEGINHTHHVYRNKTYCVLGGMPSGCSGTSIFNSMINNIIIRTLLIKTFKGIDLDELNMVAYGDDVLASYPFPIDCLELARTGKEYGLTMTPADKSPCFNEVTWENATFLKRGFLPDHQFPFLIHPTMPMREIHESIRWTKDARNTQDHVRSLCLLAWHNGKEEYEKFVSTIRSVPIGKALAIPNFENLRRNWLELF' + - name: VP4-EV-A71 + sequence: 'MGSQVSTQRSGSHENSNSATEGSTINYTTINYYKDSYAATAGKQSLKQDPDKFANPVKDIFTEMAAPLK' + - name: VP2-EV-A71 + sequence: 'SPSAEACGYSDRVAQLTIGNSTITTQEAANIIVGYGEWPSYCSDNDATAVDKPTRPDVSVNRFYTLDTKLWEKSSKGWYWKFPDVLTETGVFGPNAQFHYLYRSGFCIHVQCNASKFHQGALLVAVLPEYVIGTVAGGTGTENSHPPYKQTQPGADGFELQHPYVLDAGIPISQLTVCPHQWINLRTNNCATIIVPYMNTLPFDSALNHCNFGLLVVPISPLDFDQGATPVIPITITLAPMCSEFAGLRQAVTQ' + - name: VP3-EV-A71 + sequence: 'GFPTELKPGTNQFLTTDDGVSAPILPNFHPTPCIHIPGEVRNLLELCQVETILEVNNVPTNATSLMERLRFPVSAQAGKGELCAVFRADPGRDGPWQSTMLGQLCGYYTQWSGSLEVTFMFTGSFMATGKMLIAYTPPGGPLPKDRATAMLGTHVIWDFGLQSSVTLVIPWISNTHYRAHARDGVFDYYTTGLVSIWYQTNYVVPIGAPNTAYIIALAAAQKNFTMKLCKDTSDILETATIQ' + - name: VP1-EV-A71 + sequence: 'GDRVADVIESSIGDSVSKALTPALPAPTGPDTQVSSHRLDTGKVPALQAAEIGASSNASDESMIETRCVLNSHSTAETTLDSFFSRAGLVGEIDLPLKGTTNPNGYANWDIDITGYAQMRRKVELFTYMRFDAEFTFVACTPTGRVVPQLLQYMFVPPGAPKPDSRDSLAWPTATNPSVFVKSSDPPAQVSVPFMSPASAYQWFYDGYPTFGEHKQEKDLEYGACPNNMMGTFSVRTVGSSKSEYSLVIRIYMRMKHVRAWIPRPMRNQNYLFKSNPNYAGDSIKPTGTSRTAITTL' + - name: 2A-EV-A71 + sequence: 'GKFGQQSGAIYVGNFRVVNRHLATHTDWANLVWEDSSRDLLVSSTTAQGCDTIARCNCQTGVYYCNSRRKHYPVSFSKPSLVFVEASEYYPARYQSHLMLAEGHSEPGDCGGILRCQHGVVGIVSTGGSGLVGFADVRDLLWLDEEAMEQ' + - name: 2B-EV-A71 + sequence: 'GVSDYIKGLGRAFGTGFTDAVSREVEALKNHLIGSEGAVEKILKNLVKLISALVIVIRSDYDMVTLTATLALIGCHGSPWAWIKSKTASILGIPMAQKQ' + - name: 2C-EV-A71 + sequence: 'SASWLKKFNDMANAAKGLEWIFNKISKFIDWLKEKIIPAAKEKVEFLNNLKQLPLLENQVSNLEQSAASQEDLEAMFGNVIYLAHFCRKFQPLYATEAKRVYALEKRMNNYMQFKSKHRIEPVCLIIRGSPGTGKSLATGIIARAIADKYRSSVYSLPPDPDHFDGYKQQVVAVMDDLCQNPDGKDMSLFCQMVSTVDFVPPMASLEEKGVSFTSKFVIASTNASNIIVPTVSDSDAIRRRFYMDCDIEVTDSYKTDLGRLDAGRAAKLCTENNTANFKRCSPLVCGKAIQLRDRKSKVRYSVDTVVSELIREYNNRSAIGNTIEALFQ' + - name: 3A-EV-A71 + sequence: 'GPPKFRPIRISLEEKPAPDAISDLLASVDSEEVRQYCREQGWIIPETPTNVERHLNRAVLVMQSIATVVAVVSLVYVIYKLFAGFQ' + - name: 3B-EV-A71 + sequence: 'GAYSGAPKPILKKPVLRTATVQ' + - name: 3C-EV-A71 + sequence: 'GPSLDFALSLLRRNIRQAQTDQGHFTMLGVRDRLAILPRHSQPGKTIWVEHKLINVLDAVELVDEQGVNLELTLVTLDTNEKFRDITKCIPEVITGASDATLVINTEHIPSMFVPVGDVVQYGFLNLSGKPTHRTMMYNFPTKPGQCGGVVTSVGKIIGIHIGGNGRQAFCAGLKRSYFASEQ' + - name: 3D-EV-A71 + sequence: 'GEIQWMKPNRETGRLNINGPTRTKLEPSVFHDVFEGNKEPAVLTSKDPRLEVDFEQALFSKYVGNTLHEPDEYVTQAALHYANQLKQLDINTSKMSMEEACYGTEYLEAIDLHTSAGYPYSALGIKKRDILDPVTRDTSRMKLYMDKYGLDLPNSTYVKDELSSLDKIRKGESRLIEASSLNDPVYPRLTFGHLYEVFHANPGTVTGSAVGCNPDVFWSKLPILLPGSLFAFDYSGYDASLSPVWFRALELVLREIGYSEEAVSLIEGINHTHHVYRNKTYCVLGGMPSGCSGTSIFNSMINNIIIRTLLIKTFKGIDLDELKMVAYGDDVLASYPFPIDCLEWGKTGKEYGLTMTPADKSPCFNEVTWENATFLKRGFLPDHQFPFLIHPTMPMREIHESIRWTKDARNTQDHVRSLCLLAWHNGKEEYEKFVSTIRSVPIGRALAIPNLENLRRNWLELF' + - name: VP4-EV-D68 + sequence: 'MGAQVTRQQTGTHENANIATNGSHITYNQINFYKDSYAASASKQDFSQDPSKFTEPVVEGLKAGAPVLK' + - name: VP2-EV-D68 + sequence: 'SPSAEACGYSDRVLQLKLGNSAIVTQEAANYCCAYGEWPNYLPDHEAVAIDKPTQPETSTDRFYTLRSVKWESNSTGWWWKLPDALNNIGMFGQNVQYHYLYRSGFLIHVQCNATKFHQGALLVVAIPEHQRGAHDTTTSPGFNDIMKGERGGTFNHPYVLDDGTSIACATIFPHQWINLRTNNSATIVLPWMNVAPMDFPLRHNQWTLAVIPVVPLGTRTMSSVVPITVSIAPMCCEFNGLRHAITQ' + - name: VP3-EV-D68 + sequence: 'GVPTYLLPGSGQFLTTDDHSSAPVLPCFNPTPEMHIPGQIRNMLEMIQVESMMEINNTDGANGMERLRVDISVQADLDQLLFNIPLDIQLDGPLRNTLVGNISRYYTHWSGSLEMTFMFCGSFMATGKLILCYTPPGGSCPTTRETAMLGTHIVWDFGLQSSITLIIPWISGSHYRMFNSDAKSTNANVGYVTCFMQTNLIVPSESSDTCSLIGFIAAKDDFSLRLMRDSPDIGQ' + - name: VP1-EV-D68 + sequence: 'SNHLHGAEAAYQVESIIKTATDTVKSEINAELGVVPSLNAVETGATSNTEPEEAIQTRTVINQHGVSETLVENFLGRAALVSKKSFEYKNHASSSAGTHKNFFKWTINTKSFVQLRRKLELFTYLRFDAEITILTTVAVNGNNDSTYMGLPDLTLQAMFVPTGALTPKEQDSFHWQSGSNASVFFKISDPPARMTIPFMCINSAYSVFYDGFAGFEKNGLYGINPADTIGNLCVRIVNEHQPVGFTVTVRVYMKPKHIKAWAPRPPRTMPYMSIANANYKGRDTAPNTLNAIIGNRASVTTMPHNIVTT' + - name: 2A-EV-D68 + sequence: 'GPGFGGVFVGSFKIINYHLATIEERQSAIYVDWQSDVLVTPIAAHGRHQIARCKCNTGVYYCRHRDRSYPICFEGPGIQWIEQNEYYPARYQTNVLLAAGPAEAGDCGGLLVCPHGVIGLLTAGGGGIVAFTDIRNLLWLDTDVMEQ' + - name: 2B-EV-D68 + sequence: 'GITDYIQNLGNAFGAGFTETISNKAKEVQDMLIGESSLLEKLLKALIKIISALVIVIRNSEDLITVTATLALLGCHDSPWSYLKQKVCSYLGIPYVPRQ' + - name: 2C-EV-D68 + sequence: 'SESWLKKFTEACNALRGLDWLSQKIDKFINWLKTKILPEAREKYEFVQRLKQLPVIEKQVSTIEHSCPTTERQQALFNNVQYYSHYCRKYAPLYAVESKRVAALEKKINNYIQFKSKSRIEPVCLIIHGSPGTGKSVASNLIARAITEKLGGDIYSLPPDPKYFDGYKQQTVVLMDDLMQNPDGNDISMFCQMVSTVDFIPPMASLEEKGTLYTSPFLIATTNAGSIHAPTVSDSKALSRRFKFDVDIEVTDSYKDSNKLDMSRAVEMCKPDNCTPTNYKRCCPLICGKAIQFRDRRTNARSTVDMLVTDIIKEYRTRNSTQDKLEALFQ' + - name: 3A-EV-D68 + sequence: 'GPPQFKEIKISVAPDTPAPDAINDLLRSVDSQEVRDYCQKKGWIVIHPSNELVVEKHISRAFITLQAIATFVSIAGVVYVIYKLFAGIQ' + - name: 3B-EV-D68 + sequence: 'GPYTGIPNPKPKVPSLRTAKVQ' + - name: 3C-EV-D68 + sequence: 'GPGFDFAQAIMKKNTVIARTEKGEFTMLGVYDRVAVIPTHASVGEIIYINDVETRVLDACALRDLTDTNLEITIVKLDRNQKFRDIRHFLPRCEDDYNDAVLSVHTSKFPNMYIPVGQVTNYGFLNLGGTPTHRILMYNFPTRAGQCGGVVTTTGKVIGIHVGGNGAQGFAAMLLHSYFTDTQ' + - name: 3D-EV-D68 + sequence: 'GEIVSNEKSGMCINAPAKTKLQPSVFHQVFEGSKEPAVLNSKDPRLKTDFEEAIFSKYTGNKIMLMDEYMEEAVDHYVGCLEPLDISVDPIPLENAMYGMEGLEALDLTTSAGFPYLLQGKKKRDIFNRQTRDTSEMTKMLEKYGVDLPFVTFVKDELRSREKVEKGKSRLIEASSLNDSVAMRVAFGNLYATFHNNPGTATGSAVGCDPDIFWSKIPILLDGEIFAFDYTGYDASLSPVWFACLKKVLIKLGYTHQTSFIDYLCHSVHLYKDRKYVINGGMPSGSSGTSIFNTMINNIIIRTLLIKVYKGIDLDQFKMIAYGDDVIASYPHKIDPGLLAEAGKHYGLVMTPADKGTSFIDTNWENVTFLKRYFRADDQYPFLIHPVMPMKEIHESIRWTKDPRNTQDHVRSLCYLAWHNGEEAYNEFCRKIRSVPVGRALTLPAYSSLRRKWLDSF' +displayName: Enterovirus + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-enteroviruses:8080" diff --git a/kubernetes/loculus/fixtures/organisms/not-aligned-organism.yaml b/kubernetes/loculus/fixtures/organisms/not-aligned-organism.yaml new file mode 100644 index 0000000000..295015414d --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/not-aligned-organism.yaml @@ -0,0 +1,102 @@ +schema: + submissionDataTypes: + consensusSequences: true + maxSequencesPerEntry: 1 + image: /images/organisms/rsv.jpg + organismName: Test organism (without alignment) + metadata: + - name: date + displayName: Date + type: date + initiallyVisible: true + header: Collection Details + - name: lineage + isSequenceFilter: true + lineageSearch: true + initiallyVisible: true + displayName: Lineage + autocomplete: true + type: string + lineageSystem: alternativeLineage + options: &id001 + - name: A + - name: A.1 + - name: A.1.1 + - name: A.2 + - name: B + - name: B.1 + - name: B.1.1 + - name: B.1.1.1 + - name: C + - name: C.1 + - name: region + displayName: Region + type: string + initiallyVisible: true + generateIndex: true + autocomplete: true + header: Collection Details + - name: country + displayName: Country + initiallyVisible: true + type: string + generateIndex: true + autocomplete: true + header: Collection Details + - name: division + displayName: Division + initiallyVisible: true + type: string + generateIndex: true + autocomplete: true + header: Collection Details + - name: host + displayName: Host + initiallyVisible: true + type: string + autocomplete: true + header: Collection Details + - name: versionComment + type: string + tableColumns: + - country + - division + - date + defaultOrderBy: date + defaultOrder: descending + inputFields: + - name: id + displayName: ID + example: GJP123 + noEdit: true + required: true + definition: Your sequence identifier; should match the sequence's id in the FASTA file - this is used to link the metadata to the FASTA sequence. + - name: date + displayName: Date + required: true + - name: lineage + displayName: Lineage + required: true + options: *id001 + - name: region + displayName: Region + - name: country + displayName: Country + - name: division + displayName: Division + - name: host + displayName: Host +referenceGenomes: + - name: main + references: + - name: singleReference + sequence: 'ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTTACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAGATGGCACTTGTGGCTTAGTAGAAGTTGAAAAAGGCGTTTTGCCTCAACTTGAACAGCCCTATGTGTTCATCAAACGTTCGGATGCTCGAACTGCACCTCATGGTCATGTTATGGTTGAGCTGGTAGCAGAACTCGAAGGCATTCAGTACGGTCGTAGTGGTGAGACACTTGGTGTCCTTGTCCCTCATGTGGGCGAAATACCAGTGGCTTACCGCAAGGTTCTTCTTCGTAAGAACGGTAATAAAGGAGCTGGTGGCCATAGTTACGGCGCCGATCTAAAGTCATTTGACTTAGGCGACGAGCTTGGCACTGATCCTTATGAAGATTTTCAAGAAAACTGGAACACTAAACATAGCAGTGGTGTTACCCGTGAACTCATGCGTGAGCTTAACGGAGGGGCATACACTCGCTATGTCGATAACAACTTCTGTGGCCCTGATGGCTACCCTCTTGAGTGCATTAAAGACCTTCTAGCACGTGCTGGTAAAGCTTCATGCACTTTGTCCGAACAACTGGACTTTATTGACACTAAGAGGGGTGTATACTGCTGCCGTGAACATGAGCATGAAATTGCTTGGTACACGGAACGTTCTGAAAAGAGCTATGAATTGCAGACACCTTTTGAAATTAAATTGGCAAAGAAATTTGACACCTTCAATGGGGAATGTCCAAATTTTGTATTTCCCTTAAATTCCATAATCAAGACTATTCAACCAAGGGTTGAAAAGAAAAAGCTTGATGGCTTTATGGGTAGAATTCGATCTGTCTATCCAGTTGCGTCACCAAATGAATGCAACCAAATGTGCCTTTCAACTCTCATGAAGTGTGATCATTGTGGTGAAACTTCATGGCAGACGGGCGATTTTGTTAAAGCCACTTGCGAATTTTGTGGCACTGAGAATTTGACTAAAGAAGGTGCCACTACTTGTGGTTACTTACCCCAAAATGCTGTTGTTAAAATTTATTGTCCAGCATGTCACAATTCAGAAGTAGGACCTGAGCATAGTCTTGCCGAATACCATAATGAATCTGGCTTGAAAACCATTCTTCGTAAGGGTGGTCGCACTATTGCCTTTGGAGGCTGTGTGTTCTCTTATGTTGGTTGCCATAACAAGTGTGCCTATTGGGTTCCACGTGCTAGCGCTAACATAGGTTGTAACCATACAGGTGTTGTTGGAGAAGGTTCCGAAGGTCTTAATGACAACCTTCTTGAAATACTCCAAAAAGAGAAAGTCAACATCAATATTGTTGGTGACTTTAAACTTAATGAAGAGATCGCCATTATTTTGGCATCTTTTTCTGCTTCCACAAGTGCTTTTGTGGAAACTGTGAAAGGTTTGGATTATAAAGCATTCAAACAAATTGTTGAATCCTGTGGTAATTTTAAAGTTACAAAAGGAAAAGCTAAAAAAGGTGCCTGGAATATTGGTGAACAGAAATCAATACTGAGTCCTCTTTATGCATTTGCATCAGAGGCTGCTCGTGTTGTACGATCAATTTTCTCCCGCACTCTTGAAACTGCTCAAAATTCTGTGCGTGTTTTACAGAAGGCCGCTATAACAATACTAGATGGAATTTCACAGTATTCACTGAGACTCATTGATGCTATGATGTTCACATCTGATTTGGCTACTAACAATCTAGTTGTAATGGCCTACATTACAGGTGGTGTTGTTCAGTTGACTTCGCAGTGGCTAACTAACATCTTTGGCACTGTTTATGAAAAACTCAAACCCGTCCTTGATTGGCTTGAAGAGAAGTTTAAGGAAGGTGTAGAGTTTCTTAGAGACGGTTGGGAAATTGTTAAATTTATCTCAACCTGTGCTTGTGAAATTGTCGGTGGACAAATTGTCACCTGTGCAAAGGAAATTAAGGAGAGTGTTCAGACATTCTTTAAGCTTGTAAATAAATTTTTGGCTTTGTGTGCTGACTCTATCATTATTGGTGGAGCTAAACTTAAAGCCTTGAATTTAGGTGAAACATTTGTCACGCACTCAAAGGGATTGTACAGAAAGTGTGTTAAATCCAGAGAAGAAACTGGCCTACTCATGCCTCTAAAAGCCCCAAAAGAAATTATCTTCTTAGAGGGAGAAACACTTCCCACAGAAGTGTTAACAGAGGAAGTTGTCTTGAAAACTGGTGATTTACAACCATTAGAACAACCTACTAGTGAAGCTGTTGAAGCTCCATTGGTTGGTACACCAGTTTGTATTAACGGGCTTATGTTGCTCGAAATCAAAGACACAGAAAAGTACTGTGCCCTTGCACCTAATATGATGGTAACAAACAATACCTTCACACTCAAAGGCGGTGCACCAACAAAGGTTACTTTTGGTGATGACACTGTGATAGAAGTGCAAGGTTACAAGAGTGTGAATATCACTTTTGAACTTGATGAAAGGATTGATAAAGTACTTAATGAGAAGTGCTCTGCCTATACAGTTGAACTCGGTACAGAAGTAAATGAGTTCGCCTGTGTTGTGGCAGATGCTGTCATAAAAACTTTGCAACCAGTATCTGAATTACTTACACCACTGGGCATTGATTTAGATGAGTGGAGTATGGCTACATACTACTTATTTGATGAGTCTGGTGAGTTTAAATTGGCTTCACATATGTATTGTTCTTTCTACCCTCCAGATGAGGATGAAGAAGAAGGTGATTGTGAAGAAGAAGAGTTTGAGCCATCAACTCAATATGAGTATGGTACTGAAGATGATTACCAAGGTAAACCTTTGGAATTTGGTGCCACTTCTGCTGCTCTTCAACCTGAAGAAGAGCAAGAAGAAGATTGGTTAGATGATGATAGTCAACAAACTGTTGGTCAACAAGACGGCAGTGAGGACAATCAGACAACTACTATTCAAACAATTGTTGAGGTTCAACCTCAATTAGAGATGGAACTTACACCAGTTGTTCAGACTATTGAAGTGAATAGTTTTAGTGGTTATTTAAAACTTACTGACAATGTATACATTAAAAATGCAGACATTGTGGAAGAAGCTAAAAAGGTAAAACCAACAGTGGTTGTTAATGCAGCCAATGTTTACCTTAAACATGGAGGAGGTGTTGCAGGAGCCTTAAATAAGGCTACTAACAATGCCATGCAAGTTGAATCTGATGATTACATAGCTACTAATGGACCACTTAAAGTGGGTGGTAGTTGTGTTTTAAGCGGACACAATCTTGCTAAACACTGTCTTCATGTTGTCGGCCCAAATGTTAACAAAGGTGAAGACATTCAACTTCTTAAGAGTGCTTATGAAAATTTTAATCAGCACGAAGTTCTACTTGCACCATTATTATCAGCTGGTATTTTTGGTGCTGACCCTATACATTCTTTAAGAGTTTGTGTAGATACTGTTCGCACAAATGTCTACTTAGCTGTCTTTGATAAAAATCTCTATGACAAACTTGTTTCAAGCTTTTTGGAAATGAAGAGTGAAAAGCAAGTTGAACAAAAGATCGCTGAGATTCCTAAAGAGGAAGTTAAGCCATTTATAACTGAAAGTAAACCTTCAGTTGAACAGAGAAAACAAGATGATAAGAAAATCAAAGCTTGTGTTGAAGAAGTTACAACAACTCTGGAAGAAACTAAGTTCCTCACAGAAAACTTGTTACTTTATATTGACATTAATGGCAATCTTCATCCAGATTCTGCCACTCTTGTTAGTGACATTGACATCACTTTCTTAAAGAAAGATGCTCCATATATAGTGGGTGATGTTGTTCAAGAGGGTGTTTTAACTGCTGTGGTTATACCTACTAAAAAGGCTGGTGGCACTACTGAAATGCTAGCGAAAGCTTTGAGAAAAGTGCCAACAGACAATTATATAACCACTTACCCGGGTCAGGGTTTAAATGGTTACACTGTAGAGGAGGCAAAGACAGTGCTTAAAAAGTGTAAAAGTGCCTTTTACATTCTACCATCTATTATCTCTAATGAGAAGCAAGAAATTCTTGGAACTGTTTCTTGGAATTTGCGAGAAATGCTTGCACATGCAGAAGAAACACGCAAATTAATGCCTGTCTGTGTGGAAACTAAAGCCATAGTTTCAACTATACAGCGTAAATATAAGGGTATTAAAATACAAGAGGGTGTGGTTGATTATGGTGCTAGATTTTACTTTTACACCAGTAAAACAACTGTAGCGTCACTTATCAACACACTTAACGATCTAAATGAAACTCTTGTTACAATGCCACTTGGCTATGTAACACATGGCTTAAATTTGGAAGAAGCTGCTCGGTATATGAGATCTCTCAAAGTGCCAGCTACAGTTTCTGTTTCTTCACCTGATGCTGTTACAGCGTATAATGGTTATCTTACTTCTTCTTCTAAAACACCTGAAGAACATTTTATTGAAACCATCTCACTTGCTGGTTCCTATAAAGATTGGTCCTATTCTGGACAATCTACACAACTAGGTATAGAATTTCTTAAGAGAGGTGATAAAAGTGTATATTACACTAGTAATCCTACCACATTCCACCTAGATGGTGAAGTTATCACCTTTGACAATCTTAAGACACTTCTTTCTTTGAGAGAAGTGAGGACTATTAAGGTGTTTACAACAGTAGACAACATTAACCTCCACACGCAAGTTGTGGACATGTCAATGACATATGGACAACAGTTTGGTCCAACTTATTTGGATGGAGCTGATGTTACTAAAATAAAACCTCATAATTCACATGAAGGTAAAACATTTTATGTTTTACCTAATGATGACACTCTACGTGTTGAGGCTTTTGAGTACTACCACACAACTGATCCTAGTTTTCTGGGTAGGTACATGTCAGCATTAAATCACACTAAAAAGTGGAAATACCCACAAGTTAATGGTTTAACTTCTATTAAATGGGCAGATAACAACTGTTATCTTGCCACTGCATTGTTAACACTCCAACAAATAGAGTTGAAGTTTAATCCACCTGCTCTACAAGATGCTTATTACAGAGCAAGGGCTGGTGAAGCTGCTAACTTTTGTGCACTTATCTTAGCCTACTGTAATAAGACAGTAGGTGAGTTAGGTGATGTTAGAGAAACAATGAGTTACTTGTTTCAACATGCCAATTTAGATTCTTGCAAAAGAGTCTTGAACGTGGTGTGTAAAACTTGTGGACAACAGCAGACAACCCTTAAGGGTGTAGAAGCTGTTATGTACATGGGCACACTTTCTTATGAACAATTTAAGAAAGGTGTTCAGATACCTTGTACGTGTGGTAAACAAGCTACAAAATATCTAGTACAACAGGAGTCACCTTTTGTTATGATGTCAGCACCACCTGCTCAGTATGAACTTAAGCATGGTACATTTACTTGTGCTAGTGAGTACACTGGTAATTACCAGTGTGGTCACTATAAACATATAACTTCTAAAGAAACTTTGTATTGCATAGACGGTGCTTTACTTACAAAGTCCTCAGAATACAAAGGTCCTATTACGGATGTTTTCTACAAAGAAAACAGTTACACAACAACCATAAAACCAGTTACTTATAAATTGGATGGTGTTGTTTGTACAGAAATTGACCCTAAGTTGGACAATTATTATAAGAAAGACAATTCTTATTTCACAGAGCAACCAATTGATCTTGTACCAAACCAACCATATCCAAACGCAAGCTTCGATAATTTTAAGTTTGTATGTGATAATATCAAATTTGCTGATGATTTAAACCAGTTAACTGGTTATAAGAAACCTGCTTCAAGAGAGCTTAAAGTTACATTTTTCCCTGACTTAAATGGTGATGTGGTGGCTATTGATTATAAACACTACACACCCTCTTTTAAGAAAGGAGCTAAATTGTTACATAAACCTATTGTTTGGCATGTTAACAATGCAACTAATAAAGCCACGTATAAACCAAATACCTGGTGTATACGTTGTCTTTGGAGCACAAAACCAGTTGAAACATCAAATTCGTTTGATGTACTGAAGTCAGAGGACGCGCAGGGAATGGATAATCTTGCCTGCGAAGATCTAAAACCAGTCTCTGAAGAAGTAGTGGAAAATCCTACCATACAGAAAGACGTTCTTGAGTGTAATGTGAAAACTACCGAAGTTGTAGGAGACATTATACTTAAACCAGCAAATAATAGTTTAAAAATTACAGAAGAGGTTGGCCACACAGATCTAATGGCTGCTTATGTAGACAATTCTAGTCTTACTATTAAGAAACCTAATGAATTATCTAGAGTATTAGGTTTGAAAACCCTTGCTACTCATGGTTTAGCTGCTGTTAATAGTGTCCCTTGGGATACTATAGCTAATTATGCTAAGCCTTTTCTTAACAAAGTTGTTAGTACAACTACTAACATAGTTACACGGTGTTTAAACCGTGTTTGTACTAATTATATGCCTTATTTCTTTACTTTATTGCTACAATTGTGTACTTTTACTAGAAGTACAAATTCTAGAATTAAAGCATCTATGCCGACTACTATAGCAAAGAATACTGTTAAGAGTGTCGGTAAATTTTGTCTAGAGGCTTCATTTAATTATTTGAAGTCACCTAATTTTTCTAAACTGATAAATATTATAATTTGGTTTTTACTATTAAGTGTTTGCCTAGGTTCTTTAATCTACTCAACCGCTGCTTTAGGTGTTTTAATGTCTAATTTAGGCATGCCTTCTTACTGTACTGGTTACAGAGAAGGCTATTTGAACTCTACTAATGTCACTATTGCAACCTACTGTACTGGTTCTATACCTTGTAGTGTTTGTCTTAGTGGTTTAGATTCTTTAGACACCTATCCTTCTTTAGAAACTATACAAATTACCATTTCATCTTTTAAATGGGATTTAACTGCTTTTGGCTTAGTTGCAGAGTGGTTTTTGGCATATATTCTTTTCACTAGGTTTTTCTATGTACTTGGATTGGCTGCAATCATGCAATTGTTTTTCAGCTATTTTGCAGTACATTTTATTAGTAATTCTTGGCTTATGTGGTTAATAATTAATCTTGTACAAATGGCCCCGATTTCAGCTATGGTTAGAATGTACATCTTCTTTGCATCATTTTATTATGTATGGAAAAGTTATGTGCATGTTGTAGACGGTTGTAATTCATCAACTTGTATGATGTGTTACAAACGTAATAGAGCAACAAGAGTCGAATGTACAACTATTGTTAATGGTGTTAGAAGGTCCTTTTATGTCTATGCTAATGGAGGTAAAGGCTTTTGCAAACTACACAATTGGAATTGTGTTAATTGTGATACATTCTGTGCTGGTAGTACATTTATTAGTGATGAAGTTGCGAGAGACTTGTCACTACAGTTTAAAAGACCAATAAATCCTACTGACCAGTCTTCTTACATCGTTGATAGTGTTACAGTGAAGAATGGTTCCATCCATCTTTACTTTGATAAAGCTGGTCAAAAGACTTATGAAAGACATTCTCTCTCTCATTTTGTTAACTTAGACAACCTGAGAGCTAATAACACTAAAGGTTCATTGCCTATTAATGTTATAGTTTTTGATGGTAAATCAAAATGTGAAGAATCATCTGCAAAATCAGCGTCTGTTTACTACAGTCAGCTTATGTGTCAACCTATACTGTTACTAGATCAGGCATTAGTGTCTGATGTTGGTGATAGTGCGGAAGTTGCAGTTAAAATGTTTGATGCTTACGTTAATACGTTTTCATCAACTTTTAACGTACCAATGGAAAAACTCAAAACACTAGTTGCAACTGCAGAAGCTGAACTTGCAAAGAATGTGTCCTTAGACAATGTCTTATCTACTTTTATTTCAGCAGCTCGGCAAGGGTTTGTTGATTCAGATGTAGAAACTAAAGATGTTGTTGAATGTCTTAAATTGTCACATCAATCTGACATAGAAGTTACTGGCGATAGTTGTAATAACTATATGCTCACCTATAACAAAGTTGAAAACATGACACCCCGTGACCTTGGTGCTTGTATTGACTGTAGTGCGCGTCATATTAATGCGCAGGTAGCAAAAAGTCACAACATTGCTTTGATATGGAACGTTAAAGATTTCATGTCATTGTCTGAACAACTACGAAAACAAATACGTAGTGCTGCTAAAAAGAATAACTTACCTTTTAAGTTGACATGTGCAACTACTAGACAAGTTGTTAATGTTGTAACAACAAAGATAGCACTTAAGGGTGGTAAAATTGTTAATAATTGGTTGAAGCAGTTAATTAAAGTTACACTTGTGTTCCTTTTTGTTGCTGCTATTTTCTATTTAATAACACCTGTTCATGTCATGTCTAAACATACTGACTTTTCAAGTGAAATCATAGGATACAAGGCTATTGATGGTGGTGTCACTCGTGACATAGCATCTACAGATACTTGTTTTGCTAACAAACATGCTGATTTTGACACATGGTTTAGCCAGCGTGGTGGTAGTTATACTAATGACAAAGCTTGCCCATTGATTGCTGCAGTCATAACAAGAGAAGTGGGTTTTGTCGTGCCTGGTTTGCCTGGCACGATATTACGCACAACTAATGGTGACTTTTTGCATTTCTTACCTAGAGTTTTTAGTGCAGTTGGTAACATCTGTTACACACCATCAAAACTTATAGAGTACACTGACTTTGCAACATCAGCTTGTGTTTTGGCTGCTGAATGTACAATTTTTAAAGATGCTTCTGGTAAGCCAGTACCATATTGTTATGATACCAATGTACTAGAAGGTTCTGTTGCTTATGAAAGTTTACGCCCTGACACACGTTATGTGCTCATGGATGGCTCTATTATTCAATTTCCTAACACCTACCTTGAAGGTTCTGTTAGAGTGGTAACAACTTTTGATTCTGAGTACTGTAGGCACGGCACTTGTGAAAGATCAGAAGCTGGTGTTTGTGTATCTACTAGTGGTAGATGGGTACTTAACAATGATTATTACAGATCTTTACCAGGAGTTTTCTGTGGTGTAGATGCTGTAAATTTACTTACTAATATGTTTACACCACTAATTCAACCTATTGGTGCTTTGGACATATCAGCATCTATAGTAGCTGGTGGTATTGTAGCTATCGTAGTAACATGCCTTGCCTACTATTTTATGAGGTTTAGAAGAGCTTTTGGTGAATACAGTCATGTAGTTGCCTTTAATACTTTACTATTCCTTATGTCATTCACTGTACTCTGTTTAACACCAGTTTACTCATTCTTACCTGGTGTTTATTCTGTTATTTACTTGTACTTGACATTTTATCTTACTAATGATGTTTCTTTTTTAGCACATATTCAGTGGATGGTTATGTTCACACCTTTAGTACCTTTCTGGATAACAATTGCTTATATCATTTGTATTTCCACAAAGCATTTCTATTGGTTCTTTAGTAATTACCTAAAGAGACGTGTAGTCTTTAATGGTGTTTCCTTTAGTACTTTTGAAGAAGCTGCGCTGTGCACCTTTTTGTTAAATAAAGAAATGTATCTAAAGTTGCGTAGTGATGTGCTATTACCTCTTACGCAATATAATAGATACTTAGCTCTTTATAATAAGTACAAGTATTTTAGTGGAGCAATGGATACAACTAGCTACAGAGAAGCTGCTTGTTGTCATCTCGCAAAGGCTCTCAATGACTTCAGTAACTCAGGTTCTGATGTTCTTTACCAACCACCACAAACCTCTATCACCTCAGCTGTTTTGCAGAGTGGTTTTAGAAAAATGGCATTCCCATCTGGTAAAGTTGAGGGTTGTATGGTACAAGTAACTTGTGGTACAACTACACTTAACGGTCTTTGGCTTGATGACGTAGTTTACTGTCCAAGACATGTGATCTGCACCTCTGAAGACATGCTTAACCCTAATTATGAAGATTTACTCATTCGTAAGTCTAATCATAATTTCTTGGTACAGGCTGGTAATGTTCAACTCAGGGTTATTGGACATTCTATGCAAAATTGTGTACTTAAGCTTAAGGTTGATACAGCCAATCCTAAGACACCTAAGTATAAGTTTGTTCGCATTCAACCAGGACAGACTTTTTCAGTGTTAGCTTGTTACAATGGTTCACCATCTGGTGTTTACCAATGTGCTATGAGGCCCAATTTCACTATTAAGGGTTCATTCCTTAATGGTTCATGTGGTAGTGTTGGTTTTAACATAGATTATGACTGTGTCTCTTTTTGTTACATGCACCATATGGAATTACCAACTGGAGTTCATGCTGGCACAGACTTAGAAGGTAACTTTTATGGACCTTTTGTTGACAGGCAAACAGCACAAGCAGCTGGTACGGACACAACTATTACAGTTAATGTTTTAGCTTGGTTGTACGCTGCTGTTATAAATGGAGACAGGTGGTTTCTCAATCGATTTACCACAACTCTTAATGACTTTAACCTTGTGGCTATGAAGTACAATTATGAACCTCTAACACAAGACCATGTTGACATACTAGGACCTCTTTCTGCTCAAACTGGAATTGCCGTTTTAGATATGTGTGCTTCATTAAAAGAATTACTGCAAAATGGTATGAATGGACGTACCATATTGGGTAGTGCTTTATTAGAAGATGAATTTACACCTTTTGATGTTGTTAGACAATGCTCAGGTGTTACTTTCCAAAGTGCAGTGAAAAGAACAATCAAGGGTACACACCACTGGTTGTTACTCACAATTTTGACTTCACTTTTAGTTTTAGTCCAGAGTACTCAATGGTCTTTGTTCTTTTTTTTGTATGAAAATGCCTTTTTACCTTTTGCTATGGGTATTATTGCTATGTCTGCTTTTGCAATGATGTTTGTCAAACATAAGCATGCATTTCTCTGTTTGTTTTTGTTACCTTCTCTTGCCACTGTAGCTTATTTTAATATGGTCTATATGCCTGCTAGTTGGGTGATGCGTATTATGACATGGTTGGATATGGTTGATACTAGTTTGTCTGGTTTTAAGCTAAAAGACTGTGTTATGTATGCATCAGCTGTAGTGTTACTAATCCTTATGACAGCAAGAACTGTGTATGATGATGGTGCTAGGAGAGTGTGGACACTTATGAATGTCTTGACACTCGTTTATAAAGTTTATTATGGTAATGCTTTAGATCAAGCCATTTCCATGTGGGCTCTTATAATCTCTGTTACTTCTAACTACTCAGGTGTAGTTACAACTGTCATGTTTTTGGCCAGAGGTATTGTTTTTATGTGTGTTGAGTATTGCCCTATTTTCTTCATAACTGGTAATACACTTCAGTGTATAATGCTAGTTTATTGTTTCTTAGGCTATTTTTGTACTTGTTACTTTGGCCTCTTTTGTTTACTCAACCGCTACTTTAGACTGACTCTTGGTGTTTATGATTACTTAGTTTCTACACAGGAGTTTAGATATATGAATTCACAGGGACTACTCCCACCCAAGAATAGCATAGATGCCTTCAAACTCAACATTAAATTGTTGGGTGTTGGTGGCAAACCTTGTATCAAAGTAGCCACTGTACAGTCTAAAATGTCAGATGTAAAGTGCACATCAGTAGTCTTACTCTCAGTTTTGCAACAACTCAGAGTAGAATCATCATCTAAATTGTGGGCTCAATGTGTCCAGTTACACAATGACATTCTCTTAGCTAAAGATACTACTGAAGCCTTTGAAAAAATGGTTTCACTACTTTCTGTTTTGCTTTCCATGCAGGGTGCTGTAGACATAAACAAGCTTTGTGAAGAAATGCTGGACAACAGGGCAACCTTACAAGCTATAGCCTCAGAGTTTAGTTCCCTTCCATCATATGCAGCTTTTGCTACTGCTCAAGAAGCTTATGAGCAGGCTGTTGCTAATGGTGATTCTGAAGTTGTTCTTAAAAAGTTGAAGAAGTCTTTGAATGTGGCTAAATCTGAATTTGACCGTGATGCAGCCATGCAACGTAAGTTGGAAAAGATGGCTGATCAAGCTATGACCCAAATGTATAAACAGGCTAGATCTGAGGACAAGAGGGCAAAAGTTACTAGTGCTATGCAGACAATGCTTTTCACTATGCTTAGAAAGTTGGATAATGATGCACTCAACAACATTATCAACAATGCAAGAGATGGTTGTGTTCCCTTGAACATAATACCTCTTACAACAGCAGCCAAACTAATGGTTGTCATACCAGACTATAACACATATAAAAATACGTGTGATGGTACAACATTTACTTATGCATCAGCATTGTGGGAAATCCAACAGGTTGTAGATGCAGATAGTAAAATTGTTCAACTTAGTGAAATTAGTATGGACAATTCACCTAATTTAGCATGGCCTCTTATTGTAACAGCTTTAAGGGCCAATTCTGCTGTCAAATTACAGAATAATGAGCTTAGTCCTGTTGCACTACGACAGATGTCTTGTGCTGCCGGTACTACACAAACTGCTTGCACTGATGACAATGCGTTAGCTTACTACAACACAACAAAGGGAGGTAGGTTTGTACTTGCACTGTTATCCGATTTACAGGATTTGAAATGGGCTAGATTCCCTAAGAGTGATGGAACTGGTACTATCTATACAGAACTGGAACCACCTTGTAGGTTTGTTACAGACACACCTAAAGGTCCTAAAGTGAAGTATTTATACTTTATTAAAGGATTAAACAACCTAAATAGAGGTATGGTACTTGGTAGTTTAGCTGCCACAGTACGTCTACAAGCTGGTAATGCAACAGAAGTGCCTGCCAATTCAACTGTATTATCTTTCTGTGCTTTTGCTGTAGATGCTGCTAAAGCTTACAAAGATTATCTAGCTAGTGGGGGACAACCAATCACTAATTGTGTTAAGATGTTGTGTACACACACTGGTACTGGTCAGGCAATAACAGTTACACCGGAAGCCAATATGGATCAAGAATCCTTTGGTGGTGCATCGTGTTGTCTGTACTGCCGTTGCCACATAGATCATCCAAATCCTAAAGGATTTTGTGACTTAAAAGGTAAGTATGTACAAATACCTACAACTTGTGCTAATGACCCTGTGGGTTTTACACTTAAAAACACAGTCTGTACCGTCTGCGGTATGTGGAAAGGTTATGGCTGTAGTTGTGATCAACTCCGCGAACCCATGCTTCAGTCAGCTGATGCACAATCGTTTTTAAACGGGTTTGCGGTGTAAGTGCAGCCCGTCTTACACCGTGCGGCACAGGCACTAGTACTGATGTCGTATACAGGGCTTTTGACATCTACAATGATAAAGTAGCTGGTTTTGCTAAATTCCTAAAAACTAATTGTTGTCGCTTCCAAGAAAAGGACGAAGATGACAATTTAATTGATTCTTACTTTGTAGTTAAGAGACACACTTTCTCTAACTACCAACATGAAGAAACAATTTATAATTTACTTAAGGATTGTCCAGCTGTTGCTAAACATGACTTCTTTAAGTTTAGAATAGACGGTGACATGGTACCACATATATCACGTCAACGTCTTACTAAATACACAATGGCAGACCTCGTCTATGCTTTAAGGCATTTTGATGAAGGTAATTGTGACACATTAAAAGAAATACTTGTCACATACAATTGTTGTGATGATGATTATTTCAATAAAAAGGACTGGTATGATTTTGTAGAAAACCCAGATATATTACGCGTATACGCCAACTTAGGTGAACGTGTACGCCAAGCTTTGTTAAAAACAGTACAATTCTGTGATGCCATGCGAAATGCTGGTATTGTTGGTGTACTGACATTAGATAATCAAGATCTCAATGGTAACTGGTATGATTTCGGTGATTTCATACAAACCACGCCAGGTAGTGGAGTTCCTGTTGTAGATTCTTATTATTCATTGTTAATGCCTATATTAACCTTGACCAGGGCTTTAACTGCAGAGTCACATGTTGACACTGACTTAACAAAGCCTTACATTAAGTGGGATTTGTTAAAATATGACTTCACGGAAGAGAGGTTAAAACTCTTTGACCGTTATTTTAAATATTGGGATCAGACATACCACCCAAATTGTGTTAACTGTTTGGATGACAGATGCATTCTGCATTGTGCAAACTTTAATGTTTTATTCTCTACAGTGTTCCCACCTACAAGTTTTGGACCACTAGTGAGAAAAATATTTGTTGATGGTGTTCCATTTGTAGTTTCAACTGGATACCACTTCAGAGAGCTAGGTGTTGTACATAATCAGGATGTAAACTTACATAGCTCTAGACTTAGTTTTAAGGAATTACTTGTGTATGCTGCTGACCCTGCTATGCACGCTGCTTCTGGTAATCTATTACTAGATAAACGCACTACGTGCTTTTCAGTAGCTGCACTTACTAACAATGTTGCTTTTCAAACTGTCAAACCCGGTAATTTTAACAAAGACTTCTATGACTTTGCTGTGTCTAAGGGTTTCTTTAAGGAAGGAAGTTCTGTTGAATTAAAACACTTCTTCTTTGCTCAGGATGGTAATGCTGCTATCAGCGATTATGACTACTATCGTTATAATCTACCAACAATGTGTGATATCAGACAACTACTATTTGTAGTTGAAGTTGTTGATAAGTACTTTGATTGTTACGATGGTGGCTGTATTAATGCTAACCAAGTCATCGTCAACAACCTAGACAAATCAGCTGGTTTTCCATTTAATAAATGGGGTAAGGCTAGACTTTATTATGATTCAATGAGTTATGAGGATCAAGATGCACTTTTCGCATATACAAAACGTAATGTCATCCCTACTATAACTCAAATGAATCTTAAGTATGCCATTAGTGCAAAGAATAGAGCTCGCACCGTAGCTGGTGTCTCTATCTGTAGTACTATGACCAATAGACAGTTTCATCAAAAATTATTGAAATCAATAGCCGCCACTAGAGGAGCTACTGTAGTAATTGGAACAAGCAAATTCTATGGTGGTTGGCACAACATGTTAAAAACTGTTTATAGTGATGTAGAAAACCCTCACCTTATGGGTTGGGATTATCCTAAATGTGATAGAGCCATGCCTAACATGCTTAGAATTATGGCCTCACTTGTTCTTGCTCGCAAACATACAACGTGTTGTAGCTTGTCACACCGTTTCTATAGATTAGCTAATGAGTGTGCTCAAGTATTGAGTGAAATGGTCATGTGTGGCGGTTCACTATATGTTAAACCAGGTGGAACCTCATCAGGAGATGCCACAACTGCTTATGCTAATAGTGTTTTTAACATTTGTCAAGCTGTCACGGCCAATGTTAATGCACTTTTATCTACTGATGGTAACAAAATTGCCGATAAGTATGTCCGCAATTTACAACACAGACTTTATGAGTGTCTCTATAGAAATAGAGATGTTGACACAGACTTTGTGAATGAGTTTTACGCATATTTGCGTAAACATTTCTCAATGATGATACTCTCTGACGATGCTGTTGTGTGTTTCAATAGCACTTATGCATCTCAAGGTCTAGTGGCTAGCATAAAGAACTTTAAGTCAGTTCTTTATTATCAAAACAATGTTTTTATGTCTGAAGCAAAATGTTGGACTGAGACTGACCTTACTAAAGGACCTCATGAATTTTGCTCTCAACATACAATGCTAGTTAAACAGGGTGATGATTATGTGTACCTTCCTTACCCAGATCCATCAAGAATCCTAGGGGCCGGCTGTTTTGTAGATGATATCGTAAAAACAGATGGTACACTTATGATTGAACGGTTCGTGTCTTTAGCTATAGATGCTTACCCACTTACTAAACATCCTAATCAGGAGTATGCTGATGTCTTTCATTTGTACTTACAATACATAAGAAAGCTACATGATGAGTTAACAGGACACATGTTAGACATGTATTCTGTTATGCTTACTAATGATAACACTTCAAGGTATTGGGAACCTGAGTTTTATGAGGCTATGTACACACCGCATACAGTCTTACAGGCTGTTGGGGCTTGTGTTCTTTGCAATTCACAGACTTCATTAAGATGTGGTGCTTGCATACGTAGACCATTCTTATGTTGTAAATGCTGTTACGACCATGTCATATCAACATCACATAAATTAGTCTTGTCTGTTAATCCGTATGTTTGCAATGCTCCAGGTTGTGATGTCACAGATGTGACTCAACTTTACTTAGGAGGTATGAGCTATTATTGTAAATCACATAAACCACCCATTAGTTTTCCATTGTGTGCTAATGGACAAGTTTTTGGTTTATATAAAAATACATGTGTTGGTAGCGATAATGTTACTGACTTTAATGCAATTGCAACATGTGACTGGACAAATGCTGGTGATTACATTTTAGCTAACACCTGTACTGAAAGACTCAAGCTTTTTGCAGCAGAAACGCTCAAAGCTACTGAGGAGACATTTAAACTGTCTTATGGTATTGCTACTGTACGTGAAGTGCTGTCTGACAGAGAATTACATCTTTCATGGGAAGTTGGTAAACCTAGACCACCACTTAACCGAAATTATGTCTTTACTGGTTATCGTGTAACTAAAAACAGTAAAGTACAAATAGGAGAGTACACCTTTGAAAAAGGTGACTATGGTGATGCTGTTGTTTACCGAGGTACAACAACTTACAAATTAAATGTTGGTGATTATTTTGTGCTGACATCACATACAGTAATGCCATTAAGTGCACCTACACTAGTGCCACAAGAGCACTATGTTAGAATTACTGGCTTATACCCAACACTCAATATCTCAGATGAGTTTTCTAGCAATGTTGCAAATTATCAAAAGGTTGGTATGCAAAAGTATTCTACACTCCAGGGACCACCTGGTACTGGTAAGAGTCATTTTGCTATTGGCCTAGCTCTCTACTACCCTTCTGCTCGCATAGTGTATACAGCTTGCTCTCATGCCGCTGTTGATGCACTATGTGAGAAGGCATTAAAATATTTGCCTATAGATAAATGTAGTAGAATTATACCTGCACGTGCTCGTGTAGAGTGTTTTGATAAATTCAAAGTGAATTCAACATTAGAACAGTATGTCTTTTGTACTGTAAATGCATTGCCTGAGACGACAGCAGATATAGTTGTCTTTGATGAAATTTCAATGGCCACAAATTATGATTTGAGTGTTGTCAATGCCAGATTACGTGCTAAGCACTATGTGTACATTGGCGACCCTGCTCAATTACCTGCACCACGCACATTGCTAACTAAGGGCACACTAGAACCAGAATATTTCAATTCAGTGTGTAGACTTATGAAAACTATAGGTCCAGACATGTTCCTCGGAACTTGTCGGCGTTGTCCTGCTGAAATTGTTGACACTGTGAGTGCTTTGGTTTATGATAATAAGCTTAAAGCACATAAAGACAAATCAGCTCAATGCTTTAAAATGTTTTATAAGGGTGTTATCACGCATGATGTTTCATCTGCAATTAACAGGCCACAAATAGGCGTGGTAAGAGAATTCCTTACACGTAACCCTGCTTGGAGAAAAGCTGTCTTTATTTCACCTTATAATTCACAGAATGCTGTAGCCTCAAAGATTTTGGGACTACCAACTCAAACTGTTGATTCATCACAGGGCTCAGAATATGACTATGTCATATTCACTCAAACCACTGAAACAGCTCACTCTTGTAATGTAAACAGATTTAATGTTGCTATTACCAGAGCAAAAGTAGGCATACTTTGCATAATGTCTGATAGAGACCTTTATGACAAGTTGCAATTTACAAGTCTTGAAATTCCACGTAGGAATGTGGCAACTTTACAAGCTGAAAATGTAACAGGACTCTTTAAAGATTGTAGTAAGGTAATCACTGGGTTACATCCTACACAGGCACCTACACACCTCAGTGTTGACACTAAATTCAAAACTGAAGGTTTATGTGTTGACATACCTGGCATACCTAAGGACATGACCTATAGAAGACTCATCTCTATGATGGGTTTTAAAATGAATTATCAAGTTAATGGTTACCCTAACATGTTTATCACCCGCGAAGAAGCTATAAGACATGTACGTGCATGGATTGGCTTCGATGTCGAGGGGTGTCATGCTACTAGAGAAGCTGTTGGTACCAATTTACCTTTACAGCTAGGTTTTTCTACAGGTGTTAACCTAGTTGCTGTACCTACAGGTTATGTTGATACACCTAATAATACAGATTTTTCCAGAGTTAGTGCTAAACCACCGCCTGGAGATCAATTTAAACACCTCATACCACTTATGTACAAAGGACTTCCTTGGAATGTAGTGCGTATAAAGATTGTACAAATGTTAAGTGACACACTTAAAAATCTCTCTGACAGAGTCGTATTTGTCTTATGGGCACATGGCTTTGAGTTGACATCTATGAAGTATTTTGTGAAAATAGGACCTGAGCGCACCTGTTGTCTATGTGATAGACGTGCCACATGCTTTTCCACTGCTTCAGACACTTATGCCTGTTGGCATCATTCTATTGGATTTGATTACGTCTATAATCCGTTTATGATTGATGTTCAACAATGGGGTTTTACAGGTAACCTACAAAGCAACCATGATCTGTATTGTCAAGTCCATGGTAATGCACATGTAGCTAGTTGTGATGCAATCATGACTAGGTGTCTAGCTGTCCACGAGTGCTTTGTTAAGCGTGTTGACTGGACTATTGAATATCCTATAATTGGTGATGAACTGAAGATTAATGCGGCTTGTAGAAAGGTTCAACACATGGTTGTTAAAGCTGCATTATTAGCAGACAAATTCCCAGTTCTTCACGACATTGGTAACCCTAAAGCTATTAAGTGTGTACCTCAAGCTGATGTAGAATGGAAGTTCTATGATGCACAGCCTTGTAGTGACAAAGCTTATAAAATAGAAGAATTATTCTATTCTTATGCCACACATTCTGACAAATTCACAGATGGTGTATGCCTATTTTGGAATTGCAATGTCGATAGATATCCTGCTAATTCCATTGTTTGTAGATTTGACACTAGAGTGCTATCTAACCTTAACTTGCCTGGTTGTGATGGTGGCAGTTTGTATGTAAATAAACATGCATTCCACACACCAGCTTTTGATAAAAGTGCTTTTGTTAATTTAAAACAATTACCATTTTTCTATTACTCTGACAGTCCATGTGAGTCTCATGGAAAACAAGTAGTGTCAGATATAGATTATGTACCACTAAAGTCTGCTACGTGTATAACACGTTGCAATTTAGGTGGTGCTGTCTGTAGACATCATGCTAATGAGTACAGATTGTATCTCGATGCTTATAACATGATGATCTCAGCTGGCTTTAGCTTGTGGGTTTACAAACAATTTGATACTTATAACCTCTGGAACACTTTTACAAGACTTCAGAGTTTAGAAAATGTGGCTTTTAATGTTGTAAATAAGGGACACTTTGATGGACAACAGGGTGAAGTACCAGTTTCTATCATTAATAACACTGTTTACACAAAAGTTGATGGTGTTGATGTAGAATTGTTTGAAAATAAAACAACATTACCTGTTAATGTAGCATTTGAGCTTTGGGCTAAGCGCAACATTAAACCAGTACCAGAGGTGAAAATACTCAATAATTTGGGTGTGGACATTGCTGCTAATACTGTGATCTGGGACTACAAAAGAGATGCTCCAGCACATATATCTACTATTGGTGTTTGTTCTATGACTGACATAGCCAAGAAACCAACTGAAACGATTTGTGCACCACTCACTGTCTTTTTTGATGGTAGAGTTGATGGTCAAGTAGACTTATTTAGAAATGCCCGTAATGGTGTTCTTATTACAGAAGGTAGTGTTAAAGGTTTACAACCATCTGTAGGTCCCAAACAAGCTAGTCTTAATGGAGTCACATTAATTGGAGAAGCCGTAAAAACACAGTTCAATTATTATAAGAAAGTTGATGGTGTTGTCCAACAATTACCTGAAACTTACTTTACTCAGAGTAGAAATTTACAAGAATTTAAACCCAGGAGTCAAATGGAAATTGATTTCTTAGAATTAGCTATGGATGAATTCATTGAACGGTATAAATTAGAAGGCTATGCCTTCGAACATATCGTTTATGGAGATTTTAGTCATAGTCAGTTAGGTGGTTTACATCTACTGATTGGACTAGCTAAACGTTTTAAGGAATCACCTTTTGAATTAGAAGATTTTATTCCTATGGACAGTACAGTTAAAAACTATTTCATAACAGATGCGCAAACAGGTTCATCTAAGTGTGTGTGTTCTGTTATTGATTTATTACTTGATGATTTTGTTGAAATAATAAAATCCCAAGATTTATCTGTAGTTTCTAAGGTTGTCAAAGTGACTATTGACTATACAGAAATTTCATTTATGCTTTGGTGTAAAGATGGCCATGTAGAAACATTTTACCCAAAATTACAATCTAGTCAAGCGTGGCAACCGGGTGTTGCTATGCCTAATCTTTACAAAATGCAAAGAATGCTATTAGAAAAGTGTGACCTTCAAAATTATGGTGATAGTGCAACATTACCTAAAGGCATAATGATGAATGTCGCAAAATATACTCAACTGTGTCAATATTTAAACACATTAACATTAGCTGTACCCTATAATATGAGAGTTATACATTTTGGTGCTGGTTCTGATAAAGGAGTTGCACCAGGTACAGCTGTTTTAAGACAGTGGTTGCCTACGGGTACGCTGCTTGTCGATTCAGATCTTAATGACTTTGTCTCTGATGCAGATTCAACTTTGATTGGTGATTGTGCAACTGTACATACAGCTAATAAATGGGATCTCATTATTAGTGATATGTACGACCCTAAGACTAAAAATGTTACAAAAGAAAATGACTCTAAAGAGGGTTTTTTCACTTACATTTGTGGGTTTATACAACAAAAGCTAGCTCTTGGAGGTTCCGTGGCTATAAAGATAACAGAACATTCTTGGAATGCTGATCTTTATAAGCTCATGGGACACTTCGCATGGTGGACAGCCTTTGTTACTAATGTGAATGCGTCATCATCTGAAGCATTTTTAATTGGATGTAATTATCTTGGCAAACCACGCGAACAAATAGATGGTTATGTCATGCATGCAAATTACATATTTTGGAGGAATACAAATCCAATTCAGTTGTCTTCCTATTCTTTATTTGACATGAGTAAATTTCCCCTTAAATTAAGGGGTACTGCTGTTATGTCTTTAAAAGAAGGTCAAATCAATGATATGATTTTATCTCTTCTTAGTAAAGGTAGACTTATAATTAGAGAAAACAACAGAGTTGTTATTTCTAGTGATGTTCTTGTTAACAACTAAACGAACAATGTTTGTTTTTCTTGTTTTATTGCCACTAGTCTCTAGTCAGTGTGTTAATCTTACAACCAGAACTCAATTACCCCCTGCATACACTAATTCTTTCACACGTGGTGTTTATTACCCTGACAAAGTTTTCAGATCCTCAGTTTTACATTCAACTCAGGACTTGTTCTTACCTTTCTTTTCCAATGTTACTTGGTTCCATGCTATACATGTCTCTGGGACCAATGGTACTAAGAGGTTTGATAACCCTGTCCTACCATTTAATGATGGTGTTTATTTTGCTTCCACTGAGAAGTCTAACATAATAAGAGGCTGGATTTTTGGTACTACTTTAGATTCGAAGACCCAGTCCCTACTTATTGTTAATAACGCTACTAATGTTGTTATTAAAGTCTGTGAATTTCAATTTTGTAATGATCCATTTTTGGGTGTTTATTACCACAAAAACAACAAAAGTTGGATGGAAAGTGAGTTCAGAGTTTATTCTAGTGCGAATAATTGCACTTTTGAATATGTCTCTCAGCCTTTTCTTATGGACCTTGAAGGAAAACAGGGTAATTTCAAAAATCTTAGGGAATTTGTGTTTAAGAATATTGATGGTTATTTTAAAATATATTCTAAGCACACGCCTATTAATTTAGTGCGTGATCTCCCTCAGGGTTTTTCGGCTTTAGAACCATTGGTAGATTTGCCAATAGGTATTAACATCACTAGGTTTCAAACTTTACTTGCTTTACATAGAAGTTATTTGACTCCTGGTGATTCTTCTTCAGGTTGGACAGCTGGTGCTGCAGCTTATTATGTGGGTTATCTTCAACCTAGGACTTTTCTATTAAAATATAATGAAAATGGAACCATTACAGATGCTGTAGACTGTGCACTTGACCCTCTCTCAGAAACAAAGTGTACGTTGAAATCCTTCACTGTAGAAAAAGGAATCTATCAAACTTCTAACTTTAGAGTCCAACCAACAGAATCTATTGTTAGATTTCCTAATATTACAAACTTGTGCCCTTTTGGTGAAGTTTTTAACGCCACCAGATTTGCATCTGTTTATGCTTGGAACAGGAAGAGAATCAGCAACTGTGTTGCTGATTATTCTGTCCTATATAATTCCGCATCATTTTCCACTTTTAAGTGTTATGGAGTGTCTCCTACTAAATTAAATGATCTCTGCTTTACTAATGTCTATGCAGATTCATTTGTAATTAGAGGTGATGAAGTCAGACAAATCGCTCCAGGGCAAACTGGAAAGATTGCTGATTATAATTATAAATTACCAGATGATTTTACAGGCTGCGTTATAGCTTGGAATTCTAACAATCTTGATTCTAAGGTTGGTGGTAATTATAATTACCTGTATAGATTGTTTAGGAAGTCTAATCTCAAACCTTTTGAGAGAGATATTTCAACTGAAATCTATCAGGCCGGTAGCACACCTTGTAATGGTGTTGAAGGTTTTAATTGTTACTTTCCTTTACAATCATATGGTTTCCAACCCACTAATGGTGTTGGTTACCAACCATACAGAGTAGTAGTACTTTCTTTTGAACTTCTACATGCACCAGCAACTGTTTGTGGACCTAAAAAGTCTACTAATTTGGTTAAAAACAAATGTGTCAATTTCAACTTCAATGGTTTAACAGGCACAGGTGTTCTTACTGAGTCTAACAAAAAGTTTCTGCCTTTCCAACAATTTGGCAGAGACATTGCTGACACTACTGATGCTGTCCGTGATCCACAGACACTTGAGATTCTTGACATTACACCATGTTCTTTTGGTGGTGTCAGTGTTATAACACCAGGAACAAATACTTCTAACCAGGTTGCTGTTCTTTATCAGGATGTTAACTGCACAGAAGTCCCTGTTGCTATTCATGCAGATCAACTTACTCCTACTTGGCGTGTTTATTCTACAGGTTCTAATGTTTTTCAAACACGTGCAGGCTGTTTAATAGGGGCTGAACATGTCAACAACTCATATGAGTGTGACATACCCATTGGTGCAGGTATATGCGCTAGTTATCAGACTCAGACTAATTCTCCTCGGCGGGCACGTAGTGTAGCTAGTCAATCCATCATTGCCTACACTATGTCACTTGGTGCAGAAAATTCAGTTGCTTACTCTAATAACTCTATTGCCATACCCACAAATTTTACTATTAGTGTTACCACAGAAATTCTACCAGTGTCTATGACCAAGACATCAGTAGATTGTACAATGTACATTTGTGGTGATTCAACTGAATGCAGCAATCTTTTGTTGCAATATGGCAGTTTTTGTACACAATTAAACCGTGCTTTAACTGGAATAGCTGTTGAACAAGACAAAAACACCCAAGAAGTTTTTGCACAAGTCAAACAAATTTACAAAACACCACCAATTAAAGATTTTGGTGGTTTTAATTTTTCACAAATATTACCAGATCCATCAAAACCAAGCAAGAGGTCATTTATTGAAGATCTACTTTTCAACAAAGTGACACTTGCAGATGCTGGCTTCATCAAACAATATGGTGATTGCCTTGGTGATATTGCTGCTAGAGACCTCATTTGTGCACAAAAGTTTAACGGCCTTACTGTTTTGCCACCTTTGCTCACAGATGAAATGATTGCTCAATACACTTCTGCACTGTTAGCGGGTACAATCACTTCTGGTTGGACCTTTGGTGCAGGTGCTGCATTACAAATACCATTTGCTATGCAAATGGCTTATAGGTTTAATGGTATTGGAGTTACACAGAATGTTCTCTATGAGAACCAAAAATTGATTGCCAACCAATTTAATAGTGCTATTGGCAAAATTCAAGACTCACTTTCTTCCACAGCAAGTGCACTTGGAAAACTTCAAGATGTGGTCAACCAAAATGCACAAGCTTTAAACACGCTTGTTAAACAACTTAGCTCCAATTTTGGTGCAATTTCAAGTGTTTTAAATGATATCCTTTCACGTCTTGACAAAGTTGAGGCTGAAGTGCAAATTGATAGGTTGATCACAGGCAGACTTCAAAGTTTGCAGACATATGTGACTCAACAATTAATTAGAGCTGCAGAAATCAGAGCTTCTGCTAATCTTGCTGCTACTAAAATGTCAGAGTGTGTACTTGGACAATCAAAAAGAGTTGATTTTTGTGGAAAGGGCTATCATCTTATGTCCTTCCCTCAGTCAGCACCTCATGGTGTAGTCTTCTTGCATGTGACTTATGTCCCTGCACAAGAAAAGAACTTCACAACTGCTCCTGCCATTTGTCATGATGGAAAAGCACACTTTCCTCGTGAAGGTGTCTTTGTTTCAAATGGCACACACTGGTTTGTAACACAAAGGAATTTTTATGAACCACAAATCATTACTACAGACAACACATTTGTGTCTGGTAACTGTGATGTTGTAATAGGAATTGTCAACAACACAGTTTATGATCCTTTGCAACCTGAATTAGACTCATTCAAGGAGGAGTTAGATAAATATTTTAAGAATCATACATCACCAGATGTTGATTTAGGTGACATCTCTGGCATTAATGCTTCAGTTGTAAACATTCAAAAAGAAATTGACCGCCTCAATGAGGTTGCCAAGAATTTAAATGAATCTCTCATCGATCTCCAAGAACTTGGAAAGTATGAGCAGTATATAAAATGGCCATGGTACATTTGGCTAGGTTTTATAGCTGGCTTGATTGCCATAGTAATGGTGACAATTATGCTTTGCTGTATGACCAGTTGCTGTAGTTGTCTCAAGGGCTGTTGTTCTTGTGGATCCTGCTGCAAATTTGATGAAGACGACTCTGAGCCAGTGCTCAAAGGAGTCAAATTACATTACACATAAACGAACTTATGGATTTGTTTATGAGAATCTTCACAATTGGAACTGTAACTTTGAAGCAAGGTGAAATCAAGGATGCTACTCCTTCAGATTTTGTTCGCGCTACTGCAACGATACCGATACAAGCCTCACTCCCTTTCGGATGGCTTATTGTTGGCGTTGCACTTCTTGCTGTTTTTCAGAGCGCTTCCAAAATCATAACCCTCAAAAAGAGATGGCAACTAGCACTCTCCAAGGGTGTTCACTTTGTTTGCAACTTGCTGTTGTTGTTTGTAACAGTTTACTCACACCTTTTGCTCGTTGCTGCTGGCCTTGAAGCCCCTTTTCTCTATCTTTATGCTTTAGTCTACTTCTTGCAGAGTATAAACTTTGTAAGAATAATAATGAGGCTTTGGCTTTGCTGGAAATGCCGTTCCAAAAACCCATTACTTTATGATGCCAACTATTTTCTTTGCTGGCATACTAATTGTTACGACTATTGTATACCTTACAATAGTGTAACTTCTTCAATTGTCATTACTTCAGGTGATGGCACAACAAGTCCTATTTCTGAACATGACTACCAGATTGGTGGTTATACTGAAAAATGGGAATCTGGAGTAAAAGACTGTGTTGTATTACACAGTTACTTCACTTCAGACTATTACCAGCTGTACTCAACTCAATTGAGTACAGACACTGGTGTTGAACATGTTACCTTCTTCATCTACAATAAAATTGTTGATGAGCCTGAAGAACATGTCCAAATTCACACAATCGACGGTTCATCCGGAGTTGTTAATCCAGTAATGGAACCAATTTATGATGAACCGACGACGACTACTAGCGTGCCTTTGTAAGCACAAGCTGATGAGTACGAACTTATGTACTCATTCGTTTCGGAAGAGACAGGTACGTTAATAGTTAATAGCGTACTTCTTTTTCTTGCTTTCGTGGTATTCTTGCTAGTTACACTAGCCATCCTTACTGCGCTTCGATTGTGTGCGTACTGCTGCAATATTGTTAACGTGAGTCTTGTAAAACCTTCTTTTTACGTTTACTCTCGTGTTAAAAATCTGAATTCTTCTAGAGTTCCTGATCTTCTGGTCTAAACGAACTAAATATTATATTAGTTTTTCTGTTTGGAACTTTAATTTTAGCCATGGCAGATTCCAACGGTACTATTACCGTTGAAGAGCTTAAAAAGCTCCTTGAACAATGGAACCTAGTAATAGGTTTCCTATTCCTTACATGGATTTGTCTTCTACAATTTGCCTATGCCAACAGGAATAGGTTTTTGTATATAATTAAGTTAATTTTCCTCTGGCTGTTATGGCCAGTAACTTTAGCTTGTTTTGTGCTTGCTGCTGTTTACAGAATAAATTGGATCACCGGTGGAATTGCTATCGCAATGGCTTGTCTTGTAGGCTTGATGTGGCTCAGCTACTTCATTGCTTCTTTCAGACTGTTTGCGCGTACGCGTTCCATGTGGTCATTCAATCCAGAAACTAACATTCTTCTCAACGTGCCACTCCATGGCACTATTCTGACCAGACCGCTTCTAGAAAGTGAACTCGTAATCGGAGCTGTGATCCTTCGTGGACATCTTCGTATTGCTGGACACCATCTAGGACGCTGTGACATCAAGGACCTGCCTAAAGAAATCACTGTTGCTACATCACGAACGCTTTCTTATTACAAATTGGGAGCTTCGCAGCGTGTAGCAGGTGACTCAGGTTTTGCTGCATACAGTCGCTACAGGATTGGCAACTATAAATTAAACACAGACCATTCCAGTAGCAGTGACAATATTGCTTTGCTTGTACAGTAAGTGACAACAGATGTTTCATCTCGTTGACTTTCAGGTTACTATAGCAGAGATATTACTAATTATTATGAGGACTTTTAAAGTTTCCATTTGGAATCTTGATTACATCATAAACCTCATAATTAAAAATTTATCTAAGTCACTAACTGAGAATAAATATTCTCAATTAGATGAAGAGCAACCAATGGAGATTGATTAAACGAACATGAAAATTATTCTTTTCTTGGCACTGATAACACTCGCTACTTGTGAGCTTTATCACTACCAAGAGTGTGTTAGAGGTACAACAGTACTTTTAAAAGAACCTTGCTCTTCTGGAACATACGAGGGCAATTCACCATTTCATCCTCTAGCTGATAACAAATTTGCACTGACTTGCTTTAGCACTCAATTTGCTTTTGCTTGTCCTGACGGCGTAAAACACGTCTATCAGTTACGTGCCAGATCAGTTTCACCTAAACTGTTCATCAGACAAGAGGAAGTTCAAGAACTTTACTCTCCAATTTTTCTTATTGTTGCGGCAATAGTGTTTATAACACTTTGCTTCACACTCAAAAGAAAGACAGAATGATTGAACTTTCATTAATTGACTTCTATTTGTGCTTTTTAGCCTTTCTGCTATTCCTTGTTTTAATTATGCTTATTATCTTTTGGTTCTCACTTGAACTGCAAGATCATAATGAAACTTGTCACGCCTAAACGAACATGAAATTTCTTGTTTTCTTAGGAATCATCACAACTGTAGCTGCATTTCACCAAGAATGTAGTTTACAGTCATGTACTCAACATCAACCATATGTAGTTGATGACCCGTGTCCTATTCACTTCTATTCTAAATGGTATATTAGAGTAGGAGCTAGAAAATCAGCACCTTTAATTGAATTGTGCGTGGATGAGGCTGGTTCTAAATCACCCATTCAGTACATCGATATCGGTAATTATACAGTTTCCTGTTTACCTTTTACAATTAATTGCCAGGAACCTAAATTGGGTAGTCTTGTAGTGCGTTGTTCGTTCTATGAAGACTTTTTAGAGTATCATGACGTTCGTGTTGTTTTAGATTTCATCTAAACGAACAAACTAAAATGTCTGATAATGGACCCCAAAATCAGCGAAATGCACCCCGCATTACGTTTGGTGGACCCTCAGATTCAACTGGCAGTAACCAGAATGGAGAACGCAGTGGGGCGCGATCAAAACAACGTCGGCCCCAAGGTTTACCCAATAATACTGCGTCTTGGTTCACCGCTCTCACTCAACATGGCAAGGAAGACCTTAAATTCCCTCGAGGACAAGGCGTTCCAATTAACACCAATAGCAGTCCAGATGACCAAATTGGCTACTACCGAAGAGCTACCAGACGAATTCGTGGTGGTGACGGTAAAATGAAAGATCTCAGTCCAAGATGGTATTTCTACTACCTAGGAACTGGGCCAGAAGCTGGACTTCCCTATGGTGCTAACAAAGACGGCATCATATGGGTTGCAACTGAGGGAGCCTTGAATACACCAAAAGATCACATTGGCACCCGCAATCCTGCTAACAATGCTGCAATCGTGCTACAACTTCCTCAAGGAACAACATTGCCAAAAGGCTTCTACGCAGAAGGGAGCAGAGGCGGCAGTCAAGCCTCTTCTCGTTCCTCATCACGTAGTCGCAACAGTTCAAGAAATTCAACTCCAGGCAGCAGTAGGGGAACTTCTCCTGCTAGAATGGCTGGCAATGGCGGTGATGCTGCTCTTGCTTTGCTGCTGCTTGACAGATTGAACCAGCTTGAGAGCAAAATGTCTGGTAAAGGCCAACAACAACAAGGCCAAACTGTCACTAAGAAATCTGCTGCTGAGGCTTCTAAGAAGCCTCGGCAAAAACGTACTGCCACTAAAGCATACAATGTAACACAAGCTTTCGGCAGACGTGGTCCAGAACAAACCCAAGGAAATTTTGGGGACCAGGAACTAATCAGACAAGGAACTGATTACAAACATTGGCCGCAAATTGCACAATTTGCCCCCAGCGCTTCAGCGTTCTTCGGAATGTCGCGCATTGGCATGGAAGTCACACCTTCGGGAACGTGGTTGACCTACACAGGTGCCATCAAATTGGATGACAAAGATCCAAATTTCAAAGATCAAGTCATTTTGCTGAATAAGCATATTGACGCATACAAAACATTCCCACCAACAGAGCCTAAAAAGGACAAAAAGAAGAAGGCTGATGAAACTCAAGCCTTACCGCAGAGACAGAAGAAACAGCAAACTGTGACTCTTCTTCCTGCTGCAGATTTGGATGATTTCTCCAAACAATTGCAACAATCCATGAGCAGTGCTGACTCAACTCAGGCCTAAACTCATGCAGACCACACAAGGCAGATGGGCTATATAAACGTTTTCGCTTTTCCGTTTACGATATATAGTCTACTCTTGTGCAGAATGAATTCTCGTAACTACATAGCACAAGTAGATGTAGTTAACTTTAATCTCACATAGCAATCTTTAATCAGTGTGTAACATTAGGGAGGACTTGAAAGAGCCACCACATTTTCACCGAGGCCACGCGGAGTACGATCGAGTGTACAGTGAACAATGCTAGGGAGAGCTGCCTATATGGAAGAGCCCTAATGTGTAAAATTAATTTTAGTAGTGCTATCCCCATGTGATTTTAATAGCTTCTTAGGAGAATGACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +referenceGenome: + nucleotideSequences: + - name: main + sequence: 'ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTTACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAGATGGCACTTGTGGCTTAGTAGAAGTTGAAAAAGGCGTTTTGCCTCAACTTGAACAGCCCTATGTGTTCATCAAACGTTCGGATGCTCGAACTGCACCTCATGGTCATGTTATGGTTGAGCTGGTAGCAGAACTCGAAGGCATTCAGTACGGTCGTAGTGGTGAGACACTTGGTGTCCTTGTCCCTCATGTGGGCGAAATACCAGTGGCTTACCGCAAGGTTCTTCTTCGTAAGAACGGTAATAAAGGAGCTGGTGGCCATAGTTACGGCGCCGATCTAAAGTCATTTGACTTAGGCGACGAGCTTGGCACTGATCCTTATGAAGATTTTCAAGAAAACTGGAACACTAAACATAGCAGTGGTGTTACCCGTGAACTCATGCGTGAGCTTAACGGAGGGGCATACACTCGCTATGTCGATAACAACTTCTGTGGCCCTGATGGCTACCCTCTTGAGTGCATTAAAGACCTTCTAGCACGTGCTGGTAAAGCTTCATGCACTTTGTCCGAACAACTGGACTTTATTGACACTAAGAGGGGTGTATACTGCTGCCGTGAACATGAGCATGAAATTGCTTGGTACACGGAACGTTCTGAAAAGAGCTATGAATTGCAGACACCTTTTGAAATTAAATTGGCAAAGAAATTTGACACCTTCAATGGGGAATGTCCAAATTTTGTATTTCCCTTAAATTCCATAATCAAGACTATTCAACCAAGGGTTGAAAAGAAAAAGCTTGATGGCTTTATGGGTAGAATTCGATCTGTCTATCCAGTTGCGTCACCAAATGAATGCAACCAAATGTGCCTTTCAACTCTCATGAAGTGTGATCATTGTGGTGAAACTTCATGGCAGACGGGCGATTTTGTTAAAGCCACTTGCGAATTTTGTGGCACTGAGAATTTGACTAAAGAAGGTGCCACTACTTGTGGTTACTTACCCCAAAATGCTGTTGTTAAAATTTATTGTCCAGCATGTCACAATTCAGAAGTAGGACCTGAGCATAGTCTTGCCGAATACCATAATGAATCTGGCTTGAAAACCATTCTTCGTAAGGGTGGTCGCACTATTGCCTTTGGAGGCTGTGTGTTCTCTTATGTTGGTTGCCATAACAAGTGTGCCTATTGGGTTCCACGTGCTAGCGCTAACATAGGTTGTAACCATACAGGTGTTGTTGGAGAAGGTTCCGAAGGTCTTAATGACAACCTTCTTGAAATACTCCAAAAAGAGAAAGTCAACATCAATATTGTTGGTGACTTTAAACTTAATGAAGAGATCGCCATTATTTTGGCATCTTTTTCTGCTTCCACAAGTGCTTTTGTGGAAACTGTGAAAGGTTTGGATTATAAAGCATTCAAACAAATTGTTGAATCCTGTGGTAATTTTAAAGTTACAAAAGGAAAAGCTAAAAAAGGTGCCTGGAATATTGGTGAACAGAAATCAATACTGAGTCCTCTTTATGCATTTGCATCAGAGGCTGCTCGTGTTGTACGATCAATTTTCTCCCGCACTCTTGAAACTGCTCAAAATTCTGTGCGTGTTTTACAGAAGGCCGCTATAACAATACTAGATGGAATTTCACAGTATTCACTGAGACTCATTGATGCTATGATGTTCACATCTGATTTGGCTACTAACAATCTAGTTGTAATGGCCTACATTACAGGTGGTGTTGTTCAGTTGACTTCGCAGTGGCTAACTAACATCTTTGGCACTGTTTATGAAAAACTCAAACCCGTCCTTGATTGGCTTGAAGAGAAGTTTAAGGAAGGTGTAGAGTTTCTTAGAGACGGTTGGGAAATTGTTAAATTTATCTCAACCTGTGCTTGTGAAATTGTCGGTGGACAAATTGTCACCTGTGCAAAGGAAATTAAGGAGAGTGTTCAGACATTCTTTAAGCTTGTAAATAAATTTTTGGCTTTGTGTGCTGACTCTATCATTATTGGTGGAGCTAAACTTAAAGCCTTGAATTTAGGTGAAACATTTGTCACGCACTCAAAGGGATTGTACAGAAAGTGTGTTAAATCCAGAGAAGAAACTGGCCTACTCATGCCTCTAAAAGCCCCAAAAGAAATTATCTTCTTAGAGGGAGAAACACTTCCCACAGAAGTGTTAACAGAGGAAGTTGTCTTGAAAACTGGTGATTTACAACCATTAGAACAACCTACTAGTGAAGCTGTTGAAGCTCCATTGGTTGGTACACCAGTTTGTATTAACGGGCTTATGTTGCTCGAAATCAAAGACACAGAAAAGTACTGTGCCCTTGCACCTAATATGATGGTAACAAACAATACCTTCACACTCAAAGGCGGTGCACCAACAAAGGTTACTTTTGGTGATGACACTGTGATAGAAGTGCAAGGTTACAAGAGTGTGAATATCACTTTTGAACTTGATGAAAGGATTGATAAAGTACTTAATGAGAAGTGCTCTGCCTATACAGTTGAACTCGGTACAGAAGTAAATGAGTTCGCCTGTGTTGTGGCAGATGCTGTCATAAAAACTTTGCAACCAGTATCTGAATTACTTACACCACTGGGCATTGATTTAGATGAGTGGAGTATGGCTACATACTACTTATTTGATGAGTCTGGTGAGTTTAAATTGGCTTCACATATGTATTGTTCTTTCTACCCTCCAGATGAGGATGAAGAAGAAGGTGATTGTGAAGAAGAAGAGTTTGAGCCATCAACTCAATATGAGTATGGTACTGAAGATGATTACCAAGGTAAACCTTTGGAATTTGGTGCCACTTCTGCTGCTCTTCAACCTGAAGAAGAGCAAGAAGAAGATTGGTTAGATGATGATAGTCAACAAACTGTTGGTCAACAAGACGGCAGTGAGGACAATCAGACAACTACTATTCAAACAATTGTTGAGGTTCAACCTCAATTAGAGATGGAACTTACACCAGTTGTTCAGACTATTGAAGTGAATAGTTTTAGTGGTTATTTAAAACTTACTGACAATGTATACATTAAAAATGCAGACATTGTGGAAGAAGCTAAAAAGGTAAAACCAACAGTGGTTGTTAATGCAGCCAATGTTTACCTTAAACATGGAGGAGGTGTTGCAGGAGCCTTAAATAAGGCTACTAACAATGCCATGCAAGTTGAATCTGATGATTACATAGCTACTAATGGACCACTTAAAGTGGGTGGTAGTTGTGTTTTAAGCGGACACAATCTTGCTAAACACTGTCTTCATGTTGTCGGCCCAAATGTTAACAAAGGTGAAGACATTCAACTTCTTAAGAGTGCTTATGAAAATTTTAATCAGCACGAAGTTCTACTTGCACCATTATTATCAGCTGGTATTTTTGGTGCTGACCCTATACATTCTTTAAGAGTTTGTGTAGATACTGTTCGCACAAATGTCTACTTAGCTGTCTTTGATAAAAATCTCTATGACAAACTTGTTTCAAGCTTTTTGGAAATGAAGAGTGAAAAGCAAGTTGAACAAAAGATCGCTGAGATTCCTAAAGAGGAAGTTAAGCCATTTATAACTGAAAGTAAACCTTCAGTTGAACAGAGAAAACAAGATGATAAGAAAATCAAAGCTTGTGTTGAAGAAGTTACAACAACTCTGGAAGAAACTAAGTTCCTCACAGAAAACTTGTTACTTTATATTGACATTAATGGCAATCTTCATCCAGATTCTGCCACTCTTGTTAGTGACATTGACATCACTTTCTTAAAGAAAGATGCTCCATATATAGTGGGTGATGTTGTTCAAGAGGGTGTTTTAACTGCTGTGGTTATACCTACTAAAAAGGCTGGTGGCACTACTGAAATGCTAGCGAAAGCTTTGAGAAAAGTGCCAACAGACAATTATATAACCACTTACCCGGGTCAGGGTTTAAATGGTTACACTGTAGAGGAGGCAAAGACAGTGCTTAAAAAGTGTAAAAGTGCCTTTTACATTCTACCATCTATTATCTCTAATGAGAAGCAAGAAATTCTTGGAACTGTTTCTTGGAATTTGCGAGAAATGCTTGCACATGCAGAAGAAACACGCAAATTAATGCCTGTCTGTGTGGAAACTAAAGCCATAGTTTCAACTATACAGCGTAAATATAAGGGTATTAAAATACAAGAGGGTGTGGTTGATTATGGTGCTAGATTTTACTTTTACACCAGTAAAACAACTGTAGCGTCACTTATCAACACACTTAACGATCTAAATGAAACTCTTGTTACAATGCCACTTGGCTATGTAACACATGGCTTAAATTTGGAAGAAGCTGCTCGGTATATGAGATCTCTCAAAGTGCCAGCTACAGTTTCTGTTTCTTCACCTGATGCTGTTACAGCGTATAATGGTTATCTTACTTCTTCTTCTAAAACACCTGAAGAACATTTTATTGAAACCATCTCACTTGCTGGTTCCTATAAAGATTGGTCCTATTCTGGACAATCTACACAACTAGGTATAGAATTTCTTAAGAGAGGTGATAAAAGTGTATATTACACTAGTAATCCTACCACATTCCACCTAGATGGTGAAGTTATCACCTTTGACAATCTTAAGACACTTCTTTCTTTGAGAGAAGTGAGGACTATTAAGGTGTTTACAACAGTAGACAACATTAACCTCCACACGCAAGTTGTGGACATGTCAATGACATATGGACAACAGTTTGGTCCAACTTATTTGGATGGAGCTGATGTTACTAAAATAAAACCTCATAATTCACATGAAGGTAAAACATTTTATGTTTTACCTAATGATGACACTCTACGTGTTGAGGCTTTTGAGTACTACCACACAACTGATCCTAGTTTTCTGGGTAGGTACATGTCAGCATTAAATCACACTAAAAAGTGGAAATACCCACAAGTTAATGGTTTAACTTCTATTAAATGGGCAGATAACAACTGTTATCTTGCCACTGCATTGTTAACACTCCAACAAATAGAGTTGAAGTTTAATCCACCTGCTCTACAAGATGCTTATTACAGAGCAAGGGCTGGTGAAGCTGCTAACTTTTGTGCACTTATCTTAGCCTACTGTAATAAGACAGTAGGTGAGTTAGGTGATGTTAGAGAAACAATGAGTTACTTGTTTCAACATGCCAATTTAGATTCTTGCAAAAGAGTCTTGAACGTGGTGTGTAAAACTTGTGGACAACAGCAGACAACCCTTAAGGGTGTAGAAGCTGTTATGTACATGGGCACACTTTCTTATGAACAATTTAAGAAAGGTGTTCAGATACCTTGTACGTGTGGTAAACAAGCTACAAAATATCTAGTACAACAGGAGTCACCTTTTGTTATGATGTCAGCACCACCTGCTCAGTATGAACTTAAGCATGGTACATTTACTTGTGCTAGTGAGTACACTGGTAATTACCAGTGTGGTCACTATAAACATATAACTTCTAAAGAAACTTTGTATTGCATAGACGGTGCTTTACTTACAAAGTCCTCAGAATACAAAGGTCCTATTACGGATGTTTTCTACAAAGAAAACAGTTACACAACAACCATAAAACCAGTTACTTATAAATTGGATGGTGTTGTTTGTACAGAAATTGACCCTAAGTTGGACAATTATTATAAGAAAGACAATTCTTATTTCACAGAGCAACCAATTGATCTTGTACCAAACCAACCATATCCAAACGCAAGCTTCGATAATTTTAAGTTTGTATGTGATAATATCAAATTTGCTGATGATTTAAACCAGTTAACTGGTTATAAGAAACCTGCTTCAAGAGAGCTTAAAGTTACATTTTTCCCTGACTTAAATGGTGATGTGGTGGCTATTGATTATAAACACTACACACCCTCTTTTAAGAAAGGAGCTAAATTGTTACATAAACCTATTGTTTGGCATGTTAACAATGCAACTAATAAAGCCACGTATAAACCAAATACCTGGTGTATACGTTGTCTTTGGAGCACAAAACCAGTTGAAACATCAAATTCGTTTGATGTACTGAAGTCAGAGGACGCGCAGGGAATGGATAATCTTGCCTGCGAAGATCTAAAACCAGTCTCTGAAGAAGTAGTGGAAAATCCTACCATACAGAAAGACGTTCTTGAGTGTAATGTGAAAACTACCGAAGTTGTAGGAGACATTATACTTAAACCAGCAAATAATAGTTTAAAAATTACAGAAGAGGTTGGCCACACAGATCTAATGGCTGCTTATGTAGACAATTCTAGTCTTACTATTAAGAAACCTAATGAATTATCTAGAGTATTAGGTTTGAAAACCCTTGCTACTCATGGTTTAGCTGCTGTTAATAGTGTCCCTTGGGATACTATAGCTAATTATGCTAAGCCTTTTCTTAACAAAGTTGTTAGTACAACTACTAACATAGTTACACGGTGTTTAAACCGTGTTTGTACTAATTATATGCCTTATTTCTTTACTTTATTGCTACAATTGTGTACTTTTACTAGAAGTACAAATTCTAGAATTAAAGCATCTATGCCGACTACTATAGCAAAGAATACTGTTAAGAGTGTCGGTAAATTTTGTCTAGAGGCTTCATTTAATTATTTGAAGTCACCTAATTTTTCTAAACTGATAAATATTATAATTTGGTTTTTACTATTAAGTGTTTGCCTAGGTTCTTTAATCTACTCAACCGCTGCTTTAGGTGTTTTAATGTCTAATTTAGGCATGCCTTCTTACTGTACTGGTTACAGAGAAGGCTATTTGAACTCTACTAATGTCACTATTGCAACCTACTGTACTGGTTCTATACCTTGTAGTGTTTGTCTTAGTGGTTTAGATTCTTTAGACACCTATCCTTCTTTAGAAACTATACAAATTACCATTTCATCTTTTAAATGGGATTTAACTGCTTTTGGCTTAGTTGCAGAGTGGTTTTTGGCATATATTCTTTTCACTAGGTTTTTCTATGTACTTGGATTGGCTGCAATCATGCAATTGTTTTTCAGCTATTTTGCAGTACATTTTATTAGTAATTCTTGGCTTATGTGGTTAATAATTAATCTTGTACAAATGGCCCCGATTTCAGCTATGGTTAGAATGTACATCTTCTTTGCATCATTTTATTATGTATGGAAAAGTTATGTGCATGTTGTAGACGGTTGTAATTCATCAACTTGTATGATGTGTTACAAACGTAATAGAGCAACAAGAGTCGAATGTACAACTATTGTTAATGGTGTTAGAAGGTCCTTTTATGTCTATGCTAATGGAGGTAAAGGCTTTTGCAAACTACACAATTGGAATTGTGTTAATTGTGATACATTCTGTGCTGGTAGTACATTTATTAGTGATGAAGTTGCGAGAGACTTGTCACTACAGTTTAAAAGACCAATAAATCCTACTGACCAGTCTTCTTACATCGTTGATAGTGTTACAGTGAAGAATGGTTCCATCCATCTTTACTTTGATAAAGCTGGTCAAAAGACTTATGAAAGACATTCTCTCTCTCATTTTGTTAACTTAGACAACCTGAGAGCTAATAACACTAAAGGTTCATTGCCTATTAATGTTATAGTTTTTGATGGTAAATCAAAATGTGAAGAATCATCTGCAAAATCAGCGTCTGTTTACTACAGTCAGCTTATGTGTCAACCTATACTGTTACTAGATCAGGCATTAGTGTCTGATGTTGGTGATAGTGCGGAAGTTGCAGTTAAAATGTTTGATGCTTACGTTAATACGTTTTCATCAACTTTTAACGTACCAATGGAAAAACTCAAAACACTAGTTGCAACTGCAGAAGCTGAACTTGCAAAGAATGTGTCCTTAGACAATGTCTTATCTACTTTTATTTCAGCAGCTCGGCAAGGGTTTGTTGATTCAGATGTAGAAACTAAAGATGTTGTTGAATGTCTTAAATTGTCACATCAATCTGACATAGAAGTTACTGGCGATAGTTGTAATAACTATATGCTCACCTATAACAAAGTTGAAAACATGACACCCCGTGACCTTGGTGCTTGTATTGACTGTAGTGCGCGTCATATTAATGCGCAGGTAGCAAAAAGTCACAACATTGCTTTGATATGGAACGTTAAAGATTTCATGTCATTGTCTGAACAACTACGAAAACAAATACGTAGTGCTGCTAAAAAGAATAACTTACCTTTTAAGTTGACATGTGCAACTACTAGACAAGTTGTTAATGTTGTAACAACAAAGATAGCACTTAAGGGTGGTAAAATTGTTAATAATTGGTTGAAGCAGTTAATTAAAGTTACACTTGTGTTCCTTTTTGTTGCTGCTATTTTCTATTTAATAACACCTGTTCATGTCATGTCTAAACATACTGACTTTTCAAGTGAAATCATAGGATACAAGGCTATTGATGGTGGTGTCACTCGTGACATAGCATCTACAGATACTTGTTTTGCTAACAAACATGCTGATTTTGACACATGGTTTAGCCAGCGTGGTGGTAGTTATACTAATGACAAAGCTTGCCCATTGATTGCTGCAGTCATAACAAGAGAAGTGGGTTTTGTCGTGCCTGGTTTGCCTGGCACGATATTACGCACAACTAATGGTGACTTTTTGCATTTCTTACCTAGAGTTTTTAGTGCAGTTGGTAACATCTGTTACACACCATCAAAACTTATAGAGTACACTGACTTTGCAACATCAGCTTGTGTTTTGGCTGCTGAATGTACAATTTTTAAAGATGCTTCTGGTAAGCCAGTACCATATTGTTATGATACCAATGTACTAGAAGGTTCTGTTGCTTATGAAAGTTTACGCCCTGACACACGTTATGTGCTCATGGATGGCTCTATTATTCAATTTCCTAACACCTACCTTGAAGGTTCTGTTAGAGTGGTAACAACTTTTGATTCTGAGTACTGTAGGCACGGCACTTGTGAAAGATCAGAAGCTGGTGTTTGTGTATCTACTAGTGGTAGATGGGTACTTAACAATGATTATTACAGATCTTTACCAGGAGTTTTCTGTGGTGTAGATGCTGTAAATTTACTTACTAATATGTTTACACCACTAATTCAACCTATTGGTGCTTTGGACATATCAGCATCTATAGTAGCTGGTGGTATTGTAGCTATCGTAGTAACATGCCTTGCCTACTATTTTATGAGGTTTAGAAGAGCTTTTGGTGAATACAGTCATGTAGTTGCCTTTAATACTTTACTATTCCTTATGTCATTCACTGTACTCTGTTTAACACCAGTTTACTCATTCTTACCTGGTGTTTATTCTGTTATTTACTTGTACTTGACATTTTATCTTACTAATGATGTTTCTTTTTTAGCACATATTCAGTGGATGGTTATGTTCACACCTTTAGTACCTTTCTGGATAACAATTGCTTATATCATTTGTATTTCCACAAAGCATTTCTATTGGTTCTTTAGTAATTACCTAAAGAGACGTGTAGTCTTTAATGGTGTTTCCTTTAGTACTTTTGAAGAAGCTGCGCTGTGCACCTTTTTGTTAAATAAAGAAATGTATCTAAAGTTGCGTAGTGATGTGCTATTACCTCTTACGCAATATAATAGATACTTAGCTCTTTATAATAAGTACAAGTATTTTAGTGGAGCAATGGATACAACTAGCTACAGAGAAGCTGCTTGTTGTCATCTCGCAAAGGCTCTCAATGACTTCAGTAACTCAGGTTCTGATGTTCTTTACCAACCACCACAAACCTCTATCACCTCAGCTGTTTTGCAGAGTGGTTTTAGAAAAATGGCATTCCCATCTGGTAAAGTTGAGGGTTGTATGGTACAAGTAACTTGTGGTACAACTACACTTAACGGTCTTTGGCTTGATGACGTAGTTTACTGTCCAAGACATGTGATCTGCACCTCTGAAGACATGCTTAACCCTAATTATGAAGATTTACTCATTCGTAAGTCTAATCATAATTTCTTGGTACAGGCTGGTAATGTTCAACTCAGGGTTATTGGACATTCTATGCAAAATTGTGTACTTAAGCTTAAGGTTGATACAGCCAATCCTAAGACACCTAAGTATAAGTTTGTTCGCATTCAACCAGGACAGACTTTTTCAGTGTTAGCTTGTTACAATGGTTCACCATCTGGTGTTTACCAATGTGCTATGAGGCCCAATTTCACTATTAAGGGTTCATTCCTTAATGGTTCATGTGGTAGTGTTGGTTTTAACATAGATTATGACTGTGTCTCTTTTTGTTACATGCACCATATGGAATTACCAACTGGAGTTCATGCTGGCACAGACTTAGAAGGTAACTTTTATGGACCTTTTGTTGACAGGCAAACAGCACAAGCAGCTGGTACGGACACAACTATTACAGTTAATGTTTTAGCTTGGTTGTACGCTGCTGTTATAAATGGAGACAGGTGGTTTCTCAATCGATTTACCACAACTCTTAATGACTTTAACCTTGTGGCTATGAAGTACAATTATGAACCTCTAACACAAGACCATGTTGACATACTAGGACCTCTTTCTGCTCAAACTGGAATTGCCGTTTTAGATATGTGTGCTTCATTAAAAGAATTACTGCAAAATGGTATGAATGGACGTACCATATTGGGTAGTGCTTTATTAGAAGATGAATTTACACCTTTTGATGTTGTTAGACAATGCTCAGGTGTTACTTTCCAAAGTGCAGTGAAAAGAACAATCAAGGGTACACACCACTGGTTGTTACTCACAATTTTGACTTCACTTTTAGTTTTAGTCCAGAGTACTCAATGGTCTTTGTTCTTTTTTTTGTATGAAAATGCCTTTTTACCTTTTGCTATGGGTATTATTGCTATGTCTGCTTTTGCAATGATGTTTGTCAAACATAAGCATGCATTTCTCTGTTTGTTTTTGTTACCTTCTCTTGCCACTGTAGCTTATTTTAATATGGTCTATATGCCTGCTAGTTGGGTGATGCGTATTATGACATGGTTGGATATGGTTGATACTAGTTTGTCTGGTTTTAAGCTAAAAGACTGTGTTATGTATGCATCAGCTGTAGTGTTACTAATCCTTATGACAGCAAGAACTGTGTATGATGATGGTGCTAGGAGAGTGTGGACACTTATGAATGTCTTGACACTCGTTTATAAAGTTTATTATGGTAATGCTTTAGATCAAGCCATTTCCATGTGGGCTCTTATAATCTCTGTTACTTCTAACTACTCAGGTGTAGTTACAACTGTCATGTTTTTGGCCAGAGGTATTGTTTTTATGTGTGTTGAGTATTGCCCTATTTTCTTCATAACTGGTAATACACTTCAGTGTATAATGCTAGTTTATTGTTTCTTAGGCTATTTTTGTACTTGTTACTTTGGCCTCTTTTGTTTACTCAACCGCTACTTTAGACTGACTCTTGGTGTTTATGATTACTTAGTTTCTACACAGGAGTTTAGATATATGAATTCACAGGGACTACTCCCACCCAAGAATAGCATAGATGCCTTCAAACTCAACATTAAATTGTTGGGTGTTGGTGGCAAACCTTGTATCAAAGTAGCCACTGTACAGTCTAAAATGTCAGATGTAAAGTGCACATCAGTAGTCTTACTCTCAGTTTTGCAACAACTCAGAGTAGAATCATCATCTAAATTGTGGGCTCAATGTGTCCAGTTACACAATGACATTCTCTTAGCTAAAGATACTACTGAAGCCTTTGAAAAAATGGTTTCACTACTTTCTGTTTTGCTTTCCATGCAGGGTGCTGTAGACATAAACAAGCTTTGTGAAGAAATGCTGGACAACAGGGCAACCTTACAAGCTATAGCCTCAGAGTTTAGTTCCCTTCCATCATATGCAGCTTTTGCTACTGCTCAAGAAGCTTATGAGCAGGCTGTTGCTAATGGTGATTCTGAAGTTGTTCTTAAAAAGTTGAAGAAGTCTTTGAATGTGGCTAAATCTGAATTTGACCGTGATGCAGCCATGCAACGTAAGTTGGAAAAGATGGCTGATCAAGCTATGACCCAAATGTATAAACAGGCTAGATCTGAGGACAAGAGGGCAAAAGTTACTAGTGCTATGCAGACAATGCTTTTCACTATGCTTAGAAAGTTGGATAATGATGCACTCAACAACATTATCAACAATGCAAGAGATGGTTGTGTTCCCTTGAACATAATACCTCTTACAACAGCAGCCAAACTAATGGTTGTCATACCAGACTATAACACATATAAAAATACGTGTGATGGTACAACATTTACTTATGCATCAGCATTGTGGGAAATCCAACAGGTTGTAGATGCAGATAGTAAAATTGTTCAACTTAGTGAAATTAGTATGGACAATTCACCTAATTTAGCATGGCCTCTTATTGTAACAGCTTTAAGGGCCAATTCTGCTGTCAAATTACAGAATAATGAGCTTAGTCCTGTTGCACTACGACAGATGTCTTGTGCTGCCGGTACTACACAAACTGCTTGCACTGATGACAATGCGTTAGCTTACTACAACACAACAAAGGGAGGTAGGTTTGTACTTGCACTGTTATCCGATTTACAGGATTTGAAATGGGCTAGATTCCCTAAGAGTGATGGAACTGGTACTATCTATACAGAACTGGAACCACCTTGTAGGTTTGTTACAGACACACCTAAAGGTCCTAAAGTGAAGTATTTATACTTTATTAAAGGATTAAACAACCTAAATAGAGGTATGGTACTTGGTAGTTTAGCTGCCACAGTACGTCTACAAGCTGGTAATGCAACAGAAGTGCCTGCCAATTCAACTGTATTATCTTTCTGTGCTTTTGCTGTAGATGCTGCTAAAGCTTACAAAGATTATCTAGCTAGTGGGGGACAACCAATCACTAATTGTGTTAAGATGTTGTGTACACACACTGGTACTGGTCAGGCAATAACAGTTACACCGGAAGCCAATATGGATCAAGAATCCTTTGGTGGTGCATCGTGTTGTCTGTACTGCCGTTGCCACATAGATCATCCAAATCCTAAAGGATTTTGTGACTTAAAAGGTAAGTATGTACAAATACCTACAACTTGTGCTAATGACCCTGTGGGTTTTACACTTAAAAACACAGTCTGTACCGTCTGCGGTATGTGGAAAGGTTATGGCTGTAGTTGTGATCAACTCCGCGAACCCATGCTTCAGTCAGCTGATGCACAATCGTTTTTAAACGGGTTTGCGGTGTAAGTGCAGCCCGTCTTACACCGTGCGGCACAGGCACTAGTACTGATGTCGTATACAGGGCTTTTGACATCTACAATGATAAAGTAGCTGGTTTTGCTAAATTCCTAAAAACTAATTGTTGTCGCTTCCAAGAAAAGGACGAAGATGACAATTTAATTGATTCTTACTTTGTAGTTAAGAGACACACTTTCTCTAACTACCAACATGAAGAAACAATTTATAATTTACTTAAGGATTGTCCAGCTGTTGCTAAACATGACTTCTTTAAGTTTAGAATAGACGGTGACATGGTACCACATATATCACGTCAACGTCTTACTAAATACACAATGGCAGACCTCGTCTATGCTTTAAGGCATTTTGATGAAGGTAATTGTGACACATTAAAAGAAATACTTGTCACATACAATTGTTGTGATGATGATTATTTCAATAAAAAGGACTGGTATGATTTTGTAGAAAACCCAGATATATTACGCGTATACGCCAACTTAGGTGAACGTGTACGCCAAGCTTTGTTAAAAACAGTACAATTCTGTGATGCCATGCGAAATGCTGGTATTGTTGGTGTACTGACATTAGATAATCAAGATCTCAATGGTAACTGGTATGATTTCGGTGATTTCATACAAACCACGCCAGGTAGTGGAGTTCCTGTTGTAGATTCTTATTATTCATTGTTAATGCCTATATTAACCTTGACCAGGGCTTTAACTGCAGAGTCACATGTTGACACTGACTTAACAAAGCCTTACATTAAGTGGGATTTGTTAAAATATGACTTCACGGAAGAGAGGTTAAAACTCTTTGACCGTTATTTTAAATATTGGGATCAGACATACCACCCAAATTGTGTTAACTGTTTGGATGACAGATGCATTCTGCATTGTGCAAACTTTAATGTTTTATTCTCTACAGTGTTCCCACCTACAAGTTTTGGACCACTAGTGAGAAAAATATTTGTTGATGGTGTTCCATTTGTAGTTTCAACTGGATACCACTTCAGAGAGCTAGGTGTTGTACATAATCAGGATGTAAACTTACATAGCTCTAGACTTAGTTTTAAGGAATTACTTGTGTATGCTGCTGACCCTGCTATGCACGCTGCTTCTGGTAATCTATTACTAGATAAACGCACTACGTGCTTTTCAGTAGCTGCACTTACTAACAATGTTGCTTTTCAAACTGTCAAACCCGGTAATTTTAACAAAGACTTCTATGACTTTGCTGTGTCTAAGGGTTTCTTTAAGGAAGGAAGTTCTGTTGAATTAAAACACTTCTTCTTTGCTCAGGATGGTAATGCTGCTATCAGCGATTATGACTACTATCGTTATAATCTACCAACAATGTGTGATATCAGACAACTACTATTTGTAGTTGAAGTTGTTGATAAGTACTTTGATTGTTACGATGGTGGCTGTATTAATGCTAACCAAGTCATCGTCAACAACCTAGACAAATCAGCTGGTTTTCCATTTAATAAATGGGGTAAGGCTAGACTTTATTATGATTCAATGAGTTATGAGGATCAAGATGCACTTTTCGCATATACAAAACGTAATGTCATCCCTACTATAACTCAAATGAATCTTAAGTATGCCATTAGTGCAAAGAATAGAGCTCGCACCGTAGCTGGTGTCTCTATCTGTAGTACTATGACCAATAGACAGTTTCATCAAAAATTATTGAAATCAATAGCCGCCACTAGAGGAGCTACTGTAGTAATTGGAACAAGCAAATTCTATGGTGGTTGGCACAACATGTTAAAAACTGTTTATAGTGATGTAGAAAACCCTCACCTTATGGGTTGGGATTATCCTAAATGTGATAGAGCCATGCCTAACATGCTTAGAATTATGGCCTCACTTGTTCTTGCTCGCAAACATACAACGTGTTGTAGCTTGTCACACCGTTTCTATAGATTAGCTAATGAGTGTGCTCAAGTATTGAGTGAAATGGTCATGTGTGGCGGTTCACTATATGTTAAACCAGGTGGAACCTCATCAGGAGATGCCACAACTGCTTATGCTAATAGTGTTTTTAACATTTGTCAAGCTGTCACGGCCAATGTTAATGCACTTTTATCTACTGATGGTAACAAAATTGCCGATAAGTATGTCCGCAATTTACAACACAGACTTTATGAGTGTCTCTATAGAAATAGAGATGTTGACACAGACTTTGTGAATGAGTTTTACGCATATTTGCGTAAACATTTCTCAATGATGATACTCTCTGACGATGCTGTTGTGTGTTTCAATAGCACTTATGCATCTCAAGGTCTAGTGGCTAGCATAAAGAACTTTAAGTCAGTTCTTTATTATCAAAACAATGTTTTTATGTCTGAAGCAAAATGTTGGACTGAGACTGACCTTACTAAAGGACCTCATGAATTTTGCTCTCAACATACAATGCTAGTTAAACAGGGTGATGATTATGTGTACCTTCCTTACCCAGATCCATCAAGAATCCTAGGGGCCGGCTGTTTTGTAGATGATATCGTAAAAACAGATGGTACACTTATGATTGAACGGTTCGTGTCTTTAGCTATAGATGCTTACCCACTTACTAAACATCCTAATCAGGAGTATGCTGATGTCTTTCATTTGTACTTACAATACATAAGAAAGCTACATGATGAGTTAACAGGACACATGTTAGACATGTATTCTGTTATGCTTACTAATGATAACACTTCAAGGTATTGGGAACCTGAGTTTTATGAGGCTATGTACACACCGCATACAGTCTTACAGGCTGTTGGGGCTTGTGTTCTTTGCAATTCACAGACTTCATTAAGATGTGGTGCTTGCATACGTAGACCATTCTTATGTTGTAAATGCTGTTACGACCATGTCATATCAACATCACATAAATTAGTCTTGTCTGTTAATCCGTATGTTTGCAATGCTCCAGGTTGTGATGTCACAGATGTGACTCAACTTTACTTAGGAGGTATGAGCTATTATTGTAAATCACATAAACCACCCATTAGTTTTCCATTGTGTGCTAATGGACAAGTTTTTGGTTTATATAAAAATACATGTGTTGGTAGCGATAATGTTACTGACTTTAATGCAATTGCAACATGTGACTGGACAAATGCTGGTGATTACATTTTAGCTAACACCTGTACTGAAAGACTCAAGCTTTTTGCAGCAGAAACGCTCAAAGCTACTGAGGAGACATTTAAACTGTCTTATGGTATTGCTACTGTACGTGAAGTGCTGTCTGACAGAGAATTACATCTTTCATGGGAAGTTGGTAAACCTAGACCACCACTTAACCGAAATTATGTCTTTACTGGTTATCGTGTAACTAAAAACAGTAAAGTACAAATAGGAGAGTACACCTTTGAAAAAGGTGACTATGGTGATGCTGTTGTTTACCGAGGTACAACAACTTACAAATTAAATGTTGGTGATTATTTTGTGCTGACATCACATACAGTAATGCCATTAAGTGCACCTACACTAGTGCCACAAGAGCACTATGTTAGAATTACTGGCTTATACCCAACACTCAATATCTCAGATGAGTTTTCTAGCAATGTTGCAAATTATCAAAAGGTTGGTATGCAAAAGTATTCTACACTCCAGGGACCACCTGGTACTGGTAAGAGTCATTTTGCTATTGGCCTAGCTCTCTACTACCCTTCTGCTCGCATAGTGTATACAGCTTGCTCTCATGCCGCTGTTGATGCACTATGTGAGAAGGCATTAAAATATTTGCCTATAGATAAATGTAGTAGAATTATACCTGCACGTGCTCGTGTAGAGTGTTTTGATAAATTCAAAGTGAATTCAACATTAGAACAGTATGTCTTTTGTACTGTAAATGCATTGCCTGAGACGACAGCAGATATAGTTGTCTTTGATGAAATTTCAATGGCCACAAATTATGATTTGAGTGTTGTCAATGCCAGATTACGTGCTAAGCACTATGTGTACATTGGCGACCCTGCTCAATTACCTGCACCACGCACATTGCTAACTAAGGGCACACTAGAACCAGAATATTTCAATTCAGTGTGTAGACTTATGAAAACTATAGGTCCAGACATGTTCCTCGGAACTTGTCGGCGTTGTCCTGCTGAAATTGTTGACACTGTGAGTGCTTTGGTTTATGATAATAAGCTTAAAGCACATAAAGACAAATCAGCTCAATGCTTTAAAATGTTTTATAAGGGTGTTATCACGCATGATGTTTCATCTGCAATTAACAGGCCACAAATAGGCGTGGTAAGAGAATTCCTTACACGTAACCCTGCTTGGAGAAAAGCTGTCTTTATTTCACCTTATAATTCACAGAATGCTGTAGCCTCAAAGATTTTGGGACTACCAACTCAAACTGTTGATTCATCACAGGGCTCAGAATATGACTATGTCATATTCACTCAAACCACTGAAACAGCTCACTCTTGTAATGTAAACAGATTTAATGTTGCTATTACCAGAGCAAAAGTAGGCATACTTTGCATAATGTCTGATAGAGACCTTTATGACAAGTTGCAATTTACAAGTCTTGAAATTCCACGTAGGAATGTGGCAACTTTACAAGCTGAAAATGTAACAGGACTCTTTAAAGATTGTAGTAAGGTAATCACTGGGTTACATCCTACACAGGCACCTACACACCTCAGTGTTGACACTAAATTCAAAACTGAAGGTTTATGTGTTGACATACCTGGCATACCTAAGGACATGACCTATAGAAGACTCATCTCTATGATGGGTTTTAAAATGAATTATCAAGTTAATGGTTACCCTAACATGTTTATCACCCGCGAAGAAGCTATAAGACATGTACGTGCATGGATTGGCTTCGATGTCGAGGGGTGTCATGCTACTAGAGAAGCTGTTGGTACCAATTTACCTTTACAGCTAGGTTTTTCTACAGGTGTTAACCTAGTTGCTGTACCTACAGGTTATGTTGATACACCTAATAATACAGATTTTTCCAGAGTTAGTGCTAAACCACCGCCTGGAGATCAATTTAAACACCTCATACCACTTATGTACAAAGGACTTCCTTGGAATGTAGTGCGTATAAAGATTGTACAAATGTTAAGTGACACACTTAAAAATCTCTCTGACAGAGTCGTATTTGTCTTATGGGCACATGGCTTTGAGTTGACATCTATGAAGTATTTTGTGAAAATAGGACCTGAGCGCACCTGTTGTCTATGTGATAGACGTGCCACATGCTTTTCCACTGCTTCAGACACTTATGCCTGTTGGCATCATTCTATTGGATTTGATTACGTCTATAATCCGTTTATGATTGATGTTCAACAATGGGGTTTTACAGGTAACCTACAAAGCAACCATGATCTGTATTGTCAAGTCCATGGTAATGCACATGTAGCTAGTTGTGATGCAATCATGACTAGGTGTCTAGCTGTCCACGAGTGCTTTGTTAAGCGTGTTGACTGGACTATTGAATATCCTATAATTGGTGATGAACTGAAGATTAATGCGGCTTGTAGAAAGGTTCAACACATGGTTGTTAAAGCTGCATTATTAGCAGACAAATTCCCAGTTCTTCACGACATTGGTAACCCTAAAGCTATTAAGTGTGTACCTCAAGCTGATGTAGAATGGAAGTTCTATGATGCACAGCCTTGTAGTGACAAAGCTTATAAAATAGAAGAATTATTCTATTCTTATGCCACACATTCTGACAAATTCACAGATGGTGTATGCCTATTTTGGAATTGCAATGTCGATAGATATCCTGCTAATTCCATTGTTTGTAGATTTGACACTAGAGTGCTATCTAACCTTAACTTGCCTGGTTGTGATGGTGGCAGTTTGTATGTAAATAAACATGCATTCCACACACCAGCTTTTGATAAAAGTGCTTTTGTTAATTTAAAACAATTACCATTTTTCTATTACTCTGACAGTCCATGTGAGTCTCATGGAAAACAAGTAGTGTCAGATATAGATTATGTACCACTAAAGTCTGCTACGTGTATAACACGTTGCAATTTAGGTGGTGCTGTCTGTAGACATCATGCTAATGAGTACAGATTGTATCTCGATGCTTATAACATGATGATCTCAGCTGGCTTTAGCTTGTGGGTTTACAAACAATTTGATACTTATAACCTCTGGAACACTTTTACAAGACTTCAGAGTTTAGAAAATGTGGCTTTTAATGTTGTAAATAAGGGACACTTTGATGGACAACAGGGTGAAGTACCAGTTTCTATCATTAATAACACTGTTTACACAAAAGTTGATGGTGTTGATGTAGAATTGTTTGAAAATAAAACAACATTACCTGTTAATGTAGCATTTGAGCTTTGGGCTAAGCGCAACATTAAACCAGTACCAGAGGTGAAAATACTCAATAATTTGGGTGTGGACATTGCTGCTAATACTGTGATCTGGGACTACAAAAGAGATGCTCCAGCACATATATCTACTATTGGTGTTTGTTCTATGACTGACATAGCCAAGAAACCAACTGAAACGATTTGTGCACCACTCACTGTCTTTTTTGATGGTAGAGTTGATGGTCAAGTAGACTTATTTAGAAATGCCCGTAATGGTGTTCTTATTACAGAAGGTAGTGTTAAAGGTTTACAACCATCTGTAGGTCCCAAACAAGCTAGTCTTAATGGAGTCACATTAATTGGAGAAGCCGTAAAAACACAGTTCAATTATTATAAGAAAGTTGATGGTGTTGTCCAACAATTACCTGAAACTTACTTTACTCAGAGTAGAAATTTACAAGAATTTAAACCCAGGAGTCAAATGGAAATTGATTTCTTAGAATTAGCTATGGATGAATTCATTGAACGGTATAAATTAGAAGGCTATGCCTTCGAACATATCGTTTATGGAGATTTTAGTCATAGTCAGTTAGGTGGTTTACATCTACTGATTGGACTAGCTAAACGTTTTAAGGAATCACCTTTTGAATTAGAAGATTTTATTCCTATGGACAGTACAGTTAAAAACTATTTCATAACAGATGCGCAAACAGGTTCATCTAAGTGTGTGTGTTCTGTTATTGATTTATTACTTGATGATTTTGTTGAAATAATAAAATCCCAAGATTTATCTGTAGTTTCTAAGGTTGTCAAAGTGACTATTGACTATACAGAAATTTCATTTATGCTTTGGTGTAAAGATGGCCATGTAGAAACATTTTACCCAAAATTACAATCTAGTCAAGCGTGGCAACCGGGTGTTGCTATGCCTAATCTTTACAAAATGCAAAGAATGCTATTAGAAAAGTGTGACCTTCAAAATTATGGTGATAGTGCAACATTACCTAAAGGCATAATGATGAATGTCGCAAAATATACTCAACTGTGTCAATATTTAAACACATTAACATTAGCTGTACCCTATAATATGAGAGTTATACATTTTGGTGCTGGTTCTGATAAAGGAGTTGCACCAGGTACAGCTGTTTTAAGACAGTGGTTGCCTACGGGTACGCTGCTTGTCGATTCAGATCTTAATGACTTTGTCTCTGATGCAGATTCAACTTTGATTGGTGATTGTGCAACTGTACATACAGCTAATAAATGGGATCTCATTATTAGTGATATGTACGACCCTAAGACTAAAAATGTTACAAAAGAAAATGACTCTAAAGAGGGTTTTTTCACTTACATTTGTGGGTTTATACAACAAAAGCTAGCTCTTGGAGGTTCCGTGGCTATAAAGATAACAGAACATTCTTGGAATGCTGATCTTTATAAGCTCATGGGACACTTCGCATGGTGGACAGCCTTTGTTACTAATGTGAATGCGTCATCATCTGAAGCATTTTTAATTGGATGTAATTATCTTGGCAAACCACGCGAACAAATAGATGGTTATGTCATGCATGCAAATTACATATTTTGGAGGAATACAAATCCAATTCAGTTGTCTTCCTATTCTTTATTTGACATGAGTAAATTTCCCCTTAAATTAAGGGGTACTGCTGTTATGTCTTTAAAAGAAGGTCAAATCAATGATATGATTTTATCTCTTCTTAGTAAAGGTAGACTTATAATTAGAGAAAACAACAGAGTTGTTATTTCTAGTGATGTTCTTGTTAACAACTAAACGAACAATGTTTGTTTTTCTTGTTTTATTGCCACTAGTCTCTAGTCAGTGTGTTAATCTTACAACCAGAACTCAATTACCCCCTGCATACACTAATTCTTTCACACGTGGTGTTTATTACCCTGACAAAGTTTTCAGATCCTCAGTTTTACATTCAACTCAGGACTTGTTCTTACCTTTCTTTTCCAATGTTACTTGGTTCCATGCTATACATGTCTCTGGGACCAATGGTACTAAGAGGTTTGATAACCCTGTCCTACCATTTAATGATGGTGTTTATTTTGCTTCCACTGAGAAGTCTAACATAATAAGAGGCTGGATTTTTGGTACTACTTTAGATTCGAAGACCCAGTCCCTACTTATTGTTAATAACGCTACTAATGTTGTTATTAAAGTCTGTGAATTTCAATTTTGTAATGATCCATTTTTGGGTGTTTATTACCACAAAAACAACAAAAGTTGGATGGAAAGTGAGTTCAGAGTTTATTCTAGTGCGAATAATTGCACTTTTGAATATGTCTCTCAGCCTTTTCTTATGGACCTTGAAGGAAAACAGGGTAATTTCAAAAATCTTAGGGAATTTGTGTTTAAGAATATTGATGGTTATTTTAAAATATATTCTAAGCACACGCCTATTAATTTAGTGCGTGATCTCCCTCAGGGTTTTTCGGCTTTAGAACCATTGGTAGATTTGCCAATAGGTATTAACATCACTAGGTTTCAAACTTTACTTGCTTTACATAGAAGTTATTTGACTCCTGGTGATTCTTCTTCAGGTTGGACAGCTGGTGCTGCAGCTTATTATGTGGGTTATCTTCAACCTAGGACTTTTCTATTAAAATATAATGAAAATGGAACCATTACAGATGCTGTAGACTGTGCACTTGACCCTCTCTCAGAAACAAAGTGTACGTTGAAATCCTTCACTGTAGAAAAAGGAATCTATCAAACTTCTAACTTTAGAGTCCAACCAACAGAATCTATTGTTAGATTTCCTAATATTACAAACTTGTGCCCTTTTGGTGAAGTTTTTAACGCCACCAGATTTGCATCTGTTTATGCTTGGAACAGGAAGAGAATCAGCAACTGTGTTGCTGATTATTCTGTCCTATATAATTCCGCATCATTTTCCACTTTTAAGTGTTATGGAGTGTCTCCTACTAAATTAAATGATCTCTGCTTTACTAATGTCTATGCAGATTCATTTGTAATTAGAGGTGATGAAGTCAGACAAATCGCTCCAGGGCAAACTGGAAAGATTGCTGATTATAATTATAAATTACCAGATGATTTTACAGGCTGCGTTATAGCTTGGAATTCTAACAATCTTGATTCTAAGGTTGGTGGTAATTATAATTACCTGTATAGATTGTTTAGGAAGTCTAATCTCAAACCTTTTGAGAGAGATATTTCAACTGAAATCTATCAGGCCGGTAGCACACCTTGTAATGGTGTTGAAGGTTTTAATTGTTACTTTCCTTTACAATCATATGGTTTCCAACCCACTAATGGTGTTGGTTACCAACCATACAGAGTAGTAGTACTTTCTTTTGAACTTCTACATGCACCAGCAACTGTTTGTGGACCTAAAAAGTCTACTAATTTGGTTAAAAACAAATGTGTCAATTTCAACTTCAATGGTTTAACAGGCACAGGTGTTCTTACTGAGTCTAACAAAAAGTTTCTGCCTTTCCAACAATTTGGCAGAGACATTGCTGACACTACTGATGCTGTCCGTGATCCACAGACACTTGAGATTCTTGACATTACACCATGTTCTTTTGGTGGTGTCAGTGTTATAACACCAGGAACAAATACTTCTAACCAGGTTGCTGTTCTTTATCAGGATGTTAACTGCACAGAAGTCCCTGTTGCTATTCATGCAGATCAACTTACTCCTACTTGGCGTGTTTATTCTACAGGTTCTAATGTTTTTCAAACACGTGCAGGCTGTTTAATAGGGGCTGAACATGTCAACAACTCATATGAGTGTGACATACCCATTGGTGCAGGTATATGCGCTAGTTATCAGACTCAGACTAATTCTCCTCGGCGGGCACGTAGTGTAGCTAGTCAATCCATCATTGCCTACACTATGTCACTTGGTGCAGAAAATTCAGTTGCTTACTCTAATAACTCTATTGCCATACCCACAAATTTTACTATTAGTGTTACCACAGAAATTCTACCAGTGTCTATGACCAAGACATCAGTAGATTGTACAATGTACATTTGTGGTGATTCAACTGAATGCAGCAATCTTTTGTTGCAATATGGCAGTTTTTGTACACAATTAAACCGTGCTTTAACTGGAATAGCTGTTGAACAAGACAAAAACACCCAAGAAGTTTTTGCACAAGTCAAACAAATTTACAAAACACCACCAATTAAAGATTTTGGTGGTTTTAATTTTTCACAAATATTACCAGATCCATCAAAACCAAGCAAGAGGTCATTTATTGAAGATCTACTTTTCAACAAAGTGACACTTGCAGATGCTGGCTTCATCAAACAATATGGTGATTGCCTTGGTGATATTGCTGCTAGAGACCTCATTTGTGCACAAAAGTTTAACGGCCTTACTGTTTTGCCACCTTTGCTCACAGATGAAATGATTGCTCAATACACTTCTGCACTGTTAGCGGGTACAATCACTTCTGGTTGGACCTTTGGTGCAGGTGCTGCATTACAAATACCATTTGCTATGCAAATGGCTTATAGGTTTAATGGTATTGGAGTTACACAGAATGTTCTCTATGAGAACCAAAAATTGATTGCCAACCAATTTAATAGTGCTATTGGCAAAATTCAAGACTCACTTTCTTCCACAGCAAGTGCACTTGGAAAACTTCAAGATGTGGTCAACCAAAATGCACAAGCTTTAAACACGCTTGTTAAACAACTTAGCTCCAATTTTGGTGCAATTTCAAGTGTTTTAAATGATATCCTTTCACGTCTTGACAAAGTTGAGGCTGAAGTGCAAATTGATAGGTTGATCACAGGCAGACTTCAAAGTTTGCAGACATATGTGACTCAACAATTAATTAGAGCTGCAGAAATCAGAGCTTCTGCTAATCTTGCTGCTACTAAAATGTCAGAGTGTGTACTTGGACAATCAAAAAGAGTTGATTTTTGTGGAAAGGGCTATCATCTTATGTCCTTCCCTCAGTCAGCACCTCATGGTGTAGTCTTCTTGCATGTGACTTATGTCCCTGCACAAGAAAAGAACTTCACAACTGCTCCTGCCATTTGTCATGATGGAAAAGCACACTTTCCTCGTGAAGGTGTCTTTGTTTCAAATGGCACACACTGGTTTGTAACACAAAGGAATTTTTATGAACCACAAATCATTACTACAGACAACACATTTGTGTCTGGTAACTGTGATGTTGTAATAGGAATTGTCAACAACACAGTTTATGATCCTTTGCAACCTGAATTAGACTCATTCAAGGAGGAGTTAGATAAATATTTTAAGAATCATACATCACCAGATGTTGATTTAGGTGACATCTCTGGCATTAATGCTTCAGTTGTAAACATTCAAAAAGAAATTGACCGCCTCAATGAGGTTGCCAAGAATTTAAATGAATCTCTCATCGATCTCCAAGAACTTGGAAAGTATGAGCAGTATATAAAATGGCCATGGTACATTTGGCTAGGTTTTATAGCTGGCTTGATTGCCATAGTAATGGTGACAATTATGCTTTGCTGTATGACCAGTTGCTGTAGTTGTCTCAAGGGCTGTTGTTCTTGTGGATCCTGCTGCAAATTTGATGAAGACGACTCTGAGCCAGTGCTCAAAGGAGTCAAATTACATTACACATAAACGAACTTATGGATTTGTTTATGAGAATCTTCACAATTGGAACTGTAACTTTGAAGCAAGGTGAAATCAAGGATGCTACTCCTTCAGATTTTGTTCGCGCTACTGCAACGATACCGATACAAGCCTCACTCCCTTTCGGATGGCTTATTGTTGGCGTTGCACTTCTTGCTGTTTTTCAGAGCGCTTCCAAAATCATAACCCTCAAAAAGAGATGGCAACTAGCACTCTCCAAGGGTGTTCACTTTGTTTGCAACTTGCTGTTGTTGTTTGTAACAGTTTACTCACACCTTTTGCTCGTTGCTGCTGGCCTTGAAGCCCCTTTTCTCTATCTTTATGCTTTAGTCTACTTCTTGCAGAGTATAAACTTTGTAAGAATAATAATGAGGCTTTGGCTTTGCTGGAAATGCCGTTCCAAAAACCCATTACTTTATGATGCCAACTATTTTCTTTGCTGGCATACTAATTGTTACGACTATTGTATACCTTACAATAGTGTAACTTCTTCAATTGTCATTACTTCAGGTGATGGCACAACAAGTCCTATTTCTGAACATGACTACCAGATTGGTGGTTATACTGAAAAATGGGAATCTGGAGTAAAAGACTGTGTTGTATTACACAGTTACTTCACTTCAGACTATTACCAGCTGTACTCAACTCAATTGAGTACAGACACTGGTGTTGAACATGTTACCTTCTTCATCTACAATAAAATTGTTGATGAGCCTGAAGAACATGTCCAAATTCACACAATCGACGGTTCATCCGGAGTTGTTAATCCAGTAATGGAACCAATTTATGATGAACCGACGACGACTACTAGCGTGCCTTTGTAAGCACAAGCTGATGAGTACGAACTTATGTACTCATTCGTTTCGGAAGAGACAGGTACGTTAATAGTTAATAGCGTACTTCTTTTTCTTGCTTTCGTGGTATTCTTGCTAGTTACACTAGCCATCCTTACTGCGCTTCGATTGTGTGCGTACTGCTGCAATATTGTTAACGTGAGTCTTGTAAAACCTTCTTTTTACGTTTACTCTCGTGTTAAAAATCTGAATTCTTCTAGAGTTCCTGATCTTCTGGTCTAAACGAACTAAATATTATATTAGTTTTTCTGTTTGGAACTTTAATTTTAGCCATGGCAGATTCCAACGGTACTATTACCGTTGAAGAGCTTAAAAAGCTCCTTGAACAATGGAACCTAGTAATAGGTTTCCTATTCCTTACATGGATTTGTCTTCTACAATTTGCCTATGCCAACAGGAATAGGTTTTTGTATATAATTAAGTTAATTTTCCTCTGGCTGTTATGGCCAGTAACTTTAGCTTGTTTTGTGCTTGCTGCTGTTTACAGAATAAATTGGATCACCGGTGGAATTGCTATCGCAATGGCTTGTCTTGTAGGCTTGATGTGGCTCAGCTACTTCATTGCTTCTTTCAGACTGTTTGCGCGTACGCGTTCCATGTGGTCATTCAATCCAGAAACTAACATTCTTCTCAACGTGCCACTCCATGGCACTATTCTGACCAGACCGCTTCTAGAAAGTGAACTCGTAATCGGAGCTGTGATCCTTCGTGGACATCTTCGTATTGCTGGACACCATCTAGGACGCTGTGACATCAAGGACCTGCCTAAAGAAATCACTGTTGCTACATCACGAACGCTTTCTTATTACAAATTGGGAGCTTCGCAGCGTGTAGCAGGTGACTCAGGTTTTGCTGCATACAGTCGCTACAGGATTGGCAACTATAAATTAAACACAGACCATTCCAGTAGCAGTGACAATATTGCTTTGCTTGTACAGTAAGTGACAACAGATGTTTCATCTCGTTGACTTTCAGGTTACTATAGCAGAGATATTACTAATTATTATGAGGACTTTTAAAGTTTCCATTTGGAATCTTGATTACATCATAAACCTCATAATTAAAAATTTATCTAAGTCACTAACTGAGAATAAATATTCTCAATTAGATGAAGAGCAACCAATGGAGATTGATTAAACGAACATGAAAATTATTCTTTTCTTGGCACTGATAACACTCGCTACTTGTGAGCTTTATCACTACCAAGAGTGTGTTAGAGGTACAACAGTACTTTTAAAAGAACCTTGCTCTTCTGGAACATACGAGGGCAATTCACCATTTCATCCTCTAGCTGATAACAAATTTGCACTGACTTGCTTTAGCACTCAATTTGCTTTTGCTTGTCCTGACGGCGTAAAACACGTCTATCAGTTACGTGCCAGATCAGTTTCACCTAAACTGTTCATCAGACAAGAGGAAGTTCAAGAACTTTACTCTCCAATTTTTCTTATTGTTGCGGCAATAGTGTTTATAACACTTTGCTTCACACTCAAAAGAAAGACAGAATGATTGAACTTTCATTAATTGACTTCTATTTGTGCTTTTTAGCCTTTCTGCTATTCCTTGTTTTAATTATGCTTATTATCTTTTGGTTCTCACTTGAACTGCAAGATCATAATGAAACTTGTCACGCCTAAACGAACATGAAATTTCTTGTTTTCTTAGGAATCATCACAACTGTAGCTGCATTTCACCAAGAATGTAGTTTACAGTCATGTACTCAACATCAACCATATGTAGTTGATGACCCGTGTCCTATTCACTTCTATTCTAAATGGTATATTAGAGTAGGAGCTAGAAAATCAGCACCTTTAATTGAATTGTGCGTGGATGAGGCTGGTTCTAAATCACCCATTCAGTACATCGATATCGGTAATTATACAGTTTCCTGTTTACCTTTTACAATTAATTGCCAGGAACCTAAATTGGGTAGTCTTGTAGTGCGTTGTTCGTTCTATGAAGACTTTTTAGAGTATCATGACGTTCGTGTTGTTTTAGATTTCATCTAAACGAACAAACTAAAATGTCTGATAATGGACCCCAAAATCAGCGAAATGCACCCCGCATTACGTTTGGTGGACCCTCAGATTCAACTGGCAGTAACCAGAATGGAGAACGCAGTGGGGCGCGATCAAAACAACGTCGGCCCCAAGGTTTACCCAATAATACTGCGTCTTGGTTCACCGCTCTCACTCAACATGGCAAGGAAGACCTTAAATTCCCTCGAGGACAAGGCGTTCCAATTAACACCAATAGCAGTCCAGATGACCAAATTGGCTACTACCGAAGAGCTACCAGACGAATTCGTGGTGGTGACGGTAAAATGAAAGATCTCAGTCCAAGATGGTATTTCTACTACCTAGGAACTGGGCCAGAAGCTGGACTTCCCTATGGTGCTAACAAAGACGGCATCATATGGGTTGCAACTGAGGGAGCCTTGAATACACCAAAAGATCACATTGGCACCCGCAATCCTGCTAACAATGCTGCAATCGTGCTACAACTTCCTCAAGGAACAACATTGCCAAAAGGCTTCTACGCAGAAGGGAGCAGAGGCGGCAGTCAAGCCTCTTCTCGTTCCTCATCACGTAGTCGCAACAGTTCAAGAAATTCAACTCCAGGCAGCAGTAGGGGAACTTCTCCTGCTAGAATGGCTGGCAATGGCGGTGATGCTGCTCTTGCTTTGCTGCTGCTTGACAGATTGAACCAGCTTGAGAGCAAAATGTCTGGTAAAGGCCAACAACAACAAGGCCAAACTGTCACTAAGAAATCTGCTGCTGAGGCTTCTAAGAAGCCTCGGCAAAAACGTACTGCCACTAAAGCATACAATGTAACACAAGCTTTCGGCAGACGTGGTCCAGAACAAACCCAAGGAAATTTTGGGGACCAGGAACTAATCAGACAAGGAACTGATTACAAACATTGGCCGCAAATTGCACAATTTGCCCCCAGCGCTTCAGCGTTCTTCGGAATGTCGCGCATTGGCATGGAAGTCACACCTTCGGGAACGTGGTTGACCTACACAGGTGCCATCAAATTGGATGACAAAGATCCAAATTTCAAAGATCAAGTCATTTTGCTGAATAAGCATATTGACGCATACAAAACATTCCCACCAACAGAGCCTAAAAAGGACAAAAAGAAGAAGGCTGATGAAACTCAAGCCTTACCGCAGAGACAGAAGAAACAGCAAACTGTGACTCTTCTTCCTGCTGCAGATTTGGATGATTTCTCCAAACAATTGCAACAATCCATGAGCAGTGCTGACTCAACTCAGGCCTAAACTCATGCAGACCACACAAGGCAGATGGGCTATATAAACGTTTTCGCTTTTCCGTTTACGATATATAGTCTACTCTTGTGCAGAATGAATTCTCGTAACTACATAGCACAAGTAGATGTAGTTAACTTTAATCTCACATAGCAATCTTTAATCAGTGTGTAACATTAGGGAGGACTTGAAAGAGCCACCACATTTTCACCGAGGCCACGCGGAGTACGATCGAGTGTACAGTGAACAATGCTAGGGAGAGCTGCCTATATGGAAGAGCCCTAATGTGTAAAATTAATTTTAGTAGTGCTATCCCCATGTGATTTTAATAGCTTCTTAGGAGAATGACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + genes: [] +displayName: Test organism (without alignment) + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-not-aligned-organism:8080" diff --git a/kubernetes/loculus/fixtures/organisms/west-nile.yaml b/kubernetes/loculus/fixtures/organisms/west-nile.yaml new file mode 100644 index 0000000000..3fb97fe282 --- /dev/null +++ b/kubernetes/loculus/fixtures/organisms/west-nile.yaml @@ -0,0 +1,1620 @@ +schema: + submissionDataTypes: + consensusSequences: true + maxSequencesPerEntry: 1 + loadSequencesAutomatically: true + earliestReleaseDate: + enabled: true + externalFields: + - ncbiReleaseDate + richFastaHeaderFields: + - displayName + files: + - name: annotations + displayName: Annotations + metadata: + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + header: Sample details + ingest: ncbiCollectionDate + order: 10 + orderOnDetailsPage: 200 + includeInDownloadsByDefault: true + notSearchable: true + columnWidth: 100 + type: string + - name: sampleCollectionDateRangeLower + displayName: Collection date (lower bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: lower + noInput: true + - name: sampleCollectionDateRangeUpper + displayName: Collection date (upper bound) + type: date + initiallyVisible: true + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + header: Sample details + rangeOverlapSearch: + rangeName: sampleCollectionDateRange + rangeDisplayName: Collection date + bound: upper + noInput: true + - name: displayName + displayName: Display name + definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{accessionVersion}/{sampleCollectionDate}`. + noInput: true + type: string + - name: ncbiReleaseDate + displayName: NCBI release date + definition: Date the viral nucleotide accession was first released on the INSDC. + type: date + header: INSDC + orderOnDetailsPage: 1000 + noInput: true + columnWidth: 100 + - name: earliestReleaseDate + displayName: Earliest release date + definition: The earliest release date for the accession, across all versions in both Loculus and the INSDC. + header: Sample details + type: date + rangeSearch: true + noInput: true + includeInDownloadsByDefault: true + orderOnDetailsPage: 300 + - name: ncbiUpdateDate + type: date + displayName: NCBI update date + definition: Date the viral nucleotide accession was last updated on the INSDC. + header: INSDC + orderOnDetailsPage: 1020 + noInput: true + perSegment: true + oneHeader: true + columnWidth: 100 + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 420 + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + header: Sample details + type: float + orderOnDetailsPage: 440 + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 20 + orderOnDetailsPage: 400 + ingest: country + options: &id001 + - name: Afghanistan + - name: Albania + - name: Algeria + - name: American Samoa + - name: Andorra + - name: Angola + - name: Anguilla + - name: Antarctica + - name: Antigua and Barbuda + - name: Arctic Ocean + - name: Argentina + - name: Armenia + - name: Aruba + - name: Ashmore and Cartier Islands + - name: Atlantic Ocean + - name: Australia + - name: Austria + - name: Azerbaijan + - name: Bahamas + - name: Bahrain + - name: Baltic Sea + - name: Baker Island + - name: Bangladesh + - name: Barbados + - name: Bassas da India + - name: Belarus + - name: Belgium + - name: Belize + - name: Benin + - name: Bermuda + - name: Bhutan + - name: Bolivia + - name: Borneo + - name: Bosnia and Herzegovina + - name: Botswana + - name: Bouvet Island + - name: Brazil + - name: British Virgin Islands + - name: Brunei + - name: Bulgaria + - name: Burkina Faso + - name: Burundi + - name: Cambodia + - name: Cameroon + - name: Canada + - name: Cape Verde + - name: Cayman Islands + - name: Central African Republic + - name: Chad + - name: Chile + - name: China + - name: Christmas Island + - name: Clipperton Island + - name: Cocos Islands + - name: Colombia + - name: Comoros + - name: Cook Islands + - name: Coral Sea Islands + - name: Costa Rica + - name: Cote d'Ivoire + - name: Croatia + - name: Cuba + - name: Curacao + - name: Cyprus + - name: Czechia + - name: Democratic Republic of the Congo + - name: Denmark + - name: Djibouti + - name: Dominica + - name: Dominican Republic + - name: Ecuador + - name: Egypt + - name: El Salvador + - name: Equatorial Guinea + - name: Eritrea + - name: Estonia + - name: Eswatini + - name: Ethiopia + - name: Europa Island + - name: Falkland Islands (Islas Malvinas) + - name: Faroe Islands + - name: Fiji + - name: Finland + - name: France + - name: French Guiana + - name: French Polynesia + - name: French Southern and Antarctic Lands + - name: Gabon + - name: Gambia + - name: Gaza Strip + - name: Georgia + - name: Germany + - name: Ghana + - name: Gibraltar + - name: Glorioso Islands + - name: Greece + - name: Greenland + - name: Grenada + - name: Guadeloupe + - name: Guam + - name: Guatemala + - name: Guernsey + - name: Guinea + - name: Guinea-Bissau + - name: Guyana + - name: Haiti + - name: Heard Island and McDonald Islands + - name: Honduras + - name: Hong Kong + - name: Howland Island + - name: Hungary + - name: Iceland + - name: India + - name: Indian Ocean + - name: Indonesia + - name: Iran + - name: Iraq + - name: Ireland + - name: Isle of Man + - name: Israel + - name: Italy + - name: Jamaica + - name: Jan Mayen + - name: Japan + - name: Jarvis Island + - name: Jersey + - name: Johnston Atoll + - name: Jordan + - name: Juan de Nova Island + - name: Kazakhstan + - name: Kenya + - name: Kerguelen Archipelago + - name: Kingman Reef + - name: Kiribati + - name: Kosovo + - name: Kuwait + - name: Kyrgyzstan + - name: Laos + - name: Latvia + - name: Lebanon + - name: Lesotho + - name: Liberia + - name: Libya + - name: Liechtenstein + - name: Line Islands + - name: Lithuania + - name: Luxembourg + - name: Macau + - name: Madagascar + - name: Malawi + - name: Malaysia + - name: Maldives + - name: Mali + - name: Malta + - name: Marshall Islands + - name: Martinique + - name: Mauritania + - name: Mauritius + - name: Mayotte + - name: Mediterranean Sea + - name: Mexico + - name: Micronesia, Federated States of + - name: Midway Islands + - name: Moldova + - name: Monaco + - name: Mongolia + - name: Montenegro + - name: Montserrat + - name: Morocco + - name: Mozambique + - name: Myanmar + - name: Namibia + - name: Nauru + - name: Navassa Island + - name: Nepal + - name: Netherlands + - name: New Caledonia + - name: New Zealand + - name: Nicaragua + - name: Niger + - name: Nigeria + - name: Niue + - name: Norfolk Island + - name: North Korea + - name: North Macedonia + - name: North Sea + - name: Northern Mariana Islands + - name: Norway + - name: Oman + - name: Pacific Ocean + - name: Pakistan + - name: Palau + - name: Palmyra Atoll + - name: Panama + - name: Papua New Guinea + - name: Paracel Islands + - name: Paraguay + - name: Peru + - name: Philippines + - name: Pitcairn Islands + - name: Poland + - name: Portugal + - name: Puerto Rico + - name: Qatar + - name: Republic of the Congo + - name: Reunion + - name: Romania + - name: Ross Sea + - name: Russia + - name: Rwanda + - name: Saint Barthelemy + - name: Saint Helena + - name: Saint Kitts and Nevis + - name: Saint Lucia + - name: Saint Martin + - name: Saint Pierre and Miquelon + - name: Saint Vincent and the Grenadines + - name: Samoa + - name: San Marino + - name: Sao Tome and Principe + - name: Saudi Arabia + - name: Senegal + - name: Serbia + - name: Seychelles + - name: Sierra Leone + - name: Singapore + - name: Sint Maarten + - name: Slovakia + - name: Slovenia + - name: Solomon Islands + - name: Somalia + - name: South Africa + - name: South Georgia and the South Sandwich Islands + - name: South Korea + - name: South Sudan + - name: Southern Ocean + - name: Spain + - name: Spratly Islands + - name: Sri Lanka + - name: State of Palestine + - name: Sudan + - name: Suriname + - name: Svalbard + - name: Sweden + - name: Switzerland + - name: Syria + - name: Taiwan + - name: Tajikistan + - name: Tanzania + - name: Tasman Sea + - name: Thailand + - name: Timor-Leste + - name: Togo + - name: Tokelau + - name: Tonga + - name: Trinidad and Tobago + - name: Tromelin Island + - name: Tunisia + - name: Turkey + - name: Turkmenistan + - name: Turks and Caicos Islands + - name: Tuvalu + - name: Uganda + - name: Ukraine + - name: United Arab Emirates + - name: United Kingdom + - name: Uruguay + - name: USA + - name: Uzbekistan + - name: Vanuatu + - name: Venezuela + - name: Viet Nam + - name: Virgin Islands + - name: Wake Island + - name: Wallis and Futuna + - name: West Bank + - name: Western Sahara + - name: Yemen + - name: Zambia + - name: Zimbabwe + - name: Belgian Congo + - name: British Guiana + - name: Burma + - name: Czechoslovakia + - name: Czech Republic + - name: East Timor + - name: Korea + - name: Macedonia + - name: Micronesia + - name: Netherlands Antilles + - name: Serbia and Montenegro + - name: Siam + - name: Swaziland + - name: The former Yugoslav Republic of Macedonia + - name: USSR + - name: Yugoslavia + - name: Zaire + type: string + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + generateIndex: true + autocomplete: true + initiallyVisible: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + order: 30 + orderOnDetailsPage: 460 + ingest: division + type: string + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + generateIndex: true + autocomplete: true + customDisplay: + type: geoLocation + displayGroup: geoLocation + label: Sampling location + header: Sample details + orderOnDetailsPage: 480 + type: string + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + generateIndex: true + autocomplete: true + header: Sample details + orderOnDetailsPage: 500 + type: string + - name: geoLocSite + ontology_id: GENEPIO:0100436 + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + header: Sample details + orderOnDetailsPage: 520 + type: string + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + header: Sample details + ingest: ncbiIsolateName + substringSearch: true + orderOnDetailsPage: 600 + type: string + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + type: authors + header: Authors + substringSearch: true + order: 40 + orderOnDetailsPage: 800 + includeInDownloadsByDefault: true + ingest: ncbiSubmitterNames + columnWidth: 140 + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + substringSearch: true + header: Authors + ingest: ncbiSubmitterAffiliation + includeInDownloadsByDefault: true + orderOnDetailsPage: 820 + type: string + - name: ncbiSubmitterCountry + displayName: NCBI submitter country + definition: The country representing the submitter's affilation. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: insdcAccessionBase + displayName: INSDC accession base + definition: The base accession assigned by the INSDC (GenBank/ENA/DDBJ) for this sequence, without the version number. + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + type: string + - name: insdcVersion + displayName: INSDC version + definition: The version number of the INSDC record, incremented each time the sequence record is updated. + type: int + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + perSegment: true + oneHeader: true + - name: insdcAccessionFull + displayName: INSDC accession + definition: The full versioned INSDC accession (base accession plus version number), searchable across GenBank, ENA, and DDBJ. + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/nuccore/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://getentry.ddbj.nig.ac.jp/getentry/na/__value__ + header: INSDC + orderOnDetailsPage: 1040 + ingest: genbankAccession + noInput: true + perSegment: true + oneHeader: true + type: string + - name: bioprojectAccession + displayName: BioProject accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/bioproject/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/bioproject/__value__ + header: INSDC + orderOnDetailsPage: 1060 + ingest: bioprojects + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + type: string + - name: gcaAccession + displayName: GCA accession + definition: The GenBank genome assembly accession number for the sequence. + customDisplay: + type: link + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + header: INSDC + orderOnDetailsPage: 1100 + noInput: true + oneHeader: true + type: string + - name: biosampleAccession + displayName: BioSample accession + customDisplay: + type: linkWithMenu + linkMenuItems: + - name: View in NCBI + url: https://www.ncbi.nlm.nih.gov/biosample/__value__ + - name: View in ENA + url: https://www.ebi.ac.uk/ena/browser/view/__value__ + - name: View in DDBJ + url: https://ddbj.nig.ac.jp/search/entry/biosample/__value__ + header: INSDC + orderOnDetailsPage: 1080 + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + oneHeader: true + type: string + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + header: Sample details + orderOnDetailsPage: 620 + type: string + - name: sampleReceivedDate + ontology_id: GENEPIO:0001177 + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + type: date + orderOnDetailsPage: 260 + header: Sample details + - name: sampleType + displayName: Sample type + definition: Method of sampling. + header: Sampling + orderOnDetailsPage: 1200 + type: string + - name: purposeOfSampling + displayName: Purpose of sampling + ontology_id: GENEPIO:0001198 + definition: The reason that the sample was collected. + header: Sampling + orderOnDetailsPage: 1220 + ingest: ncbiPurposeOfSampling + type: string + - name: presamplingActivity + ontology_id: GENEPIO:0100433 + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + header: Sampling + orderOnDetailsPage: 1240 + type: string + - name: anatomicalMaterial + ontology_id: GENEPIO:0001211 + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + header: Sampling + orderOnDetailsPage: 1260 + type: string + - name: anatomicalPart + ontology_id: GENEPIO:0001214 + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + header: Sampling + orderOnDetailsPage: 1280 + type: string + - name: bodyProduct + ontology_id: GENEPIO:0001216 + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + header: Sampling + orderOnDetailsPage: 1300 + type: string + - name: environmentalMaterial + ontology_id: GENEPIO:0001223 + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + header: Sampling + orderOnDetailsPage: 1320 + type: string + - name: environmentalSite + ontology_id: GENEPIO:0001232 + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + header: Sampling + orderOnDetailsPage: 1340 + type: string + - name: collectionDevice + ontology_id: GENEPIO:0001234 + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + header: Sampling + orderOnDetailsPage: 1360 + type: string + - name: collectionMethod + ontology_id: GENEPIO:0001241 + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + header: Sampling + orderOnDetailsPage: 1380 + type: string + - name: foodProduct + ontology_id: GENEPIO:0100444 + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + header: Sampling + orderOnDetailsPage: 1400 + type: string + - name: foodProductProperties + ontology_id: GENEPIO:0100445 + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + header: Sampling + orderOnDetailsPage: 1420 + type: string + - name: specimenProcessing + ontology_id: GENEPIO:0100435 + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + header: Specimen processing + orderOnDetailsPage: 1500 + type: string + - name: specimenProcessingDetails + ontology_id: GENEPIO:0100311 + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + header: Specimen processing + orderOnDetailsPage: 1520 + type: string + - name: experimentalSpecimenRoleType + ontology_id: GENEPIO:0100921 + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + header: Specimen processing + type: string + - name: hostAge + ontology_id: GENEPIO:0001392 + definition: Age of host at the time of sampling. + displayName: Host age + type: int + header: Host + rangeSearch: true + - name: hostAgeBin + ontology_id: GENEPIO:0001394 + definition: The age category of the host at the time of sampling. + displayName: Host age bin + header: Host + type: string + - name: hostGender + ontology_id: GENEPIO:0001395 + definition: The gender of the host at the time of sample collection. + displayName: Host gender + header: Host + ingest: ncbiHostSex + orderOnDetailsPage: 1640 + type: string + - name: hostOriginCountry + ontology_id: GENEPIO:0100438 + definition: The country of origin of the host. + displayName: Host origin country + header: Host + type: string + - name: hostDisease + ontology_id: GENEPIO:0001391 + definition: The name of the disease experienced by the host. + displayName: Host disease + header: Host + type: string + - name: signsAndSymptoms + ontology_id: GENEPIO:0001400 + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + header: Host + type: string + - name: hostHealthState + ontology_id: GENEPIO:0001388 + definition: Health status of the host at the time of sample collection. + displayName: Host health state + header: Host + type: string + - name: hostHealthOutcome + ontology_id: GENEPIO:0001390 + definition: Disease outcome in the host. + displayName: Host health outcome + header: Host + orderOnDetailsPage: 1740 + type: string + - name: travelHistory + ontology_id: GENEPIO:0001416 + definition: Travel history in last six months. + displayName: Travel history + header: Host + orderOnDetailsPage: 1760 + type: string + - name: exposureEvent + ontology_id: GENEPIO:0001417 + definition: Event leading to exposure. + displayName: Exposure event + header: Host + orderOnDetailsPage: 1780 + type: string + - name: hostRole + ontology_id: GENEPIO:0001419 + definition: The role of the host in relation to the exposure setting. + displayName: Host role + header: Host + orderOnDetailsPage: 1800 + type: string + - name: exposureSetting + ontology_id: GENEPIO:0001428 + definition: The setting leading to exposure. + displayName: Exposure setting + header: Host + orderOnDetailsPage: 1820 + type: string + - name: exposureDetails + ontology_id: GENEPIO:0001431 + definition: Additional host exposure information. + displayName: Exposure details + header: Host + orderOnDetailsPage: 1840 + type: string + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + header: Host + orderOnDetailsPage: 1860 + type: string + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + header: Host + orderOnDetailsPage: 1880 + type: string + - name: hostVaccinationStatus + ontology_id: GENEPIO:0001404 + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + header: Host + orderOnDetailsPage: 1900 + type: string + - name: purposeOfSequencing + ontology_id: GENEPIO:0001445 + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + header: Sequencing + orderOnDetailsPage: 2000 + type: string + - name: sequencingDate + ontology_id: GENEPIO:0001447 + definition: The date the sample was sequenced. + displayName: Sequencing date + type: date + orderOnDetailsPage: 2020 + header: Sequencing + - name: ampliconPcrPrimerScheme + ontology_id: GENEPIO:0001456 + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + header: Sequencing + orderOnDetailsPage: 2040 + type: string + - name: ampliconSize + ontology_id: GENEPIO:0001449 + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + header: Sequencing + orderOnDetailsPage: 2060 + type: string + - name: sequencingInstrument + ontology_id: GENEPIO:0001452 + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + header: Sequencing + orderOnDetailsPage: 2080 + type: string + - name: sequencingProtocol + ontology_id: GENEPIO:0001454 + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + header: Sequencing + orderOnDetailsPage: 2100 + type: string + - name: sequencingAssayType + ontology_id: GENEPIO:0100997 + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + header: Sequencing + orderOnDetailsPage: 2120 + type: string + - name: sequencedByOrganization + ontology_id: GENEPIO:0100416 + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + header: Sequencing + orderOnDetailsPage: 2140 + type: string + - name: sequencedByContactName + ontology_id: GENEPIO:0100471 + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + header: Sequencing + orderOnDetailsPage: 2160 + type: string + - name: sequencedByContactEmail + ontology_id: GENEPIO:0100422 + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + header: Sequencing + orderOnDetailsPage: 2180 + type: string + - name: rawSequenceDataProcessingMethod + ontology_id: GENEPIO:0001458 + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + header: Sequencing + orderOnDetailsPage: 2200 + type: string + - name: dehostingMethod + ontology_id: GENEPIO:0001459 + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + header: Sequencing + orderOnDetailsPage: 2220 + type: string + - name: referenceGenomeAccession + ontology_id: GENEPIO:0001485 + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + header: Sequencing + orderOnDetailsPage: 2240 + type: string + - name: consensusSequenceSoftwareName + ontology_id: GENEPIO:0001463 + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + header: Sequencing + orderOnDetailsPage: 2260 + type: string + - name: consensusSequenceSoftwareVersion + ontology_id: GENEPIO:0001469 + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + header: Sequencing + orderOnDetailsPage: 2280 + type: string + - name: depthOfCoverage + ontology_id: GENEPIO:0001474 + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2300 + - name: breadthOfCoverage + ontology_id: GENEPIO:0001475 + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + type: int + header: Sequencing + orderOnDetailsPage: 2320 + - name: qualityControlMethodName + ontology_id: GENEPIO:0100557 + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + header: Sequencing + orderOnDetailsPage: 2340 + type: string + - name: qualityControlMethodVersion + ontology_id: GENEPIO:0100558 + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + header: Sequencing + orderOnDetailsPage: 2360 + type: string + - name: qualityControlDetermination + ontology_id: GENEPIO:0100559 + definition: The determination of a quality control assessment. + displayName: Quality control determination + header: Sequencing + orderOnDetailsPage: 2380 + type: string + - name: qualityControlIssues + ontology_id: GENEPIO:0100560 + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + header: Sequencing + orderOnDetailsPage: 2400 + type: string + - name: qualityControlDetails + ontology_id: GENEPIO:0100561 + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + header: Diagnostics + orderOnDetailsPage: 2500 + type: string + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + header: Diagnostics + orderOnDetailsPage: 2520 + type: string + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + header: Diagnostics + orderOnDetailsPage: 2540 + type: string + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + header: Diagnostics + orderOnDetailsPage: 2560 + type: string + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + header: Diagnostics + orderOnDetailsPage: 2580 + type: string + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + orderOnDetailsPage: 2600 + header: Diagnostics + type: string + - name: length + type: int + header: Alignment and QC metrics + isSequenceFilter: true + noInput: true + rangeSearch: true + initiallyVisible: true + perSegment: true + displayName: Length + definition: The length of the sequence. + orderOnDetailsPage: 2700 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: hostTaxonId + type: string + autocomplete: true + displayName: Host species + definition: Taxon ID for the host. + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostTaxId + noInput: true + orderOnDetailsPage: 1540 + - name: hostNameScientific + displayName: Host name - scientific + definition: The scientific name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostName + noInput: true + orderOnDetailsPage: 1560 + type: string + - name: hostNameCommon + displayName: Host name - common + definition: The common name of the host from which the sample was collected. + generateIndex: true + autocomplete: true + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostCommonName + noInput: true + orderOnDetailsPage: 1580 + type: string + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + type: boolean + autocomplete: true + header: Host + ingest: ncbiIsLabHost + orderOnDetailsPage: 1920 + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + generateIndex: true + orderOnDetailsPage: 1940 + autocomplete: true + header: Host + type: string + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + type: int + header: Host + orderOnDetailsPage: 1960 + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + generateIndex: true + autocomplete: true + header: Host + orderOnDetailsPage: 1980 + type: string + - name: ncbiSourceDb + displayName: NCBI source DB + definition: Indicates if the source of the viral nucleotide record is from a GenBank submitter or from NCBI-derived curation (RefSeq). + generateIndex: true + autocomplete: true + header: INSDC + hideOnSequenceDetailsPage: true + noInput: true + type: string + - name: ncbiVirusName + displayName: NCBI Virus name + definition: Scientific name for the organism from the NCBI Taxonomy. + generateIndex: true + autocomplete: true + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + type: string + - name: ncbiVirusTaxId + displayName: NCBI Virus tax ID + definition: Identifier for the organism from the NCBI Taxonomy. + type: int + autocomplete: true + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/labs/virus/vssi/#/virus?SeqType_s=Nucleotide&VirusLineage_ss=taxid:__value__ + hideOnSequenceDetailsPage: true + noInput: true + header: INSDC + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + customDisplay: + type: link + url: https://www.ncbi.nlm.nih.gov/sra/?term=__value__ + header: INSDC + orderOnDetailsPage: 1120 + ingest: ncbiSraAccessions + type: string + - name: totalSnps + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total SNPs + definition: Total count of nucleotide substitutions. + orderOnDetailsPage: 2720 + - name: totalInsertedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total inserted nucs + definition: Total count of inserted nucleotide positions. + orderOnDetailsPage: 2740 + - name: totalDeletedNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + orderOnDetailsPage: 2760 + displayName: Total deleted nucs + definition: Total count of deleted nucleotide positions. + - name: totalAmbiguousNucs + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total ambiguous nucs + definition: Total count of ambiguous nucleotides (not A, C, G, T, or N). + orderOnDetailsPage: 2780 + - name: totalUnknownNucs + orderOnDetailsPage: 2800 + type: int + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + rangeSearch: true + perSegment: true + displayName: Total unknown nucs + definition: Total count of missing (N) nucleotides. + - name: totalFrameShifts + isSequenceFilter: true + type: int + rangeSearch: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Total frame shifts + definition: Total count of detected frame shifts. + orderOnDetailsPage: 2820 + - name: frameShifts + isSequenceFilter: true + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Frame shifts + definition: Frame-shifting insertions or deletions detected in CDS regions. + orderOnDetailsPage: 2840 + type: string + - name: totalStopCodons + isSequenceFilter: true + perSegment: true + displayName: Total stop codons + definition: Number of penalized premature stop codons. + type: int + header: Alignment and QC metrics + noInput: true + - name: stopCodons + isSequenceFilter: true + perSegment: true + displayName: Stop codons + definition: Premature stop codons not in the ignored list (penalized). + header: Alignment and QC metrics + noInput: true + type: string + - name: completeness + isSequenceFilter: true + type: float + header: Alignment and QC metrics + noInput: true + perSegment: true + displayName: Completeness + definition: Fraction of reference positions covered by the sequence (0.0 to 1.0). + rangeSearch: true + percentage: true + orderOnDetailsPage: 2860 + customDisplay: + type: lengthCompleteness + displayGroup: lengthCompleteness + label: Length + - name: lineage + type: string + - name: mutations_from_founder + type: string + - name: variant + type: boolean + - name: versionComment + type: string + extraInputFields: + - name: host + type: string + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + hideInSearchResultsTable: true + hideOnSequenceDetailsPage: true + desired: false + position: last + required: true + organismName: West Nile Virus + image: /images/organisms/wnv_small.jpg + linkOuts: + - name: Nextclade + url: https://clades.nextstrain.org/?input-fasta={{[unalignedNucleotideSequences+rich|fasta]}}&dataset-name=nextstrain/wnv/all-lineages&dataset-server=https://raw.githubusercontent.com/nextstrain/nextclade_data/wnv/data_output + - name: Country map + url: https://mapoplexus.genomium.org/?url={{[metadata+accessionVersion,accession,version,geoLocAdmin1,geoLocAdmin2,geoLocCity,geoLocCountry,geoLocSite,hostNameCommon,hostNameScientific,authors,sampleCollectionDate]}} + metadataAdd: + - name: displayName + displayName: Display name + definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{identifier}/{sampleCollectionDate}`. The identifier is provided from `specimenCollectorSampleId` if set, otherwise `submissionId`. For sequences ingested from the INSDC, the identifier is taken directly as the provided field, assuming it contains no slashes or spaces, otherwise the `accessionVersion` is used. For other sequences, if the provided field has a display-name format itself (four or three items, separated by slashes), then the identifier is extracted as the second to last item within this. If the identifier cannot be extracted (due to invalid format of the provided field), the `accessionVersion` is used. Missing `geoLocCountry`/`sampleCollectionDate` default to `unknown`. + noInput: true + - name: hostTaxonId + type: string + autocomplete: true + displayName: Host species + definition: Taxon ID for the host. + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: Host + ingest: ncbiHostTaxId + noInput: true + orderOnDetailsPage: 1540 + - name: lineage + displayName: Lineage + definition: Assigned clade label from the nearest reference tree node. + isSequenceFilter: true + header: Lineage + noInput: true + generateIndex: true + autocomplete: true + initiallyVisible: true + includeInDownloadsByDefault: true + - name: mutations_from_founder + header: Alignment and QC metrics + displayName: Amino Acid Mutations from founder clade + definition: Private amino acid mutations in this sequence relative to the founder of its assigned clade. + customDisplay: + type: generatedBadge + noInput: true + generateIndex: false + autocomplete: false + initiallyVisible: false + includeInDownloadsByDefault: false + - name: variant + isSequenceFilter: true + perSegment: true + header: Alignment and QC metrics + displayName: Variant + definition: Indicates whether the sequence is sufficiently divergent from its closest reference to be classified as a distinct variant, based on whether the number of private nucleotide substitutions exceeds a threshold relative to sequence length. + type: boolean + noInput: true + autocomplete: true + initiallyVisible: false + includeInDownloadsByDefault: false + tableColumns: + - sampleCollectionDate + - ncbiReleaseDate + - authors + - authorAffiliations + - geoLocCountry + - geoLocAdmin1 + - length + - lineage + defaultOrderBy: sampleCollectionDate + defaultOrder: descending + multiFieldSearches: + - name: identifier + displayName: Sample Identifier + orderInSearchDisplay: -10000 + fields: + - accessionVersion + - submissionId + - insdcAccessionFull + - bioprojectAccession + - gcaAccession + - biosampleAccession + - insdcRawReadsAccession + - name: contributor + displayName: Contributor + orderInSearchDisplay: -9999 + fields: + - authors + - authorAffiliations + - sequencedByOrganization + - sequencedByContactName + - submitter + - groupName + inputFields: + - name: id + displayName: ID + example: GJP123 + noEdit: true + required: true + definition: Your sequence identifier; should match the sequence's id in the FASTA file - this is used to link the metadata to the FASTA sequence. + - name: sampleCollectionDate + displayName: Collection date + definition: The date on which the sample was collected. + required: true + - name: geoLocLatitude + displayName: Latitude + definition: Geo-coordinate latitude in decimal degree (WGS84) format. + - name: geoLocLongitude + displayName: Longitude + definition: Geo-coordinate longitude in decimal degree (WGS84) format. + - name: geoLocCountry + displayName: Collection country + definition: The country from which the sample was collected. + required: true + options: *id001 + - name: geoLocAdmin1 + displayName: Collection subdivision level 1 + definition: 'A local administrative region from which the sample was collected (ex: Province, State, Canton).' + - name: geoLocAdmin2 + displayName: Collection subdivision level 2 + definition: 'A local administrative region from which the sample was collected (ex: county or municipality).' + - name: geoLocCity + displayName: Collection city + definition: The city from which the sample was collected. + - name: geoLocSite + definition: The name of a specific geographical location e.g. Credit River (rather than river). + displayName: Collection site + - name: specimenCollectorSampleId + displayName: Isolate name + definition: A de-identified ID for the sample the sequence was obtained from. + - name: authors + displayName: Authors + definition: List of authors who should be listed on the sample. + - name: authorAffiliations + displayName: Author affiliations + definition: List of author affiliations. + - name: bioprojectAccession + displayName: BioProject accession + definition: Accession of public bioproject in the INSDC your consensus sequences should be uploaded to. + - name: biosampleAccession + displayName: BioSample accession + definition: Accession of corresponding public biosample of the raw reads in the INSDC. + - name: cultureId + displayName: Culture ID + definition: An ID useful to linking to a culture. + - name: sampleReceivedDate + definition: The date on which the sample was received by the laboratory. + displayName: Sample received date + - name: sampleType + displayName: Sample type + definition: Method of sampling. + - name: purposeOfSampling + displayName: Purpose of sampling + definition: The reason that the sample was collected. + - name: presamplingActivity + definition: The activities or variables introduced upstream of sample collection that may affect the sample collected. + displayName: Presampling activity + - name: anatomicalMaterial + definition: A substance obtained from an anatomical part of an organism e.g. tissue, blood. + displayName: Anatomical material + - name: anatomicalPart + definition: An anatomical part of an organism e.g. oropharynx. + displayName: Anatomical part + - name: bodyProduct + definition: A substance excreted/secreted from an organism e.g. feces, urine, sweat. + displayName: Body product + - name: environmentalMaterial + definition: A substance obtained from the natural or man-made environment e.g. soil, water, sewage, door handle, bed handrail, face mask. + displayName: Environmental material + - name: environmentalSite + definition: An environmental location may describe a site in the natural or built environment e.g. hospital, wet market, bat cave. + displayName: Environmental site + - name: collectionDevice + definition: The instrument or container used to collect the sample e.g. swab. + displayName: Collection device + - name: collectionMethod + definition: The process used to collect the sample e.g. phlebotomy, necropsy. + displayName: Collection method + - name: foodProduct + definition: A material consumed and digested for nutritional value or enjoyment. + displayName: Food product + - name: foodProductProperties + definition: Any characteristic of the food product pertaining to its state, processing, a label claim, or implications for consumers. + displayName: Food product properties + - name: specimenProcessing + definition: The processing applied to samples post-collection, prior to further testing, characterization, or isolation procedures. + displayName: Specimen processing + - name: specimenProcessingDetails + definition: Detailed information regarding the processing applied to a sample during or after receiving the sample. + displayName: Specimen processing details + - name: experimentalSpecimenRoleType + definition: The type of role that the sample represents in the experiment. + displayName: Experimental specimen role type + - name: hostAge + definition: Age of host at the time of sampling. + displayName: Host age + - name: hostAgeBin + definition: The age category of the host at the time of sampling. + displayName: Host age bin + - name: hostGender + definition: The gender of the host at the time of sample collection. + displayName: Host gender + - name: hostOriginCountry + definition: The country of origin of the host. + displayName: Host origin country + - name: hostDisease + definition: The name of the disease experienced by the host. + displayName: Host disease + - name: signsAndSymptoms + definition: A perceived change in function or sensation, (loss, disturbance or appearance) indicative of a disease, reported by a patient. + displayName: Signs and symptoms + - name: hostHealthState + definition: Health status of the host at the time of sample collection. + displayName: Host health state + - name: hostHealthOutcome + definition: Disease outcome in the host. + displayName: Host health outcome + - name: travelHistory + definition: Travel history in last six months. + displayName: Travel history + - name: exposureEvent + definition: Event leading to exposure. + displayName: Exposure event + - name: hostRole + definition: The role of the host in relation to the exposure setting. + displayName: Host role + - name: exposureSetting + definition: The setting leading to exposure. + displayName: Exposure setting + - name: exposureDetails + definition: Additional host exposure information. + displayName: Exposure details + - name: previousInfectionDisease + definition: The name of the disease previously experienced by the host. + displayName: Previous infection (disease) + - name: previousInfectionOrganism + definition: The name of the pathogen causing the disease previously experienced by the host. + displayName: Previous infection (organism) + - name: hostVaccinationStatus + definition: The vaccination status of the host (fully vaccinated, partially vaccinated, or not vaccinated). + displayName: Host vaccination status + - name: purposeOfSequencing + definition: The reason that the sample was sequenced. + displayName: Purpose of sequencing + - name: sequencingDate + definition: The date the sample was sequenced. + displayName: Sequencing date + - name: ampliconPcrPrimerScheme + definition: The specifications of the primers (primer sequences, binding positions, fragment size generated etc) used to generate the amplicons to be sequenced. + displayName: Amplicon PCR primer scheme + - name: ampliconSize + definition: The length of the amplicon generated by PCR amplification. + displayName: Amplicon size + - name: sequencingInstrument + definition: The model of the sequencing instrument used. + displayName: Sequencing instrument + - name: sequencingProtocol + definition: The protocol used to generate the sequence. + displayName: Sequencing protocol + - name: sequencingAssayType + definition: The overarching sequencing methodology that was used to determine the sequence of a biomaterial. + displayName: Sequencing assay type + - name: sequencedByOrganization + definition: The name of the agency, organization or institution responsible for sequencing the isolate's genome. + displayName: Sequenced by + - name: sequencedByContactName + definition: The name or title of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact name + - name: sequencedByContactEmail + definition: The email address of the contact responsible for follow-up regarding the sequence. + displayName: Sequenced by - contact email + - name: rawSequenceDataProcessingMethod + definition: The method used for raw data processing such as removing barcodes, adapter trimming, filtering etc. + displayName: Raw sequence data processing method + - name: dehostingMethod + definition: The method used to remove host reads from the pathogen sequence. + displayName: Dehosting method + - name: referenceGenomeAccession + definition: A persistent, unique identifier of a genome database entry. + displayName: Reference genome accession + - name: consensusSequenceSoftwareName + definition: The name of software used to generate the consensus sequence. + displayName: Consensus sequence software name + - name: consensusSequenceSoftwareVersion + definition: The version of the software used to generate the consensus sequence. + displayName: Consensus sequence software version + - name: depthOfCoverage + definition: The average number of reads representing a given nucleotide in the reconstructed sequence. + displayName: Depth of coverage + - name: breadthOfCoverage + definition: The threshold used as a cut-off for the depth of coverage. + displayName: Breadth of coverage + - name: qualityControlMethodName + definition: The name of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method name + - name: qualityControlMethodVersion + definition: The version number of the method used to assess whether a sequence passed a predetermined quality control threshold. + displayName: Quality control method version + - name: qualityControlDetermination + definition: The determination of a quality control assessment. + displayName: Quality control determination + - name: qualityControlIssues + definition: The reason contributing to, or causing, a low quality determination in a quality control assessment. + displayName: Quality control issues + - name: qualityControlDetails + definition: The details surrounding a low quality determination in a quality control assessment. + displayName: Quality control details + - name: diagnosticMeasurementMethod + displayName: Diagnostic measurement method + definition: Type of diagnostic test performed. + - name: diagnosticTargetPresence + displayName: Diagnostic target presence + definition: Boolean value, if the diagnostic target was present. + - name: diagnosticTargetGeneName + displayName: Gene name + definition: Name of the gene targeted by the diagnostic RT-PCR test. + - name: diagnosticMeasurementValue + displayName: Diagnostic measurement value + definition: 'Value of the diagnostic test. Ex: The Ct value result from a diagnostic SARS-CoV-2 RT-PCR test.' + - name: diagnosticMeasurementUnit + displayName: Diagnostic measurement unit + definition: Unit of the diagnostic measurement value. + - name: isLabHost + displayName: Is lab host + definition: If a laboratory host (e.g. cultured cell line) was used to propagate the sample. + - name: cellLine + displayName: Cell line + definition: Population of cells derived from a single cell or group of cells, which are cultured in the laboratory under controlled conditions. + - name: passageNumber + displayName: Passage number + definition: The number of times a cell culture has been subcultured or transferred from one vessel to another. + - name: passageMethod + displayName: Passage method + definition: The techniques used to subculture cells. + - name: insdcRawReadsAccession + displayName: Raw reads accession + definition: Accession of corresponding raw reads in the INSDC, e.g. SRR27477368. + - name: host + displayName: Host + definition: NCBI taxon ID or scientific name that uniquely identifies the host from which the sample was collected. + guidance: You can provide either the scientific name of the organism (e.g., `Aedes aegypti`) or its NCBI taxon ID (e.g., `7159`) in this field. We will validate your input and use it to populate the `host common name` and `host scientific name` fields. When uploading data from a pooled sample, we recommend using a higher-level taxon that includes all host organisms in the sample pool. For example, you can use `Culicidae` when uploading data for a pooled mosquito sample (in addition to setting the `specimenProcessing` field to `Samples pooled`, see [OBI:0600016]). + example: Aedes aegypti + desired: false + required: true +referenceGenomes: + - name: main + references: + - name: singleReference + sequence: 'AGTAGTTCGCCTGTGTGAGCTGACAAACTTAGTAGTGTTTGTGAGGATTAACAACAATTAACACAGTGCGAGCTGTTTCTTAGCACGAAGATCTCGATGTCTAAGAAACCAGGAGGGCCCGGCAAGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTTGATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCTTCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACACCTTCTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAAAAGAGGAGGAAAGACCGGAATTGCAGTCATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGATGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGAGCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTACTGTCGGCTGGTAATGATCCAGAAGACATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTCGGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCCACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCCTGGTGGCAGCCGTCATTGGTTGGATGCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTCAACTGCCTTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGCGTGACTATCATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCGCAGTTATTGCTATTTGGCTACCGTCAGCGATCTCTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACAAACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAAGGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTACGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGTTGGAGCCACTCAGGCAGGGAGACTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGTGAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTGGTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTGTGGAGGAACAGAGAGACGTTAATGGAGTTTGAGGAACCACACGCCACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTCTGCATCAAGCTTTGGCTGGAGCCATTCCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTTGAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGGTGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCTTCATTGAACGACCTAACGCCAGTGGGCAGATTGGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACCACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCATTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCAGTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTCGGAGGAGCATTCCGCTCACTGTTCGGAGGCATGTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCCATAGCTCTCACGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCACGCTGACACTGGGTGTGCCATAGACATCAGCCGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCCTGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGACTGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTTTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTGGTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAAGGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAATGTCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTGAAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAACAACTTGGCGATCCACAGTGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCATGTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAATCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGACCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGACTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAGAGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGTTGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTATAATGCTGATATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCAAGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTATGTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAAGATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGGCTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCGGTAGCTTGGATGATACTGAGAGCCATAACATTCACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACCCGGGCTGAGATGCTTGAATCTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAGGGAGAAGAGGAGTGCAGCCGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTAGCCTCAACAGGACTTTTCAACCCCATGATCCTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCTAATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTTGACATTGACTCCATGGCCATTCCAATGACTATCGCGGGGCTCATGTTTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCAGAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGCACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGTGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAGTTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAGGGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGTTGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGCCTGGACCCATACTGGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTGCAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGAAATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGCTTTATGGCAATGGAGTCATAATGCCCAACGGCTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAGCCAATCCCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAGGAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCGCCAACCAGGGTTGTGGCTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATCCGGTACCAGACATCCGCAGTGCCCAGAGAACATAATGGAAATGAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGCTGATGTCTCCTCACAGGGTGCCGAACTACAACCTGTTCGTGATGGATGAGGCTCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGGAGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGACTTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTTTGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAAAGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGGGCTAACTTCAAGGCGAGCAGGGTGATTGACAGCCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGTGATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAGTTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCGAACTTCGCCCATTGGACTGAGGCACGAATCATGCTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTCTACCAACCAGAGCGTGAGAAGGTATATACCATGGATGGGGAATACCGGCTCAGAGGAGAAGAGAGAAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTTACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGTCCTAGGACAAACACAATTTTAGAAGACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATTCTGAGGCCGCGCTGGATTGATGCCAGGGTGTACTCGGATCACCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGAAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGGGAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAAGCACTTGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGAGGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTATTGAGTGTGATGACCATGGGAGTATTCTTCCTCCTCATGCAGCGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCGCGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATTGTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCTTGTGAGCGCAGTGGCAGCCAACGAGATGGGTTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAATTGAGGTCAAGGAGAATTTCAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTGACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGTTCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCGGATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCCGGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCATCGTGGCCACGGACGTCCCAGAATTAGAGCGCACCACACCCATCATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGGTGTCTCTAGCTGCAGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTGACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTGGTTGTCATGTCTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGACGCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATCATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAAAGAAGGCAATGTCACTGGAGGGCATCCAGTCTCTAGGGGCACAGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGAGGCGGTTGGTGTTACTATATGGCAACCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAGCCCCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTGTTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCATAGGACGATTCGGGTCCTTGAAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGTCCCTACATGCCGAAAGTCATAGAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGATGTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAAAAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCCTGCTCAACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAACCACCCATATAGAACCTGGAACTATCACGGCAGTTATGATGTGAAGCCCACAGGCTCCGCCAGTTCGCTGGTCAATGGAGTGGTCAGGCTCCTCTCAAAACCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGGCAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCTGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGACCACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCAACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAATTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAAGAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTCTGGAGTTCGAGGCTCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTGGGCCTCCAAAAACTGGGTTACATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGGCTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGCTGCTTGATGGGGAACATCGGCGTCTTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTCATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGCCGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGACCCAAAGTCAGGACCTGGCTGTTTGAGAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTAAAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTGGAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCATTTCACTGAATTGATCATGAAAGATGGAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAACGTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTCCACAGAAGAGACCTGCGGCTCATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAGGAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAAACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATTGGCACAAGAGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGGACTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAGATATTTAATCAATTGTAAATAGACAATATAAGTATGCATAAAAGTGTAGTTTTATAGTAGTATTTAGTGGTGTTAGTGTAAATAGTTAAGAAAATTTTGAGGAGAAAGTCAGGCCGGGAAGTTCCCGCCACCGGAAGTTGAGTAGACGGTGCTGCCTGCGACTCAACCCCAGGAGGACTGGGTGAACAAAGCCGCGAAGTGATCCATGTAAGCCCTCAGAACCGTCTCGGAAGGAGGACCCCACATGTTGTAACTTCAAAGCCCAATGTCAGACCACGCTACGGCGTGCTACTCTGCGGAGAGTGCAGTCTGCGATAGTGCCCCAGGAGGACTGGGTTAACAAAGGCAAACCAACGCCCCACGCGGCCCTAGCCCCGGTAATGGTGTTAACCAGGGCGAAAGGACTAGAGGTTAGAGGAGACCCCGCGGTTTAAAGTGCACGGCCCAGCCTGGCTGAAGCTGTAGGTCAGGGGAAGGACTAGAGGTTAGTGGAGACCCCGTGCCACAAAACACCACAACAAAACAGCATATTGACACCTGGGATAGACTAGGAGATCTTCTGCTCTGCACAACCAGCCACACGGCACAGTGCGCCGACAATGGTGGCTGGTGGTGCGAGAACACAGGATCT' + insdcAccessionFull: NC_009942.1 + genes: + - name: 2K + sequence: 'SQTDNQLAVFLICVMTLVSAVAA' + - name: NS1 + sequence: 'DTGCAIDISRQELRCGSGVFIHNDVEAWMDRYKYYPETPQGLAKIIQKAHKEGVCGLRSVSRLEHQMWEAVKDELNTLLKENGVDLSVVVEKQEGMYKSAPKRLTATTEKLEIGWKAWGKSILFAPELANNTFVVDGPETKECPTQNRAWNSLEVEDFGFGLTSTRMFLKVRESNTTECDSKIIGTAVKNNLAIHSDLSYWIESRLNDTWKLERAVLGEVKSCTWPETHTLWGDGILESDLIIPVTLAGPRSNHNRRPGYKTQNQGPWDEGRVEIDFDYCPGTTVTLSESCGHRGPATRTTTESGKLITDWCCRSCTLPPLRYQTDSGCWYGMEIRPQRHDEKTLVQSQVNA' + - name: NS2A + sequence: 'YNADMIDPFQLGLLVVFLATQEVLRKRWTAKISMPAILIALLVLVFGGITYTDVLRYVILVGAAFAESNSGGDVVHLALMATFKIQPVFMVASFLKARWTNQENILLMLAAVFFQMAYHDARQILLWEIPDVLNSLAVAWMILRAITFTTTSNVVVPLLALLTPGLRCLNLDVYRILLLMVGIGSLIREKRSAAAKKKGASLLCLALASTGLFNPMILAAGLIACDPNRKR' + - name: NS2B + sequence: 'GWPATEVMTAVGLMFAIVGGLAELDIDSMAIPMTIAGLMFAAFVISGKSTDMWIERTADISWESDAEITGSSERVDVRLDDDGNFQLMNDPGAPWKIWMLRMVCLAISAYTPWAILPSVVGFWITLQYTKR' + - name: NS3 + sequence: 'GGVLWDTPSPKEYKKGDTTTGVYRIMTRGLLGSYQAGAGVMVEGVFHTLWHTTKGAALMSGEGRLDPYWGSVKEDRLCYGGPWKLQHKWNGQDEVQMIVVEPGKNVKNVQTKPGVFKTPEGEIGAVTLDFPTGTSGSPIVDKNGDVIGLYGNGVIMPNGSYISAIVQGERMDEPIPAGFEPEMLRKKQITVLDLHPGAGKTRRILPQIIKEAINRRLRTAVLAPTRVVAAEMAEALRGLPIRYQTSAVPREHNGNEIVDVMCHATLTHRLMSPHRVPNYNLFVMDEAHFTDPASIAARGYISTKVELGEAAAIFMTATPPGTSDPFPESNSPISDLQTEIPDRAWNSGYEWITEYTGKTVWFVPSVKMGNEIALCLQRAGKKVVQLNRKSYETEYPKCKNDDWDFVITTDISEMGANFKASRVIDSRKSVKPTIITEGEGRVILGEPSAVTAASAAQRRGRIGRNPSQVGDEYCYGGHTNEDDSNFAHWTEARIMLDNINMPNGLIAQFYQPEREKVYTMDGEYRLRGEERKNFLELLRTADLPVWLAYKVAAAGVSYHDRRWCFDGPRTNTILEDNNEVEVITKLGERKILRPRWIDARVYSDHQALKAFKDFASGKR' + - name: NS4A + sequence: 'SQIGLIEVLGKMPEHFMGKTWEALDTMYVVATAEKGGRAHRMALEELPDALQTIALIALLSVMTMGVFFLLMQRKGIGKIGLGGAVLGVATFFCWMAEVPGTKIAGMLLLSLLLMIVLIPEPEKQR' + - name: NS4B + sequence: 'NEMGWLDKTKSDISSLFGQRIEVKENFSMGEFLLDLRPATAWSLYAVTTAVLTPLLKHLITSDYINTSLTSINVQASALFTLARGFPFVDVGVSALLLAAGCWGQVTLTVTVTAATLLFCHYAYMVPGWQAEAMRSAQRRTAAGIMKNAVVDGIVATDVPELERTTPIMQKKVGQIMLILVSLAAVVVNPSVKTVREAGILITAAAVTLWENGASSVWNATTAIGLCHIMRGGWLSCLSITWTLIKNMEKPGLKR' + - name: NS5 + sequence: 'GGAKGRTLGEVWKERLNQMTKEEFTRYRKEAIIEVDRSAAKHARKEGNVTGGHPVSRGTAKLRWLVERRFLEPVGKVIDLGCGRGGWCYYMATQKRVQEVRGYTKGGPGHEEPQLVQSYGWNIVTMKSGVDVFYRPSECCDTLLCDIGESSSSAEVEEHRTIRVLEMVEDWLHRGPREFCVKVLCPYMPKVIEKMELLQRRYGGGLVRNPLSRNSTHEMYWVSRASGNVVHSVNMTSQVLLGRMEKRTWKGPQYEEDVNLGSGTRAVGKPLLNSDTSKIKNRIERLRREYSSTWHHDENHPYRTWNYHGSYDVKPTGSASSLVNGVVRLLSKPWDTITNVTTMAMTDTTPFGQQRVFKEKVDTKAPEPPEGVKYVLNETTNWLWAFLAREKRPRMCSREEFIRKVNSNAALGAMFEEQNQWRSAREAVEDPKFWEMVDEEREAHLRGECHTCIYNMMGKREKKPGEFGKAKGSRAIWFMWLGARFLEFEALGFLNEDHWLGRKNSGGGVEGLGLQKLGYILREVGTRPGGKIYADDTAGWDTRITRADLENEAKVLELLDGEHRRLARAIIELTYRHKVVKVMRPAADGRTVMDVISREDQRGSGQVVTYALNTFTNLAVQLVRMMEGEGVIGPDDVEKLTKGKGPKVRTWLFENGEERLSRMAVSGDDCVVKPLDDRFATSLHFLNAMSKVRKDIQEWKPSTGWYDWQQVPFCSNHFTELIMKDGRTLVVPCRGQDELVGRARISPGAGWNVRDTACLAKSYAQMWLLLYFHRRDLRLMANAICSAVPVNWVPTGRTTWSIHAGGEWMTTEDMLEVWNRVWIEENEWMEDKTPVEKWSDVPYSGKREDIWCGSLIGTRARATWAENIQVAINQVRAIIGDEKYVDYMSSLKRYEDTTLVEDTVL' + - name: capsid + sequence: 'MSKKPGGPGKSRAVNMLKRGMPRVLSLIGLKRAMLSLIDGKGPIRFVLALLAFFRFTAIAPTRAVLDRWRGVNKQTAMKHLLSFKKELGTLTSAINRRSSKQKKRGGKTGIAVMIGLIASVGA' + - name: env + sequence: 'FNCLGMSNRDFLEGVSGATWVDLVLEGDSCVTIMSKDKPTIDVKMMNMEAANLAEVRSYCYLATVSDLSTKAACPTMGEAHNDKRADPAFVCRQGVVDRGWGNGCGLFGKGSIDTCAKFACSTKAIGRTILKENIKYEVAIFVHGPTTVESHGNYSTQVGATQAGRLSITPAAPSYTLKLGEYGEVTVDCEPRSGIDTNAYYVMTVGTKTFLVHREWFMDLNLPWSSAGSTVWRNRETLMEFEEPHATKQSVIALGSQEGALHQALAGAIPVEFSSNTVKLTSGHLKCRVKMEKLQLKGTTYGVCSKAFKFLGTPADTGHGTVVLELQYTGTDGPCKVPISSVASLNDLTPVGRLVTVNPFVSVATANAKVLIELEPPFGDSYIVVGRGEQQINHHWHKSGSSIGKAFTTTLKGAQRLAALGDTAWDFGSVGGVFTSVGKAVHQVFGGAFRSLFGGMSWITQGLLGALLLWMGINARDRSIALTFLAVGGVLLFLSVNVHA' + - name: prM + sequence: 'VTLSNFQGKVMMTVNATDVTDVITIPTAAGKNLCIVRAMDVGYMCDDTITYECPVLSAGNDPEDIDCWCTKSAVYVRYGRCTKTRHSRRSRRSLTVQTHGESTLANKKGAWMDSTKATRYLVKTESWILRNPGYALVAAVIGWMLGSNTMQRVVFVVLLLLVAPAYS' +referenceGenome: + nucleotideSequences: + - name: main + sequence: 'AGTAGTTCGCCTGTGTGAGCTGACAAACTTAGTAGTGTTTGTGAGGATTAACAACAATTAACACAGTGCGAGCTGTTTCTTAGCACGAAGATCTCGATGTCTAAGAAACCAGGAGGGCCCGGCAAGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTTGATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCTTCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACACCTTCTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAAAAGAGGAGGAAAGACCGGAATTGCAGTCATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGATGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGAGCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTACTGTCGGCTGGTAATGATCCAGAAGACATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTCGGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCCACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCCTGGTGGCAGCCGTCATTGGTTGGATGCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTCAACTGCCTTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGCGTGACTATCATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCGCAGTTATTGCTATTTGGCTACCGTCAGCGATCTCTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACAAACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAAGGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTACGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGTTGGAGCCACTCAGGCAGGGAGACTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGTGAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTGGTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTGTGGAGGAACAGAGAGACGTTAATGGAGTTTGAGGAACCACACGCCACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTCTGCATCAAGCTTTGGCTGGAGCCATTCCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTTGAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGGTGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCTTCATTGAACGACCTAACGCCAGTGGGCAGATTGGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACCACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCATTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCAGTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTCGGAGGAGCATTCCGCTCACTGTTCGGAGGCATGTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCCATAGCTCTCACGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCACGCTGACACTGGGTGTGCCATAGACATCAGCCGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCCTGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGACTGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTTTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTGGTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAAGGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAATGTCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTGAAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAACAACTTGGCGATCCACAGTGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCATGTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAATCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGACCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGACTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAGAGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGTTGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTATAATGCTGATATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCAAGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTATGTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAAGATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGGCTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCGGTAGCTTGGATGATACTGAGAGCCATAACATTCACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACCCGGGCTGAGATGCTTGAATCTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAGGGAGAAGAGGAGTGCAGCCGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTAGCCTCAACAGGACTTTTCAACCCCATGATCCTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCTAATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTTGACATTGACTCCATGGCCATTCCAATGACTATCGCGGGGCTCATGTTTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCAGAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGCACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGTGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAGTTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAGGGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGTTGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGCCTGGACCCATACTGGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTGCAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGAAATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGCTTTATGGCAATGGAGTCATAATGCCCAACGGCTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAGCCAATCCCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAGGAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCGCCAACCAGGGTTGTGGCTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATCCGGTACCAGACATCCGCAGTGCCCAGAGAACATAATGGAAATGAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGCTGATGTCTCCTCACAGGGTGCCGAACTACAACCTGTTCGTGATGGATGAGGCTCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGGAGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGACTTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTTTGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAAAGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGGGCTAACTTCAAGGCGAGCAGGGTGATTGACAGCCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGTGATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAGTTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCGAACTTCGCCCATTGGACTGAGGCACGAATCATGCTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTCTACCAACCAGAGCGTGAGAAGGTATATACCATGGATGGGGAATACCGGCTCAGAGGAGAAGAGAGAAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTTACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGTCCTAGGACAAACACAATTTTAGAAGACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATTCTGAGGCCGCGCTGGATTGATGCCAGGGTGTACTCGGATCACCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGAAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGGGAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAAGCACTTGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGAGGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTATTGAGTGTGATGACCATGGGAGTATTCTTCCTCCTCATGCAGCGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCGCGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATTGTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCTTGTGAGCGCAGTGGCAGCCAACGAGATGGGTTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAATTGAGGTCAAGGAGAATTTCAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTGACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGTTCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCGGATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCCGGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCATCGTGGCCACGGACGTCCCAGAATTAGAGCGCACCACACCCATCATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGGTGTCTCTAGCTGCAGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTGACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTGGTTGTCATGTCTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGACGCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATCATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAAAGAAGGCAATGTCACTGGAGGGCATCCAGTCTCTAGGGGCACAGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGAGGCGGTTGGTGTTACTATATGGCAACCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAGCCCCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTGTTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCATAGGACGATTCGGGTCCTTGAAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGTCCCTACATGCCGAAAGTCATAGAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGATGTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAAAAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCCTGCTCAACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAACCACCCATATAGAACCTGGAACTATCACGGCAGTTATGATGTGAAGCCCACAGGCTCCGCCAGTTCGCTGGTCAATGGAGTGGTCAGGCTCCTCTCAAAACCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGGCAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCTGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGACCACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCAACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAATTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAAGAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTCTGGAGTTCGAGGCTCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTGGGCCTCCAAAAACTGGGTTACATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGGCTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGCTGCTTGATGGGGAACATCGGCGTCTTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTCATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGCCGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGACCCAAAGTCAGGACCTGGCTGTTTGAGAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTAAAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTGGAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCATTTCACTGAATTGATCATGAAAGATGGAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAACGTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTCCACAGAAGAGACCTGCGGCTCATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAGGAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAAACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATTGGCACAAGAGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGGACTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAGATATTTAATCAATTGTAAATAGACAATATAAGTATGCATAAAAGTGTAGTTTTATAGTAGTATTTAGTGGTGTTAGTGTAAATAGTTAAGAAAATTTTGAGGAGAAAGTCAGGCCGGGAAGTTCCCGCCACCGGAAGTTGAGTAGACGGTGCTGCCTGCGACTCAACCCCAGGAGGACTGGGTGAACAAAGCCGCGAAGTGATCCATGTAAGCCCTCAGAACCGTCTCGGAAGGAGGACCCCACATGTTGTAACTTCAAAGCCCAATGTCAGACCACGCTACGGCGTGCTACTCTGCGGAGAGTGCAGTCTGCGATAGTGCCCCAGGAGGACTGGGTTAACAAAGGCAAACCAACGCCCCACGCGGCCCTAGCCCCGGTAATGGTGTTAACCAGGGCGAAAGGACTAGAGGTTAGAGGAGACCCCGCGGTTTAAAGTGCACGGCCCAGCCTGGCTGAAGCTGTAGGTCAGGGGAAGGACTAGAGGTTAGTGGAGACCCCGTGCCACAAAACACCACAACAAAACAGCATATTGACACCTGGGATAGACTAGGAGATCTTCTGCTCTGCACAACCAGCCACACGGCACAGTGCGCCGACAATGGTGGCTGGTGGTGCGAGAACACAGGATCT' + genes: + - name: 2K + sequence: 'SQTDNQLAVFLICVMTLVSAVAA' + - name: NS1 + sequence: 'DTGCAIDISRQELRCGSGVFIHNDVEAWMDRYKYYPETPQGLAKIIQKAHKEGVCGLRSVSRLEHQMWEAVKDELNTLLKENGVDLSVVVEKQEGMYKSAPKRLTATTEKLEIGWKAWGKSILFAPELANNTFVVDGPETKECPTQNRAWNSLEVEDFGFGLTSTRMFLKVRESNTTECDSKIIGTAVKNNLAIHSDLSYWIESRLNDTWKLERAVLGEVKSCTWPETHTLWGDGILESDLIIPVTLAGPRSNHNRRPGYKTQNQGPWDEGRVEIDFDYCPGTTVTLSESCGHRGPATRTTTESGKLITDWCCRSCTLPPLRYQTDSGCWYGMEIRPQRHDEKTLVQSQVNA' + - name: NS2A + sequence: 'YNADMIDPFQLGLLVVFLATQEVLRKRWTAKISMPAILIALLVLVFGGITYTDVLRYVILVGAAFAESNSGGDVVHLALMATFKIQPVFMVASFLKARWTNQENILLMLAAVFFQMAYHDARQILLWEIPDVLNSLAVAWMILRAITFTTTSNVVVPLLALLTPGLRCLNLDVYRILLLMVGIGSLIREKRSAAAKKKGASLLCLALASTGLFNPMILAAGLIACDPNRKR' + - name: NS2B + sequence: 'GWPATEVMTAVGLMFAIVGGLAELDIDSMAIPMTIAGLMFAAFVISGKSTDMWIERTADISWESDAEITGSSERVDVRLDDDGNFQLMNDPGAPWKIWMLRMVCLAISAYTPWAILPSVVGFWITLQYTKR' + - name: NS3 + sequence: 'GGVLWDTPSPKEYKKGDTTTGVYRIMTRGLLGSYQAGAGVMVEGVFHTLWHTTKGAALMSGEGRLDPYWGSVKEDRLCYGGPWKLQHKWNGQDEVQMIVVEPGKNVKNVQTKPGVFKTPEGEIGAVTLDFPTGTSGSPIVDKNGDVIGLYGNGVIMPNGSYISAIVQGERMDEPIPAGFEPEMLRKKQITVLDLHPGAGKTRRILPQIIKEAINRRLRTAVLAPTRVVAAEMAEALRGLPIRYQTSAVPREHNGNEIVDVMCHATLTHRLMSPHRVPNYNLFVMDEAHFTDPASIAARGYISTKVELGEAAAIFMTATPPGTSDPFPESNSPISDLQTEIPDRAWNSGYEWITEYTGKTVWFVPSVKMGNEIALCLQRAGKKVVQLNRKSYETEYPKCKNDDWDFVITTDISEMGANFKASRVIDSRKSVKPTIITEGEGRVILGEPSAVTAASAAQRRGRIGRNPSQVGDEYCYGGHTNEDDSNFAHWTEARIMLDNINMPNGLIAQFYQPEREKVYTMDGEYRLRGEERKNFLELLRTADLPVWLAYKVAAAGVSYHDRRWCFDGPRTNTILEDNNEVEVITKLGERKILRPRWIDARVYSDHQALKAFKDFASGKR' + - name: NS4A + sequence: 'SQIGLIEVLGKMPEHFMGKTWEALDTMYVVATAEKGGRAHRMALEELPDALQTIALIALLSVMTMGVFFLLMQRKGIGKIGLGGAVLGVATFFCWMAEVPGTKIAGMLLLSLLLMIVLIPEPEKQR' + - name: NS4B + sequence: 'NEMGWLDKTKSDISSLFGQRIEVKENFSMGEFLLDLRPATAWSLYAVTTAVLTPLLKHLITSDYINTSLTSINVQASALFTLARGFPFVDVGVSALLLAAGCWGQVTLTVTVTAATLLFCHYAYMVPGWQAEAMRSAQRRTAAGIMKNAVVDGIVATDVPELERTTPIMQKKVGQIMLILVSLAAVVVNPSVKTVREAGILITAAAVTLWENGASSVWNATTAIGLCHIMRGGWLSCLSITWTLIKNMEKPGLKR' + - name: NS5 + sequence: 'GGAKGRTLGEVWKERLNQMTKEEFTRYRKEAIIEVDRSAAKHARKEGNVTGGHPVSRGTAKLRWLVERRFLEPVGKVIDLGCGRGGWCYYMATQKRVQEVRGYTKGGPGHEEPQLVQSYGWNIVTMKSGVDVFYRPSECCDTLLCDIGESSSSAEVEEHRTIRVLEMVEDWLHRGPREFCVKVLCPYMPKVIEKMELLQRRYGGGLVRNPLSRNSTHEMYWVSRASGNVVHSVNMTSQVLLGRMEKRTWKGPQYEEDVNLGSGTRAVGKPLLNSDTSKIKNRIERLRREYSSTWHHDENHPYRTWNYHGSYDVKPTGSASSLVNGVVRLLSKPWDTITNVTTMAMTDTTPFGQQRVFKEKVDTKAPEPPEGVKYVLNETTNWLWAFLAREKRPRMCSREEFIRKVNSNAALGAMFEEQNQWRSAREAVEDPKFWEMVDEEREAHLRGECHTCIYNMMGKREKKPGEFGKAKGSRAIWFMWLGARFLEFEALGFLNEDHWLGRKNSGGGVEGLGLQKLGYILREVGTRPGGKIYADDTAGWDTRITRADLENEAKVLELLDGEHRRLARAIIELTYRHKVVKVMRPAADGRTVMDVISREDQRGSGQVVTYALNTFTNLAVQLVRMMEGEGVIGPDDVEKLTKGKGPKVRTWLFENGEERLSRMAVSGDDCVVKPLDDRFATSLHFLNAMSKVRKDIQEWKPSTGWYDWQQVPFCSNHFTELIMKDGRTLVVPCRGQDELVGRARISPGAGWNVRDTACLAKSYAQMWLLLYFHRRDLRLMANAICSAVPVNWVPTGRTTWSIHAGGEWMTTEDMLEVWNRVWIEENEWMEDKTPVEKWSDVPYSGKREDIWCGSLIGTRARATWAENIQVAINQVRAIIGDEKYVDYMSSLKRYEDTTLVEDTVL' + - name: capsid + sequence: 'MSKKPGGPGKSRAVNMLKRGMPRVLSLIGLKRAMLSLIDGKGPIRFVLALLAFFRFTAIAPTRAVLDRWRGVNKQTAMKHLLSFKKELGTLTSAINRRSSKQKKRGGKTGIAVMIGLIASVGA' + - name: env + sequence: 'FNCLGMSNRDFLEGVSGATWVDLVLEGDSCVTIMSKDKPTIDVKMMNMEAANLAEVRSYCYLATVSDLSTKAACPTMGEAHNDKRADPAFVCRQGVVDRGWGNGCGLFGKGSIDTCAKFACSTKAIGRTILKENIKYEVAIFVHGPTTVESHGNYSTQVGATQAGRLSITPAAPSYTLKLGEYGEVTVDCEPRSGIDTNAYYVMTVGTKTFLVHREWFMDLNLPWSSAGSTVWRNRETLMEFEEPHATKQSVIALGSQEGALHQALAGAIPVEFSSNTVKLTSGHLKCRVKMEKLQLKGTTYGVCSKAFKFLGTPADTGHGTVVLELQYTGTDGPCKVPISSVASLNDLTPVGRLVTVNPFVSVATANAKVLIELEPPFGDSYIVVGRGEQQINHHWHKSGSSIGKAFTTTLKGAQRLAALGDTAWDFGSVGGVFTSVGKAVHQVFGGAFRSLFGGMSWITQGLLGALLLWMGINARDRSIALTFLAVGGVLLFLSVNVHA' + - name: prM + sequence: 'VTLSNFQGKVMMTVNATDVTDVITIPTAAGKNLCIVRAMDVGYMCDDTITYECPVLSAGNDPEDIDCWCTKSAVYVRYGRCTKTRHSRRSRRSLTVQTHGESTLANKKGAWMDSTKATRYLVKTESWILRNPGYALVAAVIGWMLGSNTMQRVVFVVLLLLVAPAYS' +displayName: West Nile Virus + +# Upstream LAPIS instance for the backend proxy / query API (in-cluster service). +lapisUrl: "http://loculus-lapis-service-west-nile:8080" diff --git a/kubernetes/loculus/fixtures/preprocessing/README.md b/kubernetes/loculus/fixtures/preprocessing/README.md new file mode 100644 index 0000000000..be05c35a41 --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/README.md @@ -0,0 +1,72 @@ +# Preprocessing config-file fixtures + +Optional, opaque preprocessing config files seeded into the backend by the +config loader (`config-tools/src/loader`). They mirror the admin-panel +"Preprocessing" feature: per organism, per pipeline version, one free-form text +file that the backend stores verbatim and serves from +`GET /api/config/organisms/{key}/preprocessing/{pipelineVersion}`. The core +never interprets the content — only the (external) preprocessing pipeline does. + +See [`config-architecture/61_preprocessingPipeline.md`](../../../../config-architecture/61_preprocessingPipeline.md). + +## Layout + +``` +preprocessing/ + / + .yaml # file stem = integer pipeline version; content is opaque +``` + +Example: `preprocessing/ebola-sudan/1.yaml` is the config file for organism +`ebola-sudan`, pipeline version 1. The extension is irrelevant (the content is +opaque); the file stem must be the integer pipeline version. + +The loader PUTs each file after the organism is created/published. Re-running is +idempotent (a PUT replaces the current value). + +## What the nextclade pipeline expects in the file + +For the in-repo nextclade pipeline, the file is the YAML its `Config` parses +(`preprocessing/nextclade/src/loculus_preprocessing/config.py`), minus the parts +the pipeline now derives itself: + +- **Keep in the file:** nextclade dataset config (`segments[].references[]` with + `nextclade_dataset_name`/`tag`/`server`, `genes`), `nextclade_dataset_server`, + alignment knobs, EMBL/INSDC/taxonomy fields (`scientific_name`, + `molecule_type`, …), operational defaults (`batch_size`, `log_level`), and any + **non-identity** `processing_spec` entries (e.g. `validate_host`, + `parse_date_into_range`, nextclade output mappings). +- **Do NOT put in the file:** identity `processing_spec` entries for plain + metadata fields — the pipeline fetches the organism's metadata from the config + API and applies identity defaults itself (expanding `perSegment` fields per + segment). Only override the fields that need non-identity processing. + +Operational/secret values (backend URL, Keycloak password) are NOT here — they +are passed by Helm as args/env. **Never put secrets in these files; they are +served by an open, public endpoint.** + +## How the current files were produced + +The committed files (`cchf`, `cchf-multi-ref`, `ebola-sudan` v1+v2, +`enteroviruses`, `not-aligned-organism`, `west-nile`) were generated to be a +**byte-faithful reproduction** of what the old Helm +`loculus-preprocessing-config.yaml` ConfigMap used to mount, so the pipeline's +behaviour is unchanged by the migration. They were extracted by rendering the +chart at the commit just before the ConfigMap template was removed +(`helm template` → the `preprocessing-config.yaml` ConfigMap data) and writing +each to `/.yaml`. + +Each file was validated to load cleanly into the pipeline's `Config` +(`preprocessing/nextclade/src/loculus_preprocessing/config.py`). + +Because these files carry the full `processing_spec` (including identity +entries), the pipeline's identity-default derivation is a no-op overlay here — +the result matches the file exactly. The files MAY later be trimmed to only the +non-identity entries (the pipeline re-derives identity defaults from the +organism metadata it fetches), but that is an optional cleanup. + +Organisms preprocessed by the **dummy** pipeline need no file (the dummy ignores +config). To regenerate or add an organism, port the nextclade dataset config +from `kubernetes/loculus/values.yaml` +(`defaultOrganisms..preprocessing[].configFile`) and that organism's +`processing_spec` (its metadata `preprocessing:` blocks, expanded per segment). diff --git a/kubernetes/loculus/fixtures/preprocessing/cchf-multi-ref/1.yaml b/kubernetes/loculus/fixtures/preprocessing/cchf-multi-ref/1.yaml new file mode 100644 index 0000000000..d5222e0eeb --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/cchf-multi-ref/1.yaml @@ -0,0 +1,1191 @@ +organism: cchf-multi-ref +alignment_requirement: ANY +batch_size: 100 +create_embl_file: true +log_level: DEBUG +molecule_type: genomic RNA +nextclade_dataset_server: https://raw.githubusercontent.com/genspectrum/nextclade-datasets/cchf-multi/data +scientific_name: Orthonairovirus haemorrhagiae +segments: + - name: L + references: + - genes: + - RdRp + name: singleReference + nextclade_dataset_name: cchfv/L + - name: M + references: + - genes: + - GPC + name: MH396653 + nextclade_dataset_name: cchfv/M-MH396653 + - genes: + - GPC + name: OR047158 + nextclade_dataset_name: cchfv/M-OR047158 + - name: S + references: + - genes: + - NP + name: 1and6 + nextclade_dataset_name: cchfv/S-1and6 + - genes: + - NP + name: 2to5 + nextclade_dataset_name: cchfv/S-2to5 +taxon_id: 3052518 +max_sequences_per_entry: 3 +processing_spec: + + ampliconPcrPrimerScheme: + function: identity + inputs: + input: ampliconPcrPrimerScheme + args: + ampliconSize: + function: identity + inputs: + input: ampliconSize + args: + anatomicalMaterial: + function: identity + inputs: + input: anatomicalMaterial + args: + anatomicalPart: + function: identity + inputs: + input: anatomicalPart + args: + authorAffiliations: + function: identity + inputs: + input: authorAffiliations + args: + authors: + function: check_authors + inputs: + authors: authors + args: + type: authors + bioprojectAccession: + function: identity + inputs: + input: bioprojectAccession + args: + biosampleAccession: + function: identity + inputs: + input: biosampleAccession + args: + bodyProduct: + function: identity + inputs: + input: bodyProduct + args: + breadthOfCoverage: + function: identity + inputs: + input: breadthOfCoverage + args: + type: int + cellLine: + function: identity + inputs: + input: cellLine + args: + collectionDevice: + function: identity + inputs: + input: collectionDevice + args: + collectionMethod: + function: identity + inputs: + input: collectionMethod + args: + completeness_L: + function: identity + inputs: + input: nextclade.coverage + args: + segment: L + type: float + completeness_M: + function: identity + inputs: + input: nextclade.coverage + args: + segment: M + type: float + completeness_S: + function: identity + inputs: + input: nextclade.coverage + args: + segment: S + type: float + consensusSequenceSoftwareName: + function: identity + inputs: + input: consensusSequenceSoftwareName + args: + consensusSequenceSoftwareVersion: + function: identity + inputs: + input: consensusSequenceSoftwareVersion + args: + cultureId: + function: identity + inputs: + input: cultureId + args: + dehostingMethod: + function: identity + inputs: + input: dehostingMethod + args: + depthOfCoverage: + function: identity + inputs: + input: depthOfCoverage + args: + type: int + diagnosticMeasurementMethod: + function: identity + inputs: + input: diagnosticMeasurementMethod + args: + diagnosticMeasurementUnit: + function: identity + inputs: + input: diagnosticMeasurementUnit + args: + diagnosticMeasurementValue: + function: identity + inputs: + input: diagnosticMeasurementValue + args: + diagnosticTargetGeneName: + function: identity + inputs: + input: diagnosticTargetGeneName + args: + diagnosticTargetPresence: + function: identity + inputs: + input: diagnosticTargetPresence + args: + displayName: + function: concatenate + inputs: + geoLocCountry: geoLocCountry + sampleCollectionDate: sampleCollectionDate + args: + + order: + - geoLocCountry + - ACCESSION_VERSION + - sampleCollectionDate + type: + - string + - ACCESSION_VERSION + - string + earliestReleaseDate: + function: identity + inputs: + input: earliestReleaseDate + args: + type: date + environmentalMaterial: + function: identity + inputs: + input: environmentalMaterial + args: + environmentalSite: + function: identity + inputs: + input: environmentalSite + args: + experimentalSpecimenRoleType: + function: identity + inputs: + input: experimentalSpecimenRoleType + args: + exposureDetails: + function: identity + inputs: + input: exposureDetails + args: + exposureEvent: + function: identity + inputs: + input: exposureEvent + args: + exposureSetting: + function: identity + inputs: + input: exposureSetting + args: + foodProduct: + function: identity + inputs: + input: foodProduct + args: + foodProductProperties: + function: identity + inputs: + input: foodProductProperties + args: + frameShifts_L: + function: identity + inputs: + input: nextclade.frameShifts + args: + segment: L + frameShifts_M: + function: identity + inputs: + input: nextclade.frameShifts + args: + segment: M + frameShifts_S: + function: identity + inputs: + input: nextclade.frameShifts + args: + segment: S + gcaAccession: + function: identity + inputs: + input: gcaAccession + args: + geoLocAdmin1: + function: identity + inputs: + input: geoLocAdmin1 + args: + geoLocAdmin2: + function: identity + inputs: + input: geoLocAdmin2 + args: + geoLocCity: + function: identity + inputs: + input: geoLocCity + args: + geoLocCountry: + function: process_options + inputs: + input: geoLocCountry + args: + options: + - Afghanistan + - Albania + - Algeria + - American Samoa + - Andorra + - Angola + - Anguilla + - Antarctica + - Antigua and Barbuda + - Arctic Ocean + - Argentina + - Armenia + - Aruba + - Ashmore and Cartier Islands + - Atlantic Ocean + - Australia + - Austria + - Azerbaijan + - Bahamas + - Bahrain + - Baltic Sea + - Baker Island + - Bangladesh + - Barbados + - Bassas da India + - Belarus + - Belgium + - Belize + - Benin + - Bermuda + - Bhutan + - Bolivia + - Borneo + - Bosnia and Herzegovina + - Botswana + - Bouvet Island + - Brazil + - British Virgin Islands + - Brunei + - Bulgaria + - Burkina Faso + - Burundi + - Cambodia + - Cameroon + - Canada + - Cape Verde + - Cayman Islands + - Central African Republic + - Chad + - Chile + - China + - Christmas Island + - Clipperton Island + - Cocos Islands + - Colombia + - Comoros + - Cook Islands + - Coral Sea Islands + - Costa Rica + - Cote d'Ivoire + - Croatia + - Cuba + - Curacao + - Cyprus + - Czechia + - Democratic Republic of the Congo + - Denmark + - Djibouti + - Dominica + - Dominican Republic + - Ecuador + - Egypt + - El Salvador + - Equatorial Guinea + - Eritrea + - Estonia + - Eswatini + - Ethiopia + - Europa Island + - Falkland Islands (Islas Malvinas) + - Faroe Islands + - Fiji + - Finland + - France + - French Guiana + - French Polynesia + - French Southern and Antarctic Lands + - Gabon + - Gambia + - Gaza Strip + - Georgia + - Germany + - Ghana + - Gibraltar + - Glorioso Islands + - Greece + - Greenland + - Grenada + - Guadeloupe + - Guam + - Guatemala + - Guernsey + - Guinea + - Guinea-Bissau + - Guyana + - Haiti + - Heard Island and McDonald Islands + - Honduras + - Hong Kong + - Howland Island + - Hungary + - Iceland + - India + - Indian Ocean + - Indonesia + - Iran + - Iraq + - Ireland + - Isle of Man + - Israel + - Italy + - Jamaica + - Jan Mayen + - Japan + - Jarvis Island + - Jersey + - Johnston Atoll + - Jordan + - Juan de Nova Island + - Kazakhstan + - Kenya + - Kerguelen Archipelago + - Kingman Reef + - Kiribati + - Kosovo + - Kuwait + - Kyrgyzstan + - Laos + - Latvia + - Lebanon + - Lesotho + - Liberia + - Libya + - Liechtenstein + - Line Islands + - Lithuania + - Luxembourg + - Macau + - Madagascar + - Malawi + - Malaysia + - Maldives + - Mali + - Malta + - Marshall Islands + - Martinique + - Mauritania + - Mauritius + - Mayotte + - Mediterranean Sea + - Mexico + - Micronesia, Federated States of + - Midway Islands + - Moldova + - Monaco + - Mongolia + - Montenegro + - Montserrat + - Morocco + - Mozambique + - Myanmar + - Namibia + - Nauru + - Navassa Island + - Nepal + - Netherlands + - New Caledonia + - New Zealand + - Nicaragua + - Niger + - Nigeria + - Niue + - Norfolk Island + - North Korea + - North Macedonia + - North Sea + - Northern Mariana Islands + - Norway + - Oman + - Pacific Ocean + - Pakistan + - Palau + - Palmyra Atoll + - Panama + - Papua New Guinea + - Paracel Islands + - Paraguay + - Peru + - Philippines + - Pitcairn Islands + - Poland + - Portugal + - Puerto Rico + - Qatar + - Republic of the Congo + - Reunion + - Romania + - Ross Sea + - Russia + - Rwanda + - Saint Barthelemy + - Saint Helena + - Saint Kitts and Nevis + - Saint Lucia + - Saint Martin + - Saint Pierre and Miquelon + - Saint Vincent and the Grenadines + - Samoa + - San Marino + - Sao Tome and Principe + - Saudi Arabia + - Senegal + - Serbia + - Seychelles + - Sierra Leone + - Singapore + - Sint Maarten + - Slovakia + - Slovenia + - Solomon Islands + - Somalia + - South Africa + - South Georgia and the South Sandwich Islands + - South Korea + - South Sudan + - Southern Ocean + - Spain + - Spratly Islands + - Sri Lanka + - State of Palestine + - Sudan + - Suriname + - Svalbard + - Sweden + - Switzerland + - Syria + - Taiwan + - Tajikistan + - Tanzania + - Tasman Sea + - Thailand + - Timor-Leste + - Togo + - Tokelau + - Tonga + - Trinidad and Tobago + - Tromelin Island + - Tunisia + - Turkey + - Turkmenistan + - Turks and Caicos Islands + - Tuvalu + - Uganda + - Ukraine + - United Arab Emirates + - United Kingdom + - Uruguay + - USA + - Uzbekistan + - Vanuatu + - Venezuela + - Viet Nam + - Virgin Islands + - Wake Island + - Wallis and Futuna + - West Bank + - Western Sahara + - Yemen + - Zambia + - Zimbabwe + - Belgian Congo + - British Guiana + - Burma + - Czechoslovakia + - Czech Republic + - East Timor + - Korea + - Macedonia + - Micronesia + - Netherlands Antilles + - Serbia and Montenegro + - Siam + - Swaziland + - The former Yugoslav Republic of Macedonia + - USSR + - Yugoslavia + - Zaire + required: true + geoLocLatitude: + function: identity + inputs: + input: geoLocLatitude + args: + type: float + geoLocLongitude: + function: identity + inputs: + input: geoLocLongitude + args: + type: float + geoLocSite: + function: identity + inputs: + input: geoLocSite + args: + hostAge: + function: identity + inputs: + input: hostAge + args: + type: int + hostAgeBin: + function: identity + inputs: + input: hostAgeBin + args: + hostDisease: + function: identity + inputs: + input: hostDisease + args: + hostGender: + function: identity + inputs: + input: hostGender + args: + hostHealthOutcome: + function: identity + inputs: + input: hostHealthOutcome + args: + hostHealthState: + function: identity + inputs: + input: hostHealthState + args: + hostNameCommon: + function: common_name_from_id + inputs: + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostNameScientific: + function: identity + inputs: + input: hostNameScientific + args: + hostOriginCountry: + function: identity + inputs: + input: hostOriginCountry + args: + hostRole: + function: identity + inputs: + input: hostRole + args: + hostTaxonId: + function: resolve_host_taxon_id + inputs: + host: host + hostNameScientific: hostNameScientific + hostTaxonId: hostTaxonId + args: + type: int + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostVaccinationStatus: + function: identity + inputs: + input: hostVaccinationStatus + args: + insdcAccessionBase_L: + function: identity + inputs: + input: insdcAccessionBase_L + args: + segment: L + insdcAccessionBase_M: + function: identity + inputs: + input: insdcAccessionBase_M + args: + segment: M + insdcAccessionBase_S: + function: identity + inputs: + input: insdcAccessionBase_S + args: + segment: S + insdcAccessionFull_L: + function: identity + inputs: + input: insdcAccessionFull_L + args: + segment: L + insdcAccessionFull_M: + function: identity + inputs: + input: insdcAccessionFull_M + args: + segment: M + insdcAccessionFull_S: + function: identity + inputs: + input: insdcAccessionFull_S + args: + segment: S + insdcRawReadsAccession: + function: identity + inputs: + input: insdcRawReadsAccession + args: + insdcVersion_L: + function: identity + inputs: + input: insdcVersion_L + args: + segment: L + type: int + insdcVersion_M: + function: identity + inputs: + input: insdcVersion_M + args: + segment: M + type: int + insdcVersion_S: + function: identity + inputs: + input: insdcVersion_S + args: + segment: S + type: int + isLabHost: + function: identity + inputs: + input: isLabHost + args: + type: boolean + length_L: + function: identity + inputs: + input: length_L + args: + segment: L + type: int + length_M: + function: identity + inputs: + input: length_M + args: + segment: M + type: int + length_S: + function: identity + inputs: + input: length_S + args: + segment: S + type: int + ncbiReleaseDate: + function: parse_timestamp + inputs: + timestamp: ncbiReleaseDate + args: + type: date + ncbiSourceDb: + function: identity + inputs: + input: ncbiSourceDb + args: + ncbiSubmitterCountry: + function: identity + inputs: + input: ncbiSubmitterCountry + args: + ncbiUpdateDate_L: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + segment: L + type: date + ncbiUpdateDate_M: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + segment: M + type: date + ncbiUpdateDate_S: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + segment: S + type: date + ncbiVirusName: + function: identity + inputs: + input: ncbiVirusName + args: + ncbiVirusTaxId: + function: identity + inputs: + input: ncbiVirusTaxId + args: + type: int + passageMethod: + function: identity + inputs: + input: passageMethod + args: + passageNumber: + function: identity + inputs: + input: passageNumber + args: + type: int + presamplingActivity: + function: identity + inputs: + input: presamplingActivity + args: + previousInfectionDisease: + function: identity + inputs: + input: previousInfectionDisease + args: + previousInfectionOrganism: + function: identity + inputs: + input: previousInfectionOrganism + args: + purposeOfSampling: + function: identity + inputs: + input: purposeOfSampling + args: + purposeOfSequencing: + function: identity + inputs: + input: purposeOfSequencing + args: + qualityControlDetails: + function: identity + inputs: + input: qualityControlDetails + args: + qualityControlDetermination: + function: identity + inputs: + input: qualityControlDetermination + args: + qualityControlIssues: + function: identity + inputs: + input: qualityControlIssues + args: + qualityControlMethodName: + function: identity + inputs: + input: qualityControlMethodName + args: + qualityControlMethodVersion: + function: identity + inputs: + input: qualityControlMethodVersion + args: + rawSequenceDataProcessingMethod: + function: identity + inputs: + input: rawSequenceDataProcessingMethod + args: + reference_L: + function: identity + inputs: + input: ASSIGNED_REFERENCE + args: + segment: L + reference_M: + function: identity + inputs: + input: ASSIGNED_REFERENCE + args: + segment: M + reference_S: + function: identity + inputs: + input: ASSIGNED_REFERENCE + args: + segment: S + referenceGenomeAccession: + function: identity + inputs: + input: referenceGenomeAccession + args: + sampleCollectionDate: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + + fieldType: dateRangeString + required: true + sampleCollectionDateRangeLower: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeLower + sampleCollectionDateRangeUpper: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeUpper + sampleReceivedDate: + function: parse_and_assert_past_date + inputs: + date: sampleReceivedDate + args: + type: date + sampleType: + function: identity + inputs: + input: sampleType + args: + sequencedByContactEmail: + function: identity + inputs: + input: sequencedByContactEmail + args: + sequencedByContactName: + function: identity + inputs: + input: sequencedByContactName + args: + sequencedByOrganization: + function: identity + inputs: + input: sequencedByOrganization + args: + sequencingAssayType: + function: identity + inputs: + input: sequencingAssayType + args: + sequencingDate: + function: parse_and_assert_past_date + inputs: + date: sequencingDate + args: + type: date + sequencingInstrument: + function: identity + inputs: + input: sequencingInstrument + args: + sequencingProtocol: + function: identity + inputs: + input: sequencingProtocol + args: + signsAndSymptoms: + function: identity + inputs: + input: signsAndSymptoms + args: + specimenCollectorSampleId: + function: identity + inputs: + input: specimenCollectorSampleId + args: + specimenProcessing: + function: identity + inputs: + input: specimenProcessing + args: + specimenProcessingDetails: + function: identity + inputs: + input: specimenProcessingDetails + args: + stopCodons_L: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + segment: L + stopCodons_M: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + segment: M + stopCodons_S: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + segment: S + totalAmbiguousNucs_L: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + segment: L + type: int + totalAmbiguousNucs_M: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + segment: M + type: int + totalAmbiguousNucs_S: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + segment: S + type: int + totalDeletedNucs_L: + function: identity + inputs: + input: nextclade.totalDeletions + args: + segment: L + type: int + totalDeletedNucs_M: + function: identity + inputs: + input: nextclade.totalDeletions + args: + segment: M + type: int + totalDeletedNucs_S: + function: identity + inputs: + input: nextclade.totalDeletions + args: + segment: S + type: int + totalFrameShifts_L: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + segment: L + type: int + totalFrameShifts_M: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + segment: M + type: int + totalFrameShifts_S: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + segment: S + type: int + totalInsertedNucs_L: + function: identity + inputs: + input: nextclade.totalInsertions + args: + segment: L + type: int + totalInsertedNucs_M: + function: identity + inputs: + input: nextclade.totalInsertions + args: + segment: M + type: int + totalInsertedNucs_S: + function: identity + inputs: + input: nextclade.totalInsertions + args: + segment: S + type: int + totalSnps_L: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + segment: L + type: int + totalSnps_M: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + segment: M + type: int + totalSnps_S: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + segment: S + type: int + totalStopCodons_L: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + segment: L + type: int + totalStopCodons_M: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + segment: M + type: int + totalStopCodons_S: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + segment: S + type: int + totalUnknownNucs_L: + function: identity + inputs: + input: nextclade.totalMissing + args: + segment: L + type: int + totalUnknownNucs_M: + function: identity + inputs: + input: nextclade.totalMissing + args: + segment: M + type: int + totalUnknownNucs_S: + function: identity + inputs: + input: nextclade.totalMissing + args: + segment: S + type: int + travelHistory: + function: identity + inputs: + input: travelHistory + args: + variant_L: + function: is_variant + inputs: + length: processed.length_L + numMutations: nextclade.totalSubstitutions + args: + segment: L + type: boolean + + mu: 0.004 + variant_M: + function: is_variant + inputs: + length: processed.length_M + numMutations: nextclade.totalSubstitutions + args: + segment: M + type: boolean + + mu: 0.008 + variant_S: + function: is_variant + inputs: + length: processed.length_S + numMutations: nextclade.totalSubstitutions + args: + segment: S + type: boolean + + mu: 0.004 + versionComment: + function: identity + inputs: + input: versionComment + args: diff --git a/kubernetes/loculus/fixtures/preprocessing/cchf/1.yaml b/kubernetes/loculus/fixtures/preprocessing/cchf/1.yaml new file mode 100644 index 0000000000..47c6ec3171 --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/cchf/1.yaml @@ -0,0 +1,1140 @@ +organism: cchf +batch_size: 100 +create_embl_file: true +log_level: DEBUG +molecule_type: genomic RNA +nextclade_dataset_server: https://data.clades.nextstrain.org/v3 +scientific_name: Orthonairovirus haemorrhagiae +segments: + - name: L + references: + - genes: + - RdRp + name: singleReference + nextclade_dataset_name: community/pathoplexus/cchfv/L + - name: M + references: + - genes: + - GPC + name: singleReference + nextclade_dataset_name: community/pathoplexus/cchfv/M + - name: S + references: + - genes: + - NP + name: singleReference + nextclade_dataset_name: community/pathoplexus/cchfv/S +taxon_id: 3052518 +max_sequences_per_entry: 3 +processing_spec: + + ampliconPcrPrimerScheme: + function: identity + inputs: + input: ampliconPcrPrimerScheme + args: + ampliconSize: + function: identity + inputs: + input: ampliconSize + args: + anatomicalMaterial: + function: identity + inputs: + input: anatomicalMaterial + args: + anatomicalPart: + function: identity + inputs: + input: anatomicalPart + args: + authorAffiliations: + function: identity + inputs: + input: authorAffiliations + args: + authors: + function: check_authors + inputs: + authors: authors + args: + type: authors + bioprojectAccession: + function: identity + inputs: + input: bioprojectAccession + args: + biosampleAccession: + function: identity + inputs: + input: biosampleAccession + args: + bodyProduct: + function: identity + inputs: + input: bodyProduct + args: + breadthOfCoverage: + function: identity + inputs: + input: breadthOfCoverage + args: + type: int + cellLine: + function: identity + inputs: + input: cellLine + args: + collectionDevice: + function: identity + inputs: + input: collectionDevice + args: + collectionMethod: + function: identity + inputs: + input: collectionMethod + args: + completeness_L: + function: identity + inputs: + input: nextclade.coverage + args: + segment: L + type: float + completeness_M: + function: identity + inputs: + input: nextclade.coverage + args: + segment: M + type: float + completeness_S: + function: identity + inputs: + input: nextclade.coverage + args: + segment: S + type: float + consensusSequenceSoftwareName: + function: identity + inputs: + input: consensusSequenceSoftwareName + args: + consensusSequenceSoftwareVersion: + function: identity + inputs: + input: consensusSequenceSoftwareVersion + args: + cultureId: + function: identity + inputs: + input: cultureId + args: + dehostingMethod: + function: identity + inputs: + input: dehostingMethod + args: + depthOfCoverage: + function: identity + inputs: + input: depthOfCoverage + args: + type: int + diagnosticMeasurementMethod: + function: identity + inputs: + input: diagnosticMeasurementMethod + args: + diagnosticMeasurementUnit: + function: identity + inputs: + input: diagnosticMeasurementUnit + args: + diagnosticMeasurementValue: + function: identity + inputs: + input: diagnosticMeasurementValue + args: + diagnosticTargetGeneName: + function: identity + inputs: + input: diagnosticTargetGeneName + args: + diagnosticTargetPresence: + function: identity + inputs: + input: diagnosticTargetPresence + args: + displayName: + function: concatenate + inputs: + geoLocCountry: geoLocCountry + sampleCollectionDate: sampleCollectionDate + args: + + order: + - geoLocCountry + - ACCESSION_VERSION + - sampleCollectionDate + type: + - string + - ACCESSION_VERSION + - string + earliestReleaseDate: + function: identity + inputs: + input: earliestReleaseDate + args: + type: date + environmentalMaterial: + function: identity + inputs: + input: environmentalMaterial + args: + environmentalSite: + function: identity + inputs: + input: environmentalSite + args: + experimentalSpecimenRoleType: + function: identity + inputs: + input: experimentalSpecimenRoleType + args: + exposureDetails: + function: identity + inputs: + input: exposureDetails + args: + exposureEvent: + function: identity + inputs: + input: exposureEvent + args: + exposureSetting: + function: identity + inputs: + input: exposureSetting + args: + foodProduct: + function: identity + inputs: + input: foodProduct + args: + foodProductProperties: + function: identity + inputs: + input: foodProductProperties + args: + frameShifts_L: + function: identity + inputs: + input: nextclade.frameShifts + args: + segment: L + frameShifts_M: + function: identity + inputs: + input: nextclade.frameShifts + args: + segment: M + frameShifts_S: + function: identity + inputs: + input: nextclade.frameShifts + args: + segment: S + gcaAccession: + function: identity + inputs: + input: gcaAccession + args: + geoLocAdmin1: + function: identity + inputs: + input: geoLocAdmin1 + args: + geoLocAdmin2: + function: identity + inputs: + input: geoLocAdmin2 + args: + geoLocCity: + function: identity + inputs: + input: geoLocCity + args: + geoLocCountry: + function: process_options + inputs: + input: geoLocCountry + args: + options: + - Afghanistan + - Albania + - Algeria + - American Samoa + - Andorra + - Angola + - Anguilla + - Antarctica + - Antigua and Barbuda + - Arctic Ocean + - Argentina + - Armenia + - Aruba + - Ashmore and Cartier Islands + - Atlantic Ocean + - Australia + - Austria + - Azerbaijan + - Bahamas + - Bahrain + - Baltic Sea + - Baker Island + - Bangladesh + - Barbados + - Bassas da India + - Belarus + - Belgium + - Belize + - Benin + - Bermuda + - Bhutan + - Bolivia + - Borneo + - Bosnia and Herzegovina + - Botswana + - Bouvet Island + - Brazil + - British Virgin Islands + - Brunei + - Bulgaria + - Burkina Faso + - Burundi + - Cambodia + - Cameroon + - Canada + - Cape Verde + - Cayman Islands + - Central African Republic + - Chad + - Chile + - China + - Christmas Island + - Clipperton Island + - Cocos Islands + - Colombia + - Comoros + - Cook Islands + - Coral Sea Islands + - Costa Rica + - Cote d'Ivoire + - Croatia + - Cuba + - Curacao + - Cyprus + - Czechia + - Democratic Republic of the Congo + - Denmark + - Djibouti + - Dominica + - Dominican Republic + - Ecuador + - Egypt + - El Salvador + - Equatorial Guinea + - Eritrea + - Estonia + - Eswatini + - Ethiopia + - Europa Island + - Falkland Islands (Islas Malvinas) + - Faroe Islands + - Fiji + - Finland + - France + - French Guiana + - French Polynesia + - French Southern and Antarctic Lands + - Gabon + - Gambia + - Gaza Strip + - Georgia + - Germany + - Ghana + - Gibraltar + - Glorioso Islands + - Greece + - Greenland + - Grenada + - Guadeloupe + - Guam + - Guatemala + - Guernsey + - Guinea + - Guinea-Bissau + - Guyana + - Haiti + - Heard Island and McDonald Islands + - Honduras + - Hong Kong + - Howland Island + - Hungary + - Iceland + - India + - Indian Ocean + - Indonesia + - Iran + - Iraq + - Ireland + - Isle of Man + - Israel + - Italy + - Jamaica + - Jan Mayen + - Japan + - Jarvis Island + - Jersey + - Johnston Atoll + - Jordan + - Juan de Nova Island + - Kazakhstan + - Kenya + - Kerguelen Archipelago + - Kingman Reef + - Kiribati + - Kosovo + - Kuwait + - Kyrgyzstan + - Laos + - Latvia + - Lebanon + - Lesotho + - Liberia + - Libya + - Liechtenstein + - Line Islands + - Lithuania + - Luxembourg + - Macau + - Madagascar + - Malawi + - Malaysia + - Maldives + - Mali + - Malta + - Marshall Islands + - Martinique + - Mauritania + - Mauritius + - Mayotte + - Mediterranean Sea + - Mexico + - Micronesia, Federated States of + - Midway Islands + - Moldova + - Monaco + - Mongolia + - Montenegro + - Montserrat + - Morocco + - Mozambique + - Myanmar + - Namibia + - Nauru + - Navassa Island + - Nepal + - Netherlands + - New Caledonia + - New Zealand + - Nicaragua + - Niger + - Nigeria + - Niue + - Norfolk Island + - North Korea + - North Macedonia + - North Sea + - Northern Mariana Islands + - Norway + - Oman + - Pacific Ocean + - Pakistan + - Palau + - Palmyra Atoll + - Panama + - Papua New Guinea + - Paracel Islands + - Paraguay + - Peru + - Philippines + - Pitcairn Islands + - Poland + - Portugal + - Puerto Rico + - Qatar + - Republic of the Congo + - Reunion + - Romania + - Ross Sea + - Russia + - Rwanda + - Saint Barthelemy + - Saint Helena + - Saint Kitts and Nevis + - Saint Lucia + - Saint Martin + - Saint Pierre and Miquelon + - Saint Vincent and the Grenadines + - Samoa + - San Marino + - Sao Tome and Principe + - Saudi Arabia + - Senegal + - Serbia + - Seychelles + - Sierra Leone + - Singapore + - Sint Maarten + - Slovakia + - Slovenia + - Solomon Islands + - Somalia + - South Africa + - South Georgia and the South Sandwich Islands + - South Korea + - South Sudan + - Southern Ocean + - Spain + - Spratly Islands + - Sri Lanka + - State of Palestine + - Sudan + - Suriname + - Svalbard + - Sweden + - Switzerland + - Syria + - Taiwan + - Tajikistan + - Tanzania + - Tasman Sea + - Thailand + - Timor-Leste + - Togo + - Tokelau + - Tonga + - Trinidad and Tobago + - Tromelin Island + - Tunisia + - Turkey + - Turkmenistan + - Turks and Caicos Islands + - Tuvalu + - Uganda + - Ukraine + - United Arab Emirates + - United Kingdom + - Uruguay + - USA + - Uzbekistan + - Vanuatu + - Venezuela + - Viet Nam + - Virgin Islands + - Wake Island + - Wallis and Futuna + - West Bank + - Western Sahara + - Yemen + - Zambia + - Zimbabwe + - Belgian Congo + - British Guiana + - Burma + - Czechoslovakia + - Czech Republic + - East Timor + - Korea + - Macedonia + - Micronesia + - Netherlands Antilles + - Serbia and Montenegro + - Siam + - Swaziland + - The former Yugoslav Republic of Macedonia + - USSR + - Yugoslavia + - Zaire + required: true + geoLocLatitude: + function: identity + inputs: + input: geoLocLatitude + args: + type: float + geoLocLongitude: + function: identity + inputs: + input: geoLocLongitude + args: + type: float + geoLocSite: + function: identity + inputs: + input: geoLocSite + args: + hostAge: + function: identity + inputs: + input: hostAge + args: + type: int + hostAgeBin: + function: identity + inputs: + input: hostAgeBin + args: + hostDisease: + function: identity + inputs: + input: hostDisease + args: + hostGender: + function: identity + inputs: + input: hostGender + args: + hostHealthOutcome: + function: identity + inputs: + input: hostHealthOutcome + args: + hostHealthState: + function: identity + inputs: + input: hostHealthState + args: + hostNameCommon: + function: common_name_from_id + inputs: + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostNameScientific: + function: identity + inputs: + input: hostNameScientific + args: + hostOriginCountry: + function: identity + inputs: + input: hostOriginCountry + args: + hostRole: + function: identity + inputs: + input: hostRole + args: + hostTaxonId: + function: resolve_host_taxon_id + inputs: + host: host + hostNameScientific: hostNameScientific + hostTaxonId: hostTaxonId + args: + type: int + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostVaccinationStatus: + function: identity + inputs: + input: hostVaccinationStatus + args: + insdcAccessionBase_L: + function: identity + inputs: + input: insdcAccessionBase_L + args: + segment: L + insdcAccessionBase_M: + function: identity + inputs: + input: insdcAccessionBase_M + args: + segment: M + insdcAccessionBase_S: + function: identity + inputs: + input: insdcAccessionBase_S + args: + segment: S + insdcAccessionFull_L: + function: identity + inputs: + input: insdcAccessionFull_L + args: + segment: L + insdcAccessionFull_M: + function: identity + inputs: + input: insdcAccessionFull_M + args: + segment: M + insdcAccessionFull_S: + function: identity + inputs: + input: insdcAccessionFull_S + args: + segment: S + insdcRawReadsAccession: + function: identity + inputs: + input: insdcRawReadsAccession + args: + insdcVersion_L: + function: identity + inputs: + input: insdcVersion_L + args: + segment: L + type: int + insdcVersion_M: + function: identity + inputs: + input: insdcVersion_M + args: + segment: M + type: int + insdcVersion_S: + function: identity + inputs: + input: insdcVersion_S + args: + segment: S + type: int + isLabHost: + function: identity + inputs: + input: isLabHost + args: + type: boolean + length_L: + function: identity + inputs: + input: length_L + args: + segment: L + type: int + length_M: + function: identity + inputs: + input: length_M + args: + segment: M + type: int + length_S: + function: identity + inputs: + input: length_S + args: + segment: S + type: int + lineage: + function: identity + inputs: + input: nextclade.clade + args: + segment: S + ncbiReleaseDate: + function: parse_timestamp + inputs: + timestamp: ncbiReleaseDate + args: + type: date + ncbiSourceDb: + function: identity + inputs: + input: ncbiSourceDb + args: + ncbiSubmitterCountry: + function: identity + inputs: + input: ncbiSubmitterCountry + args: + ncbiUpdateDate_L: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + segment: L + type: date + ncbiUpdateDate_M: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + segment: M + type: date + ncbiUpdateDate_S: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + segment: S + type: date + ncbiVirusName: + function: identity + inputs: + input: ncbiVirusName + args: + ncbiVirusTaxId: + function: identity + inputs: + input: ncbiVirusTaxId + args: + type: int + passageMethod: + function: identity + inputs: + input: passageMethod + args: + passageNumber: + function: identity + inputs: + input: passageNumber + args: + type: int + presamplingActivity: + function: identity + inputs: + input: presamplingActivity + args: + previousInfectionDisease: + function: identity + inputs: + input: previousInfectionDisease + args: + previousInfectionOrganism: + function: identity + inputs: + input: previousInfectionOrganism + args: + purposeOfSampling: + function: identity + inputs: + input: purposeOfSampling + args: + purposeOfSequencing: + function: identity + inputs: + input: purposeOfSequencing + args: + qualityControlDetails: + function: identity + inputs: + input: qualityControlDetails + args: + qualityControlDetermination: + function: identity + inputs: + input: qualityControlDetermination + args: + qualityControlIssues: + function: identity + inputs: + input: qualityControlIssues + args: + qualityControlMethodName: + function: identity + inputs: + input: qualityControlMethodName + args: + qualityControlMethodVersion: + function: identity + inputs: + input: qualityControlMethodVersion + args: + rawSequenceDataProcessingMethod: + function: identity + inputs: + input: rawSequenceDataProcessingMethod + args: + referenceGenomeAccession: + function: identity + inputs: + input: referenceGenomeAccession + args: + sampleCollectionDate: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + + fieldType: dateRangeString + required: true + sampleCollectionDateRangeLower: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeLower + sampleCollectionDateRangeUpper: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeUpper + sampleReceivedDate: + function: parse_and_assert_past_date + inputs: + date: sampleReceivedDate + args: + type: date + sampleType: + function: identity + inputs: + input: sampleType + args: + sequencedByContactEmail: + function: identity + inputs: + input: sequencedByContactEmail + args: + sequencedByContactName: + function: identity + inputs: + input: sequencedByContactName + args: + sequencedByOrganization: + function: identity + inputs: + input: sequencedByOrganization + args: + sequencingAssayType: + function: identity + inputs: + input: sequencingAssayType + args: + sequencingDate: + function: parse_and_assert_past_date + inputs: + date: sequencingDate + args: + type: date + sequencingInstrument: + function: identity + inputs: + input: sequencingInstrument + args: + sequencingProtocol: + function: identity + inputs: + input: sequencingProtocol + args: + signsAndSymptoms: + function: identity + inputs: + input: signsAndSymptoms + args: + specimenCollectorSampleId: + function: identity + inputs: + input: specimenCollectorSampleId + args: + specimenProcessing: + function: identity + inputs: + input: specimenProcessing + args: + specimenProcessingDetails: + function: identity + inputs: + input: specimenProcessingDetails + args: + stopCodons_L: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + segment: L + stopCodons_M: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + segment: M + stopCodons_S: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + segment: S + totalAmbiguousNucs_L: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + segment: L + type: int + totalAmbiguousNucs_M: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + segment: M + type: int + totalAmbiguousNucs_S: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + segment: S + type: int + totalDeletedNucs_L: + function: identity + inputs: + input: nextclade.totalDeletions + args: + segment: L + type: int + totalDeletedNucs_M: + function: identity + inputs: + input: nextclade.totalDeletions + args: + segment: M + type: int + totalDeletedNucs_S: + function: identity + inputs: + input: nextclade.totalDeletions + args: + segment: S + type: int + totalFrameShifts_L: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + segment: L + type: int + totalFrameShifts_M: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + segment: M + type: int + totalFrameShifts_S: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + segment: S + type: int + totalInsertedNucs_L: + function: identity + inputs: + input: nextclade.totalInsertions + args: + segment: L + type: int + totalInsertedNucs_M: + function: identity + inputs: + input: nextclade.totalInsertions + args: + segment: M + type: int + totalInsertedNucs_S: + function: identity + inputs: + input: nextclade.totalInsertions + args: + segment: S + type: int + totalSnps_L: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + segment: L + type: int + totalSnps_M: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + segment: M + type: int + totalSnps_S: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + segment: S + type: int + totalStopCodons_L: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + segment: L + type: int + totalStopCodons_M: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + segment: M + type: int + totalStopCodons_S: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + segment: S + type: int + totalUnknownNucs_L: + function: identity + inputs: + input: nextclade.totalMissing + args: + segment: L + type: int + totalUnknownNucs_M: + function: identity + inputs: + input: nextclade.totalMissing + args: + segment: M + type: int + totalUnknownNucs_S: + function: identity + inputs: + input: nextclade.totalMissing + args: + segment: S + type: int + travelHistory: + function: identity + inputs: + input: travelHistory + args: + versionComment: + function: identity + inputs: + input: versionComment + args: diff --git a/kubernetes/loculus/fixtures/preprocessing/ebola-sudan/1.yaml b/kubernetes/loculus/fixtures/preprocessing/ebola-sudan/1.yaml new file mode 100644 index 0000000000..548e7685c7 --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/ebola-sudan/1.yaml @@ -0,0 +1,916 @@ +organism: ebola-sudan +batch_size: 100 +create_embl_file: true +log_level: DEBUG +molecule_type: genomic RNA +nextclade_dataset_server: https://raw.githubusercontent.com/nextstrain/nextclade_data/ebola/data_output +scientific_name: Sudan ebolavirus +segments: + - name: main + references: + - genes: + - NP + - VP35 + - VP40 + - GP + - sGP + - ssGP + - VP30 + - VP24 + - L + name: singleReference + nextclade_dataset_name: nextstrain/ebola/sudan +taxon_id: 186540 +max_sequences_per_entry: 1 +processing_spec: + + ampliconPcrPrimerScheme: + function: identity + inputs: + input: ampliconPcrPrimerScheme + args: + ampliconSize: + function: identity + inputs: + input: ampliconSize + args: + anatomicalMaterial: + function: identity + inputs: + input: anatomicalMaterial + args: + anatomicalPart: + function: identity + inputs: + input: anatomicalPart + args: + authorAffiliations: + function: identity + inputs: + input: authorAffiliations + args: + authors: + function: check_authors + inputs: + authors: authors + args: + type: authors + bioprojectAccession: + function: identity + inputs: + input: bioprojectAccession + args: + biosampleAccession: + function: identity + inputs: + input: biosampleAccession + args: + bodyProduct: + function: identity + inputs: + input: bodyProduct + args: + breadthOfCoverage: + function: identity + inputs: + input: breadthOfCoverage + args: + type: int + cellLine: + function: identity + inputs: + input: cellLine + args: + collectionDevice: + function: identity + inputs: + input: collectionDevice + args: + collectionMethod: + function: identity + inputs: + input: collectionMethod + args: + completeness: + function: identity + inputs: + input: nextclade.coverage + args: + type: float + consensusSequenceSoftwareName: + function: identity + inputs: + input: consensusSequenceSoftwareName + args: + consensusSequenceSoftwareVersion: + function: identity + inputs: + input: consensusSequenceSoftwareVersion + args: + cultureId: + function: identity + inputs: + input: cultureId + args: + dehostingMethod: + function: identity + inputs: + input: dehostingMethod + args: + depthOfCoverage: + function: identity + inputs: + input: depthOfCoverage + args: + type: int + diagnosticMeasurementMethod: + function: identity + inputs: + input: diagnosticMeasurementMethod + args: + diagnosticMeasurementUnit: + function: identity + inputs: + input: diagnosticMeasurementUnit + args: + diagnosticMeasurementValue: + function: identity + inputs: + input: diagnosticMeasurementValue + args: + diagnosticTargetGeneName: + function: identity + inputs: + input: diagnosticTargetGeneName + args: + diagnosticTargetPresence: + function: identity + inputs: + input: diagnosticTargetPresence + args: + displayName: + function: concatenate + inputs: + geoLocCountry: geoLocCountry + sampleCollectionDate: sampleCollectionDate + args: + + order: + - geoLocCountry + - ACCESSION_VERSION + - sampleCollectionDate + type: + - string + - ACCESSION_VERSION + - string + earliestReleaseDate: + function: identity + inputs: + input: earliestReleaseDate + args: + type: date + environmentalMaterial: + function: identity + inputs: + input: environmentalMaterial + args: + environmentalSite: + function: identity + inputs: + input: environmentalSite + args: + experimentalSpecimenRoleType: + function: identity + inputs: + input: experimentalSpecimenRoleType + args: + exposureDetails: + function: identity + inputs: + input: exposureDetails + args: + exposureEvent: + function: identity + inputs: + input: exposureEvent + args: + exposureSetting: + function: identity + inputs: + input: exposureSetting + args: + foodProduct: + function: identity + inputs: + input: foodProduct + args: + foodProductProperties: + function: identity + inputs: + input: foodProductProperties + args: + frameShifts: + function: identity + inputs: + input: nextclade.frameShifts + args: + gcaAccession: + function: identity + inputs: + input: gcaAccession + args: + geoLocAdmin1: + function: identity + inputs: + input: geoLocAdmin1 + args: + geoLocAdmin2: + function: identity + inputs: + input: geoLocAdmin2 + args: + geoLocCity: + function: identity + inputs: + input: geoLocCity + args: + geoLocCountry: + function: process_options + inputs: + input: geoLocCountry + args: + options: + - Afghanistan + - Albania + - Algeria + - American Samoa + - Andorra + - Angola + - Anguilla + - Antarctica + - Antigua and Barbuda + - Arctic Ocean + - Argentina + - Armenia + - Aruba + - Ashmore and Cartier Islands + - Atlantic Ocean + - Australia + - Austria + - Azerbaijan + - Bahamas + - Bahrain + - Baltic Sea + - Baker Island + - Bangladesh + - Barbados + - Bassas da India + - Belarus + - Belgium + - Belize + - Benin + - Bermuda + - Bhutan + - Bolivia + - Borneo + - Bosnia and Herzegovina + - Botswana + - Bouvet Island + - Brazil + - British Virgin Islands + - Brunei + - Bulgaria + - Burkina Faso + - Burundi + - Cambodia + - Cameroon + - Canada + - Cape Verde + - Cayman Islands + - Central African Republic + - Chad + - Chile + - China + - Christmas Island + - Clipperton Island + - Cocos Islands + - Colombia + - Comoros + - Cook Islands + - Coral Sea Islands + - Costa Rica + - Cote d'Ivoire + - Croatia + - Cuba + - Curacao + - Cyprus + - Czechia + - Democratic Republic of the Congo + - Denmark + - Djibouti + - Dominica + - Dominican Republic + - Ecuador + - Egypt + - El Salvador + - Equatorial Guinea + - Eritrea + - Estonia + - Eswatini + - Ethiopia + - Europa Island + - Falkland Islands (Islas Malvinas) + - Faroe Islands + - Fiji + - Finland + - France + - French Guiana + - French Polynesia + - French Southern and Antarctic Lands + - Gabon + - Gambia + - Gaza Strip + - Georgia + - Germany + - Ghana + - Gibraltar + - Glorioso Islands + - Greece + - Greenland + - Grenada + - Guadeloupe + - Guam + - Guatemala + - Guernsey + - Guinea + - Guinea-Bissau + - Guyana + - Haiti + - Heard Island and McDonald Islands + - Honduras + - Hong Kong + - Howland Island + - Hungary + - Iceland + - India + - Indian Ocean + - Indonesia + - Iran + - Iraq + - Ireland + - Isle of Man + - Israel + - Italy + - Jamaica + - Jan Mayen + - Japan + - Jarvis Island + - Jersey + - Johnston Atoll + - Jordan + - Juan de Nova Island + - Kazakhstan + - Kenya + - Kerguelen Archipelago + - Kingman Reef + - Kiribati + - Kosovo + - Kuwait + - Kyrgyzstan + - Laos + - Latvia + - Lebanon + - Lesotho + - Liberia + - Libya + - Liechtenstein + - Line Islands + - Lithuania + - Luxembourg + - Macau + - Madagascar + - Malawi + - Malaysia + - Maldives + - Mali + - Malta + - Marshall Islands + - Martinique + - Mauritania + - Mauritius + - Mayotte + - Mediterranean Sea + - Mexico + - Micronesia, Federated States of + - Midway Islands + - Moldova + - Monaco + - Mongolia + - Montenegro + - Montserrat + - Morocco + - Mozambique + - Myanmar + - Namibia + - Nauru + - Navassa Island + - Nepal + - Netherlands + - New Caledonia + - New Zealand + - Nicaragua + - Niger + - Nigeria + - Niue + - Norfolk Island + - North Korea + - North Macedonia + - North Sea + - Northern Mariana Islands + - Norway + - Oman + - Pacific Ocean + - Pakistan + - Palau + - Palmyra Atoll + - Panama + - Papua New Guinea + - Paracel Islands + - Paraguay + - Peru + - Philippines + - Pitcairn Islands + - Poland + - Portugal + - Puerto Rico + - Qatar + - Republic of the Congo + - Reunion + - Romania + - Ross Sea + - Russia + - Rwanda + - Saint Barthelemy + - Saint Helena + - Saint Kitts and Nevis + - Saint Lucia + - Saint Martin + - Saint Pierre and Miquelon + - Saint Vincent and the Grenadines + - Samoa + - San Marino + - Sao Tome and Principe + - Saudi Arabia + - Senegal + - Serbia + - Seychelles + - Sierra Leone + - Singapore + - Sint Maarten + - Slovakia + - Slovenia + - Solomon Islands + - Somalia + - South Africa + - South Georgia and the South Sandwich Islands + - South Korea + - South Sudan + - Southern Ocean + - Spain + - Spratly Islands + - Sri Lanka + - State of Palestine + - Sudan + - Suriname + - Svalbard + - Sweden + - Switzerland + - Syria + - Taiwan + - Tajikistan + - Tanzania + - Tasman Sea + - Thailand + - Timor-Leste + - Togo + - Tokelau + - Tonga + - Trinidad and Tobago + - Tromelin Island + - Tunisia + - Turkey + - Turkmenistan + - Turks and Caicos Islands + - Tuvalu + - Uganda + - Ukraine + - United Arab Emirates + - United Kingdom + - Uruguay + - USA + - Uzbekistan + - Vanuatu + - Venezuela + - Viet Nam + - Virgin Islands + - Wake Island + - Wallis and Futuna + - West Bank + - Western Sahara + - Yemen + - Zambia + - Zimbabwe + - Belgian Congo + - British Guiana + - Burma + - Czechoslovakia + - Czech Republic + - East Timor + - Korea + - Macedonia + - Micronesia + - Netherlands Antilles + - Serbia and Montenegro + - Siam + - Swaziland + - The former Yugoslav Republic of Macedonia + - USSR + - Yugoslavia + - Zaire + required: true + geoLocLatitude: + function: identity + inputs: + input: geoLocLatitude + args: + type: float + geoLocLongitude: + function: identity + inputs: + input: geoLocLongitude + args: + type: float + geoLocSite: + function: identity + inputs: + input: geoLocSite + args: + hostAge: + function: identity + inputs: + input: hostAge + args: + type: int + hostAgeBin: + function: identity + inputs: + input: hostAgeBin + args: + hostDisease: + function: identity + inputs: + input: hostDisease + args: + hostGender: + function: identity + inputs: + input: hostGender + args: + hostHealthOutcome: + function: identity + inputs: + input: hostHealthOutcome + args: + hostHealthState: + function: identity + inputs: + input: hostHealthState + args: + hostNameCommon: + function: common_name_from_id + inputs: + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostNameScientific: + function: scientific_name_from_id + inputs: + hostNameScientific: hostNameScientific + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostOriginCountry: + function: identity + inputs: + input: hostOriginCountry + args: + hostRole: + function: identity + inputs: + input: hostRole + args: + hostTaxonId: + function: resolve_host_taxon_id + inputs: + host: host + hostNameScientific: hostNameScientific + hostTaxonId: hostTaxonId + args: + type: int + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostVaccinationStatus: + function: identity + inputs: + input: hostVaccinationStatus + args: + insdcAccessionBase: + function: identity + inputs: + input: insdcAccessionBase + args: + insdcAccessionFull: + function: identity + inputs: + input: insdcAccessionFull + args: + insdcRawReadsAccession: + function: identity + inputs: + input: insdcRawReadsAccession + args: + insdcVersion: + function: identity + inputs: + input: insdcVersion + args: + type: int + isLabHost: + function: identity + inputs: + input: isLabHost + args: + type: boolean + length: + function: identity + inputs: + input: length + args: + type: int + ncbiReleaseDate: + function: parse_timestamp + inputs: + timestamp: ncbiReleaseDate + args: + type: date + ncbiSourceDb: + function: identity + inputs: + input: ncbiSourceDb + args: + ncbiSubmitterCountry: + function: identity + inputs: + input: ncbiSubmitterCountry + args: + ncbiUpdateDate: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + type: date + ncbiVirusName: + function: identity + inputs: + input: ncbiVirusName + args: + ncbiVirusTaxId: + function: identity + inputs: + input: ncbiVirusTaxId + args: + type: int + passageMethod: + function: identity + inputs: + input: passageMethod + args: + passageNumber: + function: identity + inputs: + input: passageNumber + args: + type: int + presamplingActivity: + function: identity + inputs: + input: presamplingActivity + args: + previousInfectionDisease: + function: identity + inputs: + input: previousInfectionDisease + args: + previousInfectionOrganism: + function: identity + inputs: + input: previousInfectionOrganism + args: + purposeOfSampling: + function: identity + inputs: + input: purposeOfSampling + args: + purposeOfSequencing: + function: identity + inputs: + input: purposeOfSequencing + args: + qualityControlDetails: + function: identity + inputs: + input: qualityControlDetails + args: + qualityControlDetermination: + function: identity + inputs: + input: qualityControlDetermination + args: + qualityControlIssues: + function: identity + inputs: + input: qualityControlIssues + args: + qualityControlMethodName: + function: identity + inputs: + input: qualityControlMethodName + args: + qualityControlMethodVersion: + function: identity + inputs: + input: qualityControlMethodVersion + args: + rawSequenceDataProcessingMethod: + function: identity + inputs: + input: rawSequenceDataProcessingMethod + args: + referenceGenomeAccession: + function: identity + inputs: + input: referenceGenomeAccession + args: + sampleCollectionDate: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + + fieldType: dateRangeString + required: true + sampleCollectionDateRangeLower: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeLower + sampleCollectionDateRangeUpper: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeUpper + sampleReceivedDate: + function: parse_and_assert_past_date + inputs: + date: sampleReceivedDate + args: + type: date + sampleType: + function: identity + inputs: + input: sampleType + args: + sequencedByContactEmail: + function: identity + inputs: + input: sequencedByContactEmail + args: + sequencedByContactName: + function: identity + inputs: + input: sequencedByContactName + args: + sequencedByOrganization: + function: identity + inputs: + input: sequencedByOrganization + args: + sequencingAssayType: + function: identity + inputs: + input: sequencingAssayType + args: + sequencingDate: + function: parse_and_assert_past_date + inputs: + date: sequencingDate + args: + type: date + sequencingInstrument: + function: identity + inputs: + input: sequencingInstrument + args: + sequencingProtocol: + function: identity + inputs: + input: sequencingProtocol + args: + signsAndSymptoms: + function: identity + inputs: + input: signsAndSymptoms + args: + specimenCollectorSampleId: + function: identity + inputs: + input: specimenCollectorSampleId + args: + specimenProcessing: + function: identity + inputs: + input: specimenProcessing + args: + specimenProcessingDetails: + function: identity + inputs: + input: specimenProcessingDetails + args: + stopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + totalAmbiguousNucs: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + type: int + totalDeletedNucs: + function: identity + inputs: + input: nextclade.totalDeletions + args: + type: int + totalFrameShifts: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + type: int + totalInsertedNucs: + function: identity + inputs: + input: nextclade.totalInsertions + args: + type: int + totalSnps: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + type: int + totalStopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + type: int + totalUnknownNucs: + function: identity + inputs: + input: nextclade.totalMissing + args: + type: int + travelHistory: + function: identity + inputs: + input: travelHistory + args: + versionComment: + function: identity + inputs: + input: versionComment + args: diff --git a/kubernetes/loculus/fixtures/preprocessing/ebola-sudan/2.yaml b/kubernetes/loculus/fixtures/preprocessing/ebola-sudan/2.yaml new file mode 100644 index 0000000000..548e7685c7 --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/ebola-sudan/2.yaml @@ -0,0 +1,916 @@ +organism: ebola-sudan +batch_size: 100 +create_embl_file: true +log_level: DEBUG +molecule_type: genomic RNA +nextclade_dataset_server: https://raw.githubusercontent.com/nextstrain/nextclade_data/ebola/data_output +scientific_name: Sudan ebolavirus +segments: + - name: main + references: + - genes: + - NP + - VP35 + - VP40 + - GP + - sGP + - ssGP + - VP30 + - VP24 + - L + name: singleReference + nextclade_dataset_name: nextstrain/ebola/sudan +taxon_id: 186540 +max_sequences_per_entry: 1 +processing_spec: + + ampliconPcrPrimerScheme: + function: identity + inputs: + input: ampliconPcrPrimerScheme + args: + ampliconSize: + function: identity + inputs: + input: ampliconSize + args: + anatomicalMaterial: + function: identity + inputs: + input: anatomicalMaterial + args: + anatomicalPart: + function: identity + inputs: + input: anatomicalPart + args: + authorAffiliations: + function: identity + inputs: + input: authorAffiliations + args: + authors: + function: check_authors + inputs: + authors: authors + args: + type: authors + bioprojectAccession: + function: identity + inputs: + input: bioprojectAccession + args: + biosampleAccession: + function: identity + inputs: + input: biosampleAccession + args: + bodyProduct: + function: identity + inputs: + input: bodyProduct + args: + breadthOfCoverage: + function: identity + inputs: + input: breadthOfCoverage + args: + type: int + cellLine: + function: identity + inputs: + input: cellLine + args: + collectionDevice: + function: identity + inputs: + input: collectionDevice + args: + collectionMethod: + function: identity + inputs: + input: collectionMethod + args: + completeness: + function: identity + inputs: + input: nextclade.coverage + args: + type: float + consensusSequenceSoftwareName: + function: identity + inputs: + input: consensusSequenceSoftwareName + args: + consensusSequenceSoftwareVersion: + function: identity + inputs: + input: consensusSequenceSoftwareVersion + args: + cultureId: + function: identity + inputs: + input: cultureId + args: + dehostingMethod: + function: identity + inputs: + input: dehostingMethod + args: + depthOfCoverage: + function: identity + inputs: + input: depthOfCoverage + args: + type: int + diagnosticMeasurementMethod: + function: identity + inputs: + input: diagnosticMeasurementMethod + args: + diagnosticMeasurementUnit: + function: identity + inputs: + input: diagnosticMeasurementUnit + args: + diagnosticMeasurementValue: + function: identity + inputs: + input: diagnosticMeasurementValue + args: + diagnosticTargetGeneName: + function: identity + inputs: + input: diagnosticTargetGeneName + args: + diagnosticTargetPresence: + function: identity + inputs: + input: diagnosticTargetPresence + args: + displayName: + function: concatenate + inputs: + geoLocCountry: geoLocCountry + sampleCollectionDate: sampleCollectionDate + args: + + order: + - geoLocCountry + - ACCESSION_VERSION + - sampleCollectionDate + type: + - string + - ACCESSION_VERSION + - string + earliestReleaseDate: + function: identity + inputs: + input: earliestReleaseDate + args: + type: date + environmentalMaterial: + function: identity + inputs: + input: environmentalMaterial + args: + environmentalSite: + function: identity + inputs: + input: environmentalSite + args: + experimentalSpecimenRoleType: + function: identity + inputs: + input: experimentalSpecimenRoleType + args: + exposureDetails: + function: identity + inputs: + input: exposureDetails + args: + exposureEvent: + function: identity + inputs: + input: exposureEvent + args: + exposureSetting: + function: identity + inputs: + input: exposureSetting + args: + foodProduct: + function: identity + inputs: + input: foodProduct + args: + foodProductProperties: + function: identity + inputs: + input: foodProductProperties + args: + frameShifts: + function: identity + inputs: + input: nextclade.frameShifts + args: + gcaAccession: + function: identity + inputs: + input: gcaAccession + args: + geoLocAdmin1: + function: identity + inputs: + input: geoLocAdmin1 + args: + geoLocAdmin2: + function: identity + inputs: + input: geoLocAdmin2 + args: + geoLocCity: + function: identity + inputs: + input: geoLocCity + args: + geoLocCountry: + function: process_options + inputs: + input: geoLocCountry + args: + options: + - Afghanistan + - Albania + - Algeria + - American Samoa + - Andorra + - Angola + - Anguilla + - Antarctica + - Antigua and Barbuda + - Arctic Ocean + - Argentina + - Armenia + - Aruba + - Ashmore and Cartier Islands + - Atlantic Ocean + - Australia + - Austria + - Azerbaijan + - Bahamas + - Bahrain + - Baltic Sea + - Baker Island + - Bangladesh + - Barbados + - Bassas da India + - Belarus + - Belgium + - Belize + - Benin + - Bermuda + - Bhutan + - Bolivia + - Borneo + - Bosnia and Herzegovina + - Botswana + - Bouvet Island + - Brazil + - British Virgin Islands + - Brunei + - Bulgaria + - Burkina Faso + - Burundi + - Cambodia + - Cameroon + - Canada + - Cape Verde + - Cayman Islands + - Central African Republic + - Chad + - Chile + - China + - Christmas Island + - Clipperton Island + - Cocos Islands + - Colombia + - Comoros + - Cook Islands + - Coral Sea Islands + - Costa Rica + - Cote d'Ivoire + - Croatia + - Cuba + - Curacao + - Cyprus + - Czechia + - Democratic Republic of the Congo + - Denmark + - Djibouti + - Dominica + - Dominican Republic + - Ecuador + - Egypt + - El Salvador + - Equatorial Guinea + - Eritrea + - Estonia + - Eswatini + - Ethiopia + - Europa Island + - Falkland Islands (Islas Malvinas) + - Faroe Islands + - Fiji + - Finland + - France + - French Guiana + - French Polynesia + - French Southern and Antarctic Lands + - Gabon + - Gambia + - Gaza Strip + - Georgia + - Germany + - Ghana + - Gibraltar + - Glorioso Islands + - Greece + - Greenland + - Grenada + - Guadeloupe + - Guam + - Guatemala + - Guernsey + - Guinea + - Guinea-Bissau + - Guyana + - Haiti + - Heard Island and McDonald Islands + - Honduras + - Hong Kong + - Howland Island + - Hungary + - Iceland + - India + - Indian Ocean + - Indonesia + - Iran + - Iraq + - Ireland + - Isle of Man + - Israel + - Italy + - Jamaica + - Jan Mayen + - Japan + - Jarvis Island + - Jersey + - Johnston Atoll + - Jordan + - Juan de Nova Island + - Kazakhstan + - Kenya + - Kerguelen Archipelago + - Kingman Reef + - Kiribati + - Kosovo + - Kuwait + - Kyrgyzstan + - Laos + - Latvia + - Lebanon + - Lesotho + - Liberia + - Libya + - Liechtenstein + - Line Islands + - Lithuania + - Luxembourg + - Macau + - Madagascar + - Malawi + - Malaysia + - Maldives + - Mali + - Malta + - Marshall Islands + - Martinique + - Mauritania + - Mauritius + - Mayotte + - Mediterranean Sea + - Mexico + - Micronesia, Federated States of + - Midway Islands + - Moldova + - Monaco + - Mongolia + - Montenegro + - Montserrat + - Morocco + - Mozambique + - Myanmar + - Namibia + - Nauru + - Navassa Island + - Nepal + - Netherlands + - New Caledonia + - New Zealand + - Nicaragua + - Niger + - Nigeria + - Niue + - Norfolk Island + - North Korea + - North Macedonia + - North Sea + - Northern Mariana Islands + - Norway + - Oman + - Pacific Ocean + - Pakistan + - Palau + - Palmyra Atoll + - Panama + - Papua New Guinea + - Paracel Islands + - Paraguay + - Peru + - Philippines + - Pitcairn Islands + - Poland + - Portugal + - Puerto Rico + - Qatar + - Republic of the Congo + - Reunion + - Romania + - Ross Sea + - Russia + - Rwanda + - Saint Barthelemy + - Saint Helena + - Saint Kitts and Nevis + - Saint Lucia + - Saint Martin + - Saint Pierre and Miquelon + - Saint Vincent and the Grenadines + - Samoa + - San Marino + - Sao Tome and Principe + - Saudi Arabia + - Senegal + - Serbia + - Seychelles + - Sierra Leone + - Singapore + - Sint Maarten + - Slovakia + - Slovenia + - Solomon Islands + - Somalia + - South Africa + - South Georgia and the South Sandwich Islands + - South Korea + - South Sudan + - Southern Ocean + - Spain + - Spratly Islands + - Sri Lanka + - State of Palestine + - Sudan + - Suriname + - Svalbard + - Sweden + - Switzerland + - Syria + - Taiwan + - Tajikistan + - Tanzania + - Tasman Sea + - Thailand + - Timor-Leste + - Togo + - Tokelau + - Tonga + - Trinidad and Tobago + - Tromelin Island + - Tunisia + - Turkey + - Turkmenistan + - Turks and Caicos Islands + - Tuvalu + - Uganda + - Ukraine + - United Arab Emirates + - United Kingdom + - Uruguay + - USA + - Uzbekistan + - Vanuatu + - Venezuela + - Viet Nam + - Virgin Islands + - Wake Island + - Wallis and Futuna + - West Bank + - Western Sahara + - Yemen + - Zambia + - Zimbabwe + - Belgian Congo + - British Guiana + - Burma + - Czechoslovakia + - Czech Republic + - East Timor + - Korea + - Macedonia + - Micronesia + - Netherlands Antilles + - Serbia and Montenegro + - Siam + - Swaziland + - The former Yugoslav Republic of Macedonia + - USSR + - Yugoslavia + - Zaire + required: true + geoLocLatitude: + function: identity + inputs: + input: geoLocLatitude + args: + type: float + geoLocLongitude: + function: identity + inputs: + input: geoLocLongitude + args: + type: float + geoLocSite: + function: identity + inputs: + input: geoLocSite + args: + hostAge: + function: identity + inputs: + input: hostAge + args: + type: int + hostAgeBin: + function: identity + inputs: + input: hostAgeBin + args: + hostDisease: + function: identity + inputs: + input: hostDisease + args: + hostGender: + function: identity + inputs: + input: hostGender + args: + hostHealthOutcome: + function: identity + inputs: + input: hostHealthOutcome + args: + hostHealthState: + function: identity + inputs: + input: hostHealthState + args: + hostNameCommon: + function: common_name_from_id + inputs: + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostNameScientific: + function: scientific_name_from_id + inputs: + hostNameScientific: hostNameScientific + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostOriginCountry: + function: identity + inputs: + input: hostOriginCountry + args: + hostRole: + function: identity + inputs: + input: hostRole + args: + hostTaxonId: + function: resolve_host_taxon_id + inputs: + host: host + hostNameScientific: hostNameScientific + hostTaxonId: hostTaxonId + args: + type: int + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostVaccinationStatus: + function: identity + inputs: + input: hostVaccinationStatus + args: + insdcAccessionBase: + function: identity + inputs: + input: insdcAccessionBase + args: + insdcAccessionFull: + function: identity + inputs: + input: insdcAccessionFull + args: + insdcRawReadsAccession: + function: identity + inputs: + input: insdcRawReadsAccession + args: + insdcVersion: + function: identity + inputs: + input: insdcVersion + args: + type: int + isLabHost: + function: identity + inputs: + input: isLabHost + args: + type: boolean + length: + function: identity + inputs: + input: length + args: + type: int + ncbiReleaseDate: + function: parse_timestamp + inputs: + timestamp: ncbiReleaseDate + args: + type: date + ncbiSourceDb: + function: identity + inputs: + input: ncbiSourceDb + args: + ncbiSubmitterCountry: + function: identity + inputs: + input: ncbiSubmitterCountry + args: + ncbiUpdateDate: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + type: date + ncbiVirusName: + function: identity + inputs: + input: ncbiVirusName + args: + ncbiVirusTaxId: + function: identity + inputs: + input: ncbiVirusTaxId + args: + type: int + passageMethod: + function: identity + inputs: + input: passageMethod + args: + passageNumber: + function: identity + inputs: + input: passageNumber + args: + type: int + presamplingActivity: + function: identity + inputs: + input: presamplingActivity + args: + previousInfectionDisease: + function: identity + inputs: + input: previousInfectionDisease + args: + previousInfectionOrganism: + function: identity + inputs: + input: previousInfectionOrganism + args: + purposeOfSampling: + function: identity + inputs: + input: purposeOfSampling + args: + purposeOfSequencing: + function: identity + inputs: + input: purposeOfSequencing + args: + qualityControlDetails: + function: identity + inputs: + input: qualityControlDetails + args: + qualityControlDetermination: + function: identity + inputs: + input: qualityControlDetermination + args: + qualityControlIssues: + function: identity + inputs: + input: qualityControlIssues + args: + qualityControlMethodName: + function: identity + inputs: + input: qualityControlMethodName + args: + qualityControlMethodVersion: + function: identity + inputs: + input: qualityControlMethodVersion + args: + rawSequenceDataProcessingMethod: + function: identity + inputs: + input: rawSequenceDataProcessingMethod + args: + referenceGenomeAccession: + function: identity + inputs: + input: referenceGenomeAccession + args: + sampleCollectionDate: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + + fieldType: dateRangeString + required: true + sampleCollectionDateRangeLower: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeLower + sampleCollectionDateRangeUpper: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeUpper + sampleReceivedDate: + function: parse_and_assert_past_date + inputs: + date: sampleReceivedDate + args: + type: date + sampleType: + function: identity + inputs: + input: sampleType + args: + sequencedByContactEmail: + function: identity + inputs: + input: sequencedByContactEmail + args: + sequencedByContactName: + function: identity + inputs: + input: sequencedByContactName + args: + sequencedByOrganization: + function: identity + inputs: + input: sequencedByOrganization + args: + sequencingAssayType: + function: identity + inputs: + input: sequencingAssayType + args: + sequencingDate: + function: parse_and_assert_past_date + inputs: + date: sequencingDate + args: + type: date + sequencingInstrument: + function: identity + inputs: + input: sequencingInstrument + args: + sequencingProtocol: + function: identity + inputs: + input: sequencingProtocol + args: + signsAndSymptoms: + function: identity + inputs: + input: signsAndSymptoms + args: + specimenCollectorSampleId: + function: identity + inputs: + input: specimenCollectorSampleId + args: + specimenProcessing: + function: identity + inputs: + input: specimenProcessing + args: + specimenProcessingDetails: + function: identity + inputs: + input: specimenProcessingDetails + args: + stopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + totalAmbiguousNucs: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + type: int + totalDeletedNucs: + function: identity + inputs: + input: nextclade.totalDeletions + args: + type: int + totalFrameShifts: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + type: int + totalInsertedNucs: + function: identity + inputs: + input: nextclade.totalInsertions + args: + type: int + totalSnps: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + type: int + totalStopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + type: int + totalUnknownNucs: + function: identity + inputs: + input: nextclade.totalMissing + args: + type: int + travelHistory: + function: identity + inputs: + input: travelHistory + args: + versionComment: + function: identity + inputs: + input: versionComment + args: diff --git a/kubernetes/loculus/fixtures/preprocessing/enteroviruses/1.yaml b/kubernetes/loculus/fixtures/preprocessing/enteroviruses/1.yaml new file mode 100644 index 0000000000..5066573196 --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/enteroviruses/1.yaml @@ -0,0 +1,1002 @@ +organism: enteroviruses +batch_size: 100 +create_embl_file: true +log_level: DEBUG +minimizer_url: https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/evs_minimizer-index.json +nextclade_dataset_server: https://raw.githubusercontent.com/nextstrain/nextclade_data/evs-datasets/data_output +segment_classification_method: minimizer +segments: + - name: main + references: + - accepted_dataset_matches: + - community/hodcroftlab/enterovirus/cva16 + - community/hodcroftlab/enterovirus/enterovirus/linked/CV-A16 + genes: + - VP4 + - VP2 + - VP3 + - VP1 + - 2A + - 2B + - 2C + - 3A + - 3B + - 3C + - 3D + name: CV-A16 + nextclade_dataset_name: enpen/enterovirus/cv-a16 + - accepted_dataset_matches: + - community/hodcroftlab/enterovirus/enterovirus/linked/CV-A10 + genes: + - VP4 + - VP2 + - VP3 + - VP1 + - 2A + - 2B + - 2C + - 3A + - 3B + - 3C + - 3D + name: CV-A10 + nextclade_dataset_name: enpen/enterovirus/cv-a10 + - accepted_dataset_matches: + - community/hodcroftlab/enterovirus/enterovirus/linked/EV-A71 + genes: + - VP4 + - VP2 + - VP3 + - VP1 + - 2A + - 2B + - 2C + - 3A + - 3B + - 3C + - 3D + name: EV-A71 + nextclade_dataset_name: enpen/enterovirus/ev-a71 + - accepted_dataset_matches: + - community/hodcroftlab/enterovirus/enterovirus/linked/EV-D68 + genes: + - VP4 + - VP2 + - VP3 + - VP1 + - 2A + - 2B + - 2C + - 3A + - 3B + - 3C + - 3D + name: EV-D68 + nextclade_dataset_name: enpen/enterovirus/ev-d68 + nextclade_dataset_server: https://data.clades.nextstrain.org/v3 +max_sequences_per_entry: 1 +processing_spec: + + ampliconPcrPrimerScheme: + function: identity + inputs: + input: ampliconPcrPrimerScheme + args: + ampliconSize: + function: identity + inputs: + input: ampliconSize + args: + anatomicalMaterial: + function: identity + inputs: + input: anatomicalMaterial + args: + anatomicalPart: + function: identity + inputs: + input: anatomicalPart + args: + authorAffiliations: + function: identity + inputs: + input: authorAffiliations + args: + authors: + function: check_authors + inputs: + authors: authors + args: + type: authors + bioprojectAccession: + function: identity + inputs: + input: bioprojectAccession + args: + biosampleAccession: + function: identity + inputs: + input: biosampleAccession + args: + bodyProduct: + function: identity + inputs: + input: bodyProduct + args: + breadthOfCoverage: + function: identity + inputs: + input: breadthOfCoverage + args: + type: int + cellLine: + function: identity + inputs: + input: cellLine + args: + clade_cv_a10: + function: identity + inputs: + input: nextclade.clade + args: + segment: main + reference: CV-A10 + clade_cv_a16: + function: identity + inputs: + input: nextclade.clade + args: + segment: main + reference: CV-A16 + clade_ev_a71: + function: identity + inputs: + input: nextclade.clade + args: + segment: main + reference: EV-A71 + clade_ev_d68: + function: identity + inputs: + input: nextclade.clade + args: + segment: main + reference: EV-D68 + collectionDevice: + function: identity + inputs: + input: collectionDevice + args: + collectionMethod: + function: identity + inputs: + input: collectionMethod + args: + completeness: + function: identity + inputs: + input: nextclade.coverage + args: + type: float + consensusSequenceSoftwareName: + function: identity + inputs: + input: consensusSequenceSoftwareName + args: + consensusSequenceSoftwareVersion: + function: identity + inputs: + input: consensusSequenceSoftwareVersion + args: + cultureId: + function: identity + inputs: + input: cultureId + args: + dehostingMethod: + function: identity + inputs: + input: dehostingMethod + args: + depthOfCoverage: + function: identity + inputs: + input: depthOfCoverage + args: + type: int + diagnosticMeasurementMethod: + function: identity + inputs: + input: diagnosticMeasurementMethod + args: + diagnosticMeasurementUnit: + function: identity + inputs: + input: diagnosticMeasurementUnit + args: + diagnosticMeasurementValue: + function: identity + inputs: + input: diagnosticMeasurementValue + args: + diagnosticTargetGeneName: + function: identity + inputs: + input: diagnosticTargetGeneName + args: + diagnosticTargetPresence: + function: identity + inputs: + input: diagnosticTargetPresence + args: + displayName: + function: concatenate + inputs: + geoLocCountry: geoLocCountry + sampleCollectionDate: sampleCollectionDate + args: + + order: + - geoLocCountry + - ACCESSION_VERSION + - sampleCollectionDate + type: + - string + - ACCESSION_VERSION + - string + earliestReleaseDate: + function: identity + inputs: + input: earliestReleaseDate + args: + type: date + environmentalMaterial: + function: identity + inputs: + input: environmentalMaterial + args: + environmentalSite: + function: identity + inputs: + input: environmentalSite + args: + experimentalSpecimenRoleType: + function: identity + inputs: + input: experimentalSpecimenRoleType + args: + exposureDetails: + function: identity + inputs: + input: exposureDetails + args: + exposureEvent: + function: identity + inputs: + input: exposureEvent + args: + exposureSetting: + function: identity + inputs: + input: exposureSetting + args: + foodProduct: + function: identity + inputs: + input: foodProduct + args: + foodProductProperties: + function: identity + inputs: + input: foodProductProperties + args: + frameShifts: + function: identity + inputs: + input: nextclade.frameShifts + args: + gcaAccession: + function: identity + inputs: + input: gcaAccession + args: + genotype: + function: identity + inputs: + input: ASSIGNED_REFERENCE + args: + geoLocAdmin1: + function: identity + inputs: + input: geoLocAdmin1 + args: + geoLocAdmin2: + function: identity + inputs: + input: geoLocAdmin2 + args: + geoLocCity: + function: identity + inputs: + input: geoLocCity + args: + geoLocCountry: + function: process_options + inputs: + input: geoLocCountry + args: + options: + - Afghanistan + - Albania + - Algeria + - American Samoa + - Andorra + - Angola + - Anguilla + - Antarctica + - Antigua and Barbuda + - Arctic Ocean + - Argentina + - Armenia + - Aruba + - Ashmore and Cartier Islands + - Atlantic Ocean + - Australia + - Austria + - Azerbaijan + - Bahamas + - Bahrain + - Baltic Sea + - Baker Island + - Bangladesh + - Barbados + - Bassas da India + - Belarus + - Belgium + - Belize + - Benin + - Bermuda + - Bhutan + - Bolivia + - Borneo + - Bosnia and Herzegovina + - Botswana + - Bouvet Island + - Brazil + - British Virgin Islands + - Brunei + - Bulgaria + - Burkina Faso + - Burundi + - Cambodia + - Cameroon + - Canada + - Cape Verde + - Cayman Islands + - Central African Republic + - Chad + - Chile + - China + - Christmas Island + - Clipperton Island + - Cocos Islands + - Colombia + - Comoros + - Cook Islands + - Coral Sea Islands + - Costa Rica + - Cote d'Ivoire + - Croatia + - Cuba + - Curacao + - Cyprus + - Czechia + - Democratic Republic of the Congo + - Denmark + - Djibouti + - Dominica + - Dominican Republic + - Ecuador + - Egypt + - El Salvador + - Equatorial Guinea + - Eritrea + - Estonia + - Eswatini + - Ethiopia + - Europa Island + - Falkland Islands (Islas Malvinas) + - Faroe Islands + - Fiji + - Finland + - France + - French Guiana + - French Polynesia + - French Southern and Antarctic Lands + - Gabon + - Gambia + - Gaza Strip + - Georgia + - Germany + - Ghana + - Gibraltar + - Glorioso Islands + - Greece + - Greenland + - Grenada + - Guadeloupe + - Guam + - Guatemala + - Guernsey + - Guinea + - Guinea-Bissau + - Guyana + - Haiti + - Heard Island and McDonald Islands + - Honduras + - Hong Kong + - Howland Island + - Hungary + - Iceland + - India + - Indian Ocean + - Indonesia + - Iran + - Iraq + - Ireland + - Isle of Man + - Israel + - Italy + - Jamaica + - Jan Mayen + - Japan + - Jarvis Island + - Jersey + - Johnston Atoll + - Jordan + - Juan de Nova Island + - Kazakhstan + - Kenya + - Kerguelen Archipelago + - Kingman Reef + - Kiribati + - Kosovo + - Kuwait + - Kyrgyzstan + - Laos + - Latvia + - Lebanon + - Lesotho + - Liberia + - Libya + - Liechtenstein + - Line Islands + - Lithuania + - Luxembourg + - Macau + - Madagascar + - Malawi + - Malaysia + - Maldives + - Mali + - Malta + - Marshall Islands + - Martinique + - Mauritania + - Mauritius + - Mayotte + - Mediterranean Sea + - Mexico + - Micronesia, Federated States of + - Midway Islands + - Moldova + - Monaco + - Mongolia + - Montenegro + - Montserrat + - Morocco + - Mozambique + - Myanmar + - Namibia + - Nauru + - Navassa Island + - Nepal + - Netherlands + - New Caledonia + - New Zealand + - Nicaragua + - Niger + - Nigeria + - Niue + - Norfolk Island + - North Korea + - North Macedonia + - North Sea + - Northern Mariana Islands + - Norway + - Oman + - Pacific Ocean + - Pakistan + - Palau + - Palmyra Atoll + - Panama + - Papua New Guinea + - Paracel Islands + - Paraguay + - Peru + - Philippines + - Pitcairn Islands + - Poland + - Portugal + - Puerto Rico + - Qatar + - Republic of the Congo + - Reunion + - Romania + - Ross Sea + - Russia + - Rwanda + - Saint Barthelemy + - Saint Helena + - Saint Kitts and Nevis + - Saint Lucia + - Saint Martin + - Saint Pierre and Miquelon + - Saint Vincent and the Grenadines + - Samoa + - San Marino + - Sao Tome and Principe + - Saudi Arabia + - Senegal + - Serbia + - Seychelles + - Sierra Leone + - Singapore + - Sint Maarten + - Slovakia + - Slovenia + - Solomon Islands + - Somalia + - South Africa + - South Georgia and the South Sandwich Islands + - South Korea + - South Sudan + - Southern Ocean + - Spain + - Spratly Islands + - Sri Lanka + - State of Palestine + - Sudan + - Suriname + - Svalbard + - Sweden + - Switzerland + - Syria + - Taiwan + - Tajikistan + - Tanzania + - Tasman Sea + - Thailand + - Timor-Leste + - Togo + - Tokelau + - Tonga + - Trinidad and Tobago + - Tromelin Island + - Tunisia + - Turkey + - Turkmenistan + - Turks and Caicos Islands + - Tuvalu + - Uganda + - Ukraine + - United Arab Emirates + - United Kingdom + - Uruguay + - USA + - Uzbekistan + - Vanuatu + - Venezuela + - Viet Nam + - Virgin Islands + - Wake Island + - Wallis and Futuna + - West Bank + - Western Sahara + - Yemen + - Zambia + - Zimbabwe + - Belgian Congo + - British Guiana + - Burma + - Czechoslovakia + - Czech Republic + - East Timor + - Korea + - Macedonia + - Micronesia + - Netherlands Antilles + - Serbia and Montenegro + - Siam + - Swaziland + - The former Yugoslav Republic of Macedonia + - USSR + - Yugoslavia + - Zaire + required: true + geoLocLatitude: + function: identity + inputs: + input: geoLocLatitude + args: + type: float + geoLocLongitude: + function: identity + inputs: + input: geoLocLongitude + args: + type: float + geoLocSite: + function: identity + inputs: + input: geoLocSite + args: + hostAge: + function: identity + inputs: + input: hostAge + args: + type: int + hostAgeBin: + function: identity + inputs: + input: hostAgeBin + args: + hostDisease: + function: identity + inputs: + input: hostDisease + args: + hostGender: + function: identity + inputs: + input: hostGender + args: + hostHealthOutcome: + function: identity + inputs: + input: hostHealthOutcome + args: + hostHealthState: + function: identity + inputs: + input: hostHealthState + args: + hostNameCommon: + function: common_name_from_id + inputs: + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostNameScientific: + function: scientific_name_from_id + inputs: + hostNameScientific: hostNameScientific + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostOriginCountry: + function: identity + inputs: + input: hostOriginCountry + args: + hostRole: + function: identity + inputs: + input: hostRole + args: + hostTaxonId: + function: resolve_host_taxon_id + inputs: + host: host + hostNameScientific: hostNameScientific + hostTaxonId: hostTaxonId + args: + type: int + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostVaccinationStatus: + function: identity + inputs: + input: hostVaccinationStatus + args: + insdcAccessionBase: + function: identity + inputs: + input: insdcAccessionBase + args: + insdcAccessionFull: + function: identity + inputs: + input: insdcAccessionFull + args: + insdcRawReadsAccession: + function: identity + inputs: + input: insdcRawReadsAccession + args: + insdcVersion: + function: identity + inputs: + input: insdcVersion + args: + type: int + isLabHost: + function: identity + inputs: + input: isLabHost + args: + type: boolean + length: + function: identity + inputs: + input: length + args: + type: int + ncbiReleaseDate: + function: parse_timestamp + inputs: + timestamp: ncbiReleaseDate + args: + type: date + ncbiSourceDb: + function: identity + inputs: + input: ncbiSourceDb + args: + ncbiSubmitterCountry: + function: identity + inputs: + input: ncbiSubmitterCountry + args: + ncbiUpdateDate: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + type: date + ncbiVirusName: + function: identity + inputs: + input: ncbiVirusName + args: + ncbiVirusTaxId: + function: identity + inputs: + input: ncbiVirusTaxId + args: + type: int + passageMethod: + function: identity + inputs: + input: passageMethod + args: + passageNumber: + function: identity + inputs: + input: passageNumber + args: + type: int + presamplingActivity: + function: identity + inputs: + input: presamplingActivity + args: + previousInfectionDisease: + function: identity + inputs: + input: previousInfectionDisease + args: + previousInfectionOrganism: + function: identity + inputs: + input: previousInfectionOrganism + args: + purposeOfSampling: + function: identity + inputs: + input: purposeOfSampling + args: + purposeOfSequencing: + function: identity + inputs: + input: purposeOfSequencing + args: + qualityControlDetails: + function: identity + inputs: + input: qualityControlDetails + args: + qualityControlDetermination: + function: identity + inputs: + input: qualityControlDetermination + args: + qualityControlIssues: + function: identity + inputs: + input: qualityControlIssues + args: + qualityControlMethodName: + function: identity + inputs: + input: qualityControlMethodName + args: + qualityControlMethodVersion: + function: identity + inputs: + input: qualityControlMethodVersion + args: + rawSequenceDataProcessingMethod: + function: identity + inputs: + input: rawSequenceDataProcessingMethod + args: + referenceGenomeAccession: + function: identity + inputs: + input: referenceGenomeAccession + args: + sampleCollectionDate: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + + fieldType: dateRangeString + required: true + sampleCollectionDateRangeLower: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeLower + sampleCollectionDateRangeUpper: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeUpper + sampleReceivedDate: + function: parse_and_assert_past_date + inputs: + date: sampleReceivedDate + args: + type: date + sampleType: + function: identity + inputs: + input: sampleType + args: + sequencedByContactEmail: + function: identity + inputs: + input: sequencedByContactEmail + args: + sequencedByContactName: + function: identity + inputs: + input: sequencedByContactName + args: + sequencedByOrganization: + function: identity + inputs: + input: sequencedByOrganization + args: + sequencingAssayType: + function: identity + inputs: + input: sequencingAssayType + args: + sequencingDate: + function: parse_and_assert_past_date + inputs: + date: sequencingDate + args: + type: date + sequencingInstrument: + function: identity + inputs: + input: sequencingInstrument + args: + sequencingProtocol: + function: identity + inputs: + input: sequencingProtocol + args: + signsAndSymptoms: + function: identity + inputs: + input: signsAndSymptoms + args: + specimenCollectorSampleId: + function: identity + inputs: + input: specimenCollectorSampleId + args: + specimenProcessing: + function: identity + inputs: + input: specimenProcessing + args: + specimenProcessingDetails: + function: identity + inputs: + input: specimenProcessingDetails + args: + stopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + totalAmbiguousNucs: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + type: int + totalDeletedNucs: + function: identity + inputs: + input: nextclade.totalDeletions + args: + type: int + totalFrameShifts: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + type: int + totalInsertedNucs: + function: identity + inputs: + input: nextclade.totalInsertions + args: + type: int + totalSnps: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + type: int + totalStopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + type: int + totalUnknownNucs: + function: identity + inputs: + input: nextclade.totalMissing + args: + type: int + travelHistory: + function: identity + inputs: + input: travelHistory + args: + versionComment: + function: identity + inputs: + input: versionComment + args: diff --git a/kubernetes/loculus/fixtures/preprocessing/not-aligned-organism/4.yaml b/kubernetes/loculus/fixtures/preprocessing/not-aligned-organism/4.yaml new file mode 100644 index 0000000000..7888b0b43d --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/not-aligned-organism/4.yaml @@ -0,0 +1,65 @@ +organism: not-aligned-organism +batch_size: 100 +log_level: DEBUG +segments: + - name: main + references: + - genes: [] + name: singleReference +max_sequences_per_entry: 1 +processing_spec: + + country: + function: identity + inputs: + input: country + args: + type: string + date: + function: parse_and_assert_past_date + inputs: + date: date + args: + type: date + required: true + division: + function: identity + inputs: + input: division + args: + type: string + host: + function: identity + inputs: + input: host + args: + type: string + lineage: + function: process_options + inputs: + input: lineage + args: + type: string + options: + - A + - A.1 + - A.1.1 + - A.2 + - B + - B.1 + - B.1.1 + - B.1.1.1 + - C + - C.1 + required: true + region: + function: identity + inputs: + input: region + args: + type: string + versionComment: + function: identity + inputs: + input: versionComment + args: diff --git a/kubernetes/loculus/fixtures/preprocessing/west-nile/1.yaml b/kubernetes/loculus/fixtures/preprocessing/west-nile/1.yaml new file mode 100644 index 0000000000..22428dab69 --- /dev/null +++ b/kubernetes/loculus/fixtures/preprocessing/west-nile/1.yaml @@ -0,0 +1,945 @@ +organism: west-nile +batch_size: 100 +create_embl_file: true +log_level: DEBUG +molecule_type: genomic RNA +nextclade_dataset_server: https://raw.githubusercontent.com/nextstrain/nextclade_data/wnv/data_output +scientific_name: West Nile virus +segments: + - name: main + references: + - genes: + - capsid + - prM + - env + - NS1 + - NS2A + - NS2B + - NS3 + - NS4A + - 2K + - NS4B + - NS5 + name: singleReference + nextclade_additional_args: + - --allowed-mismatches + - "8" + - --min-seed-cover + - "0.01" + nextclade_dataset_name: nextstrain/wnv/all-lineages +taxon_id: 11082 +max_sequences_per_entry: 1 +processing_spec: + + ampliconPcrPrimerScheme: + function: identity + inputs: + input: ampliconPcrPrimerScheme + args: + ampliconSize: + function: identity + inputs: + input: ampliconSize + args: + anatomicalMaterial: + function: identity + inputs: + input: anatomicalMaterial + args: + anatomicalPart: + function: identity + inputs: + input: anatomicalPart + args: + authorAffiliations: + function: identity + inputs: + input: authorAffiliations + args: + authors: + function: check_authors + inputs: + authors: authors + args: + type: authors + bioprojectAccession: + function: identity + inputs: + input: bioprojectAccession + args: + biosampleAccession: + function: identity + inputs: + input: biosampleAccession + args: + bodyProduct: + function: identity + inputs: + input: bodyProduct + args: + breadthOfCoverage: + function: identity + inputs: + input: breadthOfCoverage + args: + type: int + cellLine: + function: identity + inputs: + input: cellLine + args: + collectionDevice: + function: identity + inputs: + input: collectionDevice + args: + collectionMethod: + function: identity + inputs: + input: collectionMethod + args: + completeness: + function: identity + inputs: + input: nextclade.coverage + args: + type: float + consensusSequenceSoftwareName: + function: identity + inputs: + input: consensusSequenceSoftwareName + args: + consensusSequenceSoftwareVersion: + function: identity + inputs: + input: consensusSequenceSoftwareVersion + args: + cultureId: + function: identity + inputs: + input: cultureId + args: + dehostingMethod: + function: identity + inputs: + input: dehostingMethod + args: + depthOfCoverage: + function: identity + inputs: + input: depthOfCoverage + args: + type: int + diagnosticMeasurementMethod: + function: identity + inputs: + input: diagnosticMeasurementMethod + args: + diagnosticMeasurementUnit: + function: identity + inputs: + input: diagnosticMeasurementUnit + args: + diagnosticMeasurementValue: + function: identity + inputs: + input: diagnosticMeasurementValue + args: + diagnosticTargetGeneName: + function: identity + inputs: + input: diagnosticTargetGeneName + args: + diagnosticTargetPresence: + function: identity + inputs: + input: diagnosticTargetPresence + args: + displayName: + function: build_display_name + inputs: + geoLocCountry: geoLocCountry + sampleCollectionDateRangeLower: processed.sampleCollectionDateRangeLower + specimenCollectorSampleId: specimenCollectorSampleId + submissionId: submissionId + args: + + order: + - geoLocCountry + - IDENTIFIER + - sampleCollectionDateRangeLower + regex_pattern: ^(?:[^/]+/)?[^/]+/(?P[^/]+)/\d{4}(?:-\d{2}){0,2}$ + type: + - string + - IDENTIFIER + - string + earliestReleaseDate: + function: identity + inputs: + input: earliestReleaseDate + args: + type: date + environmentalMaterial: + function: identity + inputs: + input: environmentalMaterial + args: + environmentalSite: + function: identity + inputs: + input: environmentalSite + args: + experimentalSpecimenRoleType: + function: identity + inputs: + input: experimentalSpecimenRoleType + args: + exposureDetails: + function: identity + inputs: + input: exposureDetails + args: + exposureEvent: + function: identity + inputs: + input: exposureEvent + args: + exposureSetting: + function: identity + inputs: + input: exposureSetting + args: + foodProduct: + function: identity + inputs: + input: foodProduct + args: + foodProductProperties: + function: identity + inputs: + input: foodProductProperties + args: + frameShifts: + function: identity + inputs: + input: nextclade.frameShifts + args: + gcaAccession: + function: identity + inputs: + input: gcaAccession + args: + geoLocAdmin1: + function: identity + inputs: + input: geoLocAdmin1 + args: + geoLocAdmin2: + function: identity + inputs: + input: geoLocAdmin2 + args: + geoLocCity: + function: identity + inputs: + input: geoLocCity + args: + geoLocCountry: + function: process_options + inputs: + input: geoLocCountry + args: + options: + - Afghanistan + - Albania + - Algeria + - American Samoa + - Andorra + - Angola + - Anguilla + - Antarctica + - Antigua and Barbuda + - Arctic Ocean + - Argentina + - Armenia + - Aruba + - Ashmore and Cartier Islands + - Atlantic Ocean + - Australia + - Austria + - Azerbaijan + - Bahamas + - Bahrain + - Baltic Sea + - Baker Island + - Bangladesh + - Barbados + - Bassas da India + - Belarus + - Belgium + - Belize + - Benin + - Bermuda + - Bhutan + - Bolivia + - Borneo + - Bosnia and Herzegovina + - Botswana + - Bouvet Island + - Brazil + - British Virgin Islands + - Brunei + - Bulgaria + - Burkina Faso + - Burundi + - Cambodia + - Cameroon + - Canada + - Cape Verde + - Cayman Islands + - Central African Republic + - Chad + - Chile + - China + - Christmas Island + - Clipperton Island + - Cocos Islands + - Colombia + - Comoros + - Cook Islands + - Coral Sea Islands + - Costa Rica + - Cote d'Ivoire + - Croatia + - Cuba + - Curacao + - Cyprus + - Czechia + - Democratic Republic of the Congo + - Denmark + - Djibouti + - Dominica + - Dominican Republic + - Ecuador + - Egypt + - El Salvador + - Equatorial Guinea + - Eritrea + - Estonia + - Eswatini + - Ethiopia + - Europa Island + - Falkland Islands (Islas Malvinas) + - Faroe Islands + - Fiji + - Finland + - France + - French Guiana + - French Polynesia + - French Southern and Antarctic Lands + - Gabon + - Gambia + - Gaza Strip + - Georgia + - Germany + - Ghana + - Gibraltar + - Glorioso Islands + - Greece + - Greenland + - Grenada + - Guadeloupe + - Guam + - Guatemala + - Guernsey + - Guinea + - Guinea-Bissau + - Guyana + - Haiti + - Heard Island and McDonald Islands + - Honduras + - Hong Kong + - Howland Island + - Hungary + - Iceland + - India + - Indian Ocean + - Indonesia + - Iran + - Iraq + - Ireland + - Isle of Man + - Israel + - Italy + - Jamaica + - Jan Mayen + - Japan + - Jarvis Island + - Jersey + - Johnston Atoll + - Jordan + - Juan de Nova Island + - Kazakhstan + - Kenya + - Kerguelen Archipelago + - Kingman Reef + - Kiribati + - Kosovo + - Kuwait + - Kyrgyzstan + - Laos + - Latvia + - Lebanon + - Lesotho + - Liberia + - Libya + - Liechtenstein + - Line Islands + - Lithuania + - Luxembourg + - Macau + - Madagascar + - Malawi + - Malaysia + - Maldives + - Mali + - Malta + - Marshall Islands + - Martinique + - Mauritania + - Mauritius + - Mayotte + - Mediterranean Sea + - Mexico + - Micronesia, Federated States of + - Midway Islands + - Moldova + - Monaco + - Mongolia + - Montenegro + - Montserrat + - Morocco + - Mozambique + - Myanmar + - Namibia + - Nauru + - Navassa Island + - Nepal + - Netherlands + - New Caledonia + - New Zealand + - Nicaragua + - Niger + - Nigeria + - Niue + - Norfolk Island + - North Korea + - North Macedonia + - North Sea + - Northern Mariana Islands + - Norway + - Oman + - Pacific Ocean + - Pakistan + - Palau + - Palmyra Atoll + - Panama + - Papua New Guinea + - Paracel Islands + - Paraguay + - Peru + - Philippines + - Pitcairn Islands + - Poland + - Portugal + - Puerto Rico + - Qatar + - Republic of the Congo + - Reunion + - Romania + - Ross Sea + - Russia + - Rwanda + - Saint Barthelemy + - Saint Helena + - Saint Kitts and Nevis + - Saint Lucia + - Saint Martin + - Saint Pierre and Miquelon + - Saint Vincent and the Grenadines + - Samoa + - San Marino + - Sao Tome and Principe + - Saudi Arabia + - Senegal + - Serbia + - Seychelles + - Sierra Leone + - Singapore + - Sint Maarten + - Slovakia + - Slovenia + - Solomon Islands + - Somalia + - South Africa + - South Georgia and the South Sandwich Islands + - South Korea + - South Sudan + - Southern Ocean + - Spain + - Spratly Islands + - Sri Lanka + - State of Palestine + - Sudan + - Suriname + - Svalbard + - Sweden + - Switzerland + - Syria + - Taiwan + - Tajikistan + - Tanzania + - Tasman Sea + - Thailand + - Timor-Leste + - Togo + - Tokelau + - Tonga + - Trinidad and Tobago + - Tromelin Island + - Tunisia + - Turkey + - Turkmenistan + - Turks and Caicos Islands + - Tuvalu + - Uganda + - Ukraine + - United Arab Emirates + - United Kingdom + - Uruguay + - USA + - Uzbekistan + - Vanuatu + - Venezuela + - Viet Nam + - Virgin Islands + - Wake Island + - Wallis and Futuna + - West Bank + - Western Sahara + - Yemen + - Zambia + - Zimbabwe + - Belgian Congo + - British Guiana + - Burma + - Czechoslovakia + - Czech Republic + - East Timor + - Korea + - Macedonia + - Micronesia + - Netherlands Antilles + - Serbia and Montenegro + - Siam + - Swaziland + - The former Yugoslav Republic of Macedonia + - USSR + - Yugoslavia + - Zaire + required: true + geoLocLatitude: + function: identity + inputs: + input: geoLocLatitude + args: + type: float + geoLocLongitude: + function: identity + inputs: + input: geoLocLongitude + args: + type: float + geoLocSite: + function: identity + inputs: + input: geoLocSite + args: + hostAge: + function: identity + inputs: + input: hostAge + args: + type: int + hostAgeBin: + function: identity + inputs: + input: hostAgeBin + args: + hostDisease: + function: identity + inputs: + input: hostDisease + args: + hostGender: + function: identity + inputs: + input: hostGender + args: + hostHealthOutcome: + function: identity + inputs: + input: hostHealthOutcome + args: + hostHealthState: + function: identity + inputs: + input: hostHealthState + args: + hostNameCommon: + function: common_name_from_id + inputs: + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostNameScientific: + function: scientific_name_from_id + inputs: + hostNameScientific: hostNameScientific + hostTaxonId: processed.hostTaxonId + args: + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostOriginCountry: + function: identity + inputs: + input: hostOriginCountry + args: + hostRole: + function: identity + inputs: + input: hostRole + args: + hostTaxonId: + function: resolve_host_taxon_id + inputs: + host: host + hostNameScientific: hostNameScientific + hostTaxonId: hostTaxonId + args: + type: int + + taxonomy_service_url: http://loculus-taxonomy-service:5000 + hostVaccinationStatus: + function: identity + inputs: + input: hostVaccinationStatus + args: + insdcAccessionBase: + function: identity + inputs: + input: insdcAccessionBase + args: + insdcAccessionFull: + function: identity + inputs: + input: insdcAccessionFull + args: + insdcRawReadsAccession: + function: identity + inputs: + input: insdcRawReadsAccession + args: + insdcVersion: + function: identity + inputs: + input: insdcVersion + args: + type: int + isLabHost: + function: identity + inputs: + input: isLabHost + args: + type: boolean + length: + function: identity + inputs: + input: length + args: + type: int + lineage: + function: identity + inputs: + input: nextclade.clade + args: + mutations_from_founder: + function: identity + inputs: + input: nextclade.cladeFounderInfo.aaMutations.*.privateSubstitutions + args: + ncbiReleaseDate: + function: parse_timestamp + inputs: + timestamp: ncbiReleaseDate + args: + type: date + ncbiSourceDb: + function: identity + inputs: + input: ncbiSourceDb + args: + ncbiSubmitterCountry: + function: identity + inputs: + input: ncbiSubmitterCountry + args: + ncbiUpdateDate: + function: parse_timestamp + inputs: + timestamp: ncbiUpdateDate + args: + type: date + ncbiVirusName: + function: identity + inputs: + input: ncbiVirusName + args: + ncbiVirusTaxId: + function: identity + inputs: + input: ncbiVirusTaxId + args: + type: int + passageMethod: + function: identity + inputs: + input: passageMethod + args: + passageNumber: + function: identity + inputs: + input: passageNumber + args: + type: int + presamplingActivity: + function: identity + inputs: + input: presamplingActivity + args: + previousInfectionDisease: + function: identity + inputs: + input: previousInfectionDisease + args: + previousInfectionOrganism: + function: identity + inputs: + input: previousInfectionOrganism + args: + purposeOfSampling: + function: identity + inputs: + input: purposeOfSampling + args: + purposeOfSequencing: + function: identity + inputs: + input: purposeOfSequencing + args: + qualityControlDetails: + function: identity + inputs: + input: qualityControlDetails + args: + qualityControlDetermination: + function: identity + inputs: + input: qualityControlDetermination + args: + qualityControlIssues: + function: identity + inputs: + input: qualityControlIssues + args: + qualityControlMethodName: + function: identity + inputs: + input: qualityControlMethodName + args: + qualityControlMethodVersion: + function: identity + inputs: + input: qualityControlMethodVersion + args: + rawSequenceDataProcessingMethod: + function: identity + inputs: + input: rawSequenceDataProcessingMethod + args: + referenceGenomeAccession: + function: identity + inputs: + input: referenceGenomeAccession + args: + sampleCollectionDate: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + + fieldType: dateRangeString + required: true + sampleCollectionDateRangeLower: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeLower + sampleCollectionDateRangeUpper: + function: parse_date_into_range + inputs: + date: sampleCollectionDate + releaseDate: ncbiReleaseDate + args: + type: date + + fieldType: dateRangeUpper + sampleReceivedDate: + function: parse_and_assert_past_date + inputs: + date: sampleReceivedDate + args: + type: date + sampleType: + function: identity + inputs: + input: sampleType + args: + sequencedByContactEmail: + function: identity + inputs: + input: sequencedByContactEmail + args: + sequencedByContactName: + function: identity + inputs: + input: sequencedByContactName + args: + sequencedByOrganization: + function: identity + inputs: + input: sequencedByOrganization + args: + sequencingAssayType: + function: identity + inputs: + input: sequencingAssayType + args: + sequencingDate: + function: parse_and_assert_past_date + inputs: + date: sequencingDate + args: + type: date + sequencingInstrument: + function: identity + inputs: + input: sequencingInstrument + args: + sequencingProtocol: + function: identity + inputs: + input: sequencingProtocol + args: + signsAndSymptoms: + function: identity + inputs: + input: signsAndSymptoms + args: + specimenCollectorSampleId: + function: identity + inputs: + input: specimenCollectorSampleId + args: + specimenProcessing: + function: identity + inputs: + input: specimenProcessing + args: + specimenProcessingDetails: + function: identity + inputs: + input: specimenProcessingDetails + args: + stopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.stopCodons + args: + totalAmbiguousNucs: + function: identity + inputs: + input: nextclade.totalNonACGTNs + args: + type: int + totalDeletedNucs: + function: identity + inputs: + input: nextclade.totalDeletions + args: + type: int + totalFrameShifts: + function: identity + inputs: + input: nextclade.totalFrameShifts + args: + type: int + totalInsertedNucs: + function: identity + inputs: + input: nextclade.totalInsertions + args: + type: int + totalSnps: + function: identity + inputs: + input: nextclade.totalSubstitutions + args: + type: int + totalStopCodons: + function: identity + inputs: + input: nextclade.qc.stopCodons.totalStopCodons + args: + type: int + totalUnknownNucs: + function: identity + inputs: + input: nextclade.totalMissing + args: + type: int + travelHistory: + function: identity + inputs: + input: travelHistory + args: + variant: + function: is_variant + inputs: + length: processed.length + numMutations: nextclade.privateNucMutations.totalPrivateSubstitutions + args: + type: boolean + + mu: 0.002 + versionComment: + function: identity + inputs: + input: versionComment + args: diff --git a/kubernetes/loculus/templates/_common-metadata.tpl b/kubernetes/loculus/templates/_common-metadata.tpl index c96e6cfd1f..a171e58f83 100644 --- a/kubernetes/loculus/templates/_common-metadata.tpl +++ b/kubernetes/loculus/templates/_common-metadata.tpl @@ -184,404 +184,20 @@ fields: {{- set $patchedSchema "metadata" $patchedMetadata | toYaml -}} {{- end -}} -{{/* Generate website config from passed config object */}} -{{- define "loculus.generateWebsiteConfig" }} -name: {{ quote $.Values.name }} -logo: {{ $.Values.logo | toYaml | nindent 6 }} -{{ if $.Values.sequenceFlagging }} -sequenceFlagging: {{ $.Values.sequenceFlagging | toYaml | nindent 6 }} -{{ end }} -{{ if $.Values.gitHubMainUrl }} -gitHubMainUrl: {{ quote $.Values.gitHubMainUrl }} -{{ end }} -{{ if $.Values.gitHubIssuesUrl }} -gitHubIssuesUrl: {{ quote $.Values.gitHubIssuesUrl }} -{{ end }} -{{ if $.Values.issuesEmail }} -issuesEmail: {{ quote $.Values.issuesEmail }} -{{ end }} -{{ if $.Values.bannerMessageURL }} -bannerMessageURL: {{ quote $.Values.bannerMessageURL }} -{{ end }} -{{ if $.Values.bannerMessage }} -bannerMessage: {{ quote $.Values.bannerMessage }} -{{ else if or $.Values.runDevelopmentMainDatabase $.Values.runDevelopmentKeycloakDatabase }} -bannerMessage: "Warning: Development or Keycloak main database is enabled. Development environment only." -{{ end }} -{{ if $.Values.submissionBannerMessageURL }} -submissionBannerMessageURL: {{ quote $.Values.submissionBannerMessageURL }} -{{ end }} -{{ if $.Values.submissionBannerMessage }} -submissionBannerMessage: {{ quote $.Values.submissionBannerMessage }} -{{ end }} -{{ if $.Values.gitHubEditLink }} -gitHubEditLink: {{ quote $.Values.gitHubEditLink }} -{{ end }} -{{ if $.Values.welcomeMessageHTML }} -welcomeMessageHTML: {{ quote $.Values.welcomeMessageHTML }} -{{end}} -{{ if $.Values.additionalHeadHTML }} -additionalHeadHTML: {{ quote $.Values.additionalHeadHTML }} -{{end}} - -enableLoginNavigationItem: {{ $.Values.website.websiteConfig.enableLoginNavigationItem }} -enableSubmissionNavigationItem: {{ $.Values.website.websiteConfig.enableSubmissionNavigationItem }} -enableSubmissionPages: {{ $.Values.website.websiteConfig.enableSubmissionPages }} -readOnlyMode: {{ $.Values.readOnlyMode | default false }} -enableSeqSets: {{ $.Values.seqSets.enabled }} -{{- if $.Values.seqSets.fieldsToDisplay }} -seqSetsFieldsToDisplay: {{ $.Values.seqSets.fieldsToDisplay | toJson }} -{{- end }} -{{- if $.Values.seqSets.graphs }} -seqSetsGraphs: {{ $.Values.seqSets.graphs | toJson }} -{{- end }} -enableDataUseTerms: {{ $.Values.dataUseTerms.enabled }} -{{ if $.Values.dataUseTerms.agreementHTML }} -dataUseTermsAgreementHTML: {{ quote $.Values.dataUseTerms.agreementHTML }} -{{- end }} -accessionPrefix: {{ quote $.Values.accessionPrefix }} -dateFieldForGroupGraph: {{ if $.Values.dateFieldForGroupGraph }}{{ quote $.Values.dateFieldForGroupGraph }}{{ else }}null{{ end }} -{{- $commonMetadata := (include "loculus.commonMetadata" . | fromYaml).fields }} -organisms: - {{- range $_, $item := (include "loculus.enabledOrganisms" . | fromJson).organisms }} -{{- $key := $item.key }} -{{- $instance := $item.contents }} - {{ $key }}: - schema: - {{- with ($instance.schema | include "loculus.patchMetadataSchema" | fromYaml) }} - organismName: {{ quote .organismName }} - {{ if .linkOuts }} - linkOuts: - {{- range $linkOut := .linkOuts }} - - name: {{ quote $linkOut.name }} - url: {{ quote $linkOut.url }} - {{- if $linkOut.maxNumberOfRecommendedEntries }} - maxNumberOfRecommendedEntries: {{ $linkOut.maxNumberOfRecommendedEntries }} - {{- end }} - {{- if $linkOut.onlyForReferences }} - onlyForReferences: {{ $linkOut.onlyForReferences | toYaml | nindent 12 }} - {{- end }} - {{- if $linkOut.category }} - category: {{ quote $linkOut.category }} - {{- end }} - {{- end }} - {{- end }} - loadSequencesAutomatically: {{ .loadSequencesAutomatically | default false }} - {{ if .richFastaHeaderFields}} - richFastaHeaderFields: {{ toJson .richFastaHeaderFields }} - {{ end }} - {{- include "loculus.submissionDataTypes" . | nindent 6 }} - {{- $nucleotideSequences := .nucleotideSequences | default (list "main")}} - {{ if .image }} - image: {{ .image }} - {{ end }} - {{ if .description }} - description: {{ quote .description }} - {{ end }} - primaryKey: accessionVersion - inputFields: {{- include "loculus.inputFields" . | nindent 8 }} - - name: versionComment - displayName: Version comment - definition: "Reason for revising sequences or other general comments concerning a specific version." - example: "Fixed an issue in previous version where low-coverage nucleotides were erroneously filled with reference sequence." - desired: true - {{ if .files }} - files: {{ .files | toYaml | nindent 8 }} - {{ end }} - metadata: - {{- $args := dict "metadata" (concat $commonMetadata .metadata) "referenceGenomes" $instance.referenceGenomes}} - {{ $metadata := include "loculus.generateWebsiteMetadata" $args | fromYaml }} - {{ $metadata.fields | toYaml | nindent 8 }} - {{ if .files }} - {{- range .files }} - - name: {{ .name }} - {{- if .displayName }} - displayName: {{ .displayName }} - {{- end }} - type: string - header: "Files" - noInput: true - customDisplay: - type: fileList - {{- end }} - {{ end }} - {{ if .metadataTemplate }} - metadataTemplate: - {{ .metadataTemplate | toYaml | nindent 8}} - {{ end }} - {{ omit .website "multiFieldSearches" | toYaml | nindent 6 }} - {{- if .website.multiFieldSearches }} - {{- $perSegmentFields := dict }} - {{- range (concat $commonMetadata .metadata) }} - {{- if .perSegment }}{{- $_ := set $perSegmentFields .name true }}{{- end }} - {{- end }} - {{- $segments := (include "loculus.getNucleotideSegmentNames" $instance.referenceGenomes | fromYaml).segments }} - {{- $isSegmented := gt (len $segments) 1 }} - multiFieldSearches: - {{- range .website.multiFieldSearches }} - - name: {{ .name }} - displayName: {{ .displayName }} - {{- if hasKey . "orderInSearchDisplay" }} - orderInSearchDisplay: {{ .orderInSearchDisplay }} - {{- end }} - fields: - {{- range .fields }} - {{- if and $isSegmented (hasKey $perSegmentFields .) }} - {{- $field := . }} - {{- range $segments }} - - {{ printf "%s_%s" $field . }} - {{- end }} - {{- else }} - - {{ . }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} - referenceGenomes: - {{ $instance.referenceGenomes | toYaml | nindent 6 }} - {{- end }} -{{- end }} - -{{- define "loculus.standardWebsiteMetadata" }} -- type: {{ .type | default "string" | quote }} - {{- if .definition }} - definition: {{ .definition | quote }} - {{- end }} - {{- if .autocomplete }} - autocomplete: {{ .autocomplete }} - {{- end }} - {{- if .enableSubstringSearch }} - substringSearch: {{ .enableSubstringSearch }} - {{- end}} - {{- if .notSearchable }} - notSearchable: {{ .notSearchable }} - {{- end }} - {{- if .initiallyVisible }} - initiallyVisible: {{ .initiallyVisible }} - {{- end }} - {{- if .hideInSearchResultsTable }} - hideInSearchResultsTable: {{ .hideInSearchResultsTable }} - {{- end }} - {{- if or (or (eq .type "timestamp") (eq .type "date")) .rangeSearch }} - rangeSearch: true - {{- end }} - {{- if .rangeOverlapSearch }} - rangeOverlapSearch: {{ .rangeOverlapSearch | toJson }} - {{- end}} - {{- if .lineageSystem }} - lineageSearch: true - {{- end }} - {{- if .hierarchicalFilter }} - hierarchicalSearch: true - {{- end }} - {{- if and .hierarchicalFilter .hierarchicalSearchLabel }} - hierarchicalSearchLabel: {{ .hierarchicalSearchLabel }} - {{- end }} - {{- if .hideOnSequenceDetailsPage }} - hideOnSequenceDetailsPage: {{ .hideOnSequenceDetailsPage }} - {{- end }} - {{- if .columnWidth }} - columnWidth: {{ .columnWidth }} - {{- end }} - {{- if .order }} - order: {{ .order }} - {{- end }} - {{- if .orderOnDetailsPage }} - orderOnDetailsPage: {{ .orderOnDetailsPage }} - {{- end }} - {{- if .orderInSearchDisplay }} - orderInSearchDisplay: {{ .orderInSearchDisplay }} - {{- end }} - {{- if .includeInDownloadsByDefault }} - includeInDownloadsByDefault: {{ .includeInDownloadsByDefault }} - {{- end }} - {{- if .onlyForReference }} - onlyForReference: {{ .onlyForReference }} - {{- end }} - {{- if .customDisplay }} - customDisplay: - type: {{ quote .customDisplay.type }} - {{- if .customDisplay.url }} - url: {{ .customDisplay.url }} - {{- end }} - {{- if .customDisplay.linkMenuItems }} - linkMenuItems: - {{- range .customDisplay.linkMenuItems }} - - name: {{ quote .name }} - url: {{ quote .url }} - {{- end }} - {{- end }} - {{- if .customDisplay.displayGroup }} - displayGroup: {{ quote .customDisplay.displayGroup }} - {{- end }} - {{- if .customDisplay.label }} - label: {{ quote .customDisplay.label }} - {{- end }} - {{- if .customDisplay.html }} - html: {{ .customDisplay.html }} - {{- end }} - {{- end }} - {{- if .isSequenceFilter }} - isSequenceFilter: {{ .isSequenceFilter }} - {{- end }} - {{- if .relatesToSegment }} - relatesToSegment: {{ .relatesToSegment }} - {{- end }} - {{- if .percentage }} - percentage: {{ .percentage }} - {{- end }} -{{- end }} - -{{/* Generate website metadata from passed metadata array */}} -{{- define "loculus.generateWebsiteMetadata" }} -{{- $segmentsData := include "loculus.getNucleotideSegmentNames" .referenceGenomes | fromYaml -}} -{{- $rawUniqueSegments := $segmentsData.segments | default (list) -}} -{{- $displayNameMap := $segmentsData.displayNames | default (dict) -}} -{{- $isSegmented := gt (len $rawUniqueSegments) 1 }} -{{- $metadataList := .metadata }} -fields: -{{- range $metadataList }} -{{- if and $isSegmented .perSegment }} -{{- $currentItem := . }} -{{- range $segment := $rawUniqueSegments }} -{{- $segmentDisplayName := default $segment (get $displayNameMap $segment) -}} -{{- with $currentItem }} -{{ include "loculus.standardWebsiteMetadata" . }} - name: {{ printf "%s_%s" .name $segment | quote }} - {{- if .displayName }} - displayName: {{ printf "%s %s" .displayName $segmentDisplayName | quote }} - {{- end }} - {{- if (default false .oneHeader)}} - header: {{ (default "Other" .header) | quote }} - {{- else }} - header: {{ printf "%s %s" (default "Other" .header) $segmentDisplayName | quote }} - {{- end }} - relatesToSegment: {{ $segment }} - {{- if .isSequenceFilter }} - isSequenceFilter: true - {{- end }} - {{- if and .customDisplay .customDisplay.displayGroup }} - customDisplay: - type: {{ quote .customDisplay.type }} - displayGroup: {{ printf "%s_%s" .customDisplay.displayGroup $segment | quote }} - {{- if .customDisplay.label }} - label: {{ printf "%s %s" .customDisplay.label $segmentDisplayName | quote }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} -{{- else }} -{{ include "loculus.standardWebsiteMetadata" . }} - name: {{ quote .name }} - {{- if .displayName }} - displayName: {{ quote .displayName }} - {{- end }} - header: {{ default "Other" .header }} -{{- end}} -{{- end}} -{{- end}} - -{{/* Generate backend config from passed config object */}} +{{/* Backend config: technical/operational params only. Domain config + (organisms, schemas, reference genomes, metadata, dataUseTerms, + accessionPrefix, fileSharing) lives in the database (config_* tables) and is + read by the backend via ConfigService — it is NOT sourced from this file. + The backend binds the fields below from Spring properties, not this file; + they are emitted here for parity/inspection only. */}} {{- define "loculus.generateBackendConfig" }} -accessionPrefix: {{ quote $.Values.accessionPrefix }} zstdCompressionLevel: {{ $.Values.zstdCompressionLevel }} pipelineVersionUpgradeCheckIntervalSeconds: {{ $.Values.pipelineVersionUpgradeCheckIntervalSeconds }} readOnlyMode: {{ $.Values.readOnlyMode | default false }} -name: {{ quote $.Values.name }} -dataUseTerms: - {{$.Values.dataUseTerms | toYaml | nindent 2}} -{{- if .Values.fileSharing }} -fileSharing: - {{ .Values.fileSharing | toYaml | nindent 2 }} -{{- end }} websiteUrl: {{ include "loculus.websiteUrl" . }} backendUrl: {{ include "loculus.backendUrl" . }} -organisms: - {{- range $_, $item := (include "loculus.enabledOrganisms" . | fromJson).organisms }} -{{- $key := $item.key }} -{{- $instance := $item.contents }} - {{ $key }}: - schema: - {{- with $instance.schema }} - organismName: {{ quote .organismName }} - {{- include "loculus.submissionDataTypes" . | nindent 6 }} - {{- if .files }} - files: - {{ .files | toYaml | nindent 8 }} - {{- end }} - metadata: - {{- $args := dict "metadata" (include "loculus.patchMetadataSchema" . | fromYaml).metadata "referenceGenomes" $instance.referenceGenomes }} - {{- $metadata := include "loculus.generateBackendMetadata" $args | fromYaml }} - {{ $metadata.fields | toYaml | nindent 8 }} - externalMetadata: - {{- $args := dict "metadata" (include "loculus.patchMetadataSchema" . | fromYaml).metadata "referenceGenomes" $instance.referenceGenomes }} - {{- $metadata := include "loculus.generateBackendExternalMetadata" $args | fromYaml }} - {{ $metadata.fields | default list | toYaml | nindent 8 }} - earliestReleaseDate: - {{- if .earliestReleaseDate }} - {{ .earliestReleaseDate | toYaml | nindent 8 }} - {{- else }} - enabled: false - externalFields: [] - {{- end }} - {{- end }} - referenceGenome: - {{- $referenceGenome := include "loculus.mergeReferenceGenomes" $instance.referenceGenomes | fromYaml }} - {{ $referenceGenome | toYaml | nindent 10 }} - {{- end }} -{{- end }} - -{{- define "loculus.generateReferenceGenome" }} -{{ if .nucleotideSequences }} -nucleotideSequences: - {{ $nucleotideSequences := include "loculus.generateSequences" .nucleotideSequences | fromYaml }} - {{ $nucleotideSequences.fields | toYaml | nindent 8 }} -{{ else }} -nucleotideSequences: [] -{{ end }} -{{ if .genes }} -genes: - {{ $genes := include "loculus.generateSequences" .genes | fromYaml }} - {{ $genes.fields | toYaml | nindent 8 }} -{{ else }} -genes: [] -{{ end }} -{{- end }} - -{{- define "loculus.generateSequences" }} -{{- $sequences := . }} -fields: - {{- range $sequence := $sequences }} - - name: {{ printf "%s" $sequence.name | quote}} - sequence: {{ printf "%s" $sequence.sequence | quote }} - {{- end }} {{- end }} -{{/* Generate backend metadata from passed metadata array */}} -{{- define "loculus.generateBackendMetadata" }} -{{- $rawUniqueSegments := (include "loculus.getNucleotideSegmentNames" .referenceGenomes | fromYaml).segments }} -{{- $isSegmented := gt (len $rawUniqueSegments) 1 }} -{{- $metadataList := .metadata }} -fields: -{{- range $metadataList }} -{{- $currentItem := . }} -{{- if and $isSegmented .perSegment }} -{{- range $segment := $rawUniqueSegments }} -{{- with $currentItem }} - - name: {{ printf "%s_%s" .name $segment | quote }} - type: {{ .type | default "string" | quote }} -{{- end }} -{{- end}} -{{- else }} - - name: {{ quote .name }} - type: {{ .type | default "string" | quote }} -{{- end}} -{{- end}} - - name: versionComment - type: "string" -{{- end}} - {{/* Generate backend metadata from passed metadata array */}} {{- define "loculus.generateBackendExternalMetadata" }} {{- $rawUniqueSegments := (include "loculus.getNucleotideSegmentNames" .referenceGenomes | fromYaml).segments }} @@ -615,18 +231,7 @@ fields: {{- end}} {{- define "loculus.publicRuntimeConfig" }} -{{- $publicRuntimeConfig := $.Values.public }} -{{- $lapisUrlTemplate := "" }} -{{- if $publicRuntimeConfig.lapisUrlTemplate }} - {{- $lapisUrlTemplate = $publicRuntimeConfig.lapisUrlTemplate }} -{{- else if eq $.Values.environment "server" }} - {{- $lapisUrlTemplate = printf "https://lapis%s%s/%s" $.Values.subdomainSeparator $.Values.host "%organism%" }} -{{- else }} - {{- $lapisUrlTemplate = printf "http://%s:8080/%%organism%%" $.Values.localHost }} -{{- end }} -{{- $externalLapisUrlConfig := dict "lapisUrlTemplate" $lapisUrlTemplate "config" $.Values }} "backendUrl": "{{ include "loculus.backendUrl" . }}", - "lapisUrls": {{- include "loculus.generateExternalLapisUrls" $externalLapisUrlConfig | fromYaml | toJson }}, "keycloakUrl": "{{ include "loculus.keycloakUrl" . }}" {{- end }} diff --git a/kubernetes/loculus/templates/_config-adapter.tpl b/kubernetes/loculus/templates/_config-adapter.tpl new file mode 100644 index 0000000000..1e53fde842 --- /dev/null +++ b/kubernetes/loculus/templates/_config-adapter.tpl @@ -0,0 +1,93 @@ +{{- /* + Adapter init-container spec for SILO + LAPIS pods. + + Inputs (passed via `include` with a dict): + organismKey — string, the organism key (e.g. "ebola-sudan") + configVersion — int, the pinned organism config version (default: 1) + instanceVersion — int (optional), pinned instance config version + dockerTag — string, resolved Loculus docker tag + imagePullPolicy — string + backendUrl — string, in-cluster Loculus backend URL + + The init container writes + /loculus-config/{database_config.yaml,reference_genomes.json,preprocessing_config.yaml} + to the shared emptyDir mounted as `lapis-silo-database-config-processed`, + matching the path that SILO + LAPIS already mount today. +*/}} +{{- define "loculus.configAdapterInitContainer" -}} +- name: config-adapter-{{ .organismKey }} + image: "{{ .images.configAdapter.repository }}:{{ .images.configAdapter.tag | default .dockerTag }}" + imagePullPolicy: {{ .images.configAdapter.pullPolicy | default .imagePullPolicy }} + env: + - name: LOCULUS_BACKEND_URL + value: {{ .backendUrl | quote }} + - name: LOCULUS_ORGANISM_KEY + value: {{ .organismKey | quote }} + - name: LOCULUS_ORGANISM_CONFIG_VERSION + value: {{ default 1 .configVersion | quote }} + {{- if .instanceVersion }} + - name: LOCULUS_INSTANCE_CONFIG_VERSION + value: {{ .instanceVersion | quote }} + {{- end }} + - name: LOCULUS_CONFIG_OUTPUT_DIR + value: "/loculus-config" + volumeMounts: + - name: lapis-silo-database-config-processed + mountPath: /loculus-config + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 500m + memory: 256Mi +{{- end }} + +{{- /* + Adapter init-container spec for the cross-organism overview SILO + LAPIS pods. + + Inputs (passed via `include` with a dict): + viewKey — string, the SQL-backed view key + dockerTag — string, resolved Loculus docker tag + imagePullPolicy — string + images — .Values.images + backendUrl — string, in-cluster Loculus backend URL + + Runs the adapter in overview mode: it fetches only the instance config and + writes /loculus-config/{database_config.yaml,reference_genomes.json, + preprocessing_config.yaml,overview_query.sql}. +*/}} +{{- define "loculus.overviewConfigAdapterInitContainer" -}} +- name: config-adapter-{{ .viewKey }} + image: "{{ .images.configAdapter.repository }}:{{ .images.configAdapter.tag | default .dockerTag }}" + imagePullPolicy: {{ .images.configAdapter.pullPolicy | default .imagePullPolicy }} + env: + - name: LOCULUS_CONFIG_MODE + value: "overview" + - name: LOCULUS_BACKEND_URL + value: {{ .backendUrl | quote }} + - name: LOCULUS_VIEW_KEY + value: {{ .viewKey | quote }} + - name: LOCULUS_CONFIG_OUTPUT_DIR + value: "/loculus-config" + volumeMounts: + - name: lapis-silo-database-config-processed + mountPath: /loculus-config + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 500m + memory: 256Mi +{{- end }} + +{{- /* + Replacement for `loculus.configVolume`: only the processed emptyDir, no + ConfigMap source. The adapter init container writes its outputs straight to + the emptyDir. +*/}} +{{- define "loculus.adapterVolume" -}} +- name: lapis-silo-database-config-processed + emptyDir: {} +{{- end }} diff --git a/kubernetes/loculus/templates/_config-processor.tpl b/kubernetes/loculus/templates/_config-processor.tpl index cfea6861d9..aafe2f7eb0 100644 --- a/kubernetes/loculus/templates/_config-processor.tpl +++ b/kubernetes/loculus/templates/_config-processor.tpl @@ -42,6 +42,11 @@ secretKeyRef: name: service-accounts key: backendUserPassword + - name: LOCULUSSUB_configLoaderUserPassword + valueFrom: + secretKeyRef: + name: service-accounts + key: configLoaderUserPassword - name: LOCULUSSUB_backendKeycloakClientSecret valueFrom: secretKeyRef: diff --git a/kubernetes/loculus/templates/_lineage-system-for-organism.tpl b/kubernetes/loculus/templates/_lineage-system-for-organism.tpl deleted file mode 100644 index 04fde7f3dd..0000000000 --- a/kubernetes/loculus/templates/_lineage-system-for-organism.tpl +++ /dev/null @@ -1,11 +0,0 @@ -{{- define "loculus.lineageSystemForOrganism" -}} -{{- $organism := . -}} -{{- $schema := $organism.schema | include "loculus.patchMetadataSchema" | fromYaml }} -{{- $lineageSystems := list }} -{{- range $entry := $schema.metadata }} - {{- if hasKey $entry "lineageSystem" }} - {{- $lineageSystems = append $lineageSystems $entry.lineageSystem }} - {{- end }} -{{- end }} -{{- $lineageSystems | toYaml -}} -{{- end }} diff --git a/kubernetes/loculus/templates/_merged-reference-genomes.tpl b/kubernetes/loculus/templates/_merged-reference-genomes.tpl index 70aecc521d..3c3712fee0 100644 --- a/kubernetes/loculus/templates/_merged-reference-genomes.tpl +++ b/kubernetes/loculus/templates/_merged-reference-genomes.tpl @@ -1,54 +1,3 @@ -{{- define "loculus.mergeReferenceGenomes" -}} -{{- $segmentWithReferencesList := . -}} -{{- $lapisNucleotideSequences := list -}} -{{- $lapisGenes := list -}} - -{{- $singleSegment := eq (len $segmentWithReferencesList) 1 -}} - -{{- range $segment := $segmentWithReferencesList -}} - {{- $segmentName := $segment.name -}} - {{- $singleReference := eq (len $segment.references) 1 -}} - {{- range $reference := $segment.references -}} - {{- $referenceName := $reference.name -}} - {{- if $singleReference -}} - {{/* Single reference mode - no suffix */}} - {{- $lapisNucleotideSequences = append $lapisNucleotideSequences (dict - "name" $segmentName - "sequence" $reference.sequence - ) -}} - {{- else -}} - {{- $name := printf "%s%s" (ternary "" (printf "%s-" $segmentName) $singleSegment) $referenceName -}} - {{- $lapisNucleotideSequences = append $lapisNucleotideSequences (dict - "name" $name - "sequence" $reference.sequence - ) -}} - {{- end -}} - - {{/* Add genes if present */}} - {{- if $reference.genes -}} - {{- range $gene := $reference.genes -}} - {{- if $singleReference -}} - {{- $lapisGenes = append $lapisGenes (dict - "name" $gene.name - "sequence" $gene.sequence - ) -}} - {{- else -}} - {{- $geneName := printf "%s-%s" $gene.name $referenceName -}} - {{- $lapisGenes = append $lapisGenes (dict - "name" $geneName - "sequence" $gene.sequence - ) -}} - {{- end -}} - {{- end -}} - {{- end -}} - {{- end -}} -{{- end -}} - -{{- $result := dict "nucleotideSequences" $lapisNucleotideSequences "genes" $lapisGenes -}} -{{- $result | toYaml -}} -{{- end -}} - - {{- define "loculus.getNucleotideSegmentNames" -}} {{- $segmentWithReferencesList := . -}} diff --git a/kubernetes/loculus/templates/_preprocessingFromValues.tpl b/kubernetes/loculus/templates/_preprocessingFromValues.tpl deleted file mode 100644 index 3f15e6982f..0000000000 --- a/kubernetes/loculus/templates/_preprocessingFromValues.tpl +++ /dev/null @@ -1,88 +0,0 @@ -{{- define "loculus.sharedPreproSpecs" }} -{{ .key }}: {{/* 'key' is either just 'name' of a metadata field, or 'name_segmentName' for segmented fields.*/}} - {{- if .preprocessing }} - {{- if hasKey .preprocessing "function" }} - function: {{ index .preprocessing "function" }} - {{- else }} - function: identity - {{- end }} - {{- if hasKey .preprocessing "inputs" }} - inputs: - {{- with index .preprocessing "inputs" }} - {{- . | toYaml | nindent 4 }} - {{- end }} - {{- end }} - args: - {{- if .segment }} - segment: {{ .segment }} - {{- end }} - {{- if .onlyForReference }} - reference: {{ .onlyForReference }} - {{- end }} - {{- if .type }} - type: {{ .type }} - {{- end }} - {{- if .options }} - {{- $names := list }} - {{- range .options }} - {{- $names = append $names .name }} - {{- end }} - options: {{ toYaml $names | nindent 4 }} - {{- end }} - {{- with (get .preprocessing "args") }} - {{ toYaml . | nindent 4 }} - {{- end }} - {{- else }} - function: identity - inputs: - {{- if .segment }} - input: {{ printf "%s_%s" .name .segment }} - {{- else }} - input: {{ .name }} - {{- end }} - args: - {{- if .segment }} - segment: {{ .segment }} - {{- end }} - {{- if .onlyForReference }} - reference: {{ .onlyForReference }} - {{- end }} - {{- if .type }} - type: {{ .type }} - {{- end }} - {{- end }} - {{- if .required}} - required: true - {{- end }} -{{- end }} - -{{/* Expects an object { metadata: [...], referenceGenomes: {...} } - .metadata is an array of metadata fields. Each has name, type, displayName, header, required etc. - .referenceGenomes is a map of reference genome definitions directly taken from the instance config of an organism. -*/}} -{{- define "loculus.preprocessingSpecs" -}} -{{- $metadata := .metadata }} -{{- $referenceGenomes := .referenceGenomes}} - -{{- $rawUniqueSegments := (include "loculus.getNucleotideSegmentNames" $referenceGenomes | fromYaml).segments }} -{{- $isSegmented := gt (len $rawUniqueSegments) 1 }} - -{{- range $metadata }} - {{- $currentItem := . }} - {{- if and $isSegmented .perSegment }} - {{- range $segment := $rawUniqueSegments }} - {{- with $currentItem }} - {{- $args := deepCopy . | merge (dict "segment" $segment "key" (printf "%s_%s" .name $segment)) }} - {{- include "loculus.sharedPreproSpecs" $args }} - {{- end }} - {{- end }} - {{- else }} - {{- $segment := "" }} - {{- if .relatesToSegment }} - {{- $segment = .relatesToSegment }} - {{- end }} - {{- $args := deepCopy . | merge (dict "segment" $segment "key" .name) }} - {{- include "loculus.sharedPreproSpecs" $args }} - {{- end }} -{{- end }} -{{- end }} \ No newline at end of file diff --git a/kubernetes/loculus/templates/_siloDatabaseConfig.tpl b/kubernetes/loculus/templates/_siloDatabaseConfig.tpl deleted file mode 100644 index f91e77eb88..0000000000 --- a/kubernetes/loculus/templates/_siloDatabaseConfig.tpl +++ /dev/null @@ -1,46 +0,0 @@ -{{- define "loculus.siloDatabaseShared" -}} -{{- $type := default "string" .type -}} -{{- $lineageName := ternary .name (default "" .lineageSystem) (not (empty .hierarchicalFilter)) -}} -- type: {{ ($type | eq "timestamp") | ternary "int" (($type | eq "authors") | ternary "string" $type) }} - {{- if and .generateIndex (not $lineageName) }} - generateIndex: {{ .generateIndex }} - {{- end }} - {{- if $lineageName }} - generateIndex: true - generateLineageIndex: {{ $lineageName }} {{- /* must match the file name in the lineageDefinitionFilenames */}} - {{- end }} -{{- end }} - - -{{- define "loculus.siloDatabaseConfig" }} -{{- $schema := .schema }} -{{- $rawUniqueSegments := (include "loculus.getNucleotideSegmentNames" .referenceGenomes | fromYaml).segments }} -{{- $isSegmented := gt (len $rawUniqueSegments) 1 }} -schema: - instanceName: {{ $schema.organismName }} - opennessLevel: OPEN - metadata: - {{- range (concat .commonMetadata $schema.metadata) }} - {{- $currentItem := . }} - {{- if and $isSegmented .perSegment }} - {{- range $segment := $rawUniqueSegments }} - {{- with $currentItem }} - {{- include "loculus.siloDatabaseShared" . | nindent 4 }} - name: {{ printf "%s_%s" .name $segment | quote}} - {{- end }} - {{- end }} - {{- else }} - {{- include "loculus.siloDatabaseShared" . | nindent 4 }} - name: {{ .name }} - {{- end }} - {{- end }} - {{- if $schema.files }} - {{- range $schema.files }} - - type: string - name: {{ .name }} - {{- end }} - {{- end }} - primaryKey: accessionVersion - features: - - name: generalizedAdvancedQuery -{{- end }} diff --git a/kubernetes/loculus/templates/config-loader-fixtures.yaml b/kubernetes/loculus/templates/config-loader-fixtures.yaml new file mode 100644 index 0000000000..064778c557 --- /dev/null +++ b/kubernetes/loculus/templates/config-loader-fixtures.yaml @@ -0,0 +1,32 @@ +{{- /* + ConfigMap that bundles the fixture YAMLs in `kubernetes/loculus/fixtures/` + for the loculus-config-loader Job to mount and post to the backend admin API. + + Keys are flattened so a single mount serves `instance.yaml`, every + `organisms/.yaml`, and every `preprocessing//.` + config file. The Job's loader CLI walks the mount as if it were the on-disk + fixtures directory, so paths must match the loader's `/instance.yaml` + + `/organisms/.yaml` + `/preprocessing//.` + layout. + + ConfigMap keys can't contain `/`, so the nested organism/preprocessing paths + are flattened with `__` separators here and mapped back to their real paths + via volume `items` in the Job spec. +*/}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: loculus-config-loader-fixtures + annotations: + "argocd.argoproj.io/sync-wave": "-1" +data: + instance.yaml: |- +{{ .Files.Get "fixtures/instance.yaml" | indent 4 }} +{{- range $path, $_ := .Files.Glob "fixtures/organisms/*.yaml" }} + {{ base $path }}: |- +{{ $.Files.Get $path | indent 4 }} +{{- end }} +{{- range $path, $_ := .Files.Glob "fixtures/preprocessing/*/*" }} + preprocessing__{{ base (dir $path) }}__{{ base $path }}: |- +{{ $.Files.Get $path | indent 4 }} +{{- end }} diff --git a/kubernetes/loculus/templates/config-loader-job.yaml b/kubernetes/loculus/templates/config-loader-job.yaml new file mode 100644 index 0000000000..48dad8e11e --- /dev/null +++ b/kubernetes/loculus/templates/config-loader-job.yaml @@ -0,0 +1,145 @@ +{{- /* + loculus-config-loader: Job that POSTs the fixture YAMLs in + `kubernetes/loculus/fixtures/` to the Loculus admin config API. + + By default this runs as a post-install/post-upgrade Helm hook. Argo-managed + preview deployments set `runConfigLoaderAsArgoJob=true`, which renders the + same workload as a normal replaceable Job so previews load the bundled + instance, organism, and preprocessing config by default. + + Skipped when `disableBackend=true` (the `--dev` flow). In that mode the + backend runs in the developer's IDE on the host and is typically not yet + reachable when the Helm post-install hook fires. Run the loader CLI + manually from the host once the IDE backend is up — see the + "Local development instance" docs for the exact command. + + Mode: `fresh-only` (Phase-2-7 resolution + Phase-3-prep-4) — fails fast if + any organism already exists, so reruns over an existing cluster won't + publish phantom new versions. Operators who explicitly want to overwrite + the in-cluster state should run the loader CLI manually with `--mode + republish` (once that mode is fully implemented). + + Token acquisition is inline: the Job's main container runs a short shell + script that uses Keycloak's password grant on the `config_loader_user` + service account (autogen secret `service-accounts.configLoaderUserPassword`) + to obtain an access token, writes it to /tmp/token, then invokes the loader + CLI with `--admin-token-file`. +*/}} +{{- if not .Values.disableBackend }} +{{- $backendHost := .Values.disableBackend | ternary + "http://host.k3d.internal:8079" + "http://loculus-backend-service:8079" +}} +{{- $testconfig := .Values.testconfig | default false }} +{{- $keycloakHost := $testconfig | ternary (printf "http://%s:8083" $.Values.localHost) "http://loculus-keycloak-service:8083" }} +{{- $dockerTag := include "loculus.dockerTag" .Values }} +{{- $loaderImage := printf "%s:%s" .Values.images.configLoader.repository (.Values.images.configLoader.tag | default $dockerTag) }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: loculus-config-loader + annotations: +{{- if .Values.runConfigLoaderAsArgoJob }} + "argocd.argoproj.io/sync-options": "Force=true,Replace=true" + "argocd.argoproj.io/sync-wave": "0" +{{- else }} + "helm.sh/hook": "post-install,post-upgrade" + "helm.sh/hook-weight": "10" + "helm.sh/hook-delete-policy": "before-hook-creation" +{{- end }} +spec: + backoffLimit: 6 + ttlSecondsAfterFinished: 3600 + template: + metadata: + labels: + app: loculus + component: loculus-config-loader + spec: + restartPolicy: Never + {{- include "possiblePriorityClassName" . | nindent 6 }} + containers: + - name: config-loader + image: {{ $loaderImage | quote }} + imagePullPolicy: {{ .Values.images.configLoader.pullPolicy | default .Values.imagePullPolicy }} + env: + - name: KEYCLOAK_PASSWORD + valueFrom: + secretKeyRef: + name: service-accounts + key: configLoaderUserPassword + - name: BACKEND_URL + value: {{ $backendHost | quote }} + - name: KEYCLOAK_URL + value: {{ $keycloakHost | quote }} + command: + - /bin/sh + - -ec + - | + # 1. Wait for Keycloak and the backend to be reachable. The Job is + # a Helm post-install hook so its own start time isn't gated on + # other pods being ready; in a fresh cluster Keycloak may still + # be booting when we run. Retry for up to ~5 minutes. + # Up to 120 attempts × 5s = 10 minutes per URL. Keycloak's cold + # start on a fresh cluster can take well over 5 minutes, + # especially when init containers (config-processor, + # theme-prep) plus realm import all run on first boot. + wait_for_url() { + local url="$1" + local label="$2" + local attempt=0 + while [ "$attempt" -lt 120 ]; do + if wget --spider --quiet --timeout=3 "$url"; then + echo "$label reachable at $url" + return 0 + fi + attempt=$((attempt + 1)) + echo "Waiting for $label at $url (attempt $attempt/120)..." + sleep 5 + done + echo "$label not reachable after 120 attempts; aborting." >&2 + return 1 + } + wait_for_url "$KEYCLOAK_URL/realms/loculus/.well-known/openid-configuration" "Keycloak" || exit 1 + wait_for_url "$BACKEND_URL/actuator/health" "Backend" || exit 1 + + # 2. Fetch a loculus_administrator access token from Keycloak. + TOKEN_FILE=/tmp/loader-token + echo "Fetching access token from $KEYCLOAK_URL..." + ACCESS_TOKEN=$(wget -qO- --post-data="grant_type=password&client_id=backend-client&username=config_loader_user&password=$KEYCLOAK_PASSWORD" \ + "$KEYCLOAK_URL/realms/loculus/protocol/openid-connect/token" \ + | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p') + if [ -z "$ACCESS_TOKEN" ]; then + echo "Failed to obtain access token; aborting." >&2 + exit 2 + fi + printf '%s' "$ACCESS_TOKEN" > "$TOKEN_FILE" + # 3. Run the loader in fresh-only mode. + exec npx tsx /app/src/loader/cli.ts \ + --backend-url "$BACKEND_URL" \ + --fixtures /etc/fixtures \ + --admin-token-file "$TOKEN_FILE" \ + --mode fresh-only + volumeMounts: + - name: fixtures + mountPath: /etc/fixtures + readOnly: true + volumes: + - name: fixtures + projected: + sources: + - configMap: + name: loculus-config-loader-fixtures + items: + - key: instance.yaml + path: instance.yaml +{{- range $path, $_ := .Files.Glob "fixtures/organisms/*.yaml" }} + - key: {{ base $path | quote }} + path: organisms/{{ base $path }} +{{- end }} +{{- range $path, $_ := .Files.Glob "fixtures/preprocessing/*/*" }} + - key: {{ printf "preprocessing__%s__%s" (base (dir $path)) (base $path) | quote }} + path: preprocessing/{{ base (dir $path) }}/{{ base $path }} +{{- end }} +{{- end }} diff --git a/kubernetes/loculus/templates/ingest.yaml b/kubernetes/loculus/templates/ingest.yaml index adc1455578..f4da387c9d 100644 --- a/kubernetes/loculus/templates/ingest.yaml +++ b/kubernetes/loculus/templates/ingest.yaml @@ -115,13 +115,12 @@ spec: "args" (list "snakemake" "results/submitted" "results/revised" "--all-temp") ) | nindent 8 }} {{/* -PostSync-hook trigger: kicks off a one-off ingest Job from the CronJob's -template after argocd reports the sync as healthy. PostSync waits for -all resources (e.g. backend, ena-submission) to be Healthy before firing, -so the spawned ingest pod doesn't start hammering services that aren't -ready yet. The trigger itself exits in seconds; the spawned ingest Job -runs untracked by argocd. Concurrency with a scheduled CronJob run is -handled by the shared lock init container in the spawned pod. +One-off ingest trigger: kicks off a bootstrap ingest Job from the CronJob's +template after the backend and config are ready. Argo uses PostSync so it +fires after the app is Healthy; Helm uses a post-install hook ordered after +preview seed data. The trigger itself exits in seconds; the spawned ingest +Job runs untracked by the deploy tool. Concurrency with a scheduled CronJob +run is handled by the shared lock init container in the spawned pod. */}} --- apiVersion: batch/v1 @@ -129,8 +128,14 @@ kind: Job metadata: name: loculus-ingest-trigger-{{ $key }} annotations: +{{- if $.Values.runConfigLoaderAsArgoJob }} argocd.argoproj.io/hook: PostSync argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded +{{- else }} + "helm.sh/hook": "post-install,post-upgrade" + "helm.sh/hook-weight": "30" + "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" +{{- end }} spec: activeDeadlineSeconds: 600 backoffLimit: 0 diff --git a/kubernetes/loculus/templates/keycloak-config-map.yaml b/kubernetes/loculus/templates/keycloak-config-map.yaml index fc7c6bf14b..e2a6fccdb8 100644 --- a/kubernetes/loculus/templates/keycloak-config-map.yaml +++ b/kubernetes/loculus/templates/keycloak-config-map.yaml @@ -116,7 +116,59 @@ data: ] } }, + { + "username": "loculus_administrator", + "enabled": true, + "email": "loculus_administrator@void.o", + "emailVerified" : true, + "firstName": "Loculus", + "lastName": "Administrator", + "credentials": [ + { + "type": "password", + "value": "loculus_administrator" + } + ], + "realmRoles": [ + "loculus_administrator", + "offline_access" + ], + "attributes": { + "university": "University of Test" + }, + "clientRoles": { + "account": [ + "manage-account" + ] + } + }, {{ end }} + { + "username": "config_loader_user", + "enabled": true, + "email": "config_loader_user@void.o", + "emailVerified" : true, + "firstName": "Config", + "lastName": "Loader", + "credentials": [ + { + "type": "password", + "value": "[[configLoaderUserPassword]]" + } + ], + "realmRoles": [ + "loculus_administrator", + "offline_access" + ], + "attributes": { + "university": "Loculus Config Bootstrap" + }, + "clientRoles": { + "account": [ + "manage-account" + ] + } + }, { "username": "insdc_ingest_user", "enabled": true, @@ -232,6 +284,10 @@ data: "name": "admin", "description": "Administrator privileges" }, + { + "name": "loculus_administrator", + "description": "Privileges for editing Loculus instance and organism configuration" + }, { "name": "preprocessing_pipeline", "description": "Preprocessing pipeline privileges" diff --git a/kubernetes/loculus/templates/lapis-deployment.yaml b/kubernetes/loculus/templates/lapis-deployment.yaml index 4119e39368..c53598fbbf 100644 --- a/kubernetes/loculus/templates/lapis-deployment.yaml +++ b/kubernetes/loculus/templates/lapis-deployment.yaml @@ -1,8 +1,13 @@ {{- $dockerTag := include "loculus.dockerTag" .Values }} +{{- $backendHost := .Values.disableBackend | ternary + "http://host.k3d.internal:8079" + "http://loculus-backend-service:8079" +}} {{- range $_, $item := (include "loculus.enabledOrganisms" . | fromJson).organisms }} {{- $key := $item.key }} {{- $organismContent := $item.contents }} +{{- $configVersion := default 1 ($organismContent.configVersion) }} --- apiVersion: apps/v1 kind: Deployment @@ -26,7 +31,13 @@ spec: {{- include "possiblePriorityClassName" $ | nindent 6 }} {{- include "loculus.podScheduling" $ | nindent 6 }} initContainers: - {{- include "loculus.configProcessor" (dict "name" "lapis-silo-database-config" "dockerTag" $dockerTag "imagePullPolicy" $.Values.imagePullPolicy) | nindent 8 }} + {{- include "loculus.configAdapterInitContainer" (dict + "organismKey" $key + "configVersion" $configVersion + "dockerTag" $dockerTag + "imagePullPolicy" $.Values.imagePullPolicy + "images" $.Values.images + "backendUrl" $backendHost) | nindent 8 }} containers: - name: lapis image: "{{ $.Values.images.lapis.repository }}:{{ $.Values.images.lapis.tag }}" @@ -67,5 +78,5 @@ spec: failureThreshold: 3 timeoutSeconds: 5 volumes: - {{- include "loculus.configVolume" (dict "name" "lapis-silo-database-config" "configmap" (printf "lapis-silo-database-config-%s" $key)) | nindent 8 }} + {{- include "loculus.adapterVolume" . | nindent 8 }} {{- end }} diff --git a/kubernetes/loculus/templates/lapis-silo-database-config.yaml b/kubernetes/loculus/templates/lapis-silo-database-config.yaml deleted file mode 100644 index 3d1540d0e7..0000000000 --- a/kubernetes/loculus/templates/lapis-silo-database-config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -{{- $commonMetadata := (include "loculus.commonMetadata" . | fromYaml).fields }} - -{{- range $_, $item := (include "loculus.enabledOrganisms" . | fromJson).organisms }} -{{- $key := $item.key }} -{{- $organismContent := $item.contents }} - -{{- $lineageSystem := $organismContent | include "loculus.lineageSystemForOrganism" | fromYamlArray }} -{{- $hierarchicalFilters := $organismContent | include "loculus.hierarchicalFiltersForOrganism" | fromYaml }} -{{- $allLineageNames := $lineageSystem }} -{{- range $name, $_ := $hierarchicalFilters }} - {{- $allLineageNames = append $allLineageNames $name }} -{{- end }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: lapis-silo-database-config-{{ $key }} -data: - {{- $args := dict - "schema" ($organismContent.schema | include "loculus.patchMetadataSchema" | fromYaml) - "commonMetadata" $commonMetadata - "referenceGenomes" $organismContent.referenceGenomes - }} - database_config.yaml: | - {{ include "loculus.siloDatabaseConfig" $args | nindent 4 }} - - preprocessing_config.yaml: | - inputDirectory: /preprocessing/input - outputDirectory: /preprocessing/output - ndjsonInputFilename: data.ndjson.zst - referenceGenomeFilename: reference_genomes.json - {{- if $allLineageNames }} - lineageDefinitionFilenames: - {{- range $name := $allLineageNames }} - - {{ printf "%s.yaml" $name }} - {{- end }} - {{- end }} - - reference_genomes.json: | - {{ include "loculus.mergeReferenceGenomes" $organismContent.referenceGenomes | fromYaml | toJson }} -{{- end }} diff --git a/kubernetes/loculus/templates/loculus-backend.yaml b/kubernetes/loculus/templates/loculus-backend.yaml index 1a0db9b700..7900fc4a9d 100644 --- a/kubernetes/loculus/templates/loculus-backend.yaml +++ b/kubernetes/loculus/templates/loculus-backend.yaml @@ -69,6 +69,8 @@ spec: - "--spring.datasource.url=$(DB_URL)" - "--spring.datasource.username=$(DB_USERNAME)" - "--spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://loculus-keycloak-service:8083/realms/loculus/protocol/openid-connect/certs" + - "--loculus.backend.backend-url={{ include "loculus.backendUrl" . }}" + - "--loculus.backend.website-url={{ include "loculus.websiteUrl" . }}" - "--loculus.cleanup.task.reset-stale-in-processing-after-seconds={{- .Values.preprocessingTimeout | default 120 }}" - "--loculus.pipeline-version-upgrade-check.interval-seconds={{- .Values.pipelineVersionUpgradeCheckIntervalSeconds | default 10 }}" - "--loculus.s3.enabled=$(S3_ENABLED)" @@ -159,4 +161,4 @@ spec: mountPath: /config volumes: {{ include "loculus.configVolume" (dict "name" "loculus-backend-config") | nindent 8 }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/kubernetes/loculus/templates/loculus-preprocessing-config.yaml b/kubernetes/loculus/templates/loculus-preprocessing-config.yaml deleted file mode 100644 index dabe36aa3b..0000000000 --- a/kubernetes/loculus/templates/loculus-preprocessing-config.yaml +++ /dev/null @@ -1,31 +0,0 @@ -{{- range $_, $item := (include "loculus.enabledOrganisms" . | fromJson).organisms }} -{{- $organism := $item.key }} -{{- $organismConfig := $item.contents }} -{{- $metadata := ($organismConfig.schema | include "loculus.patchMetadataSchema" | fromYaml).metadata }} -{{- $referenceGenomes:= include "loculus.mergeReferenceGenomes" $organismConfig.referenceGenomes | fromYaml }} -{{- $flattened := include "loculus.flattenPreprocessingVersions" $organismConfig.preprocessing | fromJson }} -{{- range $processingIndex, $processingConfig := $flattened.items }} -{{- if $processingConfig.configFile }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: loculus-preprocessing-config-{{ $organism }}-v{{ $processingConfig.version }}-{{ $processingIndex }} -data: - preprocessing-config.yaml: | - organism: {{ $organism }} - {{- $processingConfig.configFile | toYaml | nindent 4 }} - {{- if and (hasKey $organismConfig.schema "submissionDataTypes") (hasKey $organismConfig.schema.submissionDataTypes "maxSequencesPerEntry") }} - max_sequences_per_entry: {{ $organismConfig.schema.submissionDataTypes.maxSequencesPerEntry }} - {{- end }} - processing_spec: - {{- $args := dict "metadata" $metadata "referenceGenomes" $organismConfig.referenceGenomes }} - {{- include "loculus.preprocessingSpecs" $args | nindent 6 }} - versionComment: - function: identity - inputs: - input: versionComment - args: -{{- end }} -{{- end }} -{{- end }} diff --git a/kubernetes/loculus/templates/loculus-preprocessing-deployment.yaml b/kubernetes/loculus/templates/loculus-preprocessing-deployment.yaml index 5c29a9497b..b8b936fa5b 100644 --- a/kubernetes/loculus/templates/loculus-preprocessing-deployment.yaml +++ b/kubernetes/loculus/templates/loculus-preprocessing-deployment.yaml @@ -53,17 +53,8 @@ spec: - "--backend-host={{ $backendHost }}/{{ $organism }}" - "--keycloak-host={{ $keycloakHost }}" - "--pipeline-version={{ $processingConfig.version }}" + - "--organism={{ $organism }}" - "--keycloak-password=$(KEYCLOAK_PASSWORD)" - {{- if $processingConfig.configFile }} - - "--config=/etc/config/preprocessing-config.yaml" - volumeMounts: - - name: preprocessing-config-volume-{{ $organism }}-v{{ $processingConfig.version }}-{{ $processingIndex }} - mountPath: /etc/config - volumes: - - name: preprocessing-config-volume-{{ $organism }}-v{{ $processingConfig.version }}-{{ $processingIndex }} - configMap: - name: loculus-preprocessing-config-{{ $organism }}-v{{ $processingConfig.version }}-{{ $processingIndex }} - {{- end }} {{- end }} {{- end }} {{- end }} diff --git a/kubernetes/loculus/templates/loculus-website-config.yaml b/kubernetes/loculus/templates/loculus-website-config.yaml index 3c5df57465..42218ed028 100644 --- a/kubernetes/loculus/templates/loculus-website-config.yaml +++ b/kubernetes/loculus/templates/loculus-website-config.yaml @@ -4,8 +4,10 @@ kind: ConfigMap metadata: name: loculus-website-config data: - website_config.json: | - {{ include "loculus.generateWebsiteConfig" . | fromYaml | toJson }} + # Domain config (organisms, schemas, reference genomes, metadata, branding, + # linkOuts, dataUseTerms) is fetched per-request from the backend (DB-backed) + # via the website's configMiddleware — it is no longer baked in here. Only the + # technical runtime_config.json (service URLs, Keycloak, secrets) remains. runtime_config.json: | { "name" : "{{ $.Values.name }}", @@ -19,7 +21,6 @@ data: {{- else -}} "backendUrl": "http://loculus-backend-service:8079", {{- end }} - "lapisUrls": {{- include "loculus.generateInternalLapisUrls" . | fromYaml | toJson }}, "keycloakUrl": "{{ if not .Values.disableWebsite -}}http://loculus-keycloak-service:8083{{ else -}}http://{{ $.Values.localHost }}:8083{{ end }}" {{- end }} }, diff --git a/kubernetes/loculus/templates/overview-lapis-deployment.yaml b/kubernetes/loculus/templates/overview-lapis-deployment.yaml new file mode 100644 index 0000000000..e42216567a --- /dev/null +++ b/kubernetes/loculus/templates/overview-lapis-deployment.yaml @@ -0,0 +1,102 @@ +{{- $views := dict }} +{{- range $key, $view := (.Values.views | default dict) }} + {{- $views = set $views $key $view }} +{{- end }} +{{- if and .Values.overview .Values.overview.enabled }} + {{- $legacyKey := .Values.overview.key | default "overview" }} + {{- if not (hasKey $views $legacyKey) }} + {{- $views = set $views $legacyKey .Values.overview }} + {{- end }} +{{- end }} +{{- range $key, $view := $views }} +{{- if or (not (hasKey $view "enabled")) $view.enabled }} +{{- $dockerTag := include "loculus.dockerTag" $.Values }} +{{- $backendHost := $.Values.disableBackend | ternary + "http://host.k3d.internal:8079" + "http://loculus-backend-service:8079" +}} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: loculus-lapis-{{ $key }} +spec: + replicas: {{ $.Values.replicas.lapis | default 1 }} + selector: + matchLabels: + app: loculus + component: lapis-{{ $key }} + template: + metadata: + annotations: + valuesHash: {{ $.Values | toJson | sha256sum | quote }} + labels: + app: loculus + component: lapis-{{ $key }} + spec: + {{- include "possiblePriorityClassName" $ | nindent 6 }} + initContainers: + {{- include "loculus.overviewConfigAdapterInitContainer" (dict + "dockerTag" $dockerTag + "imagePullPolicy" $.Values.imagePullPolicy + "images" $.Values.images + "backendUrl" $backendHost + "viewKey" $key) | nindent 8 }} + containers: + - name: lapis + image: "{{ $.Values.images.lapis.repository }}:{{ $.Values.images.lapis.tag }}" + imagePullPolicy: "{{ $.Values.images.lapis.pullPolicy | default $.Values.imagePullPolicy }}" + {{- include "loculus.resources" (list "lapis" $.Values $key) | nindent 10 }} + ports: + - containerPort: 8080 + args: + - "--silo.url=http://loculus-silo-service-{{ $key }}:8081" + env: + - name: JVM_OPTS + value: -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:MaxHeapFreeRatio=5 -XX:MinHeapFreeRatio=2 -XX:MaxGCPauseMillis=100 + volumeMounts: + - name: lapis-silo-database-config-processed + mountPath: /workspace/database_config.yaml + subPath: database_config.yaml + - name: lapis-silo-database-config-processed + mountPath: /workspace/reference_genomes.json + subPath: reference_genomes.json + startupProbe: + httpGet: + path: /actuator/health + port: 8080 + periodSeconds: 5 + failureThreshold: 36 + readinessProbe: + httpGet: + path: /sample/info + port: 8080 + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 5 + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 5 + volumes: + {{- include "loculus.adapterVolume" $ | nindent 8 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ template "loculus.lapisServiceName" $key }} +spec: + type: ClusterIP + selector: + app: loculus + component: lapis-{{ $key }} + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http +{{- end }} +{{- end }} diff --git a/kubernetes/loculus/templates/overview-silo-deployment.yaml b/kubernetes/loculus/templates/overview-silo-deployment.yaml new file mode 100644 index 0000000000..728aeefa74 --- /dev/null +++ b/kubernetes/loculus/templates/overview-silo-deployment.yaml @@ -0,0 +1,164 @@ +{{- $views := dict }} +{{- range $key, $view := (.Values.views | default dict) }} + {{- $views = set $views $key $view }} +{{- end }} +{{- if and .Values.overview .Values.overview.enabled }} + {{- $legacyKey := .Values.overview.key | default "overview" }} + {{- if not (hasKey $views $legacyKey) }} + {{- $views = set $views $legacyKey .Values.overview }} + {{- end }} +{{- end }} +{{- range $key, $view := $views }} +{{- if or (not (hasKey $view "enabled")) $view.enabled }} +{{- $dockerTag := include "loculus.dockerTag" $.Values }} +{{- $backendHost := $.Values.disableBackend | ternary + "http://host.k3d.internal:8079" + "http://loculus-backend-service:8079" +}} +{{- /* The overview query + manual SILO config come from the DB instance config: + the config-adapter (overview mode) init container renders them to files. + Backend base URLs (deployment topology) are computed here from the + enabled organisms. */ -}} +{{- $backendUrls := dict }} +{{- range $_, $item := (include "loculus.enabledOrganisms" $ | fromJson).organisms }} + {{- $organismKey := $item.key }} + {{- $backendUrls = set $backendUrls $organismKey (printf "%s/%s" $backendHost $organismKey) }} +{{- end }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: loculus-silo-{{ $key }} + annotations: + argocd.argoproj.io/sync-options: Replace=true{{ if (and (not $.Values.developmentDatabasePersistence) $.Values.runDevelopmentMainDatabase) }},Force=true{{ end }} +spec: + progressDeadlineSeconds: 1200 + replicas: 1 + selector: + matchLabels: + app: loculus + component: silo-{{ $key }} + template: + metadata: + annotations: + valuesHash: {{ $.Values | toJson | sha256sum | quote }} + labels: + app: loculus + component: silo-{{ $key }} + spec: + {{- include "possiblePriorityClassName" $ | nindent 6 }} + initContainers: + {{- include "loculus.overviewConfigAdapterInitContainer" (dict + "dockerTag" $dockerTag + "imagePullPolicy" $.Values.imagePullPolicy + "images" $.Values.images + "backendUrl" $backendHost + "viewKey" $key) | nindent 8 }} + containers: + - name: silo + image: "{{ $.Values.images.loculusSilo.repository }}:{{ $.Values.images.loculusSilo.tag | default $dockerTag }}" + command: ["/usr/local/bin/silo"] + imagePullPolicy: {{ $.Values.imagePullPolicy }} + {{- include "loculus.resources" (list "silo" $.Values $key) | nindent 10 }} + env: + - name: SPDLOG_LEVEL + value: "debug" + - name: SILO_DATA_DIRECTORY + value: "/data/" + ports: + - containerPort: 8081 + args: + - "api" + - "--api-threads-for-http-connections" + - {{ default 16 (($.Values.silo).apiThreadsForHttpConnections) | quote }} + - "--api-max-queued-http-connections" + - "1000" + - "--query-materialization-cutoff" + - "3276" + volumeMounts: + - name: lapis-silo-shared-data + mountPath: /data + readinessProbe: + httpGet: + path: /info + port: 8081 + initialDelaySeconds: 30 + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 5 + livenessProbe: + httpGet: + path: /health + port: 8081 + initialDelaySeconds: 30 + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 5 + - name: silo-importer + image: "{{ $.Values.images.loculusSilo.repository }}:{{ $.Values.images.loculusSilo.tag | default $dockerTag }}" + imagePullPolicy: "{{ $.Values.images.loculusSilo.pullPolicy }}" + {{- include "loculus.resources" (list "silo-importer" $.Values) | nindent 10 }} + env: + - name: SILO_IMPORT_MODE + value: "overview" + - name: OVERVIEW_BACKEND_BASE_URLS + value: {{ $backendUrls | toJson | quote }} + - name: OVERVIEW_ORGANISM_DISPLAY_NAMES + value: {{ $view.organismDisplayNames | default dict | toJson | quote }} + - name: OVERVIEW_QUERY_FILE + value: "/app/overview_query.sql" + - name: OVERVIEW_SEQUENCE_CONFIG_FILE + value: "/app/view_sequence_config.json" + - name: SILO_RUN_TIMEOUT_SECONDS + value: {{ $.Values.siloImport.siloTimeoutSeconds | quote }} + - name: HARD_REFRESH_INTERVAL + value: {{ $.Values.siloImport.hardRefreshIntervalSeconds | quote }} + - name: SILO_IMPORT_POLL_INTERVAL_SECONDS + value: {{ $.Values.siloImport.pollIntervalSeconds | quote }} + - name: PATH_TO_SILO_BINARY + value: "/usr/local/bin/silo" + - name: PREPROCESSING_CONFIG + value: "/app/preprocessing_config.yaml" + volumeMounts: + - name: lapis-silo-database-config-processed + mountPath: /preprocessing/input/reference_genomes.json + subPath: reference_genomes.json + - name: lapis-silo-database-config-processed + mountPath: /preprocessing/input/database_config.yaml + subPath: database_config.yaml + - name: lapis-silo-database-config-processed + mountPath: /app/preprocessing_config.yaml + subPath: preprocessing_config.yaml + - name: lapis-silo-database-config-processed + mountPath: /app/overview_query.sql + subPath: overview_query.sql + - name: lapis-silo-database-config-processed + mountPath: /app/view_sequence_config.json + subPath: view_sequence_config.json + - name: lapis-silo-shared-data + mountPath: /preprocessing/output + - name: lapis-silo-input-data-cache + mountPath: /preprocessing/input + volumes: + {{- include "loculus.adapterVolume" $ | nindent 8 }} + - name: lapis-silo-shared-data + emptyDir: {} + - name: lapis-silo-input-data-cache + emptyDir: {} +--- +apiVersion: v1 +kind: Service +metadata: + name: loculus-silo-service-{{ $key }} +spec: + type: ClusterIP + selector: + app: loculus + component: silo-{{ $key }} + ports: + - port: 8081 + targetPort: 8081 + protocol: TCP + name: http +{{- end }} +{{- end }} diff --git a/kubernetes/loculus/templates/seed-preview-data.yaml b/kubernetes/loculus/templates/seed-preview-data.yaml new file mode 100644 index 0000000000..d20bc2f42f --- /dev/null +++ b/kubernetes/loculus/templates/seed-preview-data.yaml @@ -0,0 +1,269 @@ +{{- $seedPreviewData := .Values.seedPreviewData | default dict }} +{{- $seedEnabled := $seedPreviewData.enabled | default false }} +{{- if and $seedEnabled (not .Values.disableBackend) (not .Values.disablePreprocessing) (not .Values.readOnlyMode) .Values.createTestAccounts }} +{{- $backendHost := .Values.disableBackend | ternary + "http://host.k3d.internal:8079" + "http://loculus-backend-service:8079" +}} +{{- $testconfig := .Values.testconfig | default false }} +{{- $keycloakHost := $testconfig | ternary (printf "http://%s:8083" $.Values.localHost) "http://loculus-keycloak-service:8083" }} +{{- $dockerTag := include "loculus.dockerTag" .Values }} +{{- $seedImage := printf "%s:%s" .Values.images.configLoader.repository (.Values.images.configLoader.tag | default $dockerTag) }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: loculus-preview-seed-data + annotations: + "argocd.argoproj.io/sync-wave": "0" +data: + seed-preview-data.mjs: |- + import { readFile } from 'node:fs/promises'; + + const backendUrl = process.env.BACKEND_URL ?? 'http://loculus-backend-service:8079'; + const keycloakUrl = process.env.KEYCLOAK_URL ?? 'http://loculus-keycloak-service:8083'; + const username = process.env.SEED_USERNAME ?? 'testuser'; + const password = process.env.SEED_PASSWORD ?? 'testuser'; + const groupName = 'Co-infection preview data'; + const expectedEntriesPerOrganism = 15; + const organisms = [ + { + key: 'ebola-sudan', + metadata: '/seed-data/ebola-sudan-metadata.tsv', + sequences: '/seed-data/ebola-sudan-sequences.fasta', + }, + { + key: 'west-nile', + metadata: '/seed-data/west-nile-metadata.tsv', + sequences: '/seed-data/west-nile-sequences.fasta', + }, + ]; + + const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)); + + async function waitFor(label, predicate, attempts = 180, intervalMs = 5000) { + let lastError; + for (let attempt = 1; attempt <= attempts; attempt += 1) { + try { + if (await predicate()) { + console.log(`${label} is ready`); + return; + } + } catch (error) { + lastError = error; + } + console.log(`Waiting for ${label} (${attempt}/${attempts})`); + await sleep(intervalMs); + } + throw new Error(`${label} did not become ready. Last error: ${lastError?.message ?? 'none'}`); + } + + async function jsonRequest(url, options = {}) { + const response = await fetch(url, options); + const text = await response.text(); + if (!response.ok) { + throw new Error(`${options.method ?? 'GET'} ${url} failed with ${response.status}: ${text}`); + } + return text === '' ? null : JSON.parse(text); + } + + async function getToken() { + const body = new URLSearchParams({ + grant_type: 'password', + client_id: 'backend-client', + username, + password, + }); + const response = await jsonRequest( + `${keycloakUrl}/realms/loculus/protocol/openid-connect/token`, + { method: 'POST', body }, + ); + return response.access_token; + } + + function authHeaders(token, headers = {}) { + return { ...headers, Authorization: `Bearer ${token}` }; + } + + async function authJson(token, path, options = {}) { + return jsonRequest(`${backendUrl}${path}`, { + ...options, + headers: authHeaders(token, options.headers ?? {}), + }); + } + + async function getOrCreateGroup(token) { + const groups = await authJson(token, '/user/groups'); + const existingGroup = groups.find((group) => group.groupName === groupName); + if (existingGroup !== undefined) { + console.log(`Using existing seed group ${existingGroup.groupId}`); + return existingGroup.groupId; + } + + const created = await authJson(token, '/groups', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + groupName, + institution: 'Loculus Preview Laboratory', + address: { + line1: '1 Preview Way', + line2: '', + city: 'Kampala', + state: 'Central Region', + postalCode: '00000', + country: 'Uganda', + }, + contactEmail: 'preview-data@loculus.org', + }), + }); + console.log(`Created seed group ${created.groupId}`); + return created.groupId; + } + + async function getEntries(token, organism, groupId) { + const response = await authJson(token, `/${organism}/get-sequences?groupIdsFilter=${groupId}`); + return response.sequenceEntries ?? []; + } + + async function submitSeedFiles(token, organism, groupId, metadataPath, sequencePath) { + const form = new FormData(); + form.set('groupId', String(groupId)); + form.set('dataUseTermsType', 'OPEN'); + form.set('metadataFile', new Blob([await readFile(metadataPath)], { type: 'text/tab-separated-values' }), `${organism}-metadata.tsv`); + form.set('sequenceFile', new Blob([await readFile(sequencePath)], { type: 'text/x-fasta' }), `${organism}-sequences.fasta`); + + const response = await fetch(`${backendUrl}/${organism}/submit`, { + method: 'POST', + headers: authHeaders(token), + body: form, + }); + const text = await response.text(); + if (!response.ok) { + throw new Error(`Submitting ${organism} seed data failed with ${response.status}: ${text}`); + } + console.log(`Submitted ${organism} seed data: ${text}`); + } + + async function waitForProcessed(token, organism, groupId) { + let entries = []; + await waitFor(`${organism} seed preprocessing`, async () => { + entries = await getEntries(token, organism, groupId); + const errored = entries.filter((entry) => entry.processingResult === 'HAS_ERRORS'); + if (errored.length > 0) { + throw new Error(`${organism} seed preprocessing produced errors: ${JSON.stringify(errored)}`); + } + const waiting = entries.filter((entry) => ['RECEIVED', 'IN_PROCESSING'].includes(entry.status)); + const ready = entries.filter((entry) => ['PROCESSED', 'APPROVED_FOR_RELEASE'].includes(entry.status)); + console.log(`${organism}: total=${entries.length}, ready=${ready.length}, waiting=${waiting.length}`); + return entries.length === expectedEntriesPerOrganism && waiting.length === 0 && ready.length === expectedEntriesPerOrganism; + }, 240, 5000); + return entries; + } + + async function approveProcessed(token, organism, groupId, entries) { + if (entries.every((entry) => entry.status === 'APPROVED_FOR_RELEASE')) { + console.log(`${organism} seed data is already approved`); + return; + } + await authJson(token, `/${organism}/approve-processed-data`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ groupIdsFilter: [groupId], scope: 'ALL' }), + }); + await waitFor(`${organism} seed approval`, async () => { + const approved = (await getEntries(token, organism, groupId)) + .filter((entry) => entry.status === 'APPROVED_FOR_RELEASE'); + console.log(`${organism}: approved=${approved.length}`); + return approved.length === expectedEntriesPerOrganism; + }, 60, 5000); + } + + async function seedOrganism(token, groupId, organism) { + const existingEntries = await getEntries(token, organism.key, groupId); + if (existingEntries.length === 0) { + await submitSeedFiles(token, organism.key, groupId, organism.metadata, organism.sequences); + } else if (existingEntries.length !== expectedEntriesPerOrganism) { + throw new Error(`${organism.key} seed group already has ${existingEntries.length} entries, expected ${expectedEntriesPerOrganism}`); + } else { + console.log(`${organism.key} seed data already exists`); + } + const processedEntries = await waitForProcessed(token, organism.key, groupId); + await approveProcessed(token, organism.key, groupId, processedEntries); + } + + await waitFor('Keycloak', async () => { + const response = await fetch(`${keycloakUrl}/realms/loculus/.well-known/openid-configuration`); + return response.ok; + }); + await waitFor('backend', async () => { + const response = await fetch(`${backendUrl}/actuator/health`); + return response.ok; + }); + for (const organism of organisms) { + await waitFor(`${organism.key} config`, async () => { + const response = await fetch(`${backendUrl}/api/config/organisms/${organism.key}`); + return response.ok; + }); + } + + const token = await getToken(); + const groupId = await getOrCreateGroup(token); + for (const organism of organisms) { + await seedOrganism(token, groupId, organism); + } + console.log('Preview co-infection seed data is ready'); +{{- range $path, $_ := .Files.Glob "test-data/*" }} + {{ base $path }}: |- +{{ $.Files.Get $path | indent 4 }} +{{- end }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: loculus-preview-seed-data + annotations: +{{- if .Values.runConfigLoaderAsArgoJob }} + "argocd.argoproj.io/sync-options": "Force=true,Replace=true" + "argocd.argoproj.io/sync-wave": "1" +{{- else }} + "helm.sh/hook": "post-install,post-upgrade" + "helm.sh/hook-weight": "20" + "helm.sh/hook-delete-policy": "before-hook-creation" +{{- end }} +spec: + backoffLimit: 2 + ttlSecondsAfterFinished: 3600 + template: + metadata: + labels: + app: loculus + component: loculus-preview-seed-data + spec: + restartPolicy: Never + {{- include "possiblePriorityClassName" . | nindent 6 }} + containers: + - name: seed-preview-data + image: {{ $seedImage | quote }} + imagePullPolicy: {{ .Values.images.configLoader.pullPolicy | default .Values.imagePullPolicy }} + env: + - name: BACKEND_URL + value: {{ $backendHost | quote }} + - name: KEYCLOAK_URL + value: {{ $keycloakHost | quote }} + - name: SEED_USERNAME + value: "testuser" + - name: SEED_PASSWORD + value: "testuser" + command: + - node + - /seed-data/seed-preview-data.mjs + volumeMounts: + - name: seed-data + mountPath: /seed-data + readOnly: true + volumes: + - name: seed-data + configMap: + name: loculus-preview-seed-data +{{- end }} diff --git a/kubernetes/loculus/templates/silo-deployment.yaml b/kubernetes/loculus/templates/silo-deployment.yaml index b3fa005d91..37d262c9e6 100644 --- a/kubernetes/loculus/templates/silo-deployment.yaml +++ b/kubernetes/loculus/templates/silo-deployment.yaml @@ -1,11 +1,18 @@ {{- $dockerTag := include "loculus.dockerTag" .Values }} {{- $keycloakTokenUrl := "http://loculus-keycloak-service:8083/realms/loculus/protocol/openid-connect/token" }} - +{{- $backendHost := .Values.disableBackend | ternary + "http://host.k3d.internal:8079" + "http://loculus-backend-service:8079" +}} +{{- /* Lineage system definitions live in the backend DB (canonical + InstanceConfig.lineageSystemDefinitions). The config-adapter init container + fetches them and writes lineage_definitions.json into the shared volume; + the silo-importer reads that file and downloads the definitions itself. */ -}} {{- range $_, $item := (include "loculus.enabledOrganisms" . | fromJson).organisms }} {{- $key := $item.key }} {{- $organismContent := $item.contents }} -{{- $lineageSystem := $organismContent | include "loculus.lineageSystemForOrganism" | fromYamlArray }} {{- $hierarchicalFilters := $organismContent | include "loculus.hierarchicalFiltersForOrganism" | fromYaml }} +{{- $configVersion := default 1 ($organismContent.configVersion) }} --- apiVersion: apps/v1 kind: Deployment @@ -32,7 +39,13 @@ spec: {{- include "possiblePriorityClassName" $ | nindent 6 }} {{- include "loculus.podScheduling" $ | nindent 6 }} initContainers: - {{- include "loculus.configProcessor" (dict "name" "lapis-silo-database-config" "dockerTag" $dockerTag "imagePullPolicy" $.Values.imagePullPolicy) | nindent 8 }} + {{- include "loculus.configAdapterInitContainer" (dict + "organismKey" $key + "configVersion" $configVersion + "dockerTag" $dockerTag + "imagePullPolicy" $.Values.imagePullPolicy + "images" $.Values.images + "backendUrl" $backendHost) | nindent 8 }} containers: - name: silo image: "{{ $.Values.images.loculusSilo.repository }}:{{ $.Values.images.loculusSilo.tag | default $dockerTag }}" @@ -84,18 +97,8 @@ spec: {{- else }} value: "http://loculus-backend-service:8079/{{ $key }}" {{- end }} - {{- if $lineageSystem }} - {{- $defsBySystem := dict -}} - {{- range $ls := $lineageSystem }} - {{- $def := index $.Values.lineageSystemDefinitions $ls -}} - {{- if not $def -}} - {{- fail (printf "lineageSystemDefinitions missing entry for lineage system '%s'" $ls) -}} - {{- end -}} - {{- $defsBySystem = set $defsBySystem $ls $def -}} - {{- end }} - - name: LINEAGE_DEFINITIONS - value: {{ $defsBySystem | toJson | quote }} - {{- end }} + - name: LINEAGE_DEFINITIONS_FILE + value: "/app/lineage_definitions.json" {{- if $hierarchicalFilters }} - name: HIERARCHICAL_FILTERS value: {{ $hierarchicalFilters | toJson | quote }} @@ -120,12 +123,15 @@ spec: - name: lapis-silo-database-config-processed mountPath: /app/preprocessing_config.yaml subPath: preprocessing_config.yaml + - name: lapis-silo-database-config-processed + mountPath: /app/lineage_definitions.json + subPath: lineage_definitions.json - name: lapis-silo-shared-data mountPath: /preprocessing/output - name: lapis-silo-input-data-cache mountPath: /preprocessing/input volumes: - {{- include "loculus.configVolume" (dict "name" "lapis-silo-database-config" "configmap" (printf "lapis-silo-database-config-%s" $key)) | nindent 8 }} + {{- include "loculus.adapterVolume" . | nindent 8 }} - name: lapis-silo-shared-data emptyDir: {} - name: lapis-silo-input-data-cache diff --git a/kubernetes/loculus/test-data b/kubernetes/loculus/test-data new file mode 120000 index 0000000000..e50575ea57 --- /dev/null +++ b/kubernetes/loculus/test-data @@ -0,0 +1 @@ +../../test-data \ No newline at end of file diff --git a/kubernetes/loculus/values.schema.json b/kubernetes/loculus/values.schema.json index cac9fc5cba..b944bf35e7 100644 --- a/kubernetes/loculus/values.schema.json +++ b/kubernetes/loculus/values.schema.json @@ -223,11 +223,6 @@ "type": "string", "description": "Which NCBI field to map to this field." }, - "preprocessing": { - "groups": ["metadata"], - "type": "object", - "description": "The values of this field will be added to the preprocessing pipeline config file and the available values depend on the chosen pipeline. For the Nextclade pipeline, please see [here](https://github.com/loculus-project/loculus/blob/main/preprocessing/nextclade/README.md#custom-preprocessing-functions)." - }, "lineageSystem": { "groups": ["metadata"], "type": "string", @@ -254,60 +249,90 @@ }, { "if": { - "properties": { "generateIndex": { "const": true } }, + "properties": { + "generateIndex": { + "const": true + } + }, "required": ["generateIndex"] }, "then": { "properties": { - "type": { "enum": ["string", "authors"] } + "type": { + "enum": ["string", "authors"] + } }, "errorMessage": "When generateIndex is true, type must be 'string' or 'authors'" } }, { "if": { - "properties": { "autocomplete": { "const": true } }, + "properties": { + "autocomplete": { + "const": true + } + }, "required": ["autocomplete"] }, "then": { "properties": { - "type": { "enum": ["string", "authors", "int", "float", "number", "boolean"] } + "type": { + "enum": ["string", "authors", "int", "float", "number", "boolean"] + } }, "errorMessage": "When autocomplete is true, type must be 'string', 'authors', 'int', 'float', 'number', or 'boolean'" } }, { "if": { - "properties": { "enableSubstringSearch": { "const": true } }, + "properties": { + "enableSubstringSearch": { + "const": true + } + }, "required": ["enableSubstringSearch"] }, "then": { "properties": { - "type": { "enum": ["string", "authors"] } + "type": { + "enum": ["string", "authors"] + } }, "errorMessage": "When enableSubstringSearch is true, type must be 'string' or 'authors'" } }, { "if": { - "properties": { "lineageSystem": { "type": "string" } }, + "properties": { + "lineageSystem": { + "type": "string" + } + }, "required": ["lineageSystem"] }, "then": { "properties": { - "type": { "enum": ["string", "authors"] } + "type": { + "enum": ["string", "authors"] + } }, "errorMessage": "When lineageSystem is set, type must be 'string' or 'authors'" } }, { "if": { - "properties": { "hierarchicalFilter": { "type": "string" } }, + "properties": { + "hierarchicalFilter": { + "type": "string" + } + }, "required": ["hierarchicalFilter"] }, "then": { "properties": { - "type": { "enum": ["string", "authors"] } + "type": { + "enum": ["string", "authors"] + } }, "errorMessage": "When hierarchicalFilter is set, type must be 'string' or 'authors'" } @@ -320,24 +345,36 @@ }, { "if": { - "properties": { "rangeSearch": { "const": true } }, + "properties": { + "rangeSearch": { + "const": true + } + }, "required": ["rangeSearch"] }, "then": { "properties": { - "type": { "enum": ["int", "float", "number", "date"] } + "type": { + "enum": ["int", "float", "number", "date"] + } }, "errorMessage": "When rangeSearch is true, type must be numeric or date ('int', 'float', 'number', or 'date')" } }, { "if": { - "properties": { "rangeOverlapSearch": { "type": "object" } }, + "properties": { + "rangeOverlapSearch": { + "type": "object" + } + }, "required": ["rangeOverlapSearch"] }, "then": { "properties": { - "type": { "enum": ["int", "float", "number", "date"] } + "type": { + "enum": ["int", "float", "number", "date"] + } }, "errorMessage": "When rangeOverlapSearch is set, type must be numeric or date ('int', 'float', 'number', or 'date')" } @@ -354,6 +391,13 @@ "default": true, "description": "Whether this organism is enabled." }, + "configVersion": { + "groups": ["organism"], + "type": "integer", + "minimum": 1, + "default": 1, + "description": "Pinned DB-backed organism config version (Phase 3). The adapter init container fetches /api/config/organisms/{key}?version={configVersion}; bump this to roll out a new published config." + }, "schema": { "groups": ["organism"], "docsIncludePrefix": false, @@ -466,14 +510,18 @@ "docsIncludePrefix": false, "type": "array", "description": "Array of [Metadata fields (type)](#metadata-type) associated with the organism.", - "items": { "$ref": "#/definitions/metadata" } + "items": { + "$ref": "#/definitions/metadata" + } }, "metadataAdd": { "groups": ["schema"], "docsIncludePrefix": false, "type": "array", "description": "Array of [Metadata fields (type)](#metadata-type) associated with the organism, in addtion to the fields in `metadata`.", - "items": { "$ref": "#/definitions/metadata" } + "items": { + "$ref": "#/definitions/metadata" + } }, "metadataTemplate": { "groups": ["schema"], @@ -529,7 +577,9 @@ }, "onlyForReferences": { "type": "object", - "additionalProperties": { "type": "string" }, + "additionalProperties": { + "type": "string" + }, "description": "Optional filter: maps segment name to reference name. When specified, this linkOut is only shown in the tool dropdown when the user has selected a matching reference (or no reference) for each listed segment. Use this to avoid showing reference-specific Nextclade datasets for the wrong reference in multi-reference organisms. Example: {main: CV-A16} or {M: MH396653}." }, "category": { @@ -683,144 +733,6 @@ "type": "string" }, "description": "Array of Strings. Arguments passed to the preprocessing pipeline." - }, - "taxonomyServiceArgs": { - "groups": ["preprocessing"], - "docsIncludePrefix": false, - "type": "object", - "description": "Settings to configure the taxonomy service host and port", - "properties": { - "taxonomy_service_url": { - "groups": ["preprocessing"], - "type": "string", - "description": "Host and port where the taxonomy service runs" - } - } - }, - "configFile": { - "groups": ["preprocessing"], - "docsIncludePrefix": false, - "type": "object", - "description": "Object of type [ConfigFile](#nextclade-preprocessing-pipeline-configfile-type), Fields that should be added to the preprocessing pipeline config file.", - "properties": { - "create_embl_file": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "boolean", - "description": "If prepreprocessing should generate an EMBL file for each sequence, note this requires additional metadata fields to be present (taxon_id, scientific_name, molecule_type)." - }, - "alignment_requirement": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "string", - "enum": ["ALL", "ANY"], - "description": "Applies only to multi-segmented organisms. Determines whether ALL or ANY segments that are submitted must align. In the case ANY it must still be possible to assign the segment." - }, - "segment_classification_method": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "string", - "enum": ["minimizer", "align", "diamond"], - "description": "Method to classify segments for multi-segmented viruses" - }, - "nextclade_dataset_server": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "string", - "description": "Server from which to download nextclade datasets." - }, - "segments": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "array", - "description": "Array of [segment](#nextclade-preprocessing-pipeline-segment-objects-type) objects containing nucleotide sequences, references and their corresponding nextclade datasets.", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "string", - "description": "Name of the nucleotide sequence to which this dataset applies - defaults to 'main'." - }, - "references": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "array", - "description": "Details of the nextclade dataset for each reference", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["name"], - "properties": { - "name": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "string", - "description": "Name of the reference to use for alignment - set name to 'singleReference' if there is only one reference." - }, - "nextclade_dataset_name": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "string", - "description": "Name of the nextclade dataset to use to align the sequence - required if this sequence should be aligned." - }, - "nextclade_dataset_tag": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "string", - "description": "Optional. Tag of the nextclade dataset to use to align the sequence." - }, - "nextclade_dataset_server": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "string", - "description": "Optional. Server from which to download the nextclade dataset to use to align the sequence. If not specified, use the global nextclade_dataset_server." - }, - "accepted_dataset_matches": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "array", - "description": "Optional. Array of strings. The dataset names that are accepted as matches when using nextclade sort or diamond, if not specified use only accept matches with the same name or nextclade_dataset_name." - }, - "genes": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "array", - "description": "Array of strings. The names of the genes to be extracted from the sequence alignment results (should correspond to gene names in the nextclade dataset)." - }, - "nextclade_additional_args": { - "groups": ["nextcladeSegment"], - "docsIncludePrefix": false, - "type": "array", - "description": "Array of strings. Additional arguments to pass to nextclade when aligning this segment." - } - } - } - } - } - } - }, - "require_nextclade_sort_match": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "boolean", - "description": "If true run nextclade sort and require that the highest scoring match is in the config.accepted_dataset_matches" - }, - "minimizer_url": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "string", - "description": "Minimizer used for nextclade sort (if require_nextclade_sort_match or if segments are to be assigned using nextclade sort)" - }, - "backend_request_timeout_seconds": { - "groups": ["nextcladePipelineConfigFile"], - "docsIncludePrefix": false, - "type": "integer", - "description": "Timeout in seconds for requests to the backend server for data to process" - } - } } }, "required": ["version", "image"] @@ -1019,12 +931,12 @@ "description": "The amino acid or nucleotide sequence for this gene" } }, - "required": ["name", "sequence"] + "required": ["name"] }, "additionalProperties": false } }, - "required": ["name", "sequence"] + "required": ["name"] } }, "additionalProperties": false @@ -1041,15 +953,23 @@ "requests": { "type": "object", "properties": { - "memory": { "type": "string" }, - "cpu": { "type": "string" } + "memory": { + "type": "string" + }, + "cpu": { + "type": "string" + } } }, "limits": { "type": "object", "properties": { - "memory": { "type": "string" }, - "cpu": { "type": "string" } + "memory": { + "type": "string" + }, + "cpu": { + "type": "string" + } } } } @@ -1239,7 +1159,13 @@ "createTestAccounts": { "groups": ["general"], "type": "boolean", - "description": "If true, creates the users testuser and superuser" + "description": "If true, creates the users testuser, superuser, and loculus_administrator" + }, + "runConfigLoaderAsArgoJob": { + "groups": ["general"], + "type": "boolean", + "default": false, + "description": "If true, renders the config loader as a normal Argo CD replaceable Job instead of a Helm hook." }, "robotsNoindexHeader": { "groups": ["general"], @@ -1639,7 +1565,9 @@ "type": "object", "description": "An object where the keys are the organism IDs and values are an [Organism (type)](#organism-type)", "patternProperties": { - "^[a-z0-9-]+$": { "$ref": "#/definitions/organism" } + "^[a-z0-9-]+$": { + "$ref": "#/definitions/organism" + } } }, "defaultOrganismConfig": { @@ -1648,30 +1576,8 @@ "defaultOrganisms": { "type": "object", "patternProperties": { - "^[a-z0-9-]+$": { "$ref": "#/definitions/organism" } - } - }, - "lineageSystemDefinitions": { - "groups": ["organism-conf"], - "type": "object", - "description": "An object where the keys are the lineage system names and values are links to lineage system definition files per pipeline version (See [Lineage system definitions](#lineage-system-definitions))", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9-]+$": { - "groups": ["lineage-system-definitions"], - "placeholder": "name", - "type": "object", - "description": "A map from pipeline versions to file URLs.", - "additionalProperties": false, - "patternProperties": { - "^[0-9]+$": { - "groups": ["lineage-system-definitions"], - "placeholder": "pipelineVersion", - "type": "string", - "format": "uri", - "description": "The URL to the lineage definition file for that lineage system and that pipeline version." - } - } + "^[a-z0-9-]+$": { + "$ref": "#/definitions/organism" } } }, @@ -1780,12 +1686,16 @@ "oneOf": [ { "properties": { - "enabled": { "const": false } + "enabled": { + "const": false + } } }, { "properties": { - "enabled": { "const": true } + "enabled": { + "const": true + } }, "required": ["bucket"] } @@ -1878,13 +1788,29 @@ "images": { "groups": ["general"], "type": "object", - "description": "Which docker images to use. You can specify an [image spec (type)](#image-spec-type) for `lapis`, `lapisSilo`, `loculusSilo`, `website` and `backend.`", + "description": "Which docker images to use. You can specify an [image spec (type)](#image-spec-type) for `lapis`, `lapisSilo`, `loculusSilo`, `website`, `backend`, `configLoader`, and `configAdapter`.", "properties": { - "lapis": { "$ref": "#/definitions/imageSpec" }, - "lapisSilo": { "$ref": "#/definitions/imageSpec" }, - "loculusSilo": { "$ref": "#/definitions/imageSpec" }, - "website": { "$ref": "#/definitions/imageSpec" }, - "backend": { "$ref": "#/definitions/imageSpec" } + "lapis": { + "$ref": "#/definitions/imageSpec" + }, + "lapisSilo": { + "$ref": "#/definitions/imageSpec" + }, + "loculusSilo": { + "$ref": "#/definitions/imageSpec" + }, + "website": { + "$ref": "#/definitions/imageSpec" + }, + "backend": { + "$ref": "#/definitions/imageSpec" + }, + "configLoader": { + "$ref": "#/definitions/imageSpec" + }, + "configAdapter": { + "$ref": "#/definitions/imageSpec" + } }, "additionalProperties": false }, @@ -1949,16 +1875,24 @@ "requests": { "type": "object", "properties": { - "memory": { "type": "string" }, - "cpu": { "type": "string" } + "memory": { + "type": "string" + }, + "cpu": { + "type": "string" + } }, "additionalProperties": false }, "limits": { "type": "object", "properties": { - "memory": { "type": "string" }, - "cpu": { "type": "string" } + "memory": { + "type": "string" + }, + "cpu": { + "type": "string" + } }, "additionalProperties": false } @@ -1968,35 +1902,75 @@ "resources": { "type": "object", "properties": { - "website": { "$ref": "#/definitions/resourceSpec" }, - "docs": { "$ref": "#/definitions/resourceSpec" }, - "ena-submission": { "$ref": "#/definitions/resourceSpec" }, - "ena-submission-list-cronjob": { "$ref": "#/definitions/resourceSpec" }, - "ingest": { "$ref": "#/definitions/resourceSpec" }, - "keycloak": { "$ref": "#/definitions/resourceSpec" }, - "silo": { "$ref": "#/definitions/resourceSpec" }, - "lapis": { "$ref": "#/definitions/resourceSpec" }, - "silo-importer": { "$ref": "#/definitions/resourceSpec" }, - "backend": { "$ref": "#/definitions/resourceSpec" }, - "preprocessing": { "$ref": "#/definitions/resourceSpec" }, + "website": { + "$ref": "#/definitions/resourceSpec" + }, + "docs": { + "$ref": "#/definitions/resourceSpec" + }, + "ena-submission": { + "$ref": "#/definitions/resourceSpec" + }, + "ena-submission-list-cronjob": { + "$ref": "#/definitions/resourceSpec" + }, + "ingest": { + "$ref": "#/definitions/resourceSpec" + }, + "keycloak": { + "$ref": "#/definitions/resourceSpec" + }, + "silo": { + "$ref": "#/definitions/resourceSpec" + }, + "lapis": { + "$ref": "#/definitions/resourceSpec" + }, + "silo-importer": { + "$ref": "#/definitions/resourceSpec" + }, + "backend": { + "$ref": "#/definitions/resourceSpec" + }, + "preprocessing": { + "$ref": "#/definitions/resourceSpec" + }, "organismSpecific": { "type": "object", "patternProperties": { ".*": { "type": "object", "properties": { - "website": { "$ref": "#/definitions/resourceSpec" }, - "ena-submission": { "$ref": "#/definitions/resourceSpec" }, + "website": { + "$ref": "#/definitions/resourceSpec" + }, + "ena-submission": { + "$ref": "#/definitions/resourceSpec" + }, "ena-submission-list-cronjob": { "$ref": "#/definitions/resourceSpec" }, - "ingest": { "$ref": "#/definitions/resourceSpec" }, - "keycloak": { "$ref": "#/definitions/resourceSpec" }, - "silo": { "$ref": "#/definitions/resourceSpec" }, - "lapis": { "$ref": "#/definitions/resourceSpec" }, - "silo-importer": { "$ref": "#/definitions/resourceSpec" }, - "backend": { "$ref": "#/definitions/resourceSpec" }, - "preprocessing": { "$ref": "#/definitions/resourceSpec" } + "ingest": { + "$ref": "#/definitions/resourceSpec" + }, + "keycloak": { + "$ref": "#/definitions/resourceSpec" + }, + "silo": { + "$ref": "#/definitions/resourceSpec" + }, + "lapis": { + "$ref": "#/definitions/resourceSpec" + }, + "silo-importer": { + "$ref": "#/definitions/resourceSpec" + }, + "backend": { + "$ref": "#/definitions/resourceSpec" + }, + "preprocessing": { + "$ref": "#/definitions/resourceSpec" + } }, "additionalProperties": false } @@ -2045,6 +2019,64 @@ }, "testconfig": { "description": "TODO" + }, + "seedPreviewData": { + "type": "object", + "description": "Preview-only seed data submission job configuration.", + "properties": { + "enabled": { + "type": "boolean", + "description": "Submit bundled preview seed data after startup.", + "default": false + } + }, + "additionalProperties": false + }, + "views": { + "type": "object", + "description": "SQL-backed metadata-only view deployments. SQL query and schema come from the DB instance config; these values control which per-view SILO/LAPIS pairs are deployed.", + "additionalProperties": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Deploy this SQL-backed view SILO/LAPIS instance", + "default": true + }, + "organismDisplayNames": { + "type": "object", + "description": "Optional organismKey -> display name overrides for the view Organism column", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "overview": { + "type": "object", + "description": "Cross-organism overview aggregating SILO/LAPIS instance. SQL query and schema come from the DB instance config; these values control deployment.", + "properties": { + "enabled": { + "type": "boolean", + "description": "Deploy the cross-organism overview SILO/LAPIS instance", + "default": false + }, + "key": { + "type": "string", + "description": "Organism-path key used to address the overview LAPIS instance", + "default": "overview" + }, + "organismDisplayNames": { + "type": "object", + "description": "Optional organismKey -> display name overrides for the overview Organism column", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false } }, "required": ["name"] diff --git a/kubernetes/loculus/values.yaml b/kubernetes/loculus/values.yaml index 797e1f468c..1a619dfdc6 100644 --- a/kubernetes/loculus/values.yaml +++ b/kubernetes/loculus/values.yaml @@ -36,6 +36,7 @@ seqSets: fields: [dataUseTerms] disableWebsite: false disableBackend: false +runConfigLoaderAsArgoJob: false disablePreprocessing: false disableIngest: false disableEnaSubmission: true @@ -50,6 +51,26 @@ siloImport: siloTimeoutSeconds: 3600 hardRefreshIntervalSeconds: 3600 pollIntervalSeconds: 30 +# Cross-organism overview SILO/LAPIS instance. The SQL query and manual schema +# are configured in the DB instance config (config_* tables); these values only +# control whether the aggregating pods are deployed and provide deployment topology. +overview: + enabled: true + key: overview + # Optional organismKey -> display-name overrides for the overview "Organism" + # column; absent organisms fall back to their key. + organismDisplayNames: {} +views: + overview: + enabled: true + real-organisms: + enabled: true + co-infections: + enabled: true + test-organisms: + enabled: true +seedPreviewData: + enabled: false ingestLimitSeconds: 1800 getSubmissionListLimitSeconds: 600 preprocessingTimeout: 600 @@ -74,14 +95,11 @@ logo: url: "/favicon.svg" width: 100 height: 100 -lineageSystemDefinitions: - pangoLineage: - 1: https://raw.githubusercontent.com/loculus-project/loculus/c400348ea0ba0b8178aa43475d5c7539fc097997/preprocessing/dummy/lineage.yaml - 2: https://raw.githubusercontent.com/loculus-project/loculus/c400348ea0ba0b8178aa43475d5c7539fc097997/preprocessing/dummy/lineage.yaml - alternativeLineage: - 4: https://raw.githubusercontent.com/loculus-project/loculus/fe3b2974071acc9a25856d650350f8a952040a7c/preprocessing/dummy/lineage-alternative.yaml - cchfS: - 1: https://pathoplexus.github.io/silo-lineage-hierarchy-definitions/definitions/cchf/S/2025-09-24--07-29-03Z/lineages.yaml +# lineageSystemDefinitions moved to kubernetes/loculus/fixtures/instance.yaml in +# Phase 3 — the canonical config is now the single source of truth. SILO/LAPIS +# templates read it from there via `.Files.Get`. To change a definition URL, +# edit fixtures/instance.yaml directly (the migration script overwrites it from +# this file on rerun, so the values.yaml block isn't necessary anymore). defaultOrganismConfig: &defaultOrganismConfig schema: &schema submissionDataTypes: &defaultSubmissionDataTypes @@ -138,13 +156,6 @@ defaultOrganismConfig: &defaultOrganismConfig order: 10 orderOnDetailsPage: 200 includeInDownloadsByDefault: true - preprocessing: - function: parse_date_into_range - inputs: - date: sampleCollectionDate - releaseDate: ncbiReleaseDate - args: - fieldType: dateRangeString required: true notSearchable: true columnWidth: 100 @@ -155,13 +166,6 @@ defaultOrganismConfig: &defaultOrganismConfig hideInSearchResultsTable: true hideOnSequenceDetailsPage: true header: Sample details - preprocessing: - function: parse_date_into_range - inputs: - date: sampleCollectionDate - releaseDate: ncbiReleaseDate - args: - fieldType: dateRangeLower rangeOverlapSearch: rangeName: sampleCollectionDateRange rangeDisplayName: Collection date @@ -174,13 +178,6 @@ defaultOrganismConfig: &defaultOrganismConfig hideInSearchResultsTable: true hideOnSequenceDetailsPage: true header: Sample details - preprocessing: - function: parse_date_into_range - inputs: - date: sampleCollectionDate - releaseDate: ncbiReleaseDate - args: - fieldType: dateRangeUpper rangeOverlapSearch: rangeName: sampleCollectionDateRange rangeDisplayName: Collection date @@ -189,14 +186,6 @@ defaultOrganismConfig: &defaultOrganismConfig - name: displayName displayName: Display name definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{accessionVersion}/{sampleCollectionDate}`. - preprocessing: - function: concatenate - inputs: - geoLocCountry: geoLocCountry - sampleCollectionDate: processed.sampleCollectionDate - args: - order: [geoLocCountry, ACCESSION_VERSION, sampleCollectionDate] - type: [string, ACCESSION_VERSION, dateRangeString] noInput: true - name: ncbiReleaseDate displayName: NCBI release date @@ -204,10 +193,6 @@ defaultOrganismConfig: &defaultOrganismConfig type: date header: "INSDC" orderOnDetailsPage: 1000 - preprocessing: - function: parse_timestamp - inputs: - timestamp: ncbiReleaseDate noInput: true columnWidth: 100 - name: earliestReleaseDate @@ -225,10 +210,6 @@ defaultOrganismConfig: &defaultOrganismConfig definition: Date the viral nucleotide accession was last updated on the INSDC. header: "INSDC" orderOnDetailsPage: 1020 - preprocessing: - function: parse_timestamp - inputs: - timestamp: ncbiUpdateDate noInput: true perSegment: true oneHeader: true @@ -571,10 +552,6 @@ defaultOrganismConfig: &defaultOrganismConfig - name: USSR - name: Yugoslavia - name: Zaire - preprocessing: - function: process_options - inputs: - input: geoLocCountry - name: geoLocAdmin1 displayName: Collection subdivision level 1 definition: "A local administrative region from which the sample was collected (ex: Province, State, Canton)." @@ -643,10 +620,6 @@ defaultOrganismConfig: &defaultOrganismConfig orderOnDetailsPage: 800 includeInDownloadsByDefault: true ingest: ncbiSubmitterNames - preprocessing: - function: check_authors - inputs: - authors: authors columnWidth: 140 - name: authorAffiliations displayName: Author affiliations @@ -762,10 +735,6 @@ defaultOrganismConfig: &defaultOrganismConfig desired: true type: date orderOnDetailsPage: 260 - preprocessing: - function: parse_and_assert_past_date - inputs: - date: sampleReceivedDate header: Sample details - name: sampleType displayName: Sample type @@ -1026,10 +995,6 @@ defaultOrganismConfig: &defaultOrganismConfig displayName: Sequencing date type: date orderOnDetailsPage: 2020 - preprocessing: - function: parse_and_assert_past_date - inputs: - date: sequencingDate header: Sequencing desired: true - name: ampliconPcrPrimerScheme @@ -1256,12 +1221,6 @@ defaultOrganismConfig: &defaultOrganismConfig ingest: ncbiHostTaxId noInput: true orderOnDetailsPage: 1540 - preprocessing: - function: resolve_host_taxon_id - inputs: - host: host - args: - taxonomy_service_url: &taxonomy_service_url http://loculus-taxonomy-service:5000 - name: hostNameScientific displayName: Host name - scientific definition: "The scientific name of the host from which the sample was collected." @@ -1275,12 +1234,6 @@ defaultOrganismConfig: &defaultOrganismConfig ingest: ncbiHostName noInput: true orderOnDetailsPage: 1560 - preprocessing: - function: scientific_name_from_id - inputs: - hostTaxonId: processed.hostTaxonId - args: - taxonomy_service_url: *taxonomy_service_url - name: hostNameCommon displayName: Host name - common definition: "The common name of the host from which the sample was collected." @@ -1294,12 +1247,6 @@ defaultOrganismConfig: &defaultOrganismConfig ingest: ncbiHostCommonName noInput: true orderOnDetailsPage: 1580 - preprocessing: - function: common_name_from_id - inputs: - hostTaxonId: processed.hostTaxonId - args: - taxonomy_service_url: *taxonomy_service_url - name: isLabHost displayName: Is lab host definition: "If a laboratory host (e.g. cultured cell line) was used to propagate the sample." @@ -1378,8 +1325,6 @@ defaultOrganismConfig: &defaultOrganismConfig displayName: Total SNPs definition: "Total count of nucleotide substitutions." orderOnDetailsPage: 2720 - preprocessing: - inputs: {input: nextclade.totalSubstitutions} - name: totalInsertedNucs type: int isSequenceFilter: true @@ -1390,8 +1335,6 @@ defaultOrganismConfig: &defaultOrganismConfig displayName: Total inserted nucs definition: "Total count of inserted nucleotide positions." orderOnDetailsPage: 2740 - preprocessing: - inputs: {input: nextclade.totalInsertions} - name: totalDeletedNucs type: int isSequenceFilter: true @@ -1400,8 +1343,6 @@ defaultOrganismConfig: &defaultOrganismConfig rangeSearch: true perSegment: true orderOnDetailsPage: 2760 - preprocessing: - inputs: {input: nextclade.totalDeletions} displayName: Total deleted nucs definition: "Total count of deleted nucleotide positions." - name: totalAmbiguousNucs @@ -1414,8 +1355,6 @@ defaultOrganismConfig: &defaultOrganismConfig displayName: Total ambiguous nucs definition: "Total count of ambiguous nucleotides (not A, C, G, T, or N)." orderOnDetailsPage: 2780 - preprocessing: - inputs: {input: "nextclade.totalNonACGTNs"} - name: totalUnknownNucs orderOnDetailsPage: 2800 type: int @@ -1426,8 +1365,6 @@ defaultOrganismConfig: &defaultOrganismConfig perSegment: true displayName: Total unknown nucs definition: "Total count of missing (N) nucleotides." - preprocessing: - inputs: {input: nextclade.totalMissing} - name: totalFrameShifts isSequenceFilter: true type: int @@ -1438,8 +1375,6 @@ defaultOrganismConfig: &defaultOrganismConfig displayName: Total frame shifts definition: "Total count of detected frame shifts." orderOnDetailsPage: 2820 - preprocessing: - inputs: {input: nextclade.totalFrameShifts} - name: frameShifts isSequenceFilter: true header: "Alignment and QC metrics" @@ -1448,8 +1383,6 @@ defaultOrganismConfig: &defaultOrganismConfig displayName: Frame shifts definition: "Frame-shifting insertions or deletions detected in CDS regions." orderOnDetailsPage: 2840 - preprocessing: - inputs: {input: nextclade.frameShifts} - name: totalStopCodons isSequenceFilter: true perSegment: true @@ -1458,8 +1391,6 @@ defaultOrganismConfig: &defaultOrganismConfig type: int header: "Alignment and QC metrics" noInput: true - preprocessing: - inputs: {input: nextclade.qc.stopCodons.totalStopCodons} - name: stopCodons isSequenceFilter: true perSegment: true @@ -1467,8 +1398,6 @@ defaultOrganismConfig: &defaultOrganismConfig definition: "Premature stop codons not in the ignored list (penalized)." header: "Alignment and QC metrics" noInput: true - preprocessing: - inputs: {input: nextclade.qc.stopCodons.stopCodons} - name: completeness isSequenceFilter: true type: float @@ -1484,8 +1413,6 @@ defaultOrganismConfig: &defaultOrganismConfig type: lengthCompleteness displayGroup: lengthCompleteness label: Length - preprocessing: - inputs: {input: nextclade.coverage} website: &website tableColumns: - sampleCollectionDate @@ -1538,18 +1465,6 @@ defaultOrganismConfig: &defaultOrganismConfig args: - "prepro" replicas: 1 - taxonomyServiceArgs: &preprocessingTaxonomyServiceArgs - taxonomy_service_url: *taxonomy_service_url - configFile: &preprocessingConfigFile - log_level: DEBUG - batch_size: 100 - create_embl_file: true - segments: - - name: main - references: - - name: singleReference - genes: [] - nextclade_dataset_server: https://data.clades.nextstrain.org/v3 ingest: &ingest image: ghcr.io/loculus-project/ingest configFile: @@ -1557,6 +1472,7 @@ defaultOrganismConfig: &defaultOrganismConfig defaultOrganisms: ebola-sudan: <<: *defaultOrganismConfig + configVersion: 1 schema: <<: *schema organismName: "Ebola Sudan" @@ -1578,18 +1494,6 @@ defaultOrganisms: preprocessing: - <<: *preprocessing version: [1, 2] - configFile: - <<: *preprocessingConfigFile - segments: - - name: main - references: - - name: singleReference - nextclade_dataset_name: nextstrain/ebola/sudan - genes: [NP, VP35, VP40, GP, sGP, ssGP, VP30, VP24, L] - nextclade_dataset_server: https://raw.githubusercontent.com/nextstrain/nextclade_data/ebola/data_output - taxon_id: 186540 - scientific_name: "Sudan ebolavirus" - molecule_type: "genomic RNA" ingest: <<: *ingest configFile: @@ -1604,29 +1508,20 @@ defaultOrganisms: - name: main references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/reference.fasta]]" insdcAccessionFull: NC_002549.1 genes: - name: NP - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/NP.fasta]]" - name: VP35 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/VP35.fasta]]" - name: VP40 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/VP40.fasta]]" - name: GP - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/GP.fasta]]" - name: ssGP - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/ssGP.fasta]]" - name: sGP - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/sGP.fasta]]" - name: VP30 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/VP30.fasta]]" - name: VP24 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/VP24.fasta]]" - name: L - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/ebola-sudan/L.fasta]]" west-nile: <<: *defaultOrganismConfig + configVersion: 1 schema: <<: *schema organismName: "West Nile Virus" @@ -1645,27 +1540,20 @@ defaultOrganisms: displayName: Display name definition: A human-readable label for the sequence record, with the format `{geoLocCountry}/{identifier}/{sampleCollectionDate}`. The identifier is provided from `specimenCollectorSampleId` if set, otherwise `submissionId`. For sequences ingested from the INSDC, the identifier is taken directly as the provided field, assuming it contains no slashes or spaces, otherwise the `accessionVersion` is used. For other sequences, if the provided field has a display-name format itself (four or three items, separated by slashes), then the identifier is extracted as the second to last item within this. If the identifier cannot be extracted (due to invalid format of the provided field), the `accessionVersion` is used. Missing `geoLocCountry`/`sampleCollectionDate` default to `unknown`. noInput: true - preprocessing: - function: build_display_name - inputs: - geoLocCountry: geoLocCountry - sampleCollectionDateRangeLower: processed.sampleCollectionDateRangeLower - specimenCollectorSampleId: specimenCollectorSampleId - submissionId: submissionId - args: - order: [geoLocCountry, IDENTIFIER, sampleCollectionDateRangeLower] - type: [string, IDENTIFIER, string] - # regex pattern constraints: - # - Cannot start with a slash - # - Four or three fields, separated by exactly two or three slashes - # - Last field is a date in format YYYY, YYYY-MM, or YYYY-MM-DD - # - Identifier is the second to last field (extracted through named capture group) - regex_pattern: '^(?:[^/]+/)?[^/]+/(?P[^/]+)/\d{4}(?:-\d{2}){0,2}$' - - <<: *hostTaxonIdConfig + - name: hostTaxonId + type: int + autocomplete: true + displayName: "Host species" + definition: "Taxon ID for the host." + customDisplay: + type: hostSpecies + displayGroup: hostSpecies + label: Host + header: "Host" + ingest: ncbiHostTaxId + noInput: true required: true - hierarchicalFilter: *taxonomy_service_url - hierarchicalSearchLabel: "include subtaxa" - initiallyVisible: true + orderOnDetailsPage: 1540 - name: lineage displayName: Lineage definition: "Assigned clade label from the nearest reference tree node." @@ -1676,8 +1564,6 @@ defaultOrganisms: autocomplete: true initiallyVisible: true includeInDownloadsByDefault: true - preprocessing: - inputs: {input: nextclade.clade} - name: mutations_from_founder header: "Alignment and QC metrics" displayName: "Amino Acid Mutations from founder clade" @@ -1689,8 +1575,6 @@ defaultOrganisms: autocomplete: false initiallyVisible: false includeInDownloadsByDefault: false - preprocessing: - inputs: {input: "nextclade.cladeFounderInfo.aaMutations.*.privateSubstitutions"} - name: variant isSequenceFilter: true perSegment: true @@ -1702,11 +1586,6 @@ defaultOrganisms: autocomplete: true initiallyVisible: false includeInDownloadsByDefault: false - preprocessing: - function: is_variant - args: - mu: 0.002 - inputs: {numMutations: "nextclade.privateNucMutations.totalPrivateSubstitutions", length: processed.length} website: <<: *website tableColumns: @@ -1723,19 +1602,6 @@ defaultOrganisms: preprocessing: - <<: *preprocessing version: 1 - configFile: - <<: *preprocessingConfigFile - segments: - - name: main - references: - - name: singleReference - nextclade_dataset_name: nextstrain/wnv/all-lineages - genes: [capsid, prM, env, NS1, NS2A, NS2B, NS3, NS4A, 2K, NS4B, NS5] - nextclade_additional_args: ["--allowed-mismatches", "8", "--min-seed-cover", "0.01"] - nextclade_dataset_server: https://raw.githubusercontent.com/nextstrain/nextclade_data/wnv/data_output - taxon_id: 11082 - scientific_name: "West Nile virus" - molecule_type: "genomic RNA" ingest: <<: *ingest configFile: @@ -1750,31 +1616,19 @@ defaultOrganisms: - name: main references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/reference.fasta]]" insdcAccessionFull: NC_009942.1 genes: - name: 2K - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/2K.fasta]]" - name: NS1 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/NS1.fasta]]" - name: NS2A - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/NS2A.fasta]]" - name: NS2B - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/NS2B.fasta]]" - name: NS3 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/NS3.fasta]]" - name: NS4A - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/NS4A.fasta]]" - name: NS4B - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/NS4B.fasta]]" - name: NS5 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/NS5.fasta]]" - name: capsid - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/capsid.fasta]]" - name: env - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/env.fasta]]" - name: prM - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/west-nile/prM.fasta]]" dummy-organism: schema: submissionDataTypes: *defaultSubmissionDataTypes @@ -1840,10 +1694,6 @@ defaultOrganisms: - name: A.1.1 - name: A.2 - name: B - preprocessing: - function: process_options - inputs: - input: lineage - name: hiddenField displayName: "Hidden Field" initiallyVisible: false @@ -1869,32 +1719,19 @@ defaultOrganisms: - name: main references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/reference.fasta]]" genes: - name: E - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/E.fasta]]" - name: M - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/M.fasta]]" - name: "N" - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/N.fasta]]" - name: ORF1a - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF1a.fasta]]" - name: ORF1b - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF1b.fasta]]" - name: ORF3a - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF3a.fasta]]" - name: ORF6 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF6.fasta]]" - name: ORF7a - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF7a.fasta]]" - name: ORF7b - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF7b.fasta]]" - name: ORF8 - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF8.fasta]]" - name: ORF9b - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/ORF9b.fasta]]" - name: S - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/S.fasta]]" dummy-organism-with-files: schema: image: "/images/organisms/hmpv.jpg" @@ -1937,10 +1774,6 @@ defaultOrganisms: initiallyVisible: true header: "Collection Details" required: true - preprocessing: - function: parse_and_assert_past_date - inputs: - date: date - name: lineage isSequenceFilter: true initiallyVisible: true @@ -1960,10 +1793,6 @@ defaultOrganisms: - name: B.1.1.1 - name: C - name: C.1 - preprocessing: - function: process_options - inputs: - input: lineage - name: region displayName: Region type: string @@ -2003,21 +1832,13 @@ defaultOrganisms: image: ghcr.io/loculus-project/preprocessing-nextclade args: - "prepro" - configFile: - log_level: DEBUG - batch_size: 100 - segments: - - name: main - references: - - name: singleReference - genes: [] referenceGenomes: - name: main references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/sars-cov-2/reference.fasta]]" cchf: <<: *defaultOrganismConfig + configVersion: 1 schema: <<: *schema submissionDataTypes: @@ -2045,8 +1866,6 @@ defaultOrganisms: initiallyVisible: true includeInDownloadsByDefault: true lineageSystem: cchfS - preprocessing: - inputs: {input: nextclade.clade} website: <<: *website tableColumns: @@ -2065,28 +1884,6 @@ defaultOrganisms: defaultOrder: descending preprocessing: - <<: *preprocessing - configFile: - <<: *preprocessingConfigFile - taxon_id: 3052518 - scientific_name: "Orthonairovirus haemorrhagiae" - molecule_type: "genomic RNA" - log_level: DEBUG - segments: - - name: L - references: - - nextclade_dataset_name: community/pathoplexus/cchfv/L - name: singleReference - genes: [RdRp] - - name: M - references: - - nextclade_dataset_name: community/pathoplexus/cchfv/M - name: singleReference - genes: [GPC] - - name: S - references: - - nextclade_dataset_name: community/pathoplexus/cchfv/S - name: singleReference - genes: [NP] ingest: <<: *ingest configFile: @@ -2105,29 +1902,24 @@ defaultOrganisms: - name: L references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/reference_L.fasta]]" insdcAccessionFull: NC_005301.3 genes: - name: RdRp - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/RdRp.fasta]]" - name: M references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/reference_M.fasta]]" insdcAccessionFull: NC_005300.2 genes: - name: GPC - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/GPC.fasta]]" - name: S references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/reference_S.fasta]]" insdcAccessionFull: NC_005302.1 genes: - name: NP - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/NP.fasta]]" cchf-multi-ref: <<: *defaultOrganismConfig + configVersion: 1 schema: <<: *schema submissionDataTypes: @@ -2178,11 +1970,6 @@ defaultOrganisms: type: variantReference displayGroup: reference_L label: Closest reference L - preprocessing: - function: is_variant - args: - mu: 0.004 - inputs: {numMutations: "nextclade.totalSubstitutions", length: processed.length_L} - name: variant_M isSequenceFilter: true header: "Clade & Lineage" @@ -2199,11 +1986,6 @@ defaultOrganisms: type: variantReference displayGroup: reference_M label: Closest reference M - preprocessing: - function: is_variant - args: - mu: 0.008 - inputs: {numMutations: "nextclade.totalSubstitutions", length: processed.length_M} - name: variant_S isSequenceFilter: true header: "Clade & Lineage" @@ -2220,12 +2002,6 @@ defaultOrganisms: type: variantReference displayGroup: reference_S label: Closest reference S - preprocessing: - function: is_variant - args: - mu: 0.004 - # custom nextclade dataset does not have private mutations, so using total substitutions as a proxy for distance from reference - inputs: {numMutations: "nextclade.totalSubstitutions", length: processed.length_S} - name: reference oneHeader: true header: "Clade & Lineage" @@ -2241,8 +2017,6 @@ defaultOrganisms: autocomplete: true initiallyVisible: true perSegment: true - preprocessing: - inputs: {input: ASSIGNED_REFERENCE} website: <<: *website tableColumns: @@ -2262,36 +2036,6 @@ defaultOrganisms: referenceIdentifierField: reference preprocessing: - <<: *preprocessing - configFile: - <<: *preprocessingConfigFile - taxon_id: 3052518 - scientific_name: "Orthonairovirus haemorrhagiae" - molecule_type: "genomic RNA" - alignment_requirement: ANY - log_level: DEBUG - nextclade_dataset_server: "https://raw.githubusercontent.com/genspectrum/nextclade-datasets/cchf-multi/data" - segments: - - name: L - references: - - nextclade_dataset_name: cchfv/L - name: singleReference - genes: [RdRp] - - name: M - references: - - nextclade_dataset_name: cchfv/M-MH396653 - name: MH396653 - genes: [GPC] - - nextclade_dataset_name: cchfv/M-OR047158 - name: OR047158 - genes: [GPC] - - name: S - references: - - nextclade_dataset_name: cchfv/S-1and6 - name: 1and6 - genes: [NP] - - nextclade_dataset_name: cchfv/S-2to5 - name: 2to5 - genes: [NP] ingest: <<: *ingest configFile: @@ -2310,47 +2054,38 @@ defaultOrganisms: displayName: L segment references: - name: singleReference - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/reference_L.fasta]]" insdcAccessionFull: NC_005301.3 genes: - name: RdRp - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/RdRp.fasta]]" - name: M displayName: M segment references: - name: MH396653 displayName: "M (MH396653.1)" - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/M/MH396653/reference.fasta]]" insdcAccessionFull: MH396653.1 genes: - name: GPC - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/M/MH396653/GPC.fasta]]" - name: OR047158 displayName: "M (OR047158.1)" - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/M/OR047158/reference.fasta]]" insdcAccessionFull: OR047158.1 genes: - name: GPC - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/M/OR047158/GPC.fasta]]" - name: S displayName: S segment references: - name: 1and6 displayName: "S11 (MK299338.1)" - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/S/MK299338/reference.fasta]]" insdcAccessionFull: MK299338.1 genes: - name: NP - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/S/MK299338/NP.fasta]]" - name: 2to5 displayName: "S1 (OL774851.1)" - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/S/OL774851/reference.fasta]]" insdcAccessionFull: OL774851.1 genes: - name: NP - sequence: "[[URL:https://corneliusroemer.github.io/seqs/artefacts/cchf/S/OL774851/NP.fasta]]" enteroviruses: <<: *defaultOrganismConfig + configVersion: 1 enabled: true schema: <<: *schema @@ -2387,8 +2122,6 @@ defaultOrganisms: autocomplete: true initiallyVisible: true includeInDownloadsByDefault: true - preprocessing: - inputs: {input: nextclade.clade} - <<: *evMetadataAdd name: clade_cv_a10 isSequenceFilter: true @@ -2396,8 +2129,6 @@ defaultOrganisms: displayName: Clade CV-A10 definition: "The clade assigned by Nextclade, only populated for sequences of genotype CV-A10." onlyForReference: CV-A10 - preprocessing: - inputs: {input: nextclade.clade} - <<: *evMetadataAdd name: clade_ev_a71 isSequenceFilter: true @@ -2405,8 +2136,6 @@ defaultOrganisms: displayName: Clade EV-A71 definition: "The clade assigned by Nextclade, only populated for sequences of genotype EV-A71." onlyForReference: EV-A71 - preprocessing: - inputs: {input: nextclade.clade} - <<: *evMetadataAdd name: clade_ev_d68 isSequenceFilter: true @@ -2414,8 +2143,6 @@ defaultOrganisms: displayName: Clade EV-D68 definition: "The clade assigned by Nextclade, only populated for sequences of genotype EV-D68." onlyForReference: EV-D68 - preprocessing: - inputs: {input: nextclade.clade} - name: genotype isSequenceFilter: true displayName: Genotype @@ -2425,8 +2152,6 @@ defaultOrganisms: generateIndex: true autocomplete: true initiallyVisible: true - preprocessing: - inputs: {input: ASSIGNED_REFERENCE} website: <<: *website tableColumns: @@ -2443,33 +2168,6 @@ defaultOrganisms: referenceIdentifierField: genotype preprocessing: - <<: *preprocessing - configFile: - <<: *preprocessingConfigFile - segment_classification_method: "minimizer" - # TODO(https://github.com/loculus-project/loculus/issues/5673): switch creation on when multi-path is supported - create_embl_file: false - minimizer_url: "https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/evs_minimizer-index.json" - segments: - - name: main - references: - - name: CV-A16 - nextclade_dataset_name: enpen/enterovirus/cv-a16 - accepted_dataset_matches: ["community/hodcroftlab/enterovirus/cva16", "community/hodcroftlab/enterovirus/enterovirus/linked/CV-A16"] - genes: ["VP4", "VP2", "VP3", "VP1", "2A", "2B", "2C", "3A", "3B", "3C", "3D"] - - name: CV-A10 - nextclade_dataset_name: enpen/enterovirus/cv-a10 - accepted_dataset_matches: ["community/hodcroftlab/enterovirus/enterovirus/linked/CV-A10"] - genes: ["VP4", "VP2", "VP3", "VP1", "2A", "2B", "2C", "3A", "3B", "3C", "3D"] - - name: EV-A71 - nextclade_dataset_name: enpen/enterovirus/ev-a71 - accepted_dataset_matches: ["community/hodcroftlab/enterovirus/enterovirus/linked/EV-A71"] - genes: ["VP4", "VP2", "VP3", "VP1", "2A", "2B", "2C", "3A", "3B", "3C", "3D"] - - name: EV-D68 - accepted_dataset_matches: ["community/hodcroftlab/enterovirus/enterovirus/linked/EV-D68"] - genes: ["VP4", "VP2", "VP3", "VP1", "2A", "2B", "2C", "3A", "3B", "3C", "3D"] - nextclade_dataset_name: enpen/enterovirus/ev-d68 - nextclade_dataset_server: https://data.clades.nextstrain.org/v3 - nextclade_dataset_server: https://raw.githubusercontent.com/nextstrain/nextclade_data/evs-datasets/data_output ingest: <<: *ingest configFile: @@ -2504,109 +2202,61 @@ defaultOrganisms: - name: main references: - name: CV-A16 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/reference-cva16.fasta]]" insdcAccessionFull: U05876.1 genes: - name: VP4 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/VP4-cva16.fasta]]" - name: VP2 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/VP2-cva16.fasta]]" - name: VP3 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/VP3-cva16.fasta]]" - name: VP1 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/VP1-cva16.fasta]]" - name: 2A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/2A-cva16.fasta]]" - name: 2B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/2B-cva16.fasta]]" - name: 2C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/2C-cva16.fasta]]" - name: 3A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/3A-cva16.fasta]]" - name: 3B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/3B-cva16.fasta]]" - name: 3C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/3C-cva16.fasta]]" - name: 3D - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva16/3D-cva16.fasta]]" - name: CV-A10 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/reference-cva10.fasta]]" insdcAccessionFull: AY421767.1 genes: - name: VP4 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/VP4-cva10.fasta]]" - name: VP2 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/VP2-cva10.fasta]]" - name: VP3 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/VP3-cva10.fasta]]" - name: VP1 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/VP1-cva10.fasta]]" - name: 2A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/2A-cva10.fasta]]" - name: 2B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/2B-cva10.fasta]]" - name: 2C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/2C-cva10.fasta]]" - name: 3A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/3A-cva10.fasta]]" - name: 3B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/3B-cva10.fasta]]" - name: 3C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/3C-cva10.fasta]]" - name: 3D - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/cva10/3D-cva10.fasta]]" - name: EV-A71 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/reference-eva71.fasta]]" insdcAccessionFull: U22521.1 genes: - name: VP4 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/VP4-eva71.fasta]]" - name: VP2 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/VP2-eva71.fasta]]" - name: VP3 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/VP3-eva71.fasta]]" - name: VP1 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/VP1-eva71.fasta]]" - name: 2A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/2A-eva71.fasta]]" - name: 2B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/2B-eva71.fasta]]" - name: 2C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/2C-eva71.fasta]]" - name: 3A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/3A-eva71.fasta]]" - name: 3B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/3B-eva71.fasta]]" - name: 3C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/3C-eva71.fasta]]" - name: 3D - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/eva71/3D-eva71.fasta]]" - name: EV-D68 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/reference-evd68.fasta]]" insdcAccessionFull: AY426531.1 genes: - name: VP4 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/VP4-evd68.fasta]]" - name: VP2 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/VP2-evd68.fasta]]" - name: VP3 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/VP3-evd68.fasta]]" - name: VP1 - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/VP1-evd68.fasta]]" - name: 2A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/2A-evd68.fasta]]" - name: 2B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/2B-evd68.fasta]]" - name: 2C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/2C-evd68.fasta]]" - name: 3A - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/3A-evd68.fasta]]" - name: 3B - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/3B-evd68.fasta]]" - name: 3C - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/3C-evd68.fasta]]" - name: 3D - sequence: "[[URL:https://raw.githubusercontent.com/alejandra-gonzalezsanchez/loculus-evs/master/enterovirus/evd68/3D-evd68.fasta]]" auth: verifyEmail: false resetPasswordAllowed: true @@ -2629,6 +2279,12 @@ images: backend: repository: "ghcr.io/loculus-project/backend" pullPolicy: Always + configLoader: + repository: "ghcr.io/loculus-project/config-loader" + pullPolicy: Always + configAdapter: + repository: "ghcr.io/loculus-project/config-adapter" + pullPolicy: Always silo: apiThreadsForHttpConnections: 16 secrets: @@ -2666,6 +2322,7 @@ secrets: preprocessingPipelinePassword: "" externalMetadataUpdaterPassword: "" backendUserPassword: "" + configLoaderUserPassword: "" keycloak-admin: type: raw data: @@ -2677,7 +2334,11 @@ secrets: ingest-ncbi: type: raw data: - api-key: "dummy" + # Empty by default: NCBI `datasets` works without an API key (just + # rate-limited). A literal placeholder like "dummy" is rejected by NCBI + # with HTTP 400, so the ingest Snakefile omits `--api-key` when this is + # empty. Real deployments override this with a valid key. + api-key: "" slack-notifications: type: raw data: diff --git a/kubernetes/loculus/values_e2e_and_dev.yaml b/kubernetes/loculus/values_e2e_and_dev.yaml index 032af7efee..e9f1960e82 100644 --- a/kubernetes/loculus/values_e2e_and_dev.yaml +++ b/kubernetes/loculus/values_e2e_and_dev.yaml @@ -6,7 +6,10 @@ secrets: preprocessingPipelinePassword: "preprocessing_pipeline" externalMetadataUpdaterPassword: "external_metadata_updater" backendUserPassword: "backend" + configLoaderUserPassword: "config_loader_user" createTestAccounts: true +seedPreviewData: + enabled: true backendExtraArgs: - "--loculus.debug-mode=true" disableEnaSubmission: true diff --git a/kubernetes/loculus/values_local_images.yaml b/kubernetes/loculus/values_local_images.yaml new file mode 100644 index 0000000000..9317128da5 --- /dev/null +++ b/kubernetes/loculus/values_local_images.yaml @@ -0,0 +1,25 @@ +# Helm values overlay for running Loculus from locally-built images imported into k3d. +# +# Applied automatically by `./deploy.py helm --branch local`. The `imagePullPolicy: +# IfNotPresent` settings stop kubelet from trying to fetch each image from ghcr.io, +# which would fail because the `:local` tag only exists in the k3d cluster's +# containerd (put there by `./build-local-images.sh`). +# +# Pair this with `--branch local` so the chart's docker-tag helper resolves to the +# literal `local` tag. + +imagePullPolicy: IfNotPresent +images: + loculusSilo: + pullPolicy: IfNotPresent + # `lapis` keeps `pullPolicy: Always` from values.yaml: that's the upstream + # GenSpectrum image which we don't build locally, so we want kubelet to pull it + # normally (and k3d caches once). + website: + pullPolicy: IfNotPresent + backend: + pullPolicy: IfNotPresent + configLoader: + pullPolicy: IfNotPresent + configAdapter: + pullPolicy: IfNotPresent diff --git a/kubernetes/loculus/values_preview_server.yaml b/kubernetes/loculus/values_preview_server.yaml index c6a1ea97d2..d9106e5626 100644 --- a/kubernetes/loculus/values_preview_server.yaml +++ b/kubernetes/loculus/values_preview_server.yaml @@ -1,4 +1,7 @@ createTestAccounts: true +seedPreviewData: + enabled: true +runConfigLoaderAsArgoJob: true secrets: smtp-password: type: sealedsecret diff --git a/loculus-silo/README.md b/loculus-silo/README.md index 085db55c6c..e0c6e66cbf 100644 --- a/loculus-silo/README.md +++ b/loculus-silo/README.md @@ -22,7 +22,7 @@ BACKEND_BASE_URL="http://localhost:8079/organism" python -m silo_import Environment variables mirror the historical shell scripts: - `BACKEND_BASE_URL` (required) -- `LINEAGE_DEFINITIONS` (optional JSON mapping `pipelineVersion -> URL`) +- `LINEAGE_DEFINITIONS_FILE` (optional, default `/app/lineage_definitions.json`) — path to a JSON file mapping `lineageSystem -> { pipelineVersion -> URL }`. Written by the `config-adapter` init container from the backend's `lineageSystemDefinitions`; the importer downloads each definition file at import time. A missing or empty file means no lineage systems. - `HARD_REFRESH_INTERVAL` (seconds, default `3600`) - `SILO_IMPORT_POLL_INTERVAL_SECONDS` (default `30`) - `SILO_RUN_TIMEOUT_SECONDS` (default `3600`) diff --git a/loculus-silo/pyproject.toml b/loculus-silo/pyproject.toml index cb8215d86e..5cf6717ccd 100644 --- a/loculus-silo/pyproject.toml +++ b/loculus-silo/pyproject.toml @@ -6,6 +6,8 @@ dependencies = [ "zstandard>=0.25.0,<0.26", "requests>=2.34.2,<3.0", "orjsonl>=1.0.0,<2.0", + "duckdb>=1.4.0,<2.0", + "pyyaml>=6.0.2,<7.0", ] [project.scripts] diff --git a/loculus-silo/src/silo_import/__main__.py b/loculus-silo/src/silo_import/__main__.py index d1ba47940e..312389ca78 100644 --- a/loculus-silo/src/silo_import/__main__.py +++ b/loculus-silo/src/silo_import/__main__.py @@ -1,8 +1,10 @@ from __future__ import annotations import logging +import os from .config import ImporterConfig +from .overview import OverviewImporterConfig, run_overview_forever from .paths import ImporterPaths from .runner import run_forever @@ -17,12 +19,25 @@ def configure_logging() -> None: def main() -> None: configure_logging() - config = ImporterConfig.from_env() - logger.info(f"Importer configuration: {config}") + if os.environ.get("SILO_IMPORT_MODE") == "overview": + overview_config = OverviewImporterConfig.from_env() + logger.info(f"Overview importer configuration: {overview_config}") + paths = ImporterPaths.from_root( + overview_config.root_dir, + overview_config.silo_binary, + overview_config.preprocessing_config, + ) + run_overview_forever(overview_config, paths) + return + + importer_config = ImporterConfig.from_env() + logger.info(f"Importer configuration: {importer_config}") paths = ImporterPaths.from_root( - config.root_dir, config.silo_binary, config.preprocessing_config + importer_config.root_dir, + importer_config.silo_binary, + importer_config.preprocessing_config, ) - run_forever(config, paths) + run_forever(importer_config, paths) if __name__ == "__main__": diff --git a/loculus-silo/src/silo_import/config.py b/loculus-silo/src/silo_import/config.py index 1c58e75ca0..3598321b3f 100644 --- a/loculus-silo/src/silo_import/config.py +++ b/loculus-silo/src/silo_import/config.py @@ -29,27 +29,9 @@ def from_env(cls) -> ImporterConfig: msg = "BACKEND_BASE_URL environment variable is required" raise RuntimeError(msg) - lineage_definitions_raw = env.get("LINEAGE_DEFINITIONS") - lineage_definitions: dict[str, dict[int, str]] | None = None - if lineage_definitions_raw: - try: # noqa: PLW0717 - data = json.loads(lineage_definitions_raw) - lineage_definitions = {} - for key, value in data.items(): - if isinstance(value, dict): - lineage_definitions[key] = {int(k): v for k, v in value.items()} - else: - msg = ( - f"Each item in LINEAGE_DEFINITIONS must be a dictionary, " - f"received: {lineage_definitions_raw}" - ) - raise TypeError(msg) - - except json.JSONDecodeError as exc: - msg = "LINEAGE_DEFINITIONS must be valid JSON" - raise RuntimeError(msg) from exc - except TypeError as exc: - raise RuntimeError(str(exc)) from exc + lineage_definitions = cls._load_lineage_definitions( + env.get("LINEAGE_DEFINITIONS_FILE", "/app/lineage_definitions.json") + ) hierarchical_filters = _parse_hierarchical_filters(env.get("HIERARCHICAL_FILTERS")) @@ -75,6 +57,33 @@ def from_env(cls) -> ImporterConfig: hierarchical_filters=hierarchical_filters, ) + @staticmethod + def _load_lineage_definitions(path: str) -> dict[str, dict[int, str]] | None: + """Read the lineage-definition URL map (system -> pipeline version -> URL) + that the config-adapter wrote from the DB instance config. The adapter + always writes the file (possibly `{}`); a missing or empty file means no + lineage systems for this organism. + """ + if not path or not os.path.exists(path): + return None + raw = Path(path).read_text(encoding="utf-8").strip() + if not raw: + return None + try: + data = json.loads(raw) + except json.JSONDecodeError as exc: + msg = f"{path} must be valid JSON" + raise RuntimeError(msg) from exc + if not data: + return None + lineage_definitions: dict[str, dict[int, str]] = {} + for key, value in data.items(): + if not isinstance(value, dict): + msg = f"Each entry in {path} must map version -> URL; got {value!r}" + raise TypeError(msg) + lineage_definitions[key] = {int(k): v for k, v in value.items()} + return lineage_definitions + @property def released_data_endpoint(self) -> str: return f"{self.backend_base_url}/get-released-data?compression=zstd" diff --git a/loculus-silo/src/silo_import/overview.py b/loculus-silo/src/silo_import/overview.py new file mode 100644 index 0000000000..242e060b8e --- /dev/null +++ b/loculus-silo/src/silo_import/overview.py @@ -0,0 +1,603 @@ +from __future__ import annotations + +import json +import logging +import os +import shutil +import time +from collections.abc import Callable, Iterator, Mapping +from dataclasses import dataclass +from datetime import date, datetime +from io import TextIOWrapper +from itertools import starmap +from pathlib import Path +from typing import Any + +import duckdb +import requests +import yaml +import zstandard + +from .constants import DATA_FILENAME, SPECIAL_ETAG_NONE, TRANSFORMED_DATA_FILENAME +from .download_manager import ( + _create_download_directory, + _download_file, + _handle_previous_directory, +) +from .errors import HashUnchangedError, NotModifiedError +from .filesystem import prune_timestamped_directories, safe_remove +from .instruct_silo import SiloRunner +from .paths import ImporterPaths +from .transformer import transform_data_format + +logger = logging.getLogger(__name__) + +DownloadFunc = Callable[[str, Path, str | None, int], Any] +HTTP_BAD_REQUEST = 400 + + +@dataclass(frozen=True) +class OverviewImporterConfig: + backend_base_urls: dict[str, str] + organism_display_names: dict[str, str] + query_file: Path + database_config: Path + hard_refresh_interval: int + poll_interval: int + silo_run_timeout: int + root_dir: Path + silo_binary: Path + preprocessing_config: Path + sequence_config_file: Path + + @classmethod + def from_env(cls) -> OverviewImporterConfig: + env = os.environ + backend_base_urls = _json_env_dict(env, "OVERVIEW_BACKEND_BASE_URLS") + if not backend_base_urls: + msg = "OVERVIEW_BACKEND_BASE_URLS environment variable is required" + raise RuntimeError(msg) + + query_file = Path(env.get("OVERVIEW_QUERY_FILE", "/app/overview_query.sql")) + if not query_file.exists(): + msg = f"OVERVIEW_QUERY_FILE does not exist: {query_file}" + raise RuntimeError(msg) + + database_config = Path( + env.get("OVERVIEW_DATABASE_CONFIG", "/preprocessing/input/database_config.yaml") + ) + if not database_config.exists(): + msg = f"OVERVIEW_DATABASE_CONFIG does not exist: {database_config}" + raise RuntimeError(msg) + + root_raw = env.get("ROOT_DIR") + root_dir = Path(root_raw).resolve() if root_raw else Path("/") + + return cls( + backend_base_urls={key: value.rstrip("/") for key, value in backend_base_urls.items()}, + organism_display_names=_json_env_dict(env, "OVERVIEW_ORGANISM_DISPLAY_NAMES"), + query_file=query_file, + database_config=database_config, + hard_refresh_interval=int(env.get("HARD_REFRESH_INTERVAL", "3600")), + poll_interval=int(env.get("SILO_IMPORT_POLL_INTERVAL_SECONDS", "30")), + silo_run_timeout=int(env.get("SILO_RUN_TIMEOUT_SECONDS", "3600")), + root_dir=root_dir, + silo_binary=Path(env.get("PATH_TO_SILO_BINARY", "/usr/local/bin/silo")), + preprocessing_config=Path( + env.get("PREPROCESSING_CONFIG", "/app/preprocessing_config.yaml") + ), + sequence_config_file=Path( + env.get("OVERVIEW_SEQUENCE_CONFIG_FILE", "/app/view_sequence_config.json") + ), + ) + + def released_data_endpoint(self, organism_key: str) -> str: + return f"{self.backend_base_urls[organism_key]}/get-released-data?compression=zstd" + + +@dataclass +class OverviewDownloadResult: + directory: Path + transformed_path: Path + transformed_paths: dict[str, Path] + etag: str + + +@dataclass(frozen=True) +class ViewSequenceConfig: + enabled: bool + segments: list[str] + source_segments: dict[str, dict[str, str]] + + def source_segment(self, organism_key: str, view_segment: str) -> str: + return self.source_segments.get(view_segment, {}).get(organism_key, view_segment) + + +class OverviewImporterRunner: + def __init__( + self, + config: OverviewImporterConfig, + paths: ImporterPaths, + download_func: DownloadFunc | None = None, + ) -> None: + self.config = config + self.paths = paths + self.paths.ensure_directories() + self._clear_download_directories() + self.silo = SiloRunner(paths.silo_binary, paths.preprocessing_config) + self.download_func = download_func or _download_file + self.current_etags: dict[str, str] = {} + self.last_hard_refresh: float = 0 + + def _clear_download_directories(self) -> None: + if not self.paths.input_dir.exists(): + return + for item in self.paths.input_dir.iterdir(): + if item.is_dir() and item.name.isdigit(): + logger.info("Clearing download directory on startup: %s", item) + safe_remove(item) + + def run_once(self) -> None: + prune_timestamped_directories(self.paths.output_dir) + hard_refresh = time.time() - self.last_hard_refresh >= self.config.hard_refresh_interval + etags = None if hard_refresh else self.current_etags + + try: + download = self.download_release(etags) + except (NotModifiedError, HashUnchangedError) as skip: + logger.info("Skipping overview run: %s", skip) + if hard_refresh: + self.last_hard_refresh = time.time() + return + + safe_remove(self.paths.silo_input_data_path) + shutil.copyfile(download.transformed_path, self.paths.silo_input_data_path) + + try: + self.silo.run_preprocessing(self.config.silo_run_timeout) + except Exception: + logger.exception("Overview SILO preprocessing failed; cleaning up input") + safe_remove(self.paths.silo_input_data_path) + safe_remove(download.directory) + raise + + self.current_etags = json.loads(download.etag) + if hard_refresh: + self.last_hard_refresh = time.time() + logger.info("Overview run complete; waiting %s seconds", self.config.poll_interval) + + def download_release(self, etags: dict[str, str] | None) -> OverviewDownloadResult: + logger.info("Starting overview download") + download_dir = _create_download_directory(self.paths.input_dir) + + try: + downloaded_paths, response_etags = self._download_all(download_dir, etags) + return self._build_overview(download_dir, downloaded_paths, response_etags) + except Exception: + safe_remove(download_dir) + raise + + def _build_overview( + self, + download_dir: Path, + downloaded_paths: dict[str, Path], + response_etags: dict[str, str], + ) -> OverviewDownloadResult: + transformed_path = download_dir / TRANSFORMED_DATA_FILENAME + transformed_paths = self._transform_all(download_dir, downloaded_paths) + self._execute_overview_query(transformed_paths, transformed_path) + + combined_etag = json.dumps(response_etags, sort_keys=True) + _handle_previous_directory(self.paths, download_dir, transformed_path, combined_etag) + prune_timestamped_directories(self.paths.input_dir) + logger.info("Rebuilt overview from %s organisms", len(transformed_paths)) + return OverviewDownloadResult( + download_dir, + transformed_path, + transformed_paths, + combined_etag, + ) + + def _download_all( + self, + download_dir: Path, + etags: dict[str, str] | None, + ) -> tuple[dict[str, Path], dict[str, str]]: + first_pass_paths, first_pass_etags, not_modified = self._download_pass(download_dir, etags) + if etags and len(not_modified) == len(self.config.backend_base_urls): + raise NotModifiedError + if etags and not_modified: + for path in first_pass_paths.values(): + safe_remove(path) + return self._download_pass(download_dir, None)[:2] + return first_pass_paths, first_pass_etags + + def _download_pass( + self, + download_dir: Path, + etags: dict[str, str] | None, + ) -> tuple[dict[str, Path], dict[str, str], set[str]]: + downloaded_paths: dict[str, Path] = {} + response_etags: dict[str, str] = {} + not_modified: set[str] = set() + + for organism_key in self.config.backend_base_urls: + path = download_dir / f"{organism_key}-{DATA_FILENAME}" + response = self.download_func( + self.config.released_data_endpoint(organism_key), + path, + etags.get(organism_key, SPECIAL_ETAG_NONE) if etags else SPECIAL_ETAG_NONE, + 300, + ) + if response.status_code == requests.codes.not_modified: + not_modified.add(organism_key) + safe_remove(path) + continue + if response.status_code >= HTTP_BAD_REQUEST: + body = ( + path.read_text(encoding="utf-8", errors="replace") + if path.exists() + else "missing" + ) + msg = ( + f"Failed to download {organism_key}: HTTP {response.status_code}. Body: {body}" + ) + raise RuntimeError(msg) + + etag = response.headers.get("etag") + if not etag: + msg = f"{organism_key} response did not contain an ETag header" + raise RuntimeError(msg) + response_etags[organism_key] = etag + downloaded_paths[organism_key] = path + + return downloaded_paths, response_etags, not_modified + + def _transform_all( + self, + download_dir: Path, + downloaded_paths: dict[str, Path], + ) -> dict[str, Path]: + transformed_paths: dict[str, Path] = {} + for organism_key, raw_path in downloaded_paths.items(): + transformed_path = download_dir / f"{organism_key}.ndjson.zst" + transform_data_format(raw_path, transformed_path) + transformed_paths[organism_key] = transformed_path + return transformed_paths + + def _execute_overview_query( + self, + transformed_paths: dict[str, Path], + output_path: Path, + ) -> None: + query = self.config.query_file.read_text(encoding="utf-8").strip() + if not query: + msg = f"Overview query file is empty: {self.config.query_file}" + raise RuntimeError(msg) + + logger.info("Executing overview query from %s", self.config.query_file) + configured_source_columns = _configured_source_columns(self.config.database_config) + sequence_config = _read_view_sequence_config(self.config.sequence_config_file) + sequences_by_accession = ( + _collect_unaligned_sequences(sequence_config, transformed_paths) + if sequence_config.enabled + else {} + ) + sequence_columns = ( + [ + column + for segment in sequence_config.segments + for column in (segment, f"unaligned_{segment}") + ] + if sequence_config.enabled + else [] + ) + with duckdb.connect(database=":memory:") as connection: + for organism_key, transformed_path in transformed_paths.items(): + _register_source_view( + connection, organism_key, transformed_path, configured_source_columns + ) + + cursor = connection.execute(query) + columns = [description[0] for description in cursor.description or []] + _write_query_result_ndjson_zst( + cursor, columns, output_path, sequence_columns, sequences_by_accession + ) + + +def _register_source_view( + connection: duckdb.DuckDBPyConnection, + organism_key: str, + transformed_path: Path, + configured_columns: dict[str, str], +) -> None: + if _zstd_contains_non_whitespace(transformed_path): + raw_view_name = f"__raw_{organism_key}" + connection.execute( + f"CREATE VIEW {_quote_identifier(raw_view_name)} AS " # noqa: S608 + f"SELECT * FROM read_json({_quote_string(str(transformed_path))}, " + "format = 'newline_delimited')" + ) + source_columns = _source_columns(connection, raw_view_name) + view_sql = _create_typed_view_sql( + organism_key, raw_view_name, source_columns, configured_columns + ) + connection.execute(view_sql) + return + + logger.info("Creating empty overview view for %s", organism_key) + connection.execute(_create_empty_view_sql(organism_key, configured_columns)) + + +def _source_columns(connection: duckdb.DuckDBPyConnection, view_name: str) -> list[str]: + rows = connection.execute( + f"DESCRIBE SELECT * FROM {_quote_identifier(view_name)}" # noqa: S608 + ).fetchall() + return [row[0] for row in rows] + + +def _create_typed_view_sql( + organism_key: str, + raw_view_name: str, + source_columns: list[str], + configured_columns: dict[str, str], +) -> str: + select_expressions: list[str] = [] + source_column_set = set(source_columns) + + for column_name in source_columns: + if column_name in configured_columns: + select_expressions.append( + f"CAST({_quote_identifier(column_name)} AS {configured_columns[column_name]}) " + f"AS {_quote_identifier(column_name)}" + ) + else: + select_expressions.append(_quote_identifier(column_name)) + + for column_name, duckdb_type in configured_columns.items(): + if column_name not in source_column_set: + select_expressions.append(_null_column_sql(column_name, duckdb_type)) + + columns = ", ".join(select_expressions) + return ( + f"CREATE VIEW {_quote_identifier(organism_key)} AS " # noqa: S608 + f"SELECT {columns} FROM {_quote_identifier(raw_view_name)}" + ) + + +def _create_empty_view_sql( + organism_key: str, + configured_columns: dict[str, str], +) -> str: + columns = ", ".join(starmap(_null_column_sql, configured_columns.items())) + return f"CREATE VIEW {_quote_identifier(organism_key)} AS SELECT {columns} WHERE false" + + +def _null_column_sql(column_name: str, duckdb_type: str) -> str: + return f"CAST(NULL AS {duckdb_type}) AS {_quote_identifier(column_name)}" + + +def _configured_source_columns(database_config: Path) -> dict[str, str]: + config = yaml.safe_load(database_config.read_text(encoding="utf-8")) or {} + schema = config.get("schema", {}) + metadata = schema.get("metadata", []) + if not isinstance(metadata, list): + msg = f"{database_config} must contain schema.metadata as a list" + raise TypeError(msg) + + columns: dict[str, str] = {} + for field in metadata: + if isinstance(field, dict) and isinstance(field.get("name"), str): + columns[field["name"]] = _duckdb_type(field.get("type")) + + # The overview query may normalize organism-specific source names into the + # manual output schema. These aliases let empty sources participate in UNIONs. + columns.setdefault("geoLocCountry", "VARCHAR") + columns.setdefault("sampleCollectionDate", "VARCHAR") + columns.setdefault("country", "VARCHAR") + columns.setdefault("date", "VARCHAR") + return columns + + +def _duckdb_type(silo_type: Any) -> str: + return { + "boolean": "BOOLEAN", + "date": "DATE", + "float": "DOUBLE", + "int": "BIGINT", + "string": "VARCHAR", + "timestamp": "BIGINT", + }.get(str(silo_type), "VARCHAR") + + +def _zstd_contains_non_whitespace(path: Path) -> bool: + decompressor = zstandard.ZstdDecompressor() + with path.open("rb") as handle, decompressor.stream_reader(handle) as reader: + while chunk := reader.read(8192): + if chunk.strip(): + return True + return False + + +def _read_view_sequence_config(path: Path) -> ViewSequenceConfig: + if not path.exists(): + return ViewSequenceConfig(enabled=False, segments=[], source_segments={}) + + data = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(data, dict): + msg = f"{path} must contain a JSON object" + raise TypeError(msg) + unaligned = data.get("unalignedNucleotideSequences", {}) + if not isinstance(unaligned, dict): + msg = f"{path} field unalignedNucleotideSequences must be an object" + raise TypeError(msg) + + enabled = unaligned.get("enabled") is True + segments = unaligned.get("segments", []) + source_segments = unaligned.get("sourceSegments", {}) + if not isinstance(segments, list) or not all(isinstance(segment, str) for segment in segments): + msg = f"{path} field unalignedNucleotideSequences.segments must be a string array" + raise TypeError(msg) + if not _is_nested_string_dict(source_segments): + msg = ( + f"{path} field unalignedNucleotideSequences.sourceSegments must map strings to strings" + ) + raise TypeError(msg) + + return ViewSequenceConfig( + enabled=enabled and bool(segments), + segments=segments, + source_segments=source_segments, + ) + + +def _collect_unaligned_sequences( + sequence_config: ViewSequenceConfig, + transformed_paths: dict[str, Path], +) -> dict[str, dict[str, str]]: + sequences_by_accession: dict[str, dict[str, str]] = {} + + for organism_key, transformed_path in transformed_paths.items(): + target_to_source_fields: dict[str, str] = {} + for view_segment in sequence_config.segments: + source_field = f"unaligned_{sequence_config.source_segment(organism_key, view_segment)}" + target_to_source_fields[view_segment] = source_field + target_to_source_fields[f"unaligned_{view_segment}"] = source_field + if not _zstd_contains_non_whitespace(transformed_path): + continue + for record in _read_zstd_ndjson(transformed_path): + accession_version = record.get("accessionVersion") + if accession_version is None: + continue + accession_key = str(accession_version) + target_record = sequences_by_accession.setdefault(accession_key, {}) + for target_field, source_field in target_to_source_fields.items(): + sequence = record.get(source_field) + if sequence is None: + continue + if not isinstance(sequence, str): + msg = f"{source_field} for {accession_key} in {organism_key} is not a string" + raise TypeError(msg) + existing = target_record.get(target_field) + if existing is not None and existing != sequence: + msg = ( + f"Conflicting {target_field} sequence for accessionVersion {accession_key}" + ) + raise RuntimeError(msg) + target_record[target_field] = sequence + + return {key: value for key, value in sequences_by_accession.items() if value} + + +def _read_zstd_ndjson(path: Path) -> Iterator[dict[str, Any]]: + decompressor = zstandard.ZstdDecompressor() + with ( + path.open("rb") as handle, + decompressor.stream_reader(handle) as reader, + TextIOWrapper(reader, encoding="utf-8") as text_reader, + ): + for line in text_reader: + if not line.strip(): + continue + record = json.loads(line) + if not isinstance(record, dict): + msg = f"{path} contains a non-object NDJSON record" + raise TypeError(msg) + yield record + + +def _is_nested_string_dict(value: Any) -> bool: + if not isinstance(value, dict): + return False + return all( + isinstance(outer_key, str) + and isinstance(inner, dict) + and all( + isinstance(inner_key, str) and isinstance(inner_value, str) + for inner_key, inner_value in inner.items() + ) + for outer_key, inner in value.items() + ) + + +def _json_env_dict(env: Mapping[str, str], key: str) -> dict[str, str]: + raw = env.get(key) + if not raw: + return {} + data = json.loads(raw) + if not isinstance(data, dict) or not all( + isinstance(k, str) and isinstance(v, str) for k, v in data.items() + ): + msg = f"{key} must be a JSON object with string values" + raise RuntimeError(msg) + return data + + +def _write_query_result_ndjson_zst( + cursor: Any, + columns: list[str], + path: Path, + sequence_columns: list[str] | None = None, + sequences_by_accession: dict[str, dict[str, str]] | None = None, +) -> None: + if sequence_columns and "accessionVersion" not in columns: + msg = "View sequence support requires the SQL query to return accessionVersion" + raise RuntimeError(msg) + + compressor = zstandard.ZstdCompressor() + record_count = 0 + with path.open("wb") as handle, compressor.stream_writer(handle) as writer: + while rows := cursor.fetchmany(10_000): + for row in rows: + record = { + column: _json_value(value) for column, value in zip(columns, row, strict=True) + } + if sequence_columns: + _attach_sequence_columns(record, sequence_columns, sequences_by_accession) + writer.write(json.dumps(record, separators=(",", ":")).encode("utf-8")) + writer.write(b"\n") + record_count += 1 + logger.info("Overview query wrote %s records to %s", record_count, path) + + +def _attach_sequence_columns( + record: dict[str, Any], + sequence_columns: list[str], + sequences_by_accession: dict[str, dict[str, str]] | None, +) -> None: + accession_version = record.get("accessionVersion") + attached_sequences = ( + sequences_by_accession.get(str(accession_version), {}) + if accession_version is not None and sequences_by_accession + else {} + ) + for sequence_column in sequence_columns: + if not sequence_column.startswith("unaligned_"): + record[sequence_column] = None + continue + sequence = attached_sequences.get(sequence_column) + record[sequence_column] = sequence + + +def _json_value(value: Any) -> Any: + if isinstance(value, date | datetime): + return value.isoformat() + return value + + +def _quote_identifier(value: str) -> str: + return '"' + value.replace('"', '""') + '"' + + +def _quote_string(value: str) -> str: + return "'" + value.replace("'", "''") + "'" + + +def run_overview_forever(config: OverviewImporterConfig, paths: ImporterPaths) -> None: + runner = OverviewImporterRunner(config, paths) + while True: + try: + runner.run_once() + except Exception: + logger.exception("Overview SILO import cycle failed") + time.sleep(config.poll_interval) diff --git a/loculus-silo/tests/test_config.py b/loculus-silo/tests/test_config.py index 9ae8f7244e..821063ff7f 100644 --- a/loculus-silo/tests/test_config.py +++ b/loculus-silo/tests/test_config.py @@ -17,9 +17,12 @@ def test_config_from_env(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: backend_url = "http://example.com/base" - lineage_json = '{"test": {"1": "http://example.com/lineage.yaml"}}' + # The config-adapter writes this file from the DB instance config; the + # silo-importer reads it and downloads the actual lineage definitions itself. + lineage_file = tmp_path / "lineage_definitions.json" + lineage_file.write_text('{"test": {"1": "http://example.com/lineage.yaml"}}', encoding="utf-8") monkeypatch.setenv("BACKEND_BASE_URL", backend_url) - monkeypatch.setenv("LINEAGE_DEFINITIONS", lineage_json) + monkeypatch.setenv("LINEAGE_DEFINITIONS_FILE", str(lineage_file)) monkeypatch.setenv("HARD_REFRESH_INTERVAL", str(HARD_REFRESH_INTERVAL)) monkeypatch.setenv("SILO_IMPORT_POLL_INTERVAL_SECONDS", str(SILO_IMPORT_POLL_INTERVAL_SECONDS)) monkeypatch.setenv("SILO_RUN_TIMEOUT_SECONDS", str(SILO_RUN_TIMEOUT_SECONDS)) @@ -37,6 +40,35 @@ def test_config_from_env(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Non assert config.hierarchical_filters is None +def test_config_lineage_definitions_absent_file( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setenv("BACKEND_BASE_URL", "http://example.com/base") + monkeypatch.setenv("LINEAGE_DEFINITIONS_FILE", str(tmp_path / "missing.json")) + assert ImporterConfig.from_env().lineage_definitions is None + + +def test_config_lineage_definitions_empty_file( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + lineage_file = tmp_path / "lineage_definitions.json" + lineage_file.write_text("{}", encoding="utf-8") + monkeypatch.setenv("BACKEND_BASE_URL", "http://example.com/base") + monkeypatch.setenv("LINEAGE_DEFINITIONS_FILE", str(lineage_file)) + assert ImporterConfig.from_env().lineage_definitions is None + + +def test_config_lineage_definitions_invalid_json( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + lineage_file = tmp_path / "lineage_definitions.json" + lineage_file.write_text("{not json", encoding="utf-8") + monkeypatch.setenv("BACKEND_BASE_URL", "http://example.com/base") + monkeypatch.setenv("LINEAGE_DEFINITIONS_FILE", str(lineage_file)) + with pytest.raises(RuntimeError): + ImporterConfig.from_env() + + def test_config_missing_backend_env(monkeypatch: pytest.MonkeyPatch) -> None: for key in list(os.environ.keys()): if key.startswith("BACKEND_BASE_URL"): diff --git a/loculus-silo/tests/test_overview.py b/loculus-silo/tests/test_overview.py new file mode 100644 index 0000000000..c4d20f3f30 --- /dev/null +++ b/loculus-silo/tests/test_overview.py @@ -0,0 +1,418 @@ +# ruff: noqa: S101 +from __future__ import annotations + +from pathlib import Path +from unittest.mock import patch + +from helpers import MockHttpResponse, compress_ndjson, make_mock_download_func, read_ndjson_file +from silo_import.overview import OverviewImporterConfig, OverviewImporterRunner +from silo_import.paths import ImporterPaths + + +def make_config(tmp_path: Path) -> OverviewImporterConfig: + query_file = tmp_path / "overview_query.sql" + query_file.write_text( + """ +select accessionVersion, accession, version, + geoLocCountry as country, sampleCollectionDate as date, 'virus-a' as organism +from "virus-a" +union all +select accessionVersion, accession, version, + country as country, date as date, 'virus-b' as organism +from "virus-b" +""".strip(), + encoding="utf-8", + ) + database_config = tmp_path / "database_config.yaml" + database_config.write_text( + """ +schema: + metadata: + - name: accessionVersion + type: string + - name: accession + type: string + - name: version + type: int + - name: country + type: string + - name: date + type: string + - name: organism + type: string +""".strip(), + encoding="utf-8", + ) + return OverviewImporterConfig( + backend_base_urls={ + "virus-a": "http://backend/virus-a", + "virus-b": "http://backend/virus-b", + }, + organism_display_names={"virus-a": "Virus A", "virus-b": "Virus B"}, + query_file=query_file, + database_config=database_config, + hard_refresh_interval=1000, + poll_interval=1, + silo_run_timeout=5, + root_dir=tmp_path, + silo_binary=tmp_path / "silo", + preprocessing_config=tmp_path / "config.yaml", + sequence_config_file=tmp_path / "view_sequence_config.json", + ) + + +def make_paths(tmp_path: Path) -> ImporterPaths: + return ImporterPaths.from_root(tmp_path, tmp_path / "silo", tmp_path / "config.yaml") + + +def flatten_metadata_transform(data_path: Path, transformed_path: Path) -> None: + records = [] + for record in read_ndjson_file(data_path): + records.append(record["metadata"]) + + transformed_path.write_bytes(compress_ndjson(records)) + + +def flatten_metadata_and_sequences_transform(data_path: Path, transformed_path: Path) -> None: + records = [] + for record in read_ndjson_file(data_path): + transformed = dict(record["metadata"]) + for segment, sequence in record.get("unalignedNucleotideSequences", {}).items(): + transformed[f"unaligned_{segment}"] = sequence + records.append(transformed) + + transformed_path.write_bytes(compress_ndjson(records)) + + +def test_overview_runner_executes_sql_view_query(tmp_path: Path) -> None: + config = make_config(tmp_path) + paths = make_paths(tmp_path) + paths.ensure_directories() + + responses = [ + MockHttpResponse( + status=200, + headers={"ETag": '"a"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "REV_1.1", + "accession": "REV_1", + "version": 1, + "sampleCollectionDate": "2024-01-01", + "geoLocCountry": "Germany", + } + } + ] + ), + ), + MockHttpResponse( + status=200, + headers={"ETag": '"b"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "REV_2.1", + "accession": "REV_2", + "version": "2", + "date": "2024-02", + "country": "Brazil", + "unconfiguredField": "ignored", + } + } + ] + ), + ), + ] + mock_download, responses_list = make_mock_download_func(responses) + + runner = OverviewImporterRunner(config, paths, download_func=mock_download) + with ( + patch.object(runner.silo, "run_preprocessing"), + patch( + "silo_import.overview.transform_data_format", + flatten_metadata_transform, + ), + ): + runner.run_once() + + assert read_ndjson_file(paths.silo_input_data_path) == [ + { + "accessionVersion": "REV_1.1", + "accession": "REV_1", + "version": 1, + "country": "Germany", + "date": "2024-01-01", + "organism": "virus-a", + }, + { + "accessionVersion": "REV_2.1", + "accession": "REV_2", + "version": 2, + "country": "Brazil", + "date": "2024-02", + "organism": "virus-b", + }, + ] + assert runner.current_etags == {"virus-a": '"a"', "virus-b": '"b"'} + assert not responses_list + + +def test_overview_runner_skips_when_all_sources_not_modified(tmp_path: Path) -> None: + config = make_config(tmp_path) + paths = make_paths(tmp_path) + paths.ensure_directories() + mock_download, responses_list = make_mock_download_func( + [MockHttpResponse(status=304, headers={}), MockHttpResponse(status=304, headers={})] + ) + + runner = OverviewImporterRunner(config, paths, download_func=mock_download) + runner.current_etags = {"virus-a": '"a"', "virus-b": '"b"'} + runner.last_hard_refresh = 9999999999 + runner.run_once() + + assert not paths.silo_input_data_path.exists() + assert not responses_list + + +def test_overview_runner_refetches_all_sources_on_partial_change(tmp_path: Path) -> None: + config = make_config(tmp_path) + paths = make_paths(tmp_path) + paths.ensure_directories() + mock_download, responses_list = make_mock_download_func( + [ + MockHttpResponse(status=304, headers={}), + MockHttpResponse( + status=200, + headers={"ETag": '"new-b"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "partial.1", + "accession": "partial", + "version": 1, + "country": "Brazil", + "date": "2024", + } + } + ] + ), + ), + MockHttpResponse( + status=200, + headers={"ETag": '"new-a"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "full-a.1", + "accession": "full-a", + "version": 1, + "geoLocCountry": "Germany", + "sampleCollectionDate": "2024", + } + } + ] + ), + ), + MockHttpResponse( + status=200, + headers={"ETag": '"new-b"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "full-b.1", + "accession": "full-b", + "version": 1, + "country": "Brazil", + "date": "2024", + } + } + ] + ), + ), + ] + ) + + runner = OverviewImporterRunner(config, paths, download_func=mock_download) + runner.current_etags = {"virus-a": '"old-a"', "virus-b": '"old-b"'} + runner.last_hard_refresh = 9999999999 + with ( + patch.object(runner.silo, "run_preprocessing"), + patch( + "silo_import.overview.transform_data_format", + flatten_metadata_transform, + ), + ): + runner.run_once() + + records = read_ndjson_file(paths.silo_input_data_path) + assert [record["accessionVersion"] for record in records] == [ + "full-a.1", + "full-b.1", + ] + assert runner.current_etags == {"virus-a": '"new-a"', "virus-b": '"new-b"'} + assert not responses_list + + +def test_overview_runner_allows_empty_sources_in_sql_union(tmp_path: Path) -> None: + config = make_config(tmp_path) + paths = make_paths(tmp_path) + paths.ensure_directories() + + responses = [ + MockHttpResponse( + status=200, + headers={"ETag": '"a"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "non-empty.1", + "accession": "non-empty", + "version": 1, + "geoLocCountry": "Germany", + "sampleCollectionDate": "2024", + } + } + ] + ), + ), + MockHttpResponse( + status=200, + headers={"ETag": '"empty"'}, + body=compress_ndjson([]), + ), + ] + mock_download, responses_list = make_mock_download_func(responses) + + runner = OverviewImporterRunner(config, paths, download_func=mock_download) + with ( + patch.object(runner.silo, "run_preprocessing"), + patch( + "silo_import.overview.transform_data_format", + flatten_metadata_transform, + ), + ): + runner.run_once() + + assert read_ndjson_file(paths.silo_input_data_path) == [ + { + "accessionVersion": "non-empty.1", + "accession": "non-empty", + "version": 1, + "country": "Germany", + "date": "2024", + "organism": "virus-a", + }, + ] + assert runner.current_etags == {"virus-a": '"a"', "virus-b": '"empty"'} + assert not responses_list + + +def test_overview_runner_attaches_unaligned_sequences_to_query_result(tmp_path: Path) -> None: + config = make_config(tmp_path) + config.sequence_config_file.write_text( + """ +{ + "unalignedNucleotideSequences": { + "enabled": true, + "segments": ["main", "L"], + "sourceSegments": { + "main": { + "virus-a": "genome" + } + } + } +} +""".strip(), + encoding="utf-8", + ) + paths = make_paths(tmp_path) + paths.ensure_directories() + + responses = [ + MockHttpResponse( + status=200, + headers={"ETag": '"a"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "REV_1.1", + "accession": "REV_1", + "version": 1, + "sampleCollectionDate": "2024-01-01", + "geoLocCountry": "Germany", + }, + "unalignedNucleotideSequences": { + "genome": "ACGT", + }, + } + ] + ), + ), + MockHttpResponse( + status=200, + headers={"ETag": '"b"'}, + body=compress_ndjson( + [ + { + "metadata": { + "accessionVersion": "REV_2.1", + "accession": "REV_2", + "version": 2, + "date": "2024-02-01", + "country": "Brazil", + }, + "unalignedNucleotideSequences": { + "L": "TTAA", + }, + } + ] + ), + ), + ] + mock_download, responses_list = make_mock_download_func(responses) + + runner = OverviewImporterRunner(config, paths, download_func=mock_download) + with ( + patch.object(runner.silo, "run_preprocessing"), + patch( + "silo_import.overview.transform_data_format", + flatten_metadata_and_sequences_transform, + ), + ): + runner.run_once() + + assert read_ndjson_file(paths.silo_input_data_path) == [ + { + "accessionVersion": "REV_1.1", + "accession": "REV_1", + "version": 1, + "country": "Germany", + "date": "2024-01-01", + "organism": "virus-a", + "main": None, + "unaligned_main": "ACGT", + "L": None, + "unaligned_L": None, + }, + { + "accessionVersion": "REV_2.1", + "accession": "REV_2", + "version": 2, + "country": "Brazil", + "date": "2024-02-01", + "organism": "virus-b", + "main": None, + "unaligned_main": None, + "L": None, + "unaligned_L": "TTAA", + }, + ] + assert not responses_list diff --git a/preprocessing/dummy/main.py b/preprocessing/dummy/main.py index e3a2b9cc14..07b4a48196 100644 --- a/preprocessing/dummy/main.py +++ b/preprocessing/dummy/main.py @@ -55,6 +55,16 @@ help="Path to Keycloak token endpoint", ) parser.add_argument("--pipeline-version", type=int, default=1) +parser.add_argument( + "--organism", + type=str, + default=None, + help=( + "Organism key this pipeline instance processes. The dummy pipeline does not " + "use it (it reads no per-organism config), but the deployment passes it " + "unconditionally to every pipeline, so we accept and ignore it." + ), +) args = parser.parse_args() backendHost = args.backend_host diff --git a/preprocessing/nextclade/src/loculus_preprocessing/backend.py b/preprocessing/nextclade/src/loculus_preprocessing/backend.py index 099ebc997d..010580b969 100644 --- a/preprocessing/nextclade/src/loculus_preprocessing/backend.py +++ b/preprocessing/nextclade/src/loculus_preprocessing/backend.py @@ -202,7 +202,11 @@ def request_upload(group_id: int, number_of_files: int, config: Config) -> Seque base_url = f"{parsed.scheme}://{parsed.netloc}" url = base_url + "/files/request-upload" - params = {"groupId": group_id, "numberFiles": number_of_files} + params: dict[str, str | int] = { + "groupId": group_id, + "numberFiles": number_of_files, + "useInternalEndpoint": "true", + } headers = { "Authorization": "Bearer " + get_jwt(config), "x-request-id": request_id, diff --git a/preprocessing/nextclade/src/loculus_preprocessing/config.py b/preprocessing/nextclade/src/loculus_preprocessing/config.py index 75ae7b596c..429cfa86ba 100644 --- a/preprocessing/nextclade/src/loculus_preprocessing/config.py +++ b/preprocessing/nextclade/src/loculus_preprocessing/config.py @@ -3,9 +3,12 @@ import logging import os from enum import StrEnum +from http import HTTPStatus from types import UnionType from typing import Any, get_args +from urllib.parse import urlparse +import requests import yaml from pydantic import BaseModel, Field, model_validator @@ -270,6 +273,82 @@ def get_processing_order(config: Config) -> tuple[str, ...]: return processing_order +def build_identity_processing_specs( + metadata: list[dict[str, Any]], segment_names: list[str] +) -> dict[str, dict[str, Any]]: + """Identity processing entries for every organism metadata field. + + The backend config no longer carries per-field `preprocessing` directives — + those (the non-identity ones) live in the pipeline's own config file. Here we + fill the gaps: every metadata field that the config file does not already + specify gets a plain identity copy. Multi-segment fields marked `perSegment` + are expanded to one entry per segment (`name_`), matching what the + Helm chart used to generate. + """ + is_segmented = len(segment_names) > 1 + specs: dict[str, dict[str, Any]] = {} + for field in metadata: + name = field["name"] + if is_segmented and field.get("perSegment"): + output_names = [f"{name}_{segment}" for segment in segment_names] + else: + output_names = [name] + for output_name in output_names: + specs[output_name] = {"function": "identity", "inputs": {"input": output_name}} + return specs + + +def fetch_config_from_backend( + backend_host: str, organism: str, pipeline_version: int +) -> dict[str, Any]: + """Assemble the pipeline config for a deployed run from the public config API. + + Two sources are combined: + * the opaque, pipeline-owned config file stored per (organism, pipeline + version) — nextclade datasets, alignment knobs, EMBL settings, and any + non-identity `processing_spec` entries; and + * the organism's metadata field list, used to fill in identity + `processing_spec` entries for fields the config file does not specify. + + The core backend never interprets the config file; this function is the + pipeline-side translation into the pipeline's own `Config` shape. + """ + parsed = urlparse(backend_host) + base_url = f"{parsed.scheme}://{parsed.netloc}" + + config_url = f"{base_url}/api/config/organisms/{organism}/preprocessing/{pipeline_version}" + config_response = requests.get(config_url, timeout=30) + if config_response.status_code == HTTPStatus.OK: + config_dict: dict[str, Any] = yaml.safe_load(config_response.text) or {} + elif config_response.status_code == HTTPStatus.NOT_FOUND: + # No config file configured for this organism + pipeline version. That's + # allowed; we still derive identity processing specs from the metadata. + config_dict = {} + else: + msg = ( + f"Failed to fetch preprocessing config from {config_url}: " + f"status {config_response.status_code}" + ) + raise RuntimeError(msg) + + organism_url = f"{base_url}/api/config/organisms/{organism}" + organism_response = requests.get(organism_url, timeout=30) + if not organism_response.ok: + msg = ( + f"Failed to fetch organism config from {organism_url}: " + f"status {organism_response.status_code}" + ) + raise RuntimeError(msg) + metadata = organism_response.json().get("config", {}).get("schema", {}).get("metadata", []) + + segment_names = [segment["name"] for segment in config_dict.get("segments", [])] + identity_specs = build_identity_processing_specs(metadata, segment_names) + # The config file's explicit specs take precedence over the identity defaults. + config_dict["processing_spec"] = {**identity_specs, **config_dict.get("processing_spec", {})} + config_dict.setdefault("organism", organism) + return config_dict + + def get_config(config_file: str | None = None, ignore_args: bool = False) -> Config: """ Config precedence: Direct function args > CLI args > ENV variables > config file > default @@ -294,12 +373,21 @@ def get_config(config_file: str | None = None, ignore_args: bool = False) -> Con config_file or args.config_file or os.environ.get("PREPROCESSING_CONFIG_FILE") ) - # Start with lowest precedence config, then overwrite with higher precedence + # Start with lowest precedence config, then overwrite with higher precedence. + # Precedence of the base layer: an explicit config file (used by tests) wins; + # otherwise, in a deployed run (backend host + organism known), fetch the + # pipeline config from the backend; otherwise fall back to defaults. + backend_host = getattr(args, "backend_host", None) + organism = getattr(args, "organism", None) + pipeline_version = getattr(args, "pipeline_version", None) or 1 if config_file_path: with open(config_file_path, encoding="utf-8") as file: yaml_config = yaml.safe_load(file) logger.debug(f"Loaded config from {config_file_path}: {yaml_config}") config = Config(**yaml_config) + elif backend_host and organism: + logger.info(f"Fetching preprocessing config for organism '{organism}' from {backend_host}") + config = Config(**fetch_config_from_backend(backend_host, organism, pipeline_version)) else: config = Config() # Use environment variables if available diff --git a/preprocessing/nextclade/tests/test_config_fetch.py b/preprocessing/nextclade/tests/test_config_fetch.py new file mode 100644 index 0000000000..b374373f47 --- /dev/null +++ b/preprocessing/nextclade/tests/test_config_fetch.py @@ -0,0 +1,130 @@ +"""Tests for fetching the pipeline config from the Loculus backend. + +The backend stores an opaque, pipeline-owned config file per (organism, pipeline +version) and serves the organism's metadata field list. The pipeline combines +them: the config file provides datasets/alignment/EMBL and any non-identity +processing specs, while every remaining metadata field gets an identity copy. +""" + +import pytest + +from loculus_preprocessing.config import ( + build_identity_processing_specs, + fetch_config_from_backend, +) + + +def test_build_identity_specs_single_segment(): + specs = build_identity_processing_specs( + [{"name": "country", "type": "string"}, {"name": "date", "type": "date"}], + segment_names=[], + ) + assert specs == { + "country": {"function": "identity", "inputs": {"input": "country"}}, + "date": {"function": "identity", "inputs": {"input": "date"}}, + } + + +def test_build_identity_specs_expands_per_segment(): + specs = build_identity_processing_specs( + [ + {"name": "country", "type": "string"}, + {"name": "coverage", "type": "float", "perSegment": True}, + ], + segment_names=["L", "M"], + ) + assert set(specs) == {"country", "coverage_L", "coverage_M"} + assert specs["coverage_L"] == {"function": "identity", "inputs": {"input": "coverage_L"}} + + +class _FakeResponse: + def __init__(self, status_code: int, *, text: str = "", payload: object = None): + self.status_code = status_code + self.text = text + self._payload = payload + + @property + def ok(self) -> bool: + return 200 <= self.status_code < 300 + + def json(self): + return self._payload + + +@pytest.fixture +def patched_backend(monkeypatch): + """Patch requests.get so the two endpoints return canned responses.""" + + config_yaml = ( + "batch_size: 50\n" + "segments:\n" + " - name: L\n" + " - name: M\n" + "processing_spec:\n" + " date:\n" + " function: parse_date\n" + " inputs: {date: date}\n" + ) + metadata_payload = { + "config": { + "schema": { + "metadata": [ + {"name": "date", "type": "date"}, + {"name": "coverage", "type": "float", "perSegment": True}, + {"name": "country", "type": "string"}, + ] + } + } + } + + def fake_get(url, timeout=None): + if url.endswith("/preprocessing/1"): + return _FakeResponse(200, text=config_yaml) + if url.endswith("/organisms/ebola-sudan"): + return _FakeResponse(200, payload=metadata_payload) + raise AssertionError(f"unexpected URL {url}") + + monkeypatch.setattr("loculus_preprocessing.config.requests.get", fake_get) + + +def test_fetch_merges_config_file_and_identity_defaults(patched_backend): + config_dict = fetch_config_from_backend("http://backend:8079/ebola-sudan", "ebola-sudan", 1) + + # The explicit config-file values survive. + assert config_dict["batch_size"] == 50 + assert config_dict["organism"] == "ebola-sudan" + + spec = config_dict["processing_spec"] + # Per-segment field is expanded over the segments declared in the file. + assert "coverage_L" in spec and "coverage_M" in spec + # A plain field gets an identity default. + assert spec["country"] == {"function": "identity", "inputs": {"input": "country"}} + # The config file's explicit spec wins over the identity default. + assert spec["date"]["function"] == "parse_date" + + +def test_fetch_handles_missing_config_file(monkeypatch): + metadata_payload = {"config": {"schema": {"metadata": [{"name": "country", "type": "string"}]}}} + + def fake_get(url, timeout=None): + if url.endswith("/preprocessing/1"): + return _FakeResponse(404) + return _FakeResponse(200, payload=metadata_payload) + + monkeypatch.setattr("loculus_preprocessing.config.requests.get", fake_get) + + config_dict = fetch_config_from_backend("http://backend:8079/mpox", "mpox", 1) + # Still derives identity specs from metadata even with no config file. + assert config_dict["processing_spec"] == { + "country": {"function": "identity", "inputs": {"input": "country"}} + } + assert config_dict["organism"] == "mpox" + + +def test_fetch_raises_on_backend_error(monkeypatch): + monkeypatch.setattr( + "loculus_preprocessing.config.requests.get", + lambda url, timeout=None: _FakeResponse(500), + ) + with pytest.raises(RuntimeError): + fetch_config_from_backend("http://backend:8079/mpox", "mpox", 1) diff --git a/preview_config_experiment.sh b/preview_config_experiment.sh new file mode 100755 index 0000000000..7bfd3705a4 --- /dev/null +++ b/preview_config_experiment.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash +# +# Create a local config-experiment preview with local images, preprocessing, +# ingest, file sharing/S3, and test accounts enabled. +# +# Common usage: +# ./preview_config_experiment.sh fresh --docs --wait-ingest +# +# The script creates the k3d cluster before building images because +# build-local-images.sh imports the images into the running cluster. + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT" + +CLUSTER="${LOCULUS_K3D_CLUSTER:-testCluster}" +RELEASE="${LOCULUS_HELM_RELEASE:-preview}" +CHART="$ROOT/kubernetes/loculus" +DOCS_PORT="${LOCULUS_DOCS_PORT:-3001}" + +MODE="${1:-}" +if [[ "$MODE" == "fresh" || "$MODE" == "up" ]]; then + shift +else + MODE="up" +fi + +BUILD_IMAGES=1 +START_DOCS=1 +WAIT_INGEST=0 +ENABLE_INGEST=1 +ENABLE_PREPROCESSING=1 +ALLOW_OTHER_BRANCH=0 + +usage() { + sed -n '2,/^set -euo/{/^set -euo/d;p;}' "$0" | sed 's/^# \{0,1\}//' + cat <&2; usage >&2; exit 2 ;; + esac + shift +done + +log() { + printf '\n==> %s\n' "$*" +} + +need() { + command -v "$1" >/dev/null 2>&1 || { + echo "Missing required command: $1" >&2 + exit 127 + } +} + +run() { + printf '+' + printf ' %q' "$@" + printf '\n' + "$@" +} + +need git +need k3d +need kubectl +need helm +need docker +need python3 +need curl + +branch="$(git branch --show-current)" +if [[ "$ALLOW_OTHER_BRANCH" -ne 1 && "$branch" != "config-experiment" ]]; then + echo "Refusing to deploy from branch '$branch'; expected config-experiment." >&2 + echo "Pass --allow-other-branch only if you intentionally want this." >&2 + exit 2 +fi + +if [[ "$MODE" == "fresh" ]]; then + if k3d cluster list "$CLUSTER" >/dev/null 2>&1; then + log "Deleting existing k3d cluster '$CLUSTER'" + run k3d cluster delete "$CLUSTER" + fi +fi + +log "Creating/validating k3d cluster '$CLUSTER'" +run python3 ./deploy.py cluster + +if [[ "$BUILD_IMAGES" -eq 1 ]]; then + log "Building and importing local images" + run ./build-local-images.sh +else + log "Skipping local image build/import" +fi + +helm_args=( + upgrade --install "$RELEASE" "$CHART" + --set environment=local + --set branch=local + -f "$CHART/values_e2e_and_dev.yaml" + -f "$CHART/values_local_images.yaml" + --skip-schema-validation + --timeout 15m + --set-string secrets.ingest-ncbi.data.api-key= +) + +if [[ "$ENABLE_PREPROCESSING" -eq 1 ]]; then + helm_args+=(--set disablePreprocessing=false) +else + helm_args+=(--set disablePreprocessing=true) +fi + +if [[ "$ENABLE_INGEST" -eq 1 ]]; then + helm_args+=(--set disableIngest=false) +else + helm_args+=(--set disableIngest=true) +fi + +log "Deploying Helm release '$RELEASE'" +run helm "${helm_args[@]}" + +log "Waiting for config loader" +kubectl wait --for=condition=complete job/loculus-config-loader --timeout=15m + +log "Waiting for deployments" +kubectl wait --for=condition=available deployment --all --timeout=15m + +if [[ "$START_DOCS" -eq 1 ]]; then + if curl -fsS "http://127.0.0.1:${DOCS_PORT}/" >/dev/null 2>&1; then + log "Docs already running at http://127.0.0.1:${DOCS_PORT}/" + else + log "Starting docs at http://127.0.0.1:${DOCS_PORT}/" + mkdir -p logs + ( + cd docs + nohup npm run dev -- --host 127.0.0.1 --port "$DOCS_PORT" > ../logs/docs-preview.log 2>&1 & + echo $! > ../logs/docs-preview.pid + ) + for _ in {1..60}; do + if curl -fsS "http://127.0.0.1:${DOCS_PORT}/" >/dev/null 2>&1; then + break + fi + sleep 1 + done + curl -fsS "http://127.0.0.1:${DOCS_PORT}/" >/dev/null + fi +fi + +if [[ "$WAIT_INGEST" -eq 1 && "$ENABLE_INGEST" -eq 1 ]]; then + log "Waiting for ingest runner jobs" + kubectl wait --for=condition=complete job -l loculus-ingest-runner=true --timeout=45m +fi + +log "Running compact verification" +check_args=() +if [[ "$START_DOCS" -ne 1 ]]; then + check_args+=(--skip-docs) +fi +"$ROOT/check_preview.sh" "${check_args[@]}" + +cat <ebola-sudan-coinfection-001 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGCTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-002 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACGAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-003 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGGTGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-004 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGCAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-005 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGCCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-006 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTATAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-007 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGTCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-008 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTTAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-009 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAACATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-010 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGCCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-011 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCGGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-012 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCGCGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-013 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCGACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-014 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTTC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTGGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA +>ebola-sudan-coinfection-015 +CGGACACACAAAAAGAAAGAAAAGTTTTTTATACTTTTTGTGTGCGAATAACTATGAGGAAGATTAATCATTTTCCTCAA +ACTCAAACTAATATTAACATTGAGATTGATCTCATCATTTACCAATTGGAGACAATTTAACTAGTCAATCCCCCATTTGG +GGGCATTCCTAAAGTGTTGCAAAGGTATGTGGGTCGTATTGCTTTGCCTTTTCCTAACCTGGCTCCTCCTACAATTCTAA +CCTGCTTGATAAGTGTGATTACCTGAGTAATAGACTAATTTCGTCCTGGTAATTAGCATTTTCTAGTAAAACCAATACTA +TCTCAAGTCCTAAGAGAAGGTGAGAAGAGGGTCCCGAGGTATCCCTCCAGTCCACAAAATCTAGCTAATTTTAGCTGAGT +GGACTGATTACTCTCATCACACGCTAACTACTAAGGGTTTACCTGAGAGCCTACAACATGGATAAACGGGTGAGAGGTTC +ATGGGCCCTGGGAGGACAATCTGAAGTTGATCTTGACTACCACAAAATATTAACAGCCGGGCTTTCGGTCCAACAAGGGA +TTGTGCGACAAAGAGTCATCCCGGTATATGTTGTGAGTGATCTTGAGGGTATTTGTCAACATATCATTCAGGCCTTTGAA +GCAGGCGTAGATTTCCAAGATAATGCTGACAGCTTCCTTTTACTTTTATGTTTACATCATGCTTACCAAGGAGATCATAG +GCTCTTCCTCAAAAGTGATGCAGTTCAATACTTAGAGGGCCATGGTTTCAGGTTTGAGGTCCGAGAAAAGGAGAATGTGC +ACCGTCTGGATGAATTGTTGCCCAATGTCACCGGTGGAAAAAATCTTAGGAGAACATTGGCTGCAATGCCTGAAGAGGAG +ACAACAGAAGCTAATGCTGGTCAGTTTTTATCCTTTGCCAGTTTGTTTCTACCCAAACTTGTCGTTGGGGAGAAAGCGTG +TCTGGAAAAAGTACAAAGGCAGATTCAGGTCCATGCAGAACAAGGGCTCATTCAATATCCAACTTCCTGGCAATCAGTTG +GACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCTACTAATACATCAGGGGATGCACATG +GTCGCAGGCCATGATGCGAATGACACAGTAATATCTAATTCTGTTGCCCAAGCAAGGTTCTCTGGTCTTCTGATTGTAAA +GACTGTTCTGGACCACATCCTACAAAAAACAGATCTTGGAGTACGACTTCATCCACTGGCCAGGACAGCAAAAGTCAAGA +ATGAGGTCAGTTCATTCAAGGCAGCTCTTGGCTCACTTGCCAAGCATGGAGAATATGCTCCATTTGCACGTCTCCTCAAT +CTTTCTGGAGTCAACAACTTGGAACATGGGCTTTATCCACAACTCTCAGCCATTGCTTTGGGTGTTGCAACTGCCCACGG +GAGCACGCTGGCTGGTGTTAATGTAGGGGAGCAATATCAGCAACTGCGTGAGGCTGCTACTGAAGCTGAAAAGCAACTCC +AACAATATGCTGAAACACGTGAGTTGGACAACCTTAGGCTTGATGAACAGGAAAAGAAGATTCTCATGAGCTTCCACCAG +AAGAAGAATGAGATCAGCTTCCAGCAAACTAACGCAATGGTAACCCTGAGGAAAGAGCGGCTGGCCAAACTGACCGAAGC +CATCACGACTGCATCAAAGATCAAGGTTGGAGATCGTTATCCTGATGACAATGATATTCCATTTCCCGGGCCGATCTATG +ATGAAACCCACCCCAACCCTTCTGATGATAATCCTGATGATTCACGTGATACAACTATCCCAGGTGGTGTTGTTGACCCG +TATGATGATGAGAGTAATAATTATCCTGACTACGAGGATTCGGCTGAAGGCACCACAGGAGATCTTGATCTCTTCAATTT +GGACGACGACGATGACGACAGCCAACCAGGACCACCAGACAGGGGGCAGAGCAAGGAAAGAGCGGCTCGGACACATGGCC +TCCAAGATCCGACCTTGGACGGAGCGAAAAAGGTGCCGGAGTTGACCCCAGGTTCCCACCAACCAGGCAACCTCCACATC +ACCAAGCCGGGTTCAAACACCAACCAACCACAAGGCAATATGTCATCTACTCTCCAGAGTATGACCCCTATACAGGAAGA +ATCAGAGCCCGATGATCAGAAAGATGATGATGACGAGAGTCTCACATCCCTTGACTCTGAAGGTGACGAAGATGTTGAGA +GCGTATCAGGGGAGAACAACCCAACTGTAGCTCCACCAGCACCAGTCTACAAAGATACTGGAGTAGACACTAATCAGCAA +AATGGACCAAGCAATGCTGTAGATGGTCAAGGTTCTGAAAGTGAAGCTCTCCCAATCAACCCCGAAAAGGGATCTGCACT +GGAAGAAACATATTATCATCTCCTAAAAACACAGGGTCCATTTGAGGCAATCAATTATTATCACCTAATGAGTGATGAGC +CCATTGCTTTTAGCACTGAAAGTGGCAAGGAATATATCTTCCCAGATTCTCTTGAAGAAGCCTACCCGCCTTGGTTGAGT +GAGAAGGAGGCCTTAGAGAAAGAAAATCGTTATCTGGTCATTGATGGCCAGCAATTCCTCTGGCCAGTAATGAGCCTACA +GGACAAGTTCCTTGCTGTTCTTCAACATGACTGAGGACCCATGATTAGTAGATTTTGTTTATTCTGAGCTTGATTATAAT +TGTTTTGATAATTCAAGTATGAGCAACCAACCCGAAATATAAACCCTATTTTAGTTATGAGGAAATTAAATAAATAATCT +GTAAGTTGTAGGACTATGAAGAGCTGCTTGTGTCAATTTATCACGGGCTAATACCCATACCGCAAGAATAATTATTTAGT +AATTTTGATCAGCTTATGATATGTACCAATAGGAAAACATTATAGCATTAAAACATAAAGTATCCTTCGATGAGCTTAGG +AGGATAATATCCTGATGAATTCTATAGAACTTAGGATTAAGAAAAAATTCATGATGAAGATTAAAACCTTCATCATCCTT +TAAAAAGAGAGCTATTCTTTATCTGAATGTCCTTATTAATGTCTAAGAGCTATTATTTTGTACCCTCTTAGCCTAGACAC +TGCCCAGCATATAAGCCATGCAGCAGGATAGGACTTATAGACATCATGGACCCGAAGTGTCTGGCTGGTTTTCTGAGCAA +TTAATGACCGGCAAAATACCGCTAACAGAGGTGTTTGTTGATGTTGAAAACAAACCAAGTCCTGCCCCGATAACCATTAT +TAGTAAGAATCCCAAGACAACACGTAAAAGTGATAAGCAAGTCCAAACAGATGATGCCAGTAGCTTATTGACAGAAGAAG +TCAAGGCTGCCATAAATTCGGTGATATCAGCTGTGCGTCGGCAAACCAATGCTATTGAATCACTAGAAGGTCGAGTAACA +ACTCTTGAGGCCAGCTTAAAACCCGTTCAAGACATGGCAAAGACCATATCATCCCTGAATCGCAGCTGTGCCGAAATGGT +TGCAAAATACGACCTACTGGTGATGACCACTGGGCGAGCAACTGCCACTGCTGCAGCAACAGAAGCATATTGGAATGAAC +ATGGACAAGCACCTCCAGGCCCATCATTGTACGAGGATGATGCTATTAAGGCTAAATTGAAAGATCCGAACGGGAAGGTT +CCAGAAAGTGTCAAACAGGCCTACATAAATCTAGATAGCACAAGTGCCCTCAATGAGGAAAATTTCGGGCGACCTTACAT +TTCAGCAAAAGATCTCAAGGAAATCATCTATGACCATCTCCCAGGATTTGGGACAGCTTTTCATCAGTTGGTGCAGGTTA +TCTGCAAAATTGGTAAGGATAATAATATCCTAGACATAATTCATGCAGAATTCCAAGCAAGCTTGGCTGAGGGAGACTCC +CCCCAGTGTGCATTAATCCAGATAACAAAACGGATCCCTGCTTTCCAAGATGCCTCTCCTCCAATTGTGCATATCAAGTC +TCGAGGAGATATACCCAAAGCCTGTCAGAAAAGCCTCCGGCCGGTCCCACCGTCACCAAAGATCGATAGAGGTTGGGTCT +GTATTTTTCAATTCCAAGACGGGAAGGCCCTTGGGCTAAAAATATGATACAGAAGCAAGGTAAGCTCATTTTGCGATGGC +CAAATGATACTTATGACTGTTTAAAATCAAGTTAGACTAATAGTCTATTGTGTCATAAGCTTATAAGTCAGTTTTAAATT +TCCCCTCTATCCTAATCAATTGATAATGCTGTCAATAGGGAAATTCCCCTGTATTGTAATAAGACCTCATTAACATATTT +CCCCTGCTTAGTACTATGCAGAAACCCCCGAGCAAATTAAAATTGATGAAGATTAAGAAAAAGAGGGATTTTCTCAGGAA +AAATCTTTTTTCTTACCTTCATCTCATTTAAACAAATTTAGGACTCAGGAAAAATGAGAAGGGTCACTGTGCCGACTGCA +CCACCTGCCTATGCTGACATTGGCTATCCTATGAGCATGCTTCCCATCAAGTCAAGCAGGGCTGTGAGTGGAATTCAACA +GAAACAAGAGGTCCTTCCTGGAATGGATACACCATCAAATTCTATGAGACCTGTTGCTGATGATAACATTGATCATACAA +GTCATACCCCGAACGGAGTGGCCTCAGCATTCATCTTGGAGGCAACTGTCAATGTGATCTCGGGGCCCAAAGTCCTCATG +AAACAAATCCCTATTTGGTTGCCACTCGGAATTGCTGACCAAAAAACGTACAGTTTTGACTCAACAACAGCAGCAATTAT +GCTCGCATCTTATACGATCACCCATTTTGGAAAGGCCAACAACCCCCTCGTTAGAGTGAATCGACTTGGTCAGGGAATAC +CGGATCACCCACTCAGATTGCTCAGGATGGGGAACCAGGCTTTCCTTCAAGAGTTTGTGCTACCACCAGTTCAACTGCCG +CAATATTTCACTTTTGATCTGACTGCACTCAAACTAGTGACACAGCCTCTCCCTGCTGCAACATGGACAGATGAGACTCC +GAGCAACCTTTCAGGAGCCCTTCGTCCCGGGCTTTCATTTCACCCAAAGCTGAGACCCGTTCTACTTCCAGGCAAGACGG +GAAAGAAAGGGCATGTTTCTGATCTGACTGCCCCAGACAAAATTCAGACAATTGTGAACCTGATGCAAGATTTCAAAATC +GTGCCAATTGATCCAGCTAAGAGTATCATTGGGATCGAGGTTCCAGAATTGCTGGTCCACAAGCTCACTGGGAAGAAAAT +GAGTCAGAAGAATGGACAGCCTATAATTCCTGTCTTACTCCCAAAATACATTGGGCTAGATCCAATCTCACCTGGAGACC +TGACTATGGTCATAACACCAGATTATGATGATTGTCATTCACCTGCCAGTTGCTCTTATCTCAGTGAAAAGTGATTCTCA +CAAAGTGAGAGAAACACCTCCAGTAAAGAAATCAAATCTTATCTATAGCAACTCAATCGACTTTTAGGAAGCTAGCAGTC +CATATACTATGGGACAACTCAACCCTCTTGTTAAAATGTACTAATCGGGTCAAGGAACTCTCACTGATCAAGCCTGAATC +CAAGATAGAACCAGCCCAAAGGGCCTCCCCAGAGTCTCTTACAAGCTTAGCCAATCAATTAACATGCATAAGCGATCCAT +ACTTCGCCCAATCAGTGTCCGATGTTCACCCCTTCAAGCCTCCTTCCTAGCAAATTGACCTAGCTGTACCAAGAGATTCC +CTCAGCCTCCTTCTCAAATAACCTGATCCTCGAGGGTTACACCTTCACCACTCTATGCTCATTTCACCCAAACATAAAAT +GAAATGTCTTAACATGATTGCACCATTAAGAAAAACAAATCTGATGAAGATTAAGCCTGATGAAGGCCCAACCTTCATCT +TTTTACCATAATCTTGTTCTCAGTACCATTTGATAAGGGTACACTTGCCAATACGCCCCCATCCTAAGGGTCTCGCAATG +GGGGGTCTTAGCCTACTCCAATTGCCCAGGGACAAATTTCGGAAAAGCTCTTTCTTTGTTTGGGTCATCATCTTATTCCA +AAAGGCCTTTTCCATGCCTTTGGGTGTTGTGACTAACAGCACTTTAGAAGTAACAGAGATTGACCAGCTAGTCTGCAAGG +ATCATCTTGCATCTACTGACCAGCTGAAATCAGTTGGTCTCAACCTCGAGGGGAGCGGAGTATCTACTGATATCCCATCT +GCAACAAAGCGTTGGGGCTTCAGATCTGGTGTTCCTCCCAAGGTGGTCAGCTATGAAGCGGGAGAATGGGCTGAAAATTG +CTACAATCTTGAAATAAAGAAGCCGGACGGGAGCGAATGCTTACCCCCACCGCCAGATGGTGTCAGAGGCTTTCCAAGGT +GCCGCTATGTTCACAAAGCCCAAGGAACCGGGCCCTGCCCAGGTGACTACGCCTTTCACAAGGATGGAGCTTTCTTCCTC +TATGACAGGCTGGCTTCAACTGTAATTTACAGAGGAGTCAATTTTGCTGAGGGGGTAATTGCATTCTTGATATTGGCTAA +ACCAAAAGAAACGTTCCTTCAGTCACCCCCCATTCGAGAGGCAGTAAACTACACTGAAAATACATCAAGTTATTATGCCA +CATCCTACTTGGAGTATGAAATCGAAAATTTTGGTGCTCAACACTCCACGACCCTTTTCAAAATTGACAATAATACTTTT +GTTCGTCTGGACAGGCCCCACACGCCTCAGTTCCTTTTCCAGCTGAATGATACCATTCACCTTCACCAACAGTTGAGTAA +TACAACTGGGAGACTAATTTGGACACTAGATGCTAATATCAATGCTGATATTGGTGAATGGGCTTTTTGGGAAAATAAAA +AAATCTCTCCGAACAACTACGTGGAGAAGAGCTGTCTTTCGAAGCTTTATCGCTCAACGAGACAGAAGACGATGATGCGG +CATCGTCGAGAATTACAAAGGGAAGAATCTCCGACCGGGCCACCAGGAAGTATTCGGACCTGGTTCCAAAGAATTCCCCT +GGGATGGTTCCATTGCACATACCAGAAGGGGAAACAACATTGCCGTCTCAGAATTCGACAGAAGGTCGAAGAGTAGGTGT +GAACACTCAGGAGACCATTACAGAGACAGCTGCAACAATTATAGGCACTAACGGCAACCATATGCAGATCTCCACCATCG +GGATAAGACCGAGCTCCAGCCAAATCCCGAGTTCCTCACCGACCACGGCACCAAGCCCTGAGGCTCAGACCCCCACAACC +CACACATCAGGTCCATCAGTGATGGCCACCGAGGAACCAACAACACCACCGGGAAGCTCCCCCGGCCCAACAACAGAAGC +ACCCACTCTCACCACCCCAGAAAATATAACAACAGCGGTTAAAACTGTCCTGCCACAGGAGTCCACAAGCAACGGTCTAA +TAACTTCAACAGTAACAGGGATTCTTGGGAGTCTTGGGCTTCGAAAACGCAGCAGAAGACAAACTAACACCAAAGCCACG +GGTAAGTGCAATCCCAACTTACACTACTGGACTGCACAAGAACAACATAATGCTGCTGGGATTGCCTGGATCCCGTACTT +TGGACCGGGTGCGGAAGGCATATACACTGAAGGCCTGATGCATAACCAAAATGCCTTAGTCTGTGGACTTAGGCAACTTG +CAAATGAAACAACTCAAGCTCTGCAGCTTTTCTTAAGAGCCACAACGGAGCTGCGGACATATACCATACTCAATAGGAAG +GCCATAGATTTCCTTCTGCGACGATGGGGCGGGACATGCAGGATCCTGGGACCAGATTGTTGCATTGAGCCACATGATTG +GACAAAAAACATCACTGATAAAATCAACCAAATCATCCATGATTTCATCGACAACCCCTTACCTAATCAGGATAATGATG +ATAATTGGTGGACGGGCTGGAGACAGTGGATCCCTGCAGGAATAGGCATTACTGGAATTATTATTGCAATTATTGCTCTT +CTTTGCGTTTGCAAGCTGCTTTGCTGAATATCAATTTGAATCATCAATTTAAGCTTGATACATTTCTAGCATTTTAAATT +ATAAACCGATACTGATACTTGAAAATCAGGCTAATGCCAAGTTCTGTGCAAAACTTGAAAGTAGGTTTACAAAAATCCTT +TGGACTGGAATGCTTTGATACTCTTTCTCAATACTATATAAGTTCCTTCCCAAGAATAATATTGATGAAGATTAAGAAAA +AGTGACATTGTGCCCACTTTTGTAATCTTCATCCACCTACACATTCATATTCAGGAATCTTTGAATTAACCCTCACACTT +GCTTAGGAAAGAGCCTATCCTCTACAAGAATCCCGAGGCGGCAATTCAGTTAATTTCATATCAAGATAACATCCATTTCC +AAGACCACAGATAACTATATTATTAATCTTTACCACAAATATGGAGAGGGGTCGTGAGCGCGGGAGATCAAGGAATTCAC +GTGCCGACCAGCAAAATTCAACAGGTCCTCAATTTAGGACAAGATCCATTTCCCGGGATAAGACAACAACAGACTACCGT +AGTAGTCGAAGTACTTCGCAAGTTAGAGTCCCTACGGTTTTCCATAAGAAAGGTACTGGGACCCTTACTGTCCCTCCAGC +ACCTAAGGATGTTTGTCCTACTCTCAGAAAAGGATTTCTATGTGATAGTAATTTCTGTAAAAAGGACCATCAACTTGAAA +GCCTAACCGACCGGGAGCTCCTACTTCTTATAGCACGGAAGACCTGTGGATCAACTGATTCATCGCTTAATATAGCTGCT +CCTAAAGACCTAAGACTAGCAAATCCTACGGCTGATGACTTCAAGCAAGACGGCAGTCCAAAATTAACCCTAAAATTACT +AGTCGAGACTGCTGAGTTTTGGGCCAATCAGAATATTAATGAAGTAGATGATGCAAAACTCCGTGCTCTCTTGACGTTGA +GTGCTGTCTTAGTGCGGAAATTCTCTAAGTCACAGCTTAGTCAATTATGTGAGAGTCATCTTAGGAGGGAAAACTTAGGA +CAAGACCAAGCTGAATCAGTTCTCGAGGTTTATCAACGTTTACATAGTGACAAAGGAGGTGCTTTTGAGGCAGCACTATG +GCAACAGTGGGATAGACAATCATTAACTATGTTTATATCTGCTTTCCTCCATGTAGCATTGCAACTTTCCTGTGAGAGCT +CCACTGTAGTGATATCAGGCCTACGCTTACTTGCCCCCCCAAGCGTTAATGAAGGGCTCCCTCCTGCACCAGGGGAATAT +ACTTGGTCAGAAGATAGTACAACTTAGCCTGTAGGGAGGACAAGTAAAACAAGATGCCCTTATCCTCTATAGATGGTATT +TTTAGAGAGGGGGACAGGATAGGAATAAAGATAATGACTAAAGCCAATATAAAGATACGAACACAAGTAGAAATTAAAAT +AGAAATCAAAACAATCTCCCCTTATTCAATATGAAATATAATAGTGAGTATTTGTTTCATGATGTCAATCATTTATTGTT +AAAAATAAACAAAGTCAGTAAGAGTGTTAGGATCGTTATATTGCAAGGATCCTCCCTAGAAGCGTTGAATCATCTCAAGT +AGCCTAGAACAAGAACAGCAGAGCATTAAATTGAAATAGATAATAAGGATATTGCTTGTTTTTAAGATAGTTTTAGGAAG +TTTAAAATTAAGAAAAAGAACCCATGGACACACTCTAGCATTGAGGATGGGGTTCCCTTGATGATAGTATAGTCTTAGGT +ATAGGGTAGTCCTACACGTACTATATTATACAGTCTAAACTTGTAAAATTAAACTACAAGAACATGATGAAAATTAATGA +GAAGGTTCCAAGATTGACTTCAATCCAAACACCTTGCTCTGCCAATTTTCATCTCCTTAAGATATATGATTTTGTTCCTG +CGAGATAAGGTTATCAAATAGGGTGTGTATCTCTTTTACATATTTGGGCTCCCACTAGGCTAGGGTTTATAGTTAAGGAA +GACTCATCACATTTTTTATTGAACTAGTCTACTCGCAGAATCCTACCGGGAATAGAAATTAGAACATTTGTGATACTTTG +ACTATAGGAAATAATTTTCAACACTACCTGAGATCAGGTTATTCTTCCAACTTATTCTGCAAGTAATTGTTTAGCATCAT +AACAACAACGTTATAATTTAAGAATCAAGTCTTGTAACAGAAATAAAGATAACAGAAAGAACCTTTATTATACGGGTCCA +TTAATTTTATAGGAGAAGCTCCTTTTACAAGCCTAAGATTCCATTAGAGATAACCAGAATGGCTAAAGCCACAGGCCGGT +ACAACTTGGTAACACCAAAACGGGAGCTAGAGCAAGGAGTTGTGTTTAGCGACCTATGCAACTTCCTAGTGACTCCAACT +GTGCAAGGATGGAAGGTTTACTGGGCTGGACTTGAGTTTGATGTCAACCAAAAGGGTATTACCCTGTTAAATCGTCTTAA +AGTGAATGATTTTGCTCCTGCATGGGCGATGACCCGGAACCTCTTCCCACACTTGTTCAAAAACCAACAGTCTGAAGTCC +AAACTCCCATTTGGGCCTTGAGGGTAATTCTTGCCGCCGGGATTCTTGACCAATTAATGGATCATTCCCTCATTGAGCCG +CTATCAGGGGCCCTGAACCTAATTGCTGATTGGTTACTAACAACATCTACTAATCACTTCAACATGAGAACTCAACGAGT +AAAGGACCAACTGAGCATGAGGATGTTATCTCTTATAAGGTCAAATATTATTAACTTTATAAATAAGCTCGAGACTCTTC +ATGTCGTTAATTACAAGGGACTTCTAAGCAGTGTTGAGATAGGAACACCAAGCTATGCAATCATCATTACCAGGACTAAT +ATGGGTTATCTTGTCGAGGTTCAGGAACCAGATAAATCTGCGATGGATATACGACACCCTGGTCCTGTCAAATTCTCCTT +ACTACATGAATCGACACTTAAACCTGTTGCCACTCCTAAACCATCAAGCATTACTTCATTGATCATGGAGTTCAACAGTT +CTTTGGCAATTTAATTGCCGTAATAAAAATTGTACGATAGGGCTAACATTGATTCCATAATCCATCGTAGGACAGAATCA +TTTTCCTGTATGATCTTAGTTTAATCTCTCTTTATACAATGATTAATAAGGAGCCTGTTTAAAATGTTACAAAAGTATAC +TGTTTGAACCCCTAGTATCCCTGTAAATATCCTCATTCAATTTTTTGCTTTTACATGTGTAGTCACCTGTATAGCATGAC +CCTAGTCATGCCTTTAATTAATACTTAATCTAACAGTTAATATAATGTATAACTTTCCATGTTCAAAGAGTAGTCAAAAC +AATGTGAGATCCAGTTTCACTCACAGCATCTATTCACTATTTACAGTATGATGAGCCCAAATTAACACGGTAGAGGTCTA +GATTTATTAATAGAACGAGGAAGATTAAGAAAAAGTCCATAATGCTGGGGAGGCAATCCTTGCCACCATAGGACTTTTTC +AATTCCTCTATTTTATGATGGCTACCCAACATACACAATATCCTGATGCAAGATTGTCTTCCCCAATTGTCTTAGACCAA +TGTGACCTAGTGACAAGAGCATGTGGACTTTACTCTGAGTATTCGCTGAACCCTAAACTAAAGACATGCCGTTTACCGAA +ACATATCTATAGATTAAAATATGACACTATTGTTTTACGATTTATTAGTGATGTCCCTGTAGCTACAATCCCAATAGACT +ACATTGCTCCGATGTTAATAAATGTTCTGGCAGATAGTAAAAATGTACCATTGGAACCTCCCTGCTTGAGTTTCTTGGAT +GAAATAGTCAATTATACCGTGCAGGATGCAGCCTTCCTTAATTATTACATGAATCAGATTAAAACACAGGAAGGAGTAAT +TACAGATCAATTAAAACAGAACATTCGTAGGGTCATTCACAAAAACAGATATCTATCTGCTCTATTCTTCTGGCATGATC +TTGCCATCCTCACCCGTCGAGGGAGAATGAACCGAGGAAATGTGCGCTCCACTTGGTTTGTAACGAATGAGGTTGTTGAC +ATTCTAGGATATGGTGATTATATCTTCTGGAAGATCCCTATTGCTCTATTACCAATGAACACAGCTAATGTTCCACATGC +ATCAACTGACTGGTACCAACCTAATATCTTCAAGGAGGCTATTCAAGGACACACACATATTATTTCAGTCTCTACAGCCG +AGGTCCTTATTATGTGTAAGGATCTTGTCACAAGTCGTTTTAATACCCTTCTGATTGCTGAGTTAGCCAGGTTGGAAGAT +CCAGTGTCTGCTGATTATCCACTAGTAGATAATATTCAATCTCTGTATAACGCAGGAGACTACCTGTTGTCCATATTGGG +ATCAGAGGGGTACAAAATAATCAAATATCTCGAACCTCTGTGTTTGGCTAAGATTCAACTATGTTCCCAATATACAGAAC +GAAAAGGGCGGTTTTTAACCCAGATGCATCTTGCAGTTATTCAGACATTGCGTGAACTCCTCCTTAATAGAGGGTTGAAA +AAATCACAATTGTCTAAAATCCGCGAGTTTCACCAACTGTTGCTCAGACTCCGATCTACACCACAACAATTATGTGAATT +ATTTTCAATCCAAAAACACTGGGGCCACCCAGTTCTGCATAGTGAAAAGGCCATCCAAAAGGTTAAAAATCATGCAACAG +TTCTAAAGGCATTGCGGCCGATTATCATCTTTGAAACGTATTGTGTATTCAAGTATAGTGTTGCAAAACATTTCTTTGAT +AGTCAAGGCACTTGGTACAGTGTGATATCAGACCGATGTTTAACGCCGGGATTGAATTCCTACATTAGGCGAAATCAATT +CCCTCCACTTCCAATGATCAAAGATCTTTTATGGGAATTTTACCATTTGGATCATCCTCCATTATTCTCCACGAAGATCA +TTAGTGACCTCAGCATTTTCATTAAAGACCGCGCAACAGCAGTTGAACAAACCTGTTGGGATGCAGTTTTTGAGCCTAAC +GTTTTGGGCTACAGTCCACCTTATCGATTCAATACCAAACGTGTACCTGAACAATTCCTGGAGCAAGAGGATTTTTCTAT +TGAGAGTGTCTTACAATACGCCCAAGAACTTAGGTACTTATTGCCCCAGAATCGAAATTTTTCTTTTTCATTGAAGGAAA +AAGAATTAAATGTTGGTAGGACATTTGGAAAATTGCCTTATTTAACCAGGAATGTCCAAACCCTCTGCGAAGCATTACTT +GCAGATGGTTTGGCTAAAGCCTTTCCAAGCAATATGATGGTTGTCACAGAGAGGGAACAAAAGGAGAGCCTCCTTCACCA +AGCATCCTGGCACCATACAAGTGATGATTTCGGAGAGCATGCCACAGTTCGTGGAAGTAGTTTTGTCACAGACCTGGAAA +AATACAATCTGGCCTTCAGGTATGAATTCACAGCTCCCTTCATCAAATATTGCAACCAATGCTATGGGGTTCGCAATGTC +TTTGATTGGATGCACTTCCTAATTCCGCAATGTTACATGCATGTTAGTGATTATTATAACCCACCACATAATGTAACCTT +AGAGAATAGGGAATATCCCCCCGAAGGACCAAGTGCTTATAGAGGCCACCTTGGCGGTATTGAGGGGCTTCAACAAAAGT +TATGGACTAGTATCTCATGTGCTCAAATCTCATTGGTAGAGATCAAGACCGGGTTCAAATTGCGATCAGCAGTCATGGGG +GATAATCAATGTATTACAGTATTATCAGTCTTTCCACTAGAATCTAGTCCGAATGAGCAGGAGAGATGCGCAGAAGACAA +TGCAGCCAGAGTGGCTGCTAGCTTGGCCAAAGTCACAAGTGCCTGTGGGATATTCCTCAAGCCTGATGAGACTTTCGTAC +ACTCAGGCTTTATCTATTTTGGCAAAAAGCAATACTTGAACGGAATTCAATTACCTCAATCACTCAAGACAGCAGCTAGG +ATGGCCCCTCTCTCAGATGCAATTTTTGATGACTTGCAAGGTACACTTGCCAGTATAGGAACTGCCTTTGAGCGATCAAT +CTCCGAAACTAGACATATTTTACCATGCCGTGTTGCAGCTGCCTTTCATACATATTTCTCTGTTCGGATCTTACAACATC +ATCACCTTGGTTTCCATAAGGGTTCAGACCTTGGACAATTGGCAATCAATAAACCTCTTGATTTCGGGACCATTGCACTA +TCCTTAGCAGTTCCTCAGGTATTGGGTGGATTATCCTTCCTAAATCCAGAAAAGTGCCTTTATCGCAACTTGGGTGATCC +TGTAACTTCAGGCCTATTTCAGTTGAAGCATTATCTGTCAATGGTGGGTATGAGTGATATCTTTCATGCACTTATTGCAA +AAAGCCCAGGGAATTGTAGCGCAATTGACTTTGTTCTAAACCCAGGCGGGTTAAATGTCCCTGGATCACAGGATTTAACA +TCTTTCCTTCGTCAGATTGTCAGAAGGAGTATCACACTTTCGGCAAGGAACAAGTTAATCAACACGTTATTTCACGCTTC +TGCAGATCTTGAAGACGAATTAGTATGTAAATGGTTACTTTCTTCAACGCCCGTGATGAGCCGTTTTGCAGCCGATATTT +TCTCACGAACACCAAGCGGGAAAAGATTACAAATCTTGGGATACCTCGAGGGAACCAGAACTTTATTAGCATCCAAAATG +ATAAGCAATAATGCAGAGACACCAATCTTGGAGAGGCTCAGAAAAATAACACTTCAAAGATGGAATCTATGGTTTAGTTA +CCTAGACCATTGTGACCCAGCTTTAATGGAAGCAATTCAACCAATTAAGTGTACTGTTGATATTGCTCAAATTCTTAGAG +AATACTCCTGGGCTCATATCCTTGATGGTAGACAGTTAATAGGGGCAACACTGCCATGTATACCTGAGCAGTTCCAAACC +ACATGGTTAAAACCTTACGAGCAATGTGTGGAATGTTCATCCACAAACAATTCTAGTCCATATGTATCAGTTGCATTAAA +AAGGAACGTGGTTAGTGCTTGGCCTGATGCATCTAGATTGGGGTGGACGATTGGTGATGGGATTCCCTACATAGGCTCAA +GAACTGAGGACAAAATAGGTCAGCCCGCTATTAAGCCGAGGTGCCCATCAGCTGCATTAAGAGAAGCTATTGAATTGACC +TCTAGGTTGACCTGGGTCACTCAAGGTAGTGCAAACAGCGATCAGTTAATTCGCCCTTTTCTTGAGGCAAGAGTAAACTT +GAGTGTACAAGAGATTCTTCAAATGACCCCCTCACATTACTCCGGTAATATTGTGCATCGGTATAATGATCAGTATAGCC +CTCACTCCTTTATGGCTAACCGCATGAGTAACACAGCAACGCGCTTGATGGTATCTACCAACACACTAGGAGAGTTTTCC +GGAGGGGGTCAGGCTGCACGTGATAGCAACATTATATTTCAAAATGTGATTAACTTTGCAGTGGCCTTGTATGACATTAG +GTTTCGGAACACTTGTACATCTTCTATTCAATATCACAGGGCCCATATTCACCTGACGAATTGTTGTACGAGGGAAGTAC +CGGCCCAATACTTAACATACACAACCACGCTAAATCTAGATTTGAGTAAGTACCGTAATAATGAACTGATTTATGATTCA +GATCCACTAAGAGGAGGTCTCAACTGCAACTTATCGATTGACAGTCCTTTGATGAAGGGCCCACGTTTAAATATTATTGA +GGATGACTTAATACGGTTGCCACATTTATCCGGCTGGGAATTAGCAAAAACAGTCTTGCAATCAATAATCTCTGATAGTA +GCAATTCATCAACAGATCCCATTAGCAGCGGTGAAACAAGATCCTTCACAACCCACTTCTTAACGTATCCCAAAATAGGG +CTTCTATACAGTTTTGGAGCCCTCATAAGTTTTTATTTGGGTAATACTATTCTATGCACGAAAAAGATCGGACTCACAGA +ATTTCTATACTATCTCCAGAATCAGATCCACAACTTATCACATAGATCCCTTCGAATCTTCAAACCGACATTTAGACACT +CAAGTGTCATGTCCAGGTTGATGGATATAGACCCCAACTTCTCAATATATATTGGTGGGACTGCAGGTGACCGTGGATTA +TCGGACGCTGCAAGATTATTTCTCCGAATTGCAATTTCAACTTTCTTGAGCTTTGTTGAGGAGTGGGTTATCTTTAGGAA +GGCAAACATCCCACTATGGGTTATCTATCCTCTCGAAGGCCAACGCTCTGATCCTCCTGGCGAATTTTTGAACCGAGTAA +AATCTCTAATTGTTGGGACTGAAGATGATAAAAATAAAGGCTCTATACTTTCAAGATCTGGAGAGAAATGCTCTTCAAAT +CTAGTTTATAATTGCAAGAGTACAGCAAGCAATTTTTTCCATGCATCATTGGCTTACTGGAGAGGTCGACATAGACCTAA +GAAGACTATAGGTGCAACTAACGCGACAACAGCTCCACATATCATTTTGCCACTGGGAAATTCTGATCGACCGCCTGGCC +TAGACCTTAATAGGAACAATGATACTTTCATTCCTACCAGAATTAAACAGATAGTCCAAGGAGACTCTAGAAACGACAGA +ACGACCACCACGAGATTTCCACCCAAAAGTAGGTCCACTCCAACATCAGCAACCGAGCCTCCTACAAAAATGTATGAGGG +TTCGACAACCCACCAAGGGAAATTAACAGATACACATTTGGATGAGGATCACAATGCCAAAGAGTTCCCATCCAATCCGC +ATCGTTTAGTAGTACCATTCTTTAAATTAACAAAAGATGGGGAATACAGCATCGAACCTTCTCCTGAAGAAAGCCGCAGT +AATATAAAAGGGTTACTTCAACATTTAAGAACCATGGTTGATACTACCATATATTGTCGCTTCACTGGAATTGTTTCATC +AATGCATTATAAGTTAGATGAAGTACTATGGGAATATAATAAATTTGAATCAGCTGTAACCCTAGCAGAAGGGGAGGGTT +CAGGTGCCTTACTACTGATCCAAAAATACGGCGTTAAGAAGTTATTTTTGAATACACTTGCTACTGAACATAGTATTGAG +AGTGAAGTGATATCAGGTTACACCACTCCAAGGATGCTACTCCCAATTATGCCTAAAACACATCGTGGTGAGCTAGAGGT +CATATTAAATAACTCAGCTAGTCAAATAACTGATATTACACATCGAGATTGGTTTTCAAATCAAAAAAATAGGATTCCAA +ATGATGCTGATATTATTACCATGGATGCTGAAACTACAGAAAACTTAGATCGTTCCAGATTATATGAAGCAGTATATACG +ATTATTTGTAATCATATCAATCCTAAAACTTTGAAAGTGGTCATCTTAAAAGTCTTCCTCAGCGATTTGGATGGGATGTG +CTGGATTAACAATTATCTTGCTCCTATGTTTGGATCAGGATATTTAATCAAACCTATAACATCAAGTGCAAAGTCAAGTG +AGTGGTATTTATGCTTATCTAATCTACTTTCAACCTTGAGAACTACTCAGCATCAAACCCAGGCAAACTGTCTCCATGTC +GTACAATGTGCTCTTCAACAGCAAGTACAAAGAGGGTCATATTGGCTAAGTCATCTTACCAAATACACCACAAGTAGATT +GCACAATAGTTATATTGCATTTGGTTTTCCTTCATTAGAGAAGGTCCTATATCATAGGTATAACCTTGTTGATTCGAGAA +ATGGACCATTAGTTTCTATAACGAGACACCTTGCCCTCCTCCAAACTGAGATCCGGGAGTTGGTAACTGATTATAATCAG +CTGCGACAAAGTCGAACCCAGACTTATCATTTCATAAAAACATCCAAGGGACGGATAACTAAACTAGTGAATGATTATCT +AAGATTTGAGTTGGTTATACGGGCTCTTAAAAATAATTCTACATGGCACCATGAGTTATACTTGCTACCAGAACTTATAG +GTGTTTGCCATCGATTTAATCATACACGTAACTGTACATGCAGTGAAAGGTTCCTGGTTCAAACTTTATATCTACACCGA +ATGAGTGATGCTGAGATAAAACTTATGGACCGGCTCACCAGCCTAGTCAATATGTTTCCTGAAGGTTTCAGGTCTAGTTC +AGTCTAATTCTAACTGCACCAAAGGCTCTAAAAATATTTTAAATAACCAGGTGTATATCAAAGTCAATACAAGTGTAAAA +ACAATATGCAAGGGACCACATTTAGGATCAGTTTATTGACTCTTCCAATACACAGAGTTGGAAGCACCGATTCAAGGTTT +CTAAGACGCCCTATCGATTATGTTGATAATGTAAATAATAGCTTTTCCTGTCTATTATGACTTAAATAATCATATCTATA +ACGACCATCACAGCTAAGTCGTTGCCCTAGTTCATATATTAAATTAAAATTTAGAAGCTAGGTTGACTCTAATTACATAA +GTATTAAGAAAAAATTACTAAGACTAATACTCTCATGCCAAGAACTAGTAATGTGTTTCACATGACAGATTATTTCTAAC +ACTAAATTGCAATTTCAATTTTAAAGCTAAGTTTAACACCTATACAGCCAAAATATTTCATAGGGCCGATGGGAATAACA +TAAGAGGAACATGATCAATGAACCCTTTATTCCAACTAGGCAGTTGATTGATAATCTACAAATTCCATAAGATGTTCTTA +CGATATTCTTTTGTTTTTAATCTCAATGTCAATGATTTAATAAGTAATAATAAAAAAATCACATTAAAGATGCAGGAAGA +TCTTGACCTCGCCAGGAAAATTAAGCGCACACAAATAAATTAAAAAATCTGTATTTTCTCTTTTTTGTGTGTCCA diff --git a/test-data/west-nile-metadata.tsv b/test-data/west-nile-metadata.tsv new file mode 100644 index 0000000000..7fa30283fd --- /dev/null +++ b/test-data/west-nile-metadata.tsv @@ -0,0 +1,16 @@ +submissionId sampleCollectionDate geoLocCountry geoLocAdmin1 geoLocCity geoLocSite specimenCollectorSampleId authors authorAffiliations sequencedByOrganization sequencedByContactName sequencedByContactEmail purposeOfSampling purposeOfSequencing sampleType hostNameScientific hostTaxonId versionComment +west-nile-coinfection-001 2024-03-01 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-001 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-002 2024-03-02 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-002 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-003 2024-03-03 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-003 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-004 2024-03-04 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-004 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-005 2024-03-05 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-005 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-006 2024-03-06 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-006 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-007 2024-03-07 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-007 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-008 2024-03-08 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-008 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-009 2024-03-09 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-009 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-010 2024-03-10 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-010 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-011 2024-03-11 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-011 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-012 2024-03-12 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-012 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-013 2024-03-13 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-013 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-014 2024-03-14 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-014 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences +west-nile-coinfection-015 2024-03-15 Uganda Central Region Kampala Mulago National Referral Hospital coinfection-preview-015 Preview, Alice; Example, Bob; Loculus Preview Laboratory; Example Public Health Institute Loculus Preview Laboratory Alice Preview preview-data@loculus.org Diagnostic testing Co-infection view preview data clinical sample Homo sapiens 9606 Synthetic co-infection preview seed data derived from public example sequences diff --git a/test-data/west-nile-sequences.fasta b/test-data/west-nile-sequences.fasta new file mode 100644 index 0000000000..deb65f5c55 --- /dev/null +++ b/test-data/west-nile-sequences.fasta @@ -0,0 +1,1950 @@ +>west-nile-coinfection-001 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTTCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-002 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTCCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-003 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCGCAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-004 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGGCAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-005 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCAGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-006 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAGATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-007 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATTTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-008 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTATCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-009 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTGCTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-010 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTTAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-011 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGTTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-012 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACTACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-013 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATAACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-014 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGCG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGGAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG +>west-nile-coinfection-015 +ATGTCTAAGAAACCAGGAGGGCCCGGCAGGAGCCGGGCTGTCAATATGCTAAAACGCGGAATGCCCCGCGTGTTGTCCTT +GATTGGACTGAAGAGGGCTATGTTGAGCCTGATCGACGGCAAGGGGCCAATACGATTTGTGTTGGCTCTCTTGGCGTTCT +TCAGGTTCACAGCAATTGCTCCGACCCGAGCAGTGCTGGATCGATGGAGAGGTGTGAACAAACAAACAGCGATGAAACAC +CTTTTGAGTTTTAAGAAGGAACTAGGGACCTTGACCAGTGCTATCAATCGGCGGAGCTCAAAACAAAAGAGAAGAGGAGG +AAAGACCGGAATTGCAGTTATGATTGGCCTGATCGCCAGCGTAGGAGCAGTTACCCTCTCTAACTTCCAAGGGAAGGTGA +TGATGACGGTAAATGCTACTGACGTCACAGATGTCATCACGATTCCAACAGCTGCTGGAAAGAACCTATGCATTGTCAGA +GCAATGGATGTGGGATACATGTGCGATGATACTATCACTTATGAATGCCCAGTGCTGTCGGCTGGTAATGATCCAGAAGA +CATCGACTGTTGGTGCACAAAGTCAGCAGTCTACGTCAGGTATGGAAGATGCACCAAGACACGCCACTCAAGACGCAGTC +GGAGGTCACTGACAGTGCAGACACACGGAGAAAGCACTCTAGCGAACAAGAAGGGGGCTTGGATGGACAGCACCAAGGCC +ACAAGGTATTTGGTAAAAACAGAATCATGGATCTTGAGGAACCCTGGATATGCCTTGGTGGCAGCCGTCATTGGCTGGAT +GCTTGGGAGCAACACCATGCAGAGAGTTGTGTTTGTCGTGCTATTGCTTTTGGTGGCCCCAGCTTACAGCTTTAACTGCC +TTGGAATGAGCAACAGAGACTTCTTGGAAGGAGTGTCTGGAGCAACATGGGTGGATTTGGTTCTCGAAGGCGACAGCTGC +GTGACTATTATGTCTAAGGACAAGCCTACCATCGATGTGAAGATGATGAATATGGAGGCGGCCAACCTGGCAGAGGTCCG +CAGTTATTGCTATTTGGCTACCGTCAGCGATCTTTCCACCAAAGCTGCGTGCCCGACCATGGGAGAAGCTCACAATGACA +AACGTGCTGACCCAGCTTTTGTGTGCAGACAAGGAGTGGTGGACAGGGGCTGGGGCAACGGCTGCGGACTATTTGGCAAA +GGAAGCATTGACACATGCGCCAAATTTGCCTGCTCTACCAAGGCAATAGGAAGAACCATCTTGAAAGAGAATATCAAGTA +CGAAGTGGCCATTTTTGTCCATGGACCAACTACTGTGGAGTCGCACGGAAACTACTCCACACAGGCTGGAGCCACTCAGG +CAGGGAGATTCAGCATCACTCCTGCGGCGCCTTCATACACACTAAAGCTTGGAGAATATGGAGAGGTGACAGTGGACTGT +GAACCACGGTCAGGGATTGACACCAATGCATACTACGTGATGACTGTTGGAACAAAGACGTTCTTGGTCCATCGTGAGTG +GTTCATGGACCTCAACCTCCCTTGGAGCAGTGCTGAAAGTACTGTATGGAGGAACAGAGAGACGTTAATGGAGTTTGAGG +AACCACACGCTACGAAGCAGTCTGTGATAGCATTGGGCTCACAAGAGGGAGCTTTGCATCAAGCTTTGGCTGGAGCCATT +CCTGTGGAATTTTCAAGCAACACTGTCAAGTTGACGTCGGGTCATTTGAAGTGTAGAGTGAAGATGGAAAAATTGCAGTT +GAAGGGAACAACCTATGGCGTCTGTTCAAAGGCTTTCAAGTTTCTTGGGACTCCCGCAGACACAGGTCACGGCACTGTGG +TGTTGGAATTGCAGTACACTGGCACGGATGGACCTTGCAAAGTTCCTATCTCGTCAGTGGCCTCATTGAACGACCTAACG +CCAGTGGGCAGATTAGTCACTGTCAACCCTTTTGTTTCAGTGGCCACGGCCAACGCTAAGGTCCTGATTGAATTGGAACC +ACCCTTTGGAGACTCATACATAGTGGTGGGCAGAGGAGAACAACAGATCAATCACCATTGGCACAAGTCTGGAAGCAGCA +TTGGCAAAGCCTTTACAACCACCCTCAAAGGAGCGCAGAGACTAGCCGCTCTAGGAGACACAGCTTGGGACTTTGGATCA +GTTGGAGGGGTGTTCACCTCAGTTGGGAAGGCTGTCCATCAAGTGTTTGGAGGAGCATTCCGCTCATTGTTCGGAGGCAT +GTCCTGGATAACGCAAGGATTGCTGGGGGCTCTCCTGTTGTGGATGGGCATCAATGCTCGTGATAGGTCTATAGCTCTCA +CGTTTCTCGCAGTTGGAGGAGTTCTGCTCTTCCTCTCCGTGAACGTGCATGCTGACACTGGGTGCGCCATAGACATCAGC +CGGCAAGAGCTGAGATGTGGAAGTGGAGTGTTCATACACAATGATGTGGAGGCTTGGATGGACCGGTACAAGTATTACCC +TGAAACGCCACAAGGCCTAGCCAAGATCATTCAGAAAGCTCATAAGGAAGGAGTGTGCGGTCTACGATCAGTTTCCAGAC +TGGAGCATCAAATGTGGGAAGCAGTGAAGGACGAGCTGAACACTCTCTTGAAGGAGAATGGTGTGGACCTTAGTGTCGTG +GTTGAGAAACAGGAGGGAATGTACAAGTCAGCACCTAAACGCCTCACCGCCACCACGGAAAAATTGGAAATTGGCTGGAA +GGCCTGGGGAAAGAGTATTTTATTTGCACCAGAACTCGCCAACAACACCTTTGTGGTTGATGGTCCGGAGACCAAGGAAT +GCCCGACTCAGAATCGCGCTTGGAATAGCTTAGAAGTGGAGGATTTTGGATTTGGTCTCACCAGCACTCGGATGTTCCTG +AAGGTCAGAGAGAGCAACACAACTGAATGTGACTCGAAGATCATTGGAACGGCTGTCAAGAATAACTTGGCGATCCACAG +TGACCTGTCCTATTGGATTGAAAGCAGGCTCAATGATACGTGGAAGCTTGAAAGGGCAGTTCTGGGTGAAGTCAAATCAT +GTACGTGGCCTGAGACGCATACCTTGTGGGGCGATGGAGTCCTTGAGAGTGACTTGATAATACCAGTCACACTGGCGGGA +CCACGAAGCAATCACAATCGGAGACCTGGGTACAAGACACAAAACCAGGGCCCATGGGACGAAGGCCGGGTAGAGATTGA +CTTCGATTACTGCCCAGGAACTACGGTCACCCTGAGTGAGAGCTGCGGACACCGTGGACCTGCCACTCGCACCACCACAG +AGAGCGGAAAGTTGATAACAGATTGGTGCTGCAGGAGCTGCACCTTACCACCACTGCGCTACCAAACTGACAGCGGCTGT +TGGTATGGTATGGAGATCAGACCACAGAGACATGATGAAAAGACCCTCGTGCAGTCACAAGTGAATGCTTACAATGCTGA +TATGATTGACCCTTTTCAGTTGGGCCTTCTGGTCGTGTTCTTGGCCACCCAGGAGGTCCTTCGCAAGAGGTGGACAGCCA +AGATCAGCATGCCAGCTATACTGATTGCTCTGCTAGTCCTGGTGTTTGGGGGCATTACTTACACTGATGTGTTACGCTAT +GTCATCTTGGTGGGGGCAGCTTTCGCAGAATCTAATTCGGGAGGAGACGTGGTACACTTGGCGCTCATGGCGACCTTCAA +GATACAACCAGTGTTTATGGTGGCATCGTTTCTCAAAGCGAGATGGACCAACCAGGAGAACATTTTGTTGATGTTGGCGG +CTGTTTTCTTTCAAATGGCTTATCACGATGCCCGCCAAATTCTGCTCTGGGAGATCCCTGATGTGTTGAATTCACTGGCG +GTAGCTTGGATGATACTGAGAGCCATAACATTTACAACGACATCAAACGTGGTTGTTCCGCTGCTAGCCCTGCTAACACC +CGGGCTGAGATGCTTGAATTTGGATGTGTACAGGATACTGCTGTTGATGGTCGGAATAGGCAGCTTGATCAAGGAGAAGA +GGAGTGCAGCTGCAAAAAAGAAAGGAGCAAGTCTGCTATGCTTGGCTCTGGCCTCAACAGGACTTTTCAACCCCATGATC +CTTGCTGCTGGACTGATTGCATGTGATCCCAACCGTAAACGCGGATGGCCCGCAACTGAAGTGATGACAGCTGTCGGCCT +AATGTTTGCCATCGTCGGAGGGCTGGCAGAGCTCGACATTGACTCCATGGCCATTCCAATGACCATCGCGGGGCTCATGT +TTGCTGCTTTCGTGATTTCTGGGAAATCAACAGATATGTGGATTGAGAGAACGGCGGACATTTCCTGGGAAAGTGATGCA +GAAATTACAGGCTCGAGCGAAAGAGTTGATGTGCGGCTTGATGATGATGGAAACTTCCAGCTCATGAATGATCCAGGAGC +ACCTTGGAAGATATGGATGCTCAGAATGGTCTGTCTCGCGATTAGCGCGTACACCCCCTGGGCAATCTTGCCCTCAGTAG +TTGGATTTTGGATAACTCTCCAATACACAAAGAGAGGAGGCGTGTTGTGGGACACTCCCTCACCAAAGGAGTACAAAAAG +GGGGACACGACCACCGGCGTCTACAGGATCATGACTCGTGGGCTGCTCGGCAGTTATCAAGCAGGAGCGGGCGTGATGGT +TGAAGGTGTTTTCCACACCCTTTGGCATACAACAAAAGGAGCCGCTTTGATGAGCGGAGAGGGCCGTCTGGACCCATACT +GGGGCAGTGTCAAGGAGGATCGACTTTGTTACGGAGGACCCTGGAAATTGCAGCACAAGTGGAACGGGCAGGATGAGGTG +CAGATGATTGTGGTGGAACCTGGCAAGAACGTTAAGAACGTCCAGACGAAACCAGGGGTGTTCAAAACACCTGAAGGAGA +AATCGGGGCCGTGACTTTGGACTTCCCCACTGGAACATCAGGCTCACCAATAGTGGACAAAAACGGTGATGTGATTGGGC +TTTATGGCAATGGAGTCATAATGCCCAACGGTTCATACATAAGCGCGATAGTGCAGGGTGAAAGGATGGATGAACCAATC +CCAGCCGGATTCGAACCTGAGATGCTGAGGAAAAAACAGATCACTGTACTGGATCTCCATCCCGGCGCCGGTAAAACAAG +GAGGATTCTGCCACAGATCATCAAAGAGGCCATAAACAGAAGACTGAGAACAGCCGTGCTAGCACCGACCAGGGTTGTGG +CTGCTGAGATGGCTGAAGCACTGAGAGGACTGCCCATTCGGTACCAGACATCCGCAGTGCCTAGAGAACACAATGGAAAT +GAGATTGTTGATGTCATGTGTCATGCTACCCTCACCCACAGGTTGATGTCTCCTCACAGGGTGCCAAACTACAACCTGTT +TGTGATGGATGAGGCCCATTTCACCGACCCAGCTAGCATTGCAGCAAGAGGTTACATTTCCACAAAGGTCGAGCTAGGGG +AGGCGGCGGCAATATTCATGACAGCCACCCCACCAGGCACTTCAGATCCATTCCCAGAGTCCAATTCACCAATTTCCGAC +TTACAGACTGAGATCCCGGATCGAGCTTGGAACTCTGGATACGAATGGATCACAGAATACACCGGGAAGACGGTTTGGTT +TGTGCCTAGTGTCAAGATGGGGAATGAGATTGCCCTTTGCCTACAACGTGCTGGAAAGAAAGTAGTCCAATTGAACAGAA +AGTCGTACGAGACGGAGTACCCAAAATGTAAGAACGATGATTGGGACTTTGTTATCACAACAGACATATCTGAAATGGGG +GCTAACTTCAAGGCGAGCAGGGTGATTGACAGTCGGAAGAGTGTGAAACCAACCATCATAACAGAAGGAGAAGGGAGAGT +GATCCTGGGAGAACCATCTGCAGTGACAGCAGCTAGTGCCGCCCAGAGACGTGGACGTATCGGTAGAAATCCGTCGCAAG +TTGGTGATGAGTACTGTTATGGGGGGCACACGAATGAAGACGACTCTAACTTCGCCCATTGGACTGAGGCACGAATCATG +CTGGACAACATCAACATGCCAAACGGACTGATCGCTCAATTTTACCAACCAGAGCGTGAGAAGGTATACACCATGGATGG +GGAATACCGGCTCAGAGGAGAAGAGAGGAAAAACTTTCTGGAACTGTTGAGGACTGCAGATCTGCCAGTTTGGCTGGCTT +ACAAGGTTGCAGCGGCTGGAGTGTCATACCACGACCGGAGGTGGTGCTTTGATGGCCCTAGGACAAACACAATTTTAGAA +GACAACAACGAAGTGGAAGTCATCACGAAGCTTGGTGAAAGGAAGATCCTGAGGCCGCGCTGGATTGACGCCAGGGTGTA +CTCGGATCATCAGGCACTAAAGGCGTTCAAGGACTTCGCCTCGGGGAAACGTTCTCAGATAGGGCTCATTGAGGTTCTGG +GAAAGATGCCTGAGCACTTCATGGGGAAGACATGGGAGGCACTCGACACCATGTACGTTGTGGCCACTGCAGAGAAAGGA +GGAAGAGCTCACAGAATGGCCCTGGAGGAACTGCCAGATGCTCTTCAGACAATTGCCTTGATTGCCTTACTGAGTGTGAT +GACCATGGGGGTATTCTTCCTCCTCATGCAGAGGAAGGGCATTGGAAAGATAGGTTTGGGAGGCGCTGTCTTGGGAGTCG +CGACCTTTTTCTGTTGGATGGCTGAAGTTCCAGGAACGAAGATCGCCGGAATGTTGCTGCTCTCCCTTCTCTTGATGATT +GTGCTAATTCCTGAGCCAGAGAAGCAACGTTCGCAGACAGACAACCAGCTAGCCGTGTTCCTGATTTGTGTCATGACCCT +TGTGAGCGCAGTGGCAGCCAACGAGATGGGCTGGCTAGATAAGACCAAGAGTGACATAAGCAGTTTGTTTGGGCAAAGAA +TTGAGGTCAAGGAGAATTTTAGCATGGGAGAGTTTCTTCTGGACTTGAGGCCGGCAACAGCCTGGTCACTGTACGCTGTG +ACAACAGCGGTCCTCACTCCACTGCTAAAGCATTTGATCACGTCAGATTACATCAACACCTCATTGACCTCAATAAACGT +TCAGGCAAGTGCACTATTCACACTCGCGCGAGGCTTCCCCTTCGTCGATGTTGGAGTGTCGGCTCTCCTGCTAGCAGCCG +GATGCTGGGGACAAGTCACCCTCACCGTTACGGTAACAGCGGCAACACTCCTTTTTTGCCACTATGCCTACATGGTTCCC +GGTTGGCAAGCTGAGGCAATGCGCTCAGCCCAGCGGCGGACAGCGGCCGGAATCATGAAGAACGCTGTAGTGGATGGCAT +CGTGGCCACGGACGTCCCCGAATTAGAGCGCACCACACCCATTATGCAGAAGAAAGTTGGACAGATCATGCTGATCTTGG +TGTCTCTAGCTGCGGTAGTAGTGAACCCGTCTGTGAAGACAGTACGAGAAGCCGGAATTTTGATCACGGCCGCAGCGGTA +ACGCTTTGGGAGAATGGAGCAAGCTCTGTTTGGAACGCAACAACTGCCATCGGACTCTGCCACATCATGCGTGGGGGTTG +GTTGTCATGTTTATCCATAACATGGACACTCATAAAGAACATGGAAAAACCAGGACTAAAAAGAGGTGGGGCAAAAGGAC +GCACCTTGGGAGAGGTTTGGAAAGAAAGACTCAACCAGATGACAAAAGAAGAGTTCACTAGGTACCGCAAAGAGGCCATC +ATCGAAGTCGATCGCTCAGCGGCAAAACACGCCAGGAGAGAAGGCAATATCACTGGAGGGCATCCAGTCTCTAGGGGCAC +AGCAAAACTGAGATGGCTGGTCGAACGGAGGTTTCTCGAACCGGTCGGAAAAGTGATTGACCTTGGATGTGGAAGGGGCG +GCTGGTGTTACTATATGGCATCCCAAAAAAGAGTCCAAGAAGTCAGAGGGTACACAAAGGGCGGTCCCGGACATGAAGAG +CCTCAACTAGTGCAAAGTTATGGATGGAACATTGTCACCATGAAGAGTGGAGTGGATGTGTTCTACAGACCTTCTGAGTG +TTGTGACACCCTCCTTTGTGACATCGGAGAGTCCTCGTCAAGTGCTGAGGTTGAAGAGCACAGGACGATTCGGGTCCTTG +AAATGGTTGAGGACTGGCTGCACCGAGGGCCAAGGGAATTTTGCGTGAAGGTGCTCTGCCCCTACATGCCGAAAGTCATA +GAGAAGATGGAGCTGCTCCAACGCCGGTATGGGGGGGGACTGGTCAGAAACCCACTCTCACGGAATTCCACGCACGAGAT +GTATTGGGTGAGTCGAGCTTCAGGCAATGTGGTACATTCAGTGAATATGACCAGCCAGGTGCTCCTAGGAAGAATGGAAA +AAAGGACCTGGAAGGGACCCCAATACGAGGAAGATGTAAACTTGGGAAGTGGAACCAGGGCGGTGGGAAAACCCTTGCTC +AACTCAGACACCAGTAAAATCAAGAACAGGATTGAACGACTCAGGCGTGAGTACAGTTCGACGTGGCACCACGATGAGAA +CCACCCATATAGAACCTGGAACTATCACGGTAGTTATGATGTGAGGCCCACAGGTTCCGCCAGTTCGCTGGTCAATGGAG +TGGTCAGGCTCCTCTCAAAGCCATGGGACACCATCACGAATGTTACCACCATGGCCATGACTGACACTACTCCCTTCGGG +CAGCAGCGAGTGTTCAAAGAGAAGGTGGACACGAAAGCTCCAGAACCGCCAGAAGGAGTGAAGTACGTGCTCAACGAGAC +CACCAACTGGTTGTGGGCGTTTTTGGCCAGAGAAAAACGTCCCAGAATGTGCTCTCGAGAGGAATTCATAAGAAAGGTCA +ACAGCAATGCAGCTTTGGGTGCCATGTTTGAAGAGCAGAATCAATGGAGGAGCGCCAGAGAAGCAGTTGAAGATCCAAAA +TTTTGGGAGATGGTGGATGAGGAGCGCGAGGCACATCTGCGGGGGGAATGTCACACTTGCATTTACAACATGATGGGAAA +GAGAGAGAAAAAACCCGGAGAGTTCGGAAAGGCCAAGGGAAGCAGAGCCATTTGGTTCATGTGGCTCGGAGCTCGCTTTC +TGGAGTTCGAGGCCCTGGGTTTTCTCAATGAAGACCACTGGCTTGGAAGAAAGAACTCAGGAGGAGGTGTCGAGGGCTTG +GGCCTCCAAAAACTGGGTTATATCCTGCGTGAAGTTGGCACCCGGCCTGGGGGCAAGATCTATGCTGATGACACAGCTGG +CTGGGACACCCGCATCACGAGAGCTGACTTGGAAAATGAAGCTAAGGTGCTTGAGTTGCTTGATGGGGAACATCGGCGTC +TTGCCAGGGCCATCATTGAGCTCACCTATCGTCACAAAGTTGTGAAAGTGATGCGCCCGGCTGCTGATGGAAGAACCGTC +ATGGATGTTATCTCCAGAGAAGATCAGAGGGGGAGTGGACAAGTTGTCACCTACGCCCTAAACACTTTCACCAACCTGGC +CGTCCAGCTGGTGAGGATGATGGAAGGGGAAGGAGTGATTGGCCCAGATGATGTGGAGAAACTCACAAAAGGGAAAGGAC +CCAAAGTCAGGACCTGGCTGTTTGAAAATGGGGAAGAAAGACTCAGCCGCATGGCTGTCAGTGGAGATGACTGTGTGGTA +AAGCCCCTGGACGATCGCTTTGCCACCTCGCTCCACTTCCTCAATGCTATGTCAAAGGTTCGCAAAGACATCCAAGAGTG +GAAACCGTCAACTGGATGGTATGATTGGCAGCAGGTTCCATTTTGCTCAAACCACTTCACTGAATTGATCATGAAAGATG +GAAGAACACTGGTGGTTCCATGCCGAGGACAGGATGAATTGGTAGGCAGAGCTCGCATATCTCCAGGGGCCGGATGGAAC +GTCCGCGACACTGCTTGTCTGGCTAAGTCTTATGCCCAGATGTGGCTGCTTCTGTACTTTCACAGAAGAGACCTGCGGCT +CATGGCCAACGCCATTTGCTCCGCTGTCCCTGTGAATTGGGTCCCTACCGGAAGAACCACGTGGTCCATCCATGCAGGAG +GAGAGTGGATGACAACAGAGGACATGTTGGAGGTCTGGAACCGTGTTTGGATAGAGGAGAATGAATGGATGGAAGACAAA +ACCCCAGTGGAGAAATGGAGTGACGTCCCATATTCAGGAAAACGAGAGGACATCTGGTGTGGCAGCCTGATCGGCACAAG +AGCCCGAGCCACGTGGGCAGAAAACATCCAGGTGGCTATCAACCAAGTCAGAGCAATCATCGGAGATGAGAAGTATGTGG +ATTACATGAGTTCACTAAAGAGATATGAAGACACAACTTTGGTTGAGGACACAGTACTGTAG diff --git a/website/Dockerfile b/website/Dockerfile index aec19ae3ac..fc03f759e1 100644 --- a/website/Dockerfile +++ b/website/Dockerfile @@ -1,27 +1,36 @@ ARG NODE_VERSION=22 -FROM node:${NODE_VERSION}-alpine as base +FROM node:${NODE_VERSION}-alpine AS base -WORKDIR /app +# Layout: the website tree lives at /build/website, the canonical-config-tools +# tree at /build/config-tools (sibling). The latter is brought in via a named +# build context (`--build-context config-tools=./config-tools`) so the +# `../../../config-tools/...` relative imports in src/types/loculusConfig.ts +# resolve at build time. CI passes the build context via build-push-action's +# `build-contexts:` input; locally the build-local-images.sh script does +# the same. +WORKDIR /build/website COPY .env.docker .env COPY package.json package-lock.json ./ -FROM base as prod-deps +FROM base AS prod-deps RUN npm ci --omit=dev -FROM base as build-deps +FROM base AS build-deps RUN npm ci -FROM build-deps as build +FROM build-deps AS build COPY . . +COPY --from=config-tools . /build/config-tools/ +RUN ln -s /build/website/node_modules /build/node_modules RUN npm run build FROM base AS runtime -COPY --from=prod-deps /app/node_modules ./node_modules -COPY --from=build /app/dist ./dist +COPY --from=prod-deps /build/website/node_modules ./node_modules +COPY --from=build /build/website/dist ./dist EXPOSE 3000 VOLUME /config VOLUME /log -CMD node --max-old-space-size=1024 ./dist/server/entry.mjs +CMD ["node", "--max-old-space-size=1024", "./dist/server/entry.mjs"] diff --git a/website/eslint.config.js b/website/eslint.config.js index f77708eec4..4f78d25d19 100644 --- a/website/eslint.config.js +++ b/website/eslint.config.js @@ -154,7 +154,7 @@ const disableFromReact = { export default defineConfig( { - ignores: ['**/.astro/content.d.ts', '.flowbite-react/**'], + ignores: ['**/.astro/content.d.ts', '.flowbite-react/**', '**/node_modules/**'], files: ['**/*.ts', '**/*.tsx'], extends: [ eslint.configs.recommended, diff --git a/website/src/components/Edit/EditPage.spec.tsx b/website/src/components/Edit/EditPage.spec.tsx index cd9a357d81..93f35a7d47 100644 --- a/website/src/components/Edit/EditPage.spec.tsx +++ b/website/src/components/Edit/EditPage.spec.tsx @@ -14,11 +14,10 @@ import { } from '../../../vitest.setup.ts'; import { type SubmittedMetadataRecord } from '../../types/backend.ts'; import type { InputField } from '../../types/config.ts'; -import type { ClientConfig } from '../../types/runtimeConfig.ts'; const queryClient = new QueryClient(); -const dummyConfig = { backendUrl: 'dummy' } as ClientConfig; +const dummyConfig = { backendUrl: 'dummy' }; const groupedInputFields = new Map([ [ 'Header', diff --git a/website/src/components/Navigation/Navigation.astro b/website/src/components/Navigation/Navigation.astro index 6db0397ae5..b138efb321 100644 --- a/website/src/components/Navigation/Navigation.astro +++ b/website/src/components/Navigation/Navigation.astro @@ -22,19 +22,29 @@ const websiteConfig = getWebsiteConfig(); const siteName = websiteConfig.name; const isLoggedIn = Astro.locals.session?.isLoggedIn ?? false; +const userRoles = Astro.locals.session?.roles ?? []; const loginUrl = await getAuthUrl(Astro.url.toString()); -const topNavigationItems = navigationItems.top(isLoggedIn, loginUrl); +const topNavigationItems = navigationItems.top(isLoggedIn, loginUrl, userRoles); +const overviewNavigationItems = topNavigationItems.filter(({ id }) => id === 'overview'); +const remainingTopNavigationItems = topNavigationItems.filter(({ id }) => id !== 'overview'); const accessionPrefix = getWebsiteConfig().accessionPrefix; ---