eva-sub-cli 0.5.3__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/.github/workflows/tests.yml +1 -1
  2. {eva_sub_cli-0.5.3/eva_sub_cli.egg-info → eva_sub_cli-0.6.0}/PKG-INFO +1 -1
  3. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docker/Dockerfile +2 -4
  4. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docs/validation_overview.md +1 -1
  5. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/_version.py +3 -3
  6. eva_sub_cli-0.6.0/eva_sub_cli/call_home.py +118 -0
  7. eva_sub_cli-0.6.0/eva_sub_cli/etc/EVA_Submission_Example.xlsx +0 -0
  8. eva_sub_cli-0.6.0/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
  9. eva_sub_cli-0.6.0/eva_sub_cli/etc/call_home_payload_schema.json +73 -0
  10. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/cli.py +36 -2
  11. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/orchestrator.py +3 -1
  12. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/report.py +4 -4
  13. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/semantic_metadata.py +12 -1
  14. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/submission_ws.py +11 -7
  15. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/validators/docker_validator.py +3 -3
  16. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/validators/validator.py +3 -3
  17. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0/eva_sub_cli.egg-info}/PKG-INFO +1 -1
  18. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli.egg-info/SOURCES.txt +3 -0
  19. eva_sub_cli-0.6.0/tests/test_call_home.py +207 -0
  20. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_cli.py +51 -3
  21. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_semantic_metadata.py +25 -1
  22. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_submit.py +3 -3
  23. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_validator.py +74 -0
  24. eva_sub_cli-0.5.3/eva_sub_cli/etc/EVA_Submission_Example.xlsx +0 -0
  25. eva_sub_cli-0.5.3/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
  26. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/.github/workflows/publish.yml +0 -0
  27. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/.github/workflows/publish_docs.yml +0 -0
  28. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/.github/workflows/trigger-eva-sub-cli-integration-tests.yml +0 -0
  29. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/.gitignore +0 -0
  30. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/LICENSE +0 -0
  31. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/README.md +0 -0
  32. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/conda/build.sh +0 -0
  33. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/conda/meta.yaml +0 -0
  34. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docs/README.md +0 -0
  35. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docs/_config.yml +0 -0
  36. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docs/how_to_submit.md +0 -0
  37. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docs/input_file_overview.md +0 -0
  38. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docs/installation.md +0 -0
  39. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/docs/using_nextflow.md +0 -0
  40. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/__init__.py +0 -0
  41. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/auth.py +0 -0
  42. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/date_utils.py +0 -0
  43. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/etc/eva-biosamples.json +0 -0
  44. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/etc/eva_logo.png +0 -0
  45. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/etc/eva_schema.json +0 -0
  46. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/etc/spreadsheet2json_conf.yaml +0 -0
  47. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/etc/spreadsheet2json_conf_V2.yaml +0 -0
  48. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/exceptions/__init__.py +0 -0
  49. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/exceptions/invalid_file_type_exception.py +0 -0
  50. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/exceptions/metadata_template_version_exception.py +0 -0
  51. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/exceptions/submission_not_found_exception.py +0 -0
  52. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/exceptions/submission_status_exception.py +0 -0
  53. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/exceptions/submission_upload_exception.py +0 -0
  54. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/__init__.py +0 -0
  55. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/check_fasta_insdc.py +0 -0
  56. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/check_metadata_semantics.py +0 -0
  57. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/evidence_type_checker.py +0 -0
  58. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/samples_checker.py +0 -0
  59. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/trim_down.py +0 -0
  60. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/executables/xlsx2json.py +0 -0
  61. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/file_utils.py +0 -0
  62. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/html/fasta_check.html +0 -0
  63. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/html/file_validation.html +0 -0
  64. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/html/metadata_validation.html +0 -0
  65. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/html/project_details.html +0 -0
  66. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/html/report.html +0 -0
  67. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/html/sample_name_check.html +0 -0
  68. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/html/shallow_validation.html +0 -0
  69. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/text/fasta_check.txt +0 -0
  70. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/text/file_validation.txt +0 -0
  71. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/text/metadata_validation.txt +0 -0
  72. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/text/project_details.txt +0 -0
  73. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/text/report.txt +0 -0
  74. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/text/sample_name_check.txt +0 -0
  75. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/jinja_templates/text/shallow_validation.txt +0 -0
  76. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/metadata.py +0 -0
  77. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/nextflow/validation.nf +0 -0
  78. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/submit.py +0 -0
  79. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/utils.py +0 -0
  80. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/validators/__init__.py +0 -0
  81. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/validators/native_validator.py +0 -0
  82. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli/validators/validation_results_parsers.py +0 -0
  83. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli.egg-info/dependency_links.txt +0 -0
  84. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli.egg-info/entry_points.txt +0 -0
  85. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli.egg-info/requires.txt +0 -0
  86. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/eva_sub_cli.egg-info/top_level.txt +0 -0
  87. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/pyproject.toml +0 -0
  88. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/pytest.ini +0 -0
  89. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/requirements.txt +0 -0
  90. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/setup.cfg +0 -0
  91. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/setup.py +0 -0
  92. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/build_and_test_docker_locally.py +0 -0
  93. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/ignore_test_nextflow_validation.yml +0 -0
  94. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_Docker_Test.xlsx +0 -0
  95. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test.json +0 -0
  96. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test.xlsx +0 -0
  97. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_V2.xlsx +0 -0
  98. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_fails.xlsx +0 -0
  99. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
  100. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_with_asm_report.json +0 -0
  101. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_with_non_vcf_files.json +0 -0
  102. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_with_non_vcf_files.xlsx +0 -0
  103. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_with_project_accession.json +0 -0
  104. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/EVA_Submission_test_with_project_accession.xlsx +0 -0
  105. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/assembly_check/invalid.vcf.assembly_check.log +0 -0
  106. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/assembly_check/invalid.vcf.text_assembly_report.txt +0 -0
  107. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/assembly_reports/input_passed.txt +0 -0
  108. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/fasta_files/Saccharomyces_cerevisiae_I.fa +0 -0
  109. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/fasta_files/input_passed.fa +0 -0
  110. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/fasta_files/multi_seq.fa +0 -0
  111. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/metadata_with_filename.json +0 -0
  112. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/sample_checker/example1.vcf.gz +0 -0
  113. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/sample_checker/example2.vcf +0 -0
  114. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/sample_checker/example3.vcf +0 -0
  115. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/sample_checker/metadata.json +0 -0
  116. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_metadata_json_report.html +0 -0
  117. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_metadata_json_report.txt +0 -0
  118. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_metadata_xlsx_report.html +0 -0
  119. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_metadata_xlsx_report.txt +0 -0
  120. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_report_metadata_json_process_not_run.html +0 -0
  121. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_report_metadata_json_process_not_run.txt +0 -0
  122. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_shallow_metadata_xlsx_report.html +0 -0
  123. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/expected_shallow_metadata_xlsx_report.txt +0 -0
  124. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.assembly_check.log +0 -0
  125. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.text_assembly_report.1680102476700.txt +0 -0
  126. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.valid_assembly_report.1680102476700.txt +0 -0
  127. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/metadata.json +0 -0
  128. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/evidence_type_checker.yml +0 -0
  129. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/file_info.txt +0 -0
  130. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/input_passed.fa_check.yml +0 -0
  131. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/input_passed_trim_down.yml +0 -0
  132. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/metadata_conversion_errors.yml +0 -0
  133. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/metadata_semantic_check.yml +0 -0
  134. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/metadata_validation.txt +0 -0
  135. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/other_validations/sample_checker.yml +0 -0
  136. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.db +0 -0
  137. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.txt +0 -0
  138. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.vcf_format.log +0 -0
  139. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/vcf_files/example2.vcf.gz +0 -0
  140. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/vcf_files/example2.vcf.gz.tbi +0 -0
  141. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/resources/vcf_files/input_passed.vcf +0 -0
  142. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_auth.py +0 -0
  143. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_check_fasta_insdc.py +0 -0
  144. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_docker_validator.py +0 -0
  145. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_evidence_type_checker.py +0 -0
  146. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_file_utils.py +0 -0
  147. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_metadata.py +0 -0
  148. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_orchestrator.py +0 -0
  149. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_report.py +0 -0
  150. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_samples_checker.py +0 -0
  151. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_trim_down.py +0 -0
  152. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_utils.py +0 -0
  153. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_validaton_results_parsers.py +0 -0
  154. {eva_sub_cli-0.5.3 → eva_sub_cli-0.6.0}/tests/test_xlsx2json.py +0 -0
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  strategy:
13
13
  matrix:
14
- python-version: [3.9]
14
+ python-version: ["3.9", "3.11", "3.12"]
15
15
 
16
16
  steps:
17
17
  - uses: actions/checkout@v2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eva_sub_cli
3
- Version: 0.5.3
3
+ Version: 0.6.0
4
4
  Summary: EBI EVA - validation and submission command line tool
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Environment :: Console
@@ -1,4 +1,4 @@
1
- FROM python:3.10
1
+ FROM --platform=linux/amd64 python:3.10
2
2
 
3
3
  ENV vcf_validator_version=0.10.2
4
4
  ENV NXF_VER=23.10.0
@@ -6,9 +6,7 @@ ENV NXF_VER=23.10.0
6
6
  WORKDIR /opt
7
7
 
8
8
  # Install JAVA and Node
9
- RUN apt update && apt install -y default-jdk build-essential \
10
- && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
11
- && apt install -y nodejs
9
+ RUN apt update && apt install -y default-jdk nodejs npm
12
10
 
13
11
  # Install VCF validator
14
12
  RUN curl -LJo /usr/local/bin/vcf_validator https://github.com/EBIvariation/vcf-validator/releases/download/v${vcf_validator_version}/vcf_validator_linux \
@@ -25,7 +25,7 @@ Key points to note before validating your metadata spreadsheet with the eva-sub-
25
25
  - Any pre-registered projects and samples must be released and not kept in private status.
26
26
  - Sample names in the spreadsheet must match those in the VCF file.
27
27
  - Analysis aliases must match across the sheets (Analysis, Sample, and File sheets).
28
- - Use the Hold Date field in the Project sheet for data that needs to be kept under embargo.
28
+ - Use the Hold Date field in the Project sheet for data that needs to be kept under embargo. The Hold Date cannot be more than 2 years in the future.
29
29
 
30
30
  Common errors seen with metadata checks:
31
31
 
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.5.3'
32
- __version_tuple__ = version_tuple = (0, 5, 3)
31
+ __version__ = version = '0.6.0'
32
+ __version_tuple__ = version_tuple = (0, 6, 0)
33
33
 
34
- __commit_id__ = commit_id = 'gc81081e35'
34
+ __commit_id__ = commit_id = 'g9b7431423'
@@ -0,0 +1,118 @@
1
+ import os
2
+ import traceback
3
+ import uuid
4
+ from datetime import datetime, timezone
5
+
6
+ import requests
7
+ from ebi_eva_common_pyutils.config import WritableConfig
8
+ from ebi_eva_common_pyutils.logger import logging_config as log_cfg
9
+
10
+ from eva_sub_cli import __version__, SUB_CLI_CONFIG_FILE, ROOT_DIR
11
+ from eva_sub_cli.submission_ws import _submission_ws_base_url
12
+
13
+ logger = log_cfg.get_logger(__name__)
14
+
15
+ CALL_HOME_PATH = 'call-home/events'
16
+ DEPLOYMENT_ID_DIR = os.path.join(os.path.expanduser('~'), '.eva-sub-cli')
17
+ DEPLOYMENT_ID_FILE = os.path.join(DEPLOYMENT_ID_DIR, 'deployment_id')
18
+
19
+ EVENT_START = 'START'
20
+ EVENT_VALIDATION_COMPLETED = 'VALIDATION_COMPLETED'
21
+ EVENT_END = 'END'
22
+ EVENT_FAILURE = 'FAILURE'
23
+
24
+
25
+ def _get_call_home_url():
26
+ return os.path.join(_submission_ws_base_url(), CALL_HOME_PATH)
27
+
28
+
29
+ def _get_or_create_deployment_id():
30
+ try:
31
+ if os.path.isfile(DEPLOYMENT_ID_FILE):
32
+ with open(DEPLOYMENT_ID_FILE, 'r') as f:
33
+ deployment_id = f.read().strip()
34
+ if deployment_id:
35
+ return deployment_id
36
+ deployment_id = str(uuid.uuid4())
37
+ os.makedirs(DEPLOYMENT_ID_DIR, exist_ok=True)
38
+ with open(DEPLOYMENT_ID_FILE, 'w') as f:
39
+ f.write(deployment_id)
40
+ return deployment_id
41
+ except Exception:
42
+ logger.debug('Failed to read or create deployment ID file, using transient ID')
43
+ return str(uuid.uuid4())
44
+
45
+
46
+ def _get_or_create_run_id(submission_dir):
47
+ try:
48
+ config_path = os.path.join(submission_dir, SUB_CLI_CONFIG_FILE)
49
+ config = WritableConfig(config_path)
50
+ run_id = config.get('run_id')
51
+ if run_id:
52
+ return str(run_id)
53
+ run_id = str(uuid.uuid4())
54
+ config.set('run_id', value=run_id)
55
+ config.write()
56
+ return run_id
57
+ except Exception:
58
+ logger.debug('Failed to read or create run ID in config, using transient ID')
59
+ return str(uuid.uuid4())
60
+
61
+
62
+ class CallHomeClient:
63
+
64
+ def __init__(self, submission_dir, executor, tasks):
65
+ self.start_time = datetime.now(timezone.utc)
66
+ self.deployment_id = _get_or_create_deployment_id()
67
+ self.run_id = _get_or_create_run_id(submission_dir)
68
+ self.executor = executor
69
+ self.tasks = tasks
70
+
71
+ def _build_payload(self, event_type, **kwargs):
72
+ now = datetime.now(timezone.utc)
73
+ if event_type == EVENT_START:
74
+ runtime_seconds = 0
75
+ else:
76
+ elapsed = now - self.start_time
77
+ runtime_seconds = int(elapsed.total_seconds())
78
+ payload = {
79
+ 'deploymentId': self.deployment_id,
80
+ 'runId': self.run_id,
81
+ 'eventType': event_type,
82
+ 'cliVersion': __version__,
83
+ 'createdAt': now.isoformat(),
84
+ 'runtimeSeconds': runtime_seconds,
85
+ 'executor': self.executor,
86
+ 'tasks': self.tasks,
87
+ }
88
+ if kwargs:
89
+ payload.update(kwargs)
90
+ return payload
91
+
92
+ def _send_event(self, event_type, **kwargs):
93
+ try:
94
+ payload = self._build_payload(event_type, **kwargs)
95
+ requests.post(_get_call_home_url(), json=payload, timeout=5)
96
+ except Exception:
97
+ logger.debug('Failed to send %s call-home event', event_type)
98
+
99
+ def send_start(self):
100
+ self._send_event(EVENT_START)
101
+
102
+ def send_validation_completed(self, validation_result: dict):
103
+ self._send_event(EVENT_VALIDATION_COMPLETED, validation_result=validation_result)
104
+
105
+ def send_end(self):
106
+ self._send_event(EVENT_END)
107
+
108
+ def send_failure(self, exception=None):
109
+ kwargs = {}
110
+ if exception is not None:
111
+ kwargs['exceptionMessage'] = str(exception)
112
+ stack_trace = ''.join(
113
+ traceback.format_exception(type(exception), exception, exception.__traceback__)
114
+ )
115
+ # Remove the location of the file to preserve anonymity in case usernames are in the path
116
+ stack_trace = stack_trace.replace(ROOT_DIR, '')
117
+ kwargs['exceptionStacktrace'] = stack_trace
118
+ self._send_event(EVENT_FAILURE, **kwargs)
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Call-Home Event Payload",
4
+ "description": "Payload sent by the eva-sub-cli to the submission WS /call-home endpoint for usage telemetry.",
5
+ "type": "object",
6
+ "properties": {
7
+ "deploymentId": {
8
+ "type": "string",
9
+ "format": "uuid",
10
+ "description": "Persistent UUID identifying a unique CLI installation. Stored at ~/.eva-sub-cli/deployment_id."
11
+ },
12
+ "runId": {
13
+ "type": "string",
14
+ "format": "uuid",
15
+ "description": "Persistent UUID identifying a submission directory run. Stored in the .eva_sub_cli_config.yml file within the submission directory."
16
+ },
17
+ "eventType": {
18
+ "type": "string",
19
+ "enum": ["START", "VALIDATION_COMPLETED", "END", "FAILURE"],
20
+ "description": "Type of lifecycle event. START is sent when main() begins, VALIDATION_COMPLETED after validation finishes, END when the program completes, FAILURE on any error."
21
+ },
22
+ "cliVersion": {
23
+ "type": "string",
24
+ "description": "Version of the eva-sub-cli package (e.g. '0.5.3')."
25
+ },
26
+ "createdAt": {
27
+ "type": "string",
28
+ "format": "date-time",
29
+ "description": "ISO 8601 UTC timestamp of when the event was created."
30
+ },
31
+ "runtimeSeconds": {
32
+ "type": "integer",
33
+ "minimum": 0,
34
+ "description": "Elapsed wall-clock seconds since the CLI started. Always 0 for START events."
35
+ },
36
+ "executor": {
37
+ "type": "string",
38
+ "enum": ["native", "docker"],
39
+ "description": "Execution backend selected for validation."
40
+ },
41
+ "tasks": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "string",
45
+ "enum": ["validate", "submit"]
46
+ },
47
+ "description": "List of tasks requested by the user."
48
+ },
49
+ "validation_result": {
50
+ "type": "object",
51
+ "description": "Outcome of the validation step. Only present in VALIDATION_COMPLETED events."
52
+ },
53
+ "exceptionMessage": {
54
+ "type": "string",
55
+ "description": "The exception message string. Only present in FAILURE events when an exception was caught."
56
+ },
57
+ "exceptionStacktrace": {
58
+ "type": "string",
59
+ "description": "Full Python stacktrace of the exception. Only present in FAILURE events when an exception was caught."
60
+ }
61
+ },
62
+ "required": [
63
+ "deploymentId",
64
+ "runId",
65
+ "eventType",
66
+ "cliVersion",
67
+ "createdAt",
68
+ "runtimeSeconds",
69
+ "executor",
70
+ "tasks"
71
+ ],
72
+ "additionalProperties": true
73
+ }
@@ -19,10 +19,12 @@ from eva_sub_cli.exceptions.metadata_template_version_exception import MetadataT
19
19
  MetadataTemplateVersionNotFoundException
20
20
  from eva_sub_cli.exceptions.submission_status_exception import SubmissionStatusException
21
21
  from eva_sub_cli.exceptions.submission_not_found_exception import SubmissionNotFoundException
22
+ from eva_sub_cli.call_home import CallHomeClient
22
23
  from eva_sub_cli.file_utils import is_submission_dir_writable, DirLockError, DirLock
23
24
  from eva_sub_cli.orchestrator import VALIDATE, SUBMIT, DOCKER, NATIVE
24
25
  from eva_sub_cli.validators.validator import ALL_VALIDATION_TASKS
25
26
 
27
+ logger = logging_config.get_logger(__name__)
26
28
 
27
29
  def validate_command_line_arguments(args, argparser):
28
30
  fail = False
@@ -107,42 +109,74 @@ def main():
107
109
  else:
108
110
  logging_config.add_stdout_handler(logging.INFO)
109
111
 
112
+ # Initialize call-home
113
+ call_home = None
114
+ try:
115
+ call_home = CallHomeClient(
116
+ submission_dir=args.submission_dir,
117
+ executor=args.executor,
118
+ tasks=args.tasks
119
+ )
120
+ call_home.send_start()
121
+ except Exception:
122
+ pass
123
+
124
+ caught_exception = None
110
125
  try:
111
126
  # lock the submission directory
112
127
  with DirLock(os.path.join(args.submission_dir)) as lock:
113
128
  # Create the log file
114
129
  logging_config.add_file_handler(os.path.join(args.submission_dir, 'eva_submission.log'), logging.DEBUG)
115
130
  # Pass on all the arguments to the orchestrator
116
- orchestrator.orchestrate_process(**args.__dict__)
117
- except DirLockError:
131
+ orchestrator.orchestrate_process(call_home=call_home, **args.__dict__)
132
+ except DirLockError as dle:
118
133
  print(f'Could not acquire the lock file for {args.submission_dir} because another process is using this '
119
134
  f'directory or a previous process did not terminate correctly. '
120
135
  f'If the problem persists, remove the lock file manually.')
136
+ caught_exception = dle
121
137
  exit_status = 65
122
138
  except FileNotFoundError as fne:
123
139
  print(fne)
140
+ caught_exception = fne
124
141
  exit_status = 66
125
142
  except SubmissionNotFoundException as snfe:
126
143
  print(f'{snfe}. Please contact EVA Helpdesk')
144
+ caught_exception = snfe
127
145
  exit_status = 67
128
146
  except SubmissionStatusException as sse:
129
147
  print(f'{sse}. Please try again later. If the problem persists, please contact EVA Helpdesk')
148
+ caught_exception = sse
130
149
  exit_status = 68
131
150
  except MetadataTemplateVersionException as mte:
132
151
  print(mte)
152
+ caught_exception = mte
133
153
  exit_status = 69
134
154
  except MetadataTemplateVersionNotFoundException as mte:
135
155
  print(mte)
156
+ caught_exception = mte
136
157
  exit_status = 70
137
158
  except SubmissionUploadException as sue:
138
159
  print(sue)
160
+ caught_exception = sue
139
161
  exit_status = 71
140
162
  except HTTPError as http_err:
141
163
  print(http_err)
142
164
  if http_err.response is not None and http_err.response.text:
143
165
  print(http_err.response.text)
166
+ caught_exception = http_err
144
167
  exit_status = 72
145
168
  except Exception as ex:
146
169
  print(ex)
170
+ caught_exception = ex
147
171
  exit_status = 73
172
+
173
+ if caught_exception:
174
+ logger.exception(caught_exception)
175
+
176
+ if call_home is not None:
177
+ if exit_status == 0:
178
+ call_home.send_end()
179
+ else:
180
+ call_home.send_failure(caught_exception)
181
+
148
182
  return exit_status
@@ -299,7 +299,7 @@ def check_validation_required(tasks, sub_config, username=None, password=None):
299
299
 
300
300
  def orchestrate_process(submission_dir, metadata_json, metadata_xlsx,
301
301
  tasks, executor, validation_tasks=ALL_VALIDATION_TASKS, username=None, password=None,
302
- shallow_validation=False, nextflow_config=None, **kwargs):
302
+ shallow_validation=False, nextflow_config=None, call_home=None, **kwargs):
303
303
  # load config
304
304
  config_file_path = os.path.join(submission_dir, SUB_CLI_CONFIG_FILE)
305
305
  sub_config = WritableConfig(config_file_path, version=__version__)
@@ -341,6 +341,8 @@ def orchestrate_process(submission_dir, metadata_json, metadata_xlsx,
341
341
  nextflow_config=nextflow_config)
342
342
  with validator:
343
343
  validator.validate_and_report()
344
+ if call_home:
345
+ call_home.send_validation_completed(validator.results)
344
346
  if not metadata_json:
345
347
  metadata_json = os.path.join(validator.output_dir, 'metadata.json')
346
348
  sub_config.set('metadata_json', value=metadata_json)
@@ -17,16 +17,16 @@ def get_logo_data():
17
17
 
18
18
  def generate_report(validation_results, validation_date, submission_dir, vcf_fasta_analysis_mapping, project_title,
19
19
  consent_statement_required, subdir, template_file):
20
-
21
- from eva_sub_cli.validators.validator import RUN_STATUS_KEY, PASS
20
+ from eva_sub_cli.validators.validator import RUN_STATUS_KEY, TRIM_DOWN, PASS
22
21
 
23
22
  results_for_report = {k: v for k, v in validation_results.items() if k != 'ready_for_submission_to_eva'}
23
+ non_file_keys = {RUN_STATUS_KEY, PASS, TRIM_DOWN}
24
24
  vcf_files = sorted(set([file_name
25
25
  for check in results_for_report if check in ["vcf_check", "assembly_check"]
26
26
  for file_name in results_for_report[check]
27
- if file_name not in {RUN_STATUS_KEY, PASS}
27
+ if file_name not in non_file_keys
28
28
  ]))
29
- fasta_files = sorted([file_name for file_name in results_for_report['fasta_check'] if file_name not in {RUN_STATUS_KEY, PASS}])
29
+ fasta_files = sorted([file_name for file_name in results_for_report['fasta_check'] if file_name not in non_file_keys])
30
30
  template = Environment(
31
31
  loader=FileSystemLoader(os.path.join(current_dir, 'jinja_templates', subdir))
32
32
  ).get_template(template_file)
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from collections import Counter
3
3
  from copy import deepcopy
4
- from datetime import datetime
4
+ from datetime import datetime, timedelta
5
5
 
6
6
  import yaml
7
7
  from ebi_eva_common_pyutils.biosamples_communicators import NoAuthHALCommunicator
@@ -27,6 +27,7 @@ TAX_ID_KEY = 'taxId'
27
27
  SCI_NAME_KEYS = ['species', 'Species', 'organism', 'Organism']
28
28
  ANALYSIS_ALIAS_KEY = 'analysisAlias'
29
29
  ANALYSIS_RUNS_KEY = 'runAccessions'
30
+ HOLD_DATE_KEY = 'holdDate'
30
31
 
31
32
  # Samples created before this date are not required to have collection date or geographic location
32
33
  threshold_2023 = datetime(2023, 1, 1)
@@ -59,6 +60,16 @@ class SemanticMetadataChecker(AppLogger):
59
60
  self.check_all_analysis_run_accessions()
60
61
  self.check_analysis_alias_coherence()
61
62
  self.check_all_analysis_contain_samples()
63
+ self.check_hold_date()
64
+
65
+ def check_hold_date(self):
66
+ """Check that holdDate is no later than 2 years from today."""
67
+ project = self.metadata[PROJECT_KEY]
68
+ if HOLD_DATE_KEY in project:
69
+ hold_date = datetime.strptime(project[HOLD_DATE_KEY], '%Y-%m-%d')
70
+ max_date = datetime.now() + timedelta(days=365 * 2)
71
+ if hold_date > max_date:
72
+ self.add_error(f'/{PROJECT_KEY}/{HOLD_DATE_KEY}', 'holdDate is more than 2 years in the future')
62
73
 
63
74
  def check_all_project_accessions(self):
64
75
  """Check that ENA project accessions exist and are public."""
@@ -9,6 +9,16 @@ from eva_sub_cli import SUBMISSION_WS_VAR
9
9
  from eva_sub_cli.auth import get_auth
10
10
  from eva_sub_cli.exceptions.submission_upload_exception import SubmissionUploadException
11
11
 
12
+ DEFAULT_SUBMISSION_WS_URL = 'https://www.ebi.ac.uk/eva/webservices/submission-ws/v1/'
13
+
14
+
15
+ def _submission_ws_base_url():
16
+ """Retrieve the base URL for the submission web services.
17
+ In order of preference from the environment variable or the hardcoded value."""
18
+ if os.environ.get(SUBMISSION_WS_VAR):
19
+ return os.environ.get(SUBMISSION_WS_VAR)
20
+ else:
21
+ return DEFAULT_SUBMISSION_WS_URL
12
22
 
13
23
  class SubmissionWSClient(AppLogger):
14
24
  """
@@ -19,19 +29,13 @@ class SubmissionWSClient(AppLogger):
19
29
  self.auth = get_auth(username, password)
20
30
  self.base_url = self._submission_ws_url
21
31
 
22
- SUBMISSION_WS_URL = 'https://www.ebi.ac.uk/eva/webservices/submission-ws/v1/'
23
32
  SUBMISSION_INITIATE_PATH = 'submission/initiate'
24
33
  SUBMISSION_UPLOADED_PATH = 'submission/{submissionId}/uploaded'
25
34
  SUBMISSION_STATUS_PATH = 'submission/{submissionId}/status'
26
35
 
27
36
  @property
28
37
  def _submission_ws_url(self):
29
- """Retrieve the base URL for the submission web services.
30
- In order of preference from the environment variable or the hardcoded value."""
31
- if os.environ.get(SUBMISSION_WS_VAR):
32
- return os.environ.get(SUBMISSION_WS_VAR)
33
- else:
34
- return self.SUBMISSION_WS_URL
38
+ return _submission_ws_base_url()
35
39
 
36
40
  def _submission_initiate_url(self):
37
41
  return os.path.join(self.base_url, self.SUBMISSION_INITIATE_PATH)
@@ -9,7 +9,7 @@ from retry import retry
9
9
  from eva_sub_cli.validators.validator import Validator, ALL_VALIDATION_TASKS
10
10
 
11
11
  default_container_image = 'ebivariation/eva-sub-cli'
12
- default_container_tag = 'v0.0.10'
12
+ default_container_tag = 'v0.0.11'
13
13
  container_validation_dir = '/opt/vcf_validation'
14
14
  container_validation_output_dir = 'vcf_validation_output'
15
15
 
@@ -153,7 +153,7 @@ class DockerValidator(Validator):
153
153
  try:
154
154
  self._run_quiet_command(
155
155
  "Running container",
156
- f"{self.docker_path} run -it --rm -d --name {self.container_name} {self.container_image}:{self.container_tag}"
156
+ f"{self.docker_path} run --platform linux/amd64 -it --rm -d --name {self.container_name} {self.container_image}:{self.container_tag}"
157
157
  )
158
158
  # Wait to give some time to container to get up and running
159
159
  time.sleep(5)
@@ -176,7 +176,7 @@ class DockerValidator(Validator):
176
176
  self.debug(f"Pulling container ({self.container_image}) image")
177
177
  try:
178
178
  self._run_quiet_command("pull container image",
179
- f"{self.docker_path} pull {self.container_image}:{self.container_tag}")
179
+ f"{self.docker_path} pull --platform linux/amd64 {self.container_image}:{self.container_tag}")
180
180
  except subprocess.CalledProcessError as ex:
181
181
  self.error(ex)
182
182
  raise RuntimeError(f"Cannot pull container ({self.container_image}) image")
@@ -350,7 +350,7 @@ class Validator(AppLogger):
350
350
  # detect output files for vcf check
351
351
  self.results[VCF_CHECK] = {RUN_STATUS_KEY: True}
352
352
  if self.shallow_validation and self.results[SHALLOW_VALIDATION][TRIM_DOWN] is True:
353
- self.results[VCF_CHECK].update(TRIM_DOWN, True)
353
+ self.results[VCF_CHECK].update({TRIM_DOWN: True})
354
354
 
355
355
  for vcf_file in self.vcf_files:
356
356
  vcf_name = os.path.basename(vcf_file)
@@ -378,7 +378,7 @@ class Validator(AppLogger):
378
378
  # detect output files for assembly check
379
379
  self.results[ASSEMBLY_CHECK] = {RUN_STATUS_KEY: True}
380
380
  if self.shallow_validation and self.results[SHALLOW_VALIDATION][TRIM_DOWN] is True:
381
- self.results[ASSEMBLY_CHECK].update(TRIM_DOWN, True)
381
+ self.results[ASSEMBLY_CHECK].update({TRIM_DOWN: True})
382
382
 
383
383
  for vcf_file in self.vcf_files:
384
384
  vcf_name = os.path.basename(vcf_file)
@@ -408,7 +408,7 @@ class Validator(AppLogger):
408
408
  def _load_fasta_check_results(self):
409
409
  self.results[FASTA_CHECK] = {RUN_STATUS_KEY: True}
410
410
  if self.shallow_validation and self.results[SHALLOW_VALIDATION][TRIM_DOWN] is True:
411
- self.results[FASTA_CHECK].update(TRIM_DOWN, True)
411
+ self.results[FASTA_CHECK].update({TRIM_DOWN: True})
412
412
 
413
413
  for fasta_file in self.fasta_files:
414
414
  fasta_file_name = os.path.basename(fasta_file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eva_sub_cli
3
- Version: 0.5.3
3
+ Version: 0.6.0
4
4
  Summary: EBI EVA - validation and submission command line tool
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Environment :: Console
@@ -23,6 +23,7 @@ docs/validation_overview.md
23
23
  eva_sub_cli/__init__.py
24
24
  eva_sub_cli/_version.py
25
25
  eva_sub_cli/auth.py
26
+ eva_sub_cli/call_home.py
26
27
  eva_sub_cli/date_utils.py
27
28
  eva_sub_cli/file_utils.py
28
29
  eva_sub_cli/metadata.py
@@ -40,6 +41,7 @@ eva_sub_cli.egg-info/requires.txt
40
41
  eva_sub_cli.egg-info/top_level.txt
41
42
  eva_sub_cli/etc/EVA_Submission_Example.xlsx
42
43
  eva_sub_cli/etc/EVA_Submission_template.xlsx
44
+ eva_sub_cli/etc/call_home_payload_schema.json
43
45
  eva_sub_cli/etc/eva-biosamples.json
44
46
  eva_sub_cli/etc/eva_logo.png
45
47
  eva_sub_cli/etc/eva_schema.json
@@ -82,6 +84,7 @@ eva_sub_cli/validators/validator.py
82
84
  tests/build_and_test_docker_locally.py
83
85
  tests/ignore_test_nextflow_validation.yml
84
86
  tests/test_auth.py
87
+ tests/test_call_home.py
85
88
  tests/test_check_fasta_insdc.py
86
89
  tests/test_cli.py
87
90
  tests/test_docker_validator.py