nmdc-schema 11.12.0rc2__tar.gz → 11.13.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 (124) hide show
  1. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/PKG-INFO +2 -3
  2. nmdc_schema-11.13.0/nmdc_schema/__init__.py +1 -0
  3. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migration_recursion.py +2 -0
  4. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/cli/run_migrator.py +10 -14
  5. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/helpers.py +24 -0
  6. nmdc_schema-11.13.0/nmdc_schema/migrators/migrator_from_11_11_0_to_11_13_0.py +35 -0
  7. nmdc_schema-11.13.0/nmdc_schema/migrators/partials/migrator_from_11_11_0_to_11_13_0/__init__.py +30 -0
  8. nmdc_schema-11.13.0/nmdc_schema/migrators/partials/migrator_from_11_11_0_to_11_13_0/migrator_from_11_11_0_to_11_13_0_part_1.py +187 -0
  9. nmdc_schema-11.13.0/nmdc_schema/migrators/partials/migrator_from_11_11_0_to_11_13_0/migrator_from_11_11_0_to_11_13_0_part_2.py +113 -0
  10. nmdc_schema-11.13.0/nmdc_schema/migrators/partials/migrator_from_11_11_0_to_11_13_0/migrator_from_11_11_0_to_11_13_0_part_3.py +47 -0
  11. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc-pydantic.py +466 -651
  12. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc.py +296 -84
  13. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc.schema.json +251 -115
  14. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc_materialized_patterns.json +552 -1689
  15. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc_materialized_patterns.schema.json +261 -115
  16. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc_materialized_patterns.yaml +450 -1154
  17. nmdc_schema-11.13.0/nmdc_schema/nmdc_schema_validation_plugin.py +103 -0
  18. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/pyproject.toml +2 -4
  19. nmdc_schema-11.12.0rc2/nmdc_schema/migrators/cli/__init__.py +0 -0
  20. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/LICENSE +0 -0
  21. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/README.md +0 -0
  22. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/README.md +0 -0
  23. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/anyuri_strings_to_iris.py +0 -0
  24. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/dump_single_modality.py +0 -0
  25. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/get_json_schema.py +0 -0
  26. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/get_nmdc_view.py +0 -0
  27. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/gold-to-mixs.sssom.tsv +0 -0
  28. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/id_helpers.py +0 -0
  29. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/.docker/.env.example +0 -0
  30. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/.docker/docker-compose.yaml +0 -0
  31. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/.docker/mongoKeyFile +0 -0
  32. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/.docker/mongo_init/initialize_replica_set.sh +0 -0
  33. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/.docker/wait-for-it.sh +0 -0
  34. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/README.md +0 -0
  35. {nmdc_schema-11.12.0rc2/nmdc_schema → nmdc_schema-11.13.0/nmdc_schema/migrators}/__init__.py +0 -0
  36. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/adapters/adapter_base.py +0 -0
  37. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/adapters/dictionary_adapter.py +0 -0
  38. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/adapters/mongo_adapter.py +0 -0
  39. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/adapters/test_dictionary_adapter.py +0 -0
  40. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/adapters/test_mongo_adapter.py +0 -0
  41. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/assets/migrator_from_10_2_0_to_11_0_0_part_08/instrument_set.yaml +0 -0
  42. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/assets/migrator_from_11_7_to_11_8/data_category_map.yaml +0 -0
  43. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/assets/migrator_from_8_1_to_9_0/study_dois_changes.yaml +0 -0
  44. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/assets/migrator_from_9_1_to_9_2/websites_dois.yaml +0 -0
  45. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/cli/README.md +0 -0
  46. {nmdc_schema-11.12.0rc2/nmdc_schema/migrators → nmdc_schema-11.13.0/nmdc_schema/migrators/cli}/__init__.py +0 -0
  47. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/cli/create_migrator.py +0 -0
  48. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/cli/migrator.py.template +0 -0
  49. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migration_reporter.py +0 -0
  50. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_base.py +0 -0
  51. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_0_0_to_10_1_2.py +0 -0
  52. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_2_0_to_11_0_0.py +0 -0
  53. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_3_0_to_10_4_0.py +0 -0
  54. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_4_0_to_10_5_4.py +0 -0
  55. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_5_4_to_10_5_5.py +0 -0
  56. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_5_5_to_10_5_6.py +0 -0
  57. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_5_6_to_10_6_0.py +0 -0
  58. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_10_6_0_to_10_7_0.py +0 -0
  59. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_0_3_to_11_1_0.py +0 -0
  60. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_10_0_to_11_11_0.py +0 -0
  61. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_1_0_to_11_2_0.py +0 -0
  62. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_3_0_to_11_4_0.py +0 -0
  63. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_4_0_to_11_5_0.py +0 -0
  64. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_5_0_to_11_5_1.py +0 -0
  65. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_5_1_to_11_6_0.py +0 -0
  66. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_6_0_to_11_6_1.py +0 -0
  67. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_6_1_to_11_7_0.py +0 -0
  68. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_7_0_to_11_8_0.py +0 -0
  69. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_8_0_to_11_9_0.py +0 -0
  70. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_11_9_1_to_11_10_0.py +0 -0
  71. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_1_0_0_to_EXAMPLE.py +0 -0
  72. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_7_7_2_to_7_8_0.py +0 -0
  73. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_7_8_0_to_8_0_0.py +0 -0
  74. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_8_0_to_8_1.py +0 -0
  75. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_8_1_to_9_0.py +0 -0
  76. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_9_1_to_9_2.py +0 -0
  77. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/migrator_from_9_3_to_10_0.py +0 -0
  78. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/README.md +0 -0
  79. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/__init__.py +0 -0
  80. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_01.py +0 -0
  81. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_02.py +0 -0
  82. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_03.py +0 -0
  83. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_04.py +0 -0
  84. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_05.py +0 -0
  85. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_06.py +0 -0
  86. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_07.py +0 -0
  87. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_08.py +0 -0
  88. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_09.py +0 -0
  89. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_10.py +0 -0
  90. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_11.py +0 -0
  91. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_12.py +0 -0
  92. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_13.py +0 -0
  93. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_14.py +0 -0
  94. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_15.py +0 -0
  95. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_10_2_0_to_11_0_0/migrator_from_10_2_0_to_11_0_0_part_16.py +0 -0
  96. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_0_3_to_11_1_0/__init__.py +0 -0
  97. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_0_3_to_11_1_0/migrator_from_11_0_3_to_11_1_0_part_1.py +0 -0
  98. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_0_3_to_11_1_0/migrator_from_11_0_3_to_11_1_0_part_2.py +0 -0
  99. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_0_3_to_11_1_0/migrator_from_11_0_3_to_11_1_0_part_3.py +0 -0
  100. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_10_0_to_11_11_0/__init__.py +0 -0
  101. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_10_0_to_11_11_0/migrator_from_11_10_0_to_11_11_0_part_1.py +0 -0
  102. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_10_0_to_11_11_0/migrator_from_11_10_0_to_11_11_0_part_2.py +0 -0
  103. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_1_0_to_11_2_0/__init__.py +0 -0
  104. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_1_0_to_11_2_0/migrator_from_11_1_0_to_11_2_0_part_1.py +0 -0
  105. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_1_0_to_11_2_0/migrator_from_11_1_0_to_11_2_0_part_2.py +0 -0
  106. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_5_1_to_11_6_0/__init__.py +0 -0
  107. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_5_1_to_11_6_0/migrator_from_11_5_1_to_11_6_0_part_1.py +0 -0
  108. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_5_1_to_11_6_0/migrator_from_11_5_1_to_11_6_0_part_2.py +0 -0
  109. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_7_0_to_11_8_0/__init__.py +0 -0
  110. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_7_0_to_11_8_0/migrator_from_11_7_0_to_11_8_0_part_1.py +0 -0
  111. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_7_0_to_11_8_0/migrator_from_11_7_0_to_11_8_0_part_2.py +0 -0
  112. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_7_0_to_11_8_0/migrator_from_11_7_0_to_11_8_0_part_3.py +0 -0
  113. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_7_0_to_11_8_0/migrator_from_11_7_0_to_11_8_0_part_4.py +0 -0
  114. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_7_0_to_11_8_0/migrator_from_11_7_0_to_11_8_0_part_5.py +0 -0
  115. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_8_0_to_11_9_0/__init__.py +0 -0
  116. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_8_0_to_11_9_0/migrator_from_11_8_0_to_11_9_0_part_1.py +0 -0
  117. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_8_0_to_11_9_0/migrator_from_11_8_0_to_11_9_0_part_2.py +0 -0
  118. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_9_1_to_11_10_0/__init__.py +0 -0
  119. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_9_1_to_11_10_0/migrator_from_11_9_1_to_11_10_0_part_1.py +0 -0
  120. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_9_1_to_11_10_0/migrator_from_11_9_1_to_11_10_0_part_2.py +0 -0
  121. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/migrators/partials/migrator_from_11_9_1_to_11_10_0/migrator_from_11_9_1_to_11_10_0_part_3.py +0 -0
  122. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc_data.py +0 -0
  123. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/nmdc_version.py +0 -0
  124. {nmdc_schema-11.12.0rc2 → nmdc_schema-11.13.0}/nmdc_schema/validate_nmdc_json.py +0 -0
@@ -1,19 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nmdc_schema
3
- Version: 11.12.0rc2
3
+ Version: 11.13.0
4
4
  Summary: Schema resources for the National Microbiome Data Collaborative (NMDC)
5
5
  License: MIT
6
6
  License-File: LICENSE
7
7
  Keywords: NMDC,schema,metadata,microbiome
8
8
  Author: Bill Duncan
9
9
  Author-email: wdduncan@gmail.com
10
- Requires-Python: >=3.9,<4.0
10
+ Requires-Python: >=3.10,<4.0
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Intended Audience :: Science/Research
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.9
17
16
  Classifier: Programming Language :: Python :: 3.10
18
17
  Classifier: Programming Language :: Python :: 3.11
19
18
  Classifier: Programming Language :: Python :: 3.12
@@ -0,0 +1 @@
1
+ from nmdc_schema.nmdc_schema_validation_plugin import NmdcSchemaValidationPlugin as NmdcSchemaValidationPlugin
@@ -14,6 +14,8 @@ from nmdc_schema.migrators.adapters.dictionary_adapter import DictionaryAdapter
14
14
  logger = logging.getLogger(__name__)
15
15
  click_log.basic_config(logger)
16
16
 
17
+ # TODO: Is this script somewhat redundant with nmdc_schema/migrators/cli/run_migrator.py? Can they
18
+ # be consolidated?
17
19
 
18
20
  # todo: log before and after states of migration
19
21
 
@@ -17,6 +17,7 @@ Environment variables (loaded from .env file if present):
17
17
  MONGO_PASSWORD - MongoDB password
18
18
  MONGO_AUTH_DB - Authentication database name
19
19
  """
20
+ import importlib
20
21
 
21
22
  import click
22
23
  import sys
@@ -25,21 +26,15 @@ from dotenv import load_dotenv
25
26
  from pymongo import MongoClient
26
27
  from nmdc_schema.migrators.adapters.mongo_adapter import MongoAdapter
27
28
 
29
+ # TODO: Is this script somewhat redundant with nmdc_schema/migration_recursion.py? Can they be
30
+ # consolidated?
31
+
28
32
  # Load environment variables from .env file if it exists
29
33
  load_dotenv()
30
34
 
31
- # Import all migrators
32
- from nmdc_schema.migrators.partials.migrator_from_11_9_1_to_11_10_0 import migrator_from_11_9_1_to_11_10_0_part_1
33
-
34
- # Map migrator names to their classes
35
- MIGRATORS = {
36
- "migrator_from_11_9_1_to_11_10_0": migrator_from_11_9_1_to_11_10_0_part_1.Migrator,
37
- # Add other migrators here as needed
38
- }
39
-
40
35
 
41
36
  @click.command()
42
- @click.argument("migrator_name", required=True, type=click.Choice(list(MIGRATORS.keys())))
37
+ @click.argument("migrator", required=True)
43
38
  @click.argument("action", required=False, default="rollback", type=click.Choice(["commit", "rollback"]))
44
39
  @click.option("--host", default=lambda: os.getenv("MONGO_HOST", "localhost"), help="MongoDB host (default: from MONGO_HOST env var or localhost)")
45
40
  @click.option("--port", default=lambda: int(os.getenv("MONGO_PORT", "27022")), type=int, help="MongoDB port (default: from MONGO_PORT env var or 27022)")
@@ -47,10 +42,10 @@ MIGRATORS = {
47
42
  @click.option("--auth-db", default=lambda: os.getenv("MONGO_AUTH_DB"), help="Authentication database name (default: from MONGO_AUTH_DB env var)")
48
43
  @click.option("--username", default=lambda: os.getenv("MONGO_USERNAME"), help="MongoDB username (default: from MONGO_USERNAME env var)")
49
44
  @click.option("--password", default=lambda: os.getenv("MONGO_PASSWORD"), help="MongoDB password (default: from MONGO_PASSWORD env var)")
50
- def run_migrator(migrator_name, action, host, port, database, auth_db, username, password):
45
+ def run_migrator(migrator, action, host, port, database, auth_db, username, password):
51
46
  """Run a specific migrator against MongoDB using PyMongo.
52
47
 
53
- MIGRATOR_NAME should be one of the available migrators.
48
+ MIGRATOR should the fully qualified module name for module containing a Migrator class
54
49
  ACTION should be either 'commit' to save changes or 'rollback' to discard changes (default: rollback).
55
50
  """
56
51
 
@@ -70,12 +65,13 @@ def run_migrator(migrator_name, action, host, port, database, auth_db, username,
70
65
 
71
66
  commit_changes = action == "commit"
72
67
 
73
- click.echo(f"Running migrator: {migrator_name}")
68
+ click.echo(f"Running migrator: {migrator}")
74
69
  click.echo(f"Action: {action.upper()} (changes will {'be saved' if commit_changes else 'be discarded'})")
75
70
  click.echo(f"MongoDB connection: {host}:{port}/{database}")
76
71
 
77
72
  # Get the migrator class
78
- migrator_class = MIGRATORS[migrator_name]
73
+ migrator_module = importlib.import_module(migrator)
74
+ migrator_class = getattr(migrator_module, "Migrator")
79
75
 
80
76
  # Connect to MongoDB using PyMongo
81
77
  try:
@@ -113,3 +113,27 @@ def get_classes_with_slots_by_range(schema_view: SchemaView, range_constraint: s
113
113
 
114
114
  return classes_with_slots
115
115
 
116
+
117
+ # TODO: Check whether this function can be replaced by a similar function from `refscan`.
118
+ def get_database_collections_for_class(schema_view: SchemaView, class_name: str) -> List[str]:
119
+ """
120
+ Returns the list of database collection names associated with the specified class.
121
+ This function retrieves the collection names from the slots of the Database class
122
+ that have the specified class as their range.
123
+
124
+ Args:
125
+ schema_view: SchemaView instance
126
+ class_name: The name of the class to find collections for
127
+
128
+ Returns:
129
+ List[str]: List of collection names which permit storage of instances of the specified class
130
+ """
131
+ collection_names = set()
132
+
133
+ for database_slot_name in schema_view.class_slots(DATABASE_CLASS_NAME):
134
+ database_slot_def = schema_view.get_slot(database_slot_name, strict=True)
135
+ database_slot_range_descendants = schema_view.class_descendants(database_slot_def.range, reflexive=True)
136
+ if class_name in database_slot_range_descendants:
137
+ collection_names.add(database_slot_name)
138
+
139
+ return list(collection_names)
@@ -0,0 +1,35 @@
1
+ from nmdc_schema.migrators.migrator_base import MigratorBase
2
+ from nmdc_schema.migrators.partials.migrator_from_11_11_0_to_11_13_0 import (
3
+ get_migrator_classes,
4
+ )
5
+
6
+
7
+ class Migrator(MigratorBase):
8
+ r"""
9
+ Migrates a database between two schemas.
10
+ """
11
+
12
+ _from_version = "11.11.0"
13
+ _to_version = "11.13.0"
14
+
15
+ def upgrade(self, commit_changes: bool = False) -> None:
16
+ r"""
17
+ Migrates the database from conforming to the original schema, to conforming to the new schema.
18
+
19
+ This migrator uses partial migrators. It runs them in the order in which they are returned by
20
+ the `get_migrator_classes` function.
21
+
22
+ Args:
23
+ commit_changes: If True, commits the changes. If False (default), performs a dry run or rollback.
24
+ """
25
+
26
+ migrator_classes = get_migrator_classes()
27
+ num_migrators = len(migrator_classes)
28
+ for idx, migrator_class in enumerate(migrator_classes):
29
+ self.logger.info(f"Running migrator {idx + 1} of {num_migrators}")
30
+ self.logger.debug(
31
+ f"Migrating from {migrator_class.get_origin_version()} "
32
+ f"to {migrator_class.get_destination_version()}"
33
+ )
34
+ migrator = migrator_class(adapter=self.adapter, logger=self.logger)
35
+ migrator.upgrade(commit_changes=commit_changes)
@@ -0,0 +1,30 @@
1
+ from typing import List, Type
2
+
3
+ from nmdc_schema.migrators.migrator_base import MigratorBase
4
+ from nmdc_schema.migrators.partials.migrator_from_11_11_0_to_11_13_0 import (
5
+ migrator_from_11_11_0_to_11_13_0_part_1,
6
+ migrator_from_11_11_0_to_11_13_0_part_2,
7
+ migrator_from_11_11_0_to_11_13_0_part_3
8
+ )
9
+
10
+
11
+ def get_migrator_classes() -> List[Type[MigratorBase]]:
12
+ r"""
13
+ Returns a list of migrator classes in the order in which they (i.e. their `upgrade` methods)
14
+ were designed to be run.
15
+
16
+ >>> migrator_classes = get_migrator_classes()
17
+ >>> type(migrator_classes) is list and len(migrator_classes) > 0 # the function returns a list
18
+ True
19
+ >>> from inspect import isclass
20
+ >>> all(isclass(c) for c in migrator_classes) # each list item is a class
21
+ True
22
+ >>> all(callable(getattr(c, "upgrade")) for c in migrator_classes) # each class has an `upgrade` method
23
+ True
24
+ """
25
+
26
+ return [
27
+ migrator_from_11_11_0_to_11_13_0_part_1.Migrator,
28
+ migrator_from_11_11_0_to_11_13_0_part_2.Migrator,
29
+ migrator_from_11_11_0_to_11_13_0_part_3.Migrator
30
+ ]
@@ -0,0 +1,187 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ from nmdc_schema.migrators.migrator_base import MigratorBase
5
+ from nmdc_schema.migrators.adapters.mongo_adapter import MongoAdapter
6
+
7
+ project_root = Path(__file__).parent.parent.parent
8
+ sys.path.insert(0, str(project_root))
9
+
10
+ class Migrator(MigratorBase):
11
+ r'''
12
+ Migrates a database between two schemas.
13
+ '''
14
+
15
+ _from_version = '11.11.0'
16
+ _to_version = '11.13.0.part_1'
17
+
18
+ def upgrade(self, commit_changes: bool = False) -> None:
19
+ r'''
20
+ Migrates the database from conforming to the original schema, to conforming to the new schema.
21
+ '''
22
+
23
+ if isinstance(self.adapter, MongoAdapter):
24
+ try:
25
+ self.adapter.execute_in_transaction(
26
+ operations_callback=self.perform_all_migration_operations,
27
+ commit_changes=commit_changes
28
+ )
29
+
30
+ if commit_changes:
31
+ self.logger.info("Transaction committed (changes have been saved)")
32
+ else:
33
+ self.logger.info("Transaction rolled back (no changes were committed)")
34
+
35
+ except Exception as e:
36
+ self.logger.error(f"Migration failed: {e}")
37
+ raise
38
+ else:
39
+ raise NotImplementedError("This migrator currently only supports MongoAdapter.")
40
+
41
+
42
+ def perform_all_migration_operations(self, _, __) -> None:
43
+ self.logger.info("Moving problematic QuantityValue entries to misc_param")
44
+ self.adapter.process_each_document("biosample_set", [
45
+ self.migrate_misc_param_to_PropertyAssertion_range,
46
+ self.move_problematic_values_to_misc_param,
47
+ ])
48
+
49
+
50
+ def migrate_misc_param_to_PropertyAssertion_range(self, biosample: dict) -> dict:
51
+ r"""The misc_param slot on Biosample used to have range TextValue, but now has range
52
+ PropertyAssertion. This migrator changes misc_param values to conform to the new range.
53
+
54
+ >>> m = Migrator()
55
+
56
+ # Test: Biosample with no misc_param
57
+ >>> biosample_no_misc = {
58
+ ... "id": "test1",
59
+ ... "type": "nmdc:Biosample"
60
+ ... }
61
+ >>> m.migrate_misc_param_to_PropertyAssertion_range(biosample_no_misc)
62
+ {'id': 'test1', 'type': 'nmdc:Biosample'}
63
+
64
+ # Test: Biosample with misc_param as TextValue
65
+ >>> biosample_with_misc = {
66
+ ... "id": "test2",
67
+ ... "type": "nmdc:Biosample",
68
+ ... "misc_param": [{"type": "nmdc:TextValue", "has_raw_value": "biomass_yield;2.565699 Mg/ha"}]
69
+ ... }
70
+ >>> m.migrate_misc_param_to_PropertyAssertion_range(biosample_with_misc)
71
+ {'id': 'test2', 'type': 'nmdc:Biosample', 'misc_param': [{'type': 'nmdc:PropertyAssertion', 'has_raw_value': 'biomass_yield;2.565699 Mg/ha', 'has_attribute_label': 'biomass_yield', 'has_numeric_value': 2.565699, 'has_unit': 'Mg/ha'}]}
72
+ """
73
+ misc_param = biosample.get("misc_param")
74
+ if not misc_param:
75
+ return biosample
76
+
77
+ property_assertions = []
78
+ for item in misc_param:
79
+ if isinstance(item, dict) and item.get("type") == "nmdc:TextValue":
80
+ # Convert TextValue to PropertyAssertion
81
+ raw_value = item.get("has_raw_value")
82
+ if not raw_value:
83
+ continue
84
+ pa = {
85
+ "type": "nmdc:PropertyAssertion",
86
+ "has_raw_value": raw_value
87
+ }
88
+ if ";" in raw_value:
89
+ [label, _, value] = item.get("has_raw_value", "").partition(";")
90
+ pa["has_attribute_label"] = label.strip()
91
+ if " " in value:
92
+ num_str, _, unit = value.strip().partition(" ")
93
+ try:
94
+ pa["has_numeric_value"] = float(num_str)
95
+ except ValueError:
96
+ pass # If conversion fails, skip adding has_numeric_value
97
+ if unit:
98
+ pa["has_unit"] = unit.strip()
99
+ property_assertions.append(pa)
100
+ else:
101
+ raise ValueError(
102
+ f"Biosample {biosample.get('id')} has misc_param item that is not a TextValue: {item}"
103
+ )
104
+ biosample["misc_param"] = property_assertions
105
+ return biosample
106
+
107
+
108
+ def move_problematic_values_to_misc_param(self, biosample: dict) -> dict:
109
+ r"""Move QuantityValue entries with invalid units to misc_param as PropertyAssertion.
110
+
111
+ >>> m = Migrator()
112
+
113
+ # Test: Biosample with valid QuantityValue entries
114
+ >>> biosample_valid = {
115
+ ... "id": "test1",
116
+ ... "type": "nmdc:Biosample",
117
+ ... "abs_air_humidity": {
118
+ ... "type": "nmdc:QuantityValue",
119
+ ... "has_unit": "kg/kg",
120
+ ... "has_numeric_value": 75.0
121
+ ... },
122
+ ... "diss_oxygen": {
123
+ ... "type": "nmdc:QuantityValue",
124
+ ... "has_unit": "umol/L",
125
+ ... "has_numeric_value": 8.0
126
+ ... },
127
+ ... "solar_irradiance": {
128
+ ... "type": "nmdc:QuantityValue",
129
+ ... "has_unit": "kW/m2/d",
130
+ ... "has_numeric_value": 200.0
131
+ ... }
132
+ ... }
133
+ >>> m.move_problematic_values_to_misc_param(biosample_valid)
134
+ {'id': 'test1', 'type': 'nmdc:Biosample', 'abs_air_humidity': {'type': 'nmdc:QuantityValue', 'has_unit': 'kg/kg', 'has_numeric_value': 75.0}, 'diss_oxygen': {'type': 'nmdc:QuantityValue', 'has_unit': 'umol/L', 'has_numeric_value': 8.0}, 'solar_irradiance': {'type': 'nmdc:QuantityValue', 'has_unit': 'kW/m2/d', 'has_numeric_value': 200.0}}
135
+
136
+ # Test: Biosample with problematic QuantityValue entries
137
+ >>> biosample_problematic = {
138
+ ... "id": "test2",
139
+ ... "type": "nmdc:Biosample",
140
+ ... "abs_air_humidity": {
141
+ ... "type": "nmdc:QuantityValue",
142
+ ... "has_unit": "kPa",
143
+ ... "has_numeric_value": 75.0
144
+ ... },
145
+ ... "diss_oxygen": {
146
+ ... "type": "nmdc:QuantityValue",
147
+ ... "has_unit": "mL/L",
148
+ ... "has_numeric_value": 8.0
149
+ ... },
150
+ ... "solar_irradiance": {
151
+ ... "type": "nmdc:QuantityValue",
152
+ ... "has_unit": "W/m2",
153
+ ... "has_numeric_value": 200.0
154
+ ... }
155
+ ... }
156
+ >>> m.move_problematic_values_to_misc_param(biosample_problematic)
157
+ {'id': 'test2', 'type': 'nmdc:Biosample', 'misc_param': [{'type': 'nmdc:PropertyAssertion', 'has_attribute_label': 'abs_air_humidity', 'has_attribute_id': 'MIXS:0000122', 'has_numeric_value': 75.0, 'has_unit': 'kPa'}, {'type': 'nmdc:PropertyAssertion', 'has_attribute_label': 'diss_oxygen', 'has_attribute_id': 'MIXS:0000119', 'has_numeric_value': 8.0, 'has_unit': 'mL/L'}, {'type': 'nmdc:PropertyAssertion', 'has_attribute_label': 'solar_irradiance', 'has_attribute_id': 'MIXS:0000112', 'has_numeric_value': 200.0, 'has_unit': 'W/m2'}]}
158
+ """
159
+ problematic_units = (
160
+ ("abs_air_humidity", "MIXS:0000122", "kPa"),
161
+ ("diss_oxygen", "MIXS:0000119", "mL/L"),
162
+ ("solar_irradiance", "MIXS:0000112", "W/m2"),
163
+ )
164
+ for slot_name, slot_uri, bad_unit in problematic_units:
165
+ qv = biosample.get(slot_name)
166
+ if isinstance(qv, dict) and qv.get("type") == "nmdc:QuantityValue":
167
+ if qv.get("has_unit") == bad_unit:
168
+ # Move to misc_param as PropertyAssertion
169
+ pa = {
170
+ "type": "nmdc:PropertyAssertion",
171
+ "has_attribute_label": slot_name,
172
+ "has_attribute_id": slot_uri,
173
+ }
174
+ if "has_numeric_value" in qv:
175
+ pa["has_numeric_value"] = qv["has_numeric_value"]
176
+ if "has_unit" in qv:
177
+ pa["has_unit"] = qv["has_unit"]
178
+ if "has_raw_value" in qv:
179
+ pa["has_raw_value"] = qv["has_raw_value"]
180
+
181
+ # Ensure misc_param is a list and append the new PropertyAssertion
182
+ if "misc_param" not in biosample or not isinstance(biosample["misc_param"], list):
183
+ biosample["misc_param"] = []
184
+ biosample["misc_param"].append(pa)
185
+ # Remove the problematic QuantityValue from its original slot
186
+ del biosample[slot_name]
187
+ return biosample
@@ -0,0 +1,113 @@
1
+ from linkml.validator import Validator
2
+
3
+ from nmdc_schema.migrators.migrator_base import MigratorBase
4
+ import sys
5
+ from pathlib import Path
6
+ from nmdc_schema.migrators.helpers import create_schema_view, get_classes_with_slots_by_range, \
7
+ get_database_collections_for_class
8
+ from nmdc_schema import NmdcSchemaValidationPlugin
9
+
10
+ project_root = Path(__file__).parent.parent.parent
11
+ sys.path.insert(0, str(project_root))
12
+
13
+
14
+ class Migrator(MigratorBase):
15
+ r"""A check-only migrator that raises an exception if any QuantityValue's has_unit slot
16
+ is not valid against the slot's storage_unit or UnitEnum constraints."""
17
+
18
+ _from_version = '11.13.0.part_1'
19
+ _to_version = '11.13.0.part_2'
20
+
21
+ def __init__(self, *args, **kwargs):
22
+ super().__init__(*args, **kwargs)
23
+
24
+ self.schema_view = create_schema_view()
25
+ self.validator = Validator(
26
+ self.schema_view.schema,
27
+ # This is intentionally *only* using the NMDC plugin, because this migrator is *only*
28
+ # concerned with validating the QuantityValue-related constraints that the plugin
29
+ # implements.
30
+ validation_plugins=[
31
+ NmdcSchemaValidationPlugin()
32
+ ]
33
+ )
34
+
35
+ def upgrade(self, commit_changes: bool = False) -> None:
36
+ r'''
37
+ Migrates the database from conforming to the original schema, to conforming to the new schema.
38
+ '''
39
+
40
+ # Get the schema classes which have slots with range QuantityValue
41
+ classes_with_qv_slots = get_classes_with_slots_by_range(self.schema_view, 'QuantityValue')
42
+
43
+ # Get the Database collection names that can hold these classes
44
+ eligible_collection_names = set()
45
+ for class_name in classes_with_qv_slots.keys():
46
+ collection_names = get_database_collections_for_class(self.schema_view, class_name)
47
+ eligible_collection_names.update(collection_names)
48
+
49
+ # Apply migrator through collections
50
+ self.logger.info("Checking QuantityValue units against UnitEnum and storage_units constraints")
51
+ for collection_name in eligible_collection_names:
52
+ self.logger.info(f" Checking collection '{collection_name}'")
53
+ self.adapter.do_for_each_document(collection_name, self.confirm_units_fit_unitenum_and_storage_units)
54
+
55
+ def confirm_units_fit_unitenum_and_storage_units(self, document: dict) -> None:
56
+ r'''
57
+ Raise an exception if the QuantityValue's has_unit slot is not valid against slot's storage_unit or UnitEnum constraints.
58
+
59
+ >>> m = Migrator()
60
+
61
+ # Test: valid QuantityValue with proper units in biosample
62
+ >>> valid_biosample = {
63
+ ... "id": "test1",
64
+ ... "type": "nmdc:Biosample",
65
+ ... "temp": {
66
+ ... "type": "nmdc:QuantityValue",
67
+ ... "has_unit": "Cel",
68
+ ... "has_numeric_value": 25.0
69
+ ... }
70
+ ... }
71
+ >>> m.confirm_units_fit_unitenum_and_storage_units(valid_biosample) # Should not raise
72
+
73
+ # Test: invalid unit not in UnitEnum
74
+ >>> invalid_biosample_enum = {
75
+ ... "id": "test2",
76
+ ... "type": "nmdc:Biosample",
77
+ ... "temp": {
78
+ ... "type": "nmdc:QuantityValue",
79
+ ... "has_unit": "invalid_unit",
80
+ ... "has_numeric_value": 25.0
81
+ ... }
82
+ ... }
83
+ >>> m.confirm_units_fit_unitenum_and_storage_units(invalid_biosample_enum)
84
+ Traceback (most recent call last):
85
+ ...
86
+ ValueError: In test2:
87
+ QuantityValue at /temp has unit 'invalid_unit' which is not allowed for slot 'temp' (allowed: Cel)
88
+
89
+ # Test: unit not allowed for specific slot's storage_units
90
+ >>> invalid_biosample_storage = {
91
+ ... "id": "test3",
92
+ ... "type": "nmdc:Biosample",
93
+ ... "temp": {
94
+ ... "type": "nmdc:QuantityValue",
95
+ ... "has_unit": "m", # Wrong unit type for temperature
96
+ ... "has_numeric_value": 25.0
97
+ ... }
98
+ ... }
99
+ >>> m.confirm_units_fit_unitenum_and_storage_units(invalid_biosample_storage)
100
+ Traceback (most recent call last):
101
+ ...
102
+ ValueError: In test3:
103
+ QuantityValue at /temp has unit 'm' which is not allowed for slot 'temp' (allowed: Cel)
104
+ '''
105
+
106
+ document_id = document.get('id', '<unknown id>')
107
+ document_type = document.get('type')
108
+ if not document_type:
109
+ raise ValueError(f"Unable to infer target_class for document with id '{document_id}'")
110
+ target_class = document_type.replace('nmdc:', '')
111
+ report = self.validator.validate(document, target_class)
112
+ if report.results:
113
+ raise ValueError(f"In {document_id}:\n" + "\n".join(" " + result.message for result in report.results))
@@ -0,0 +1,47 @@
1
+ from nmdc_schema.migrators.migrator_base import MigratorBase
2
+
3
+
4
+ class Migrator(MigratorBase):
5
+ r"""
6
+ Migrates a database between two schemas.
7
+ """
8
+
9
+ _from_version = "11.13.0.part_2"
10
+ _to_version = "11.13.0.part_3"
11
+
12
+ def upgrade(self, commit_changes: bool = False) -> None:
13
+ r"""
14
+ Migrates the database from conforming to the original schema, to conforming to the new schema.
15
+ """
16
+ self.adapter.do_for_each_document("biosample_set", self.confirm_igsn_prefix)
17
+
18
+ def confirm_igsn_prefix(self, biosample: dict) -> None:
19
+ r"""
20
+ If a biosample has a igsn_biosample_identifiers prefix of 'IGSN' raise an exception.
21
+
22
+ >>> m = Migrator()
23
+
24
+ # Test: igsn_biosample_identifiers matching "IGSN:"
25
+ >>> m.confirm_igsn_prefix({"id": 1, "type": "nmdc:Biosample", "igsn_biosample_identifiers": ["IGSN:AU124"]})
26
+ Traceback (most recent call last):
27
+ ...
28
+ ValueError: Biosample 1 'igsn_biosample_identifiers' list includes: IGSN:AU124
29
+
30
+ # Test: igsn_biosample_identifiers with another non-confirming prefix
31
+ >>> m.confirm_igsn_prefix({"id": 1, "type": "nmdc:Biosample", "igsn_biosample_identifiers": ["isng:AU124"]})
32
+ Traceback (most recent call last):
33
+ ...
34
+ ValueError: Biosample 1 'igsn_biosample_identifiers' list includes: isng:AU124
35
+
36
+ # Test: valid Biosample
37
+ >>> m.confirm_igsn_prefix({"id": 3, "type": "nmdc:Biosample", "igsn_biosample_identifiers": ["igsn:AU124"]}) is None
38
+ True
39
+ """
40
+ prefix = "igsn:"
41
+ igsn_values = biosample.get("igsn_biosample_identifiers", [])
42
+ biosample_id = biosample.get("id")
43
+ for record in igsn_values:
44
+ if not record.startswith(prefix):
45
+ raise ValueError(
46
+ f"Biosample {biosample_id} 'igsn_biosample_identifiers' list includes: {record}"
47
+ )