emmet-api 0.87.0.dev10__tar.gz → 0.87.0.dev11__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 (291) hide show
  1. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/Dockerfile +2 -1
  2. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/PKG-INFO +5 -5
  3. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/query_operator/__init__.py +2 -1
  4. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/query_operator/pagination.py +64 -0
  5. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/resource/__init__.py +2 -0
  6. emmet_api-0.87.0.dev11/emmet/api/resource/search_resource.py +144 -0
  7. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/resource/utils.py +43 -45
  8. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/electronic_structure/query_operators.py +2 -1
  9. emmet_api-0.87.0.dev11/emmet/api/routes/materials/materials/utils.py +271 -0
  10. emmet_api-0.87.0.dev11/emmet/api/routes/materials/tasks/query_operators.py +330 -0
  11. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/tasks/resources.py +14 -16
  12. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/tasks/query_operators.py +1 -1
  13. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/utils.py +2 -12
  14. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet_api.egg-info/PKG-INFO +5 -5
  15. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet_api.egg-info/SOURCES.txt +1 -0
  16. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet_api.egg-info/requires.txt +3 -3
  17. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/pyproject.toml +4 -4
  18. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/requirements/deployment.txt +37 -42
  19. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/requirements/ubuntu-latest_py3.11.txt +39 -41
  20. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/requirements/ubuntu-latest_py3.11_extras.txt +58 -60
  21. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/requirements/ubuntu-latest_py3.12.txt +39 -41
  22. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/requirements/ubuntu-latest_py3.12_extras.txt +58 -60
  23. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/requirements/ubuntu-latest_py3.13.txt +40 -42
  24. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/requirements/ubuntu-latest_py3.13_extras.txt +58 -60
  25. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/mapi/test_utils.py +22 -27
  26. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/electronic_structure/test_query_operators.py +3 -4
  27. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/tasks/test_query_operators.py +1 -1
  28. emmet_api-0.87.0.dev10/emmet/api/routes/materials/materials/utils.py +0 -148
  29. emmet_api-0.87.0.dev10/emmet/api/routes/materials/tasks/query_operators.py +0 -197
  30. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/app.py +0 -0
  31. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/defect_resources.py +0 -0
  32. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/API.py +0 -0
  33. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/__init__.py +0 -0
  34. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/core/__init__.py +0 -0
  35. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/core/api.py +0 -0
  36. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/core/assets/mp_logo.svg +0 -0
  37. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/core/assets/mp_logo_small.png +0 -0
  38. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/core/documentation.py +0 -0
  39. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/core/global_header.py +0 -0
  40. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/core/settings.py +0 -0
  41. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/default_responses.yaml +0 -0
  42. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/models.py +0 -0
  43. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/py.typed +0 -0
  44. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/query_operator/core.py +0 -0
  45. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/query_operator/dynamic.py +0 -0
  46. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/query_operator/sorting.py +0 -0
  47. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/query_operator/sparse_fields.py +0 -0
  48. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/query_operator/submission.py +0 -0
  49. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/resource/aggregation.py +0 -0
  50. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/resource/core.py +0 -0
  51. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/resource/post_resource.py +0 -0
  52. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/resource/read_resource.py +0 -0
  53. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/resource/submission.py +0 -0
  54. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/__init__.py +0 -0
  55. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_consumer/__init__.py +0 -0
  56. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_consumer/query_operator.py +0 -0
  57. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_consumer/resources.py +0 -0
  58. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_general_store/__init__.py +0 -0
  59. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_general_store/query_operator.py +0 -0
  60. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_general_store/resources.py +0 -0
  61. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_messages/__init__.py +0 -0
  62. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_messages/query_operator.py +0 -0
  63. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/_messages/resources.py +0 -0
  64. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/defects/tasks/__init__.py +0 -0
  65. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/defects/tasks/resources.py +0 -0
  66. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/dois/__init__.py +0 -0
  67. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/dois/resources.py +0 -0
  68. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/legacy/__init__.py +0 -0
  69. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/legacy/jcesr/__init__.py +0 -0
  70. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/legacy/jcesr/query_operators.py +0 -0
  71. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/legacy/jcesr/resources.py +0 -0
  72. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/__init__.py +0 -0
  73. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/absorption/__init__.py +0 -0
  74. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/absorption/resources.py +0 -0
  75. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/alloys/__init__.py +0 -0
  76. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/alloys/query_operators.py +0 -0
  77. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/alloys/resources.py +0 -0
  78. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/bonds/__init__.py +0 -0
  79. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/bonds/query_operators.py +0 -0
  80. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/bonds/resources.py +0 -0
  81. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/charge_density/__init__.py +0 -0
  82. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/charge_density/query_operators.py +0 -0
  83. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/charge_density/resources.py +0 -0
  84. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/chemenv/__init__.py +0 -0
  85. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/chemenv/query_operators.py +0 -0
  86. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/chemenv/resources.py +0 -0
  87. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/conversion_electrodes/__init__.py +0 -0
  88. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/conversion_electrodes/resources.py +0 -0
  89. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/dielectric/__init__.py +0 -0
  90. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/dielectric/query_operators.py +0 -0
  91. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/dielectric/resources.py +0 -0
  92. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/elasticity/__init__.py +0 -0
  93. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/elasticity/query_operators.py +0 -0
  94. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/elasticity/resources.py +0 -0
  95. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/electronic_structure/__init__.py +0 -0
  96. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/electronic_structure/resources.py +0 -0
  97. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/eos/__init__.py +0 -0
  98. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/eos/resources.py +0 -0
  99. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/fermi/__init__.py +0 -0
  100. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/fermi/resources.py +0 -0
  101. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/grain_boundary/__init__.py +0 -0
  102. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/grain_boundary/query_operators.py +0 -0
  103. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/grain_boundary/resources.py +0 -0
  104. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/insertion_electrodes/__init__.py +0 -0
  105. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/insertion_electrodes/query_operators.py +0 -0
  106. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/insertion_electrodes/resources.py +0 -0
  107. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/insertion_electrodes/utils.py +0 -0
  108. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/magnetism/__init__.py +0 -0
  109. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/magnetism/query_operators.py +0 -0
  110. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/magnetism/resources.py +0 -0
  111. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/materials/__init__.py +0 -0
  112. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/materials/query_operators.py +0 -0
  113. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/materials/resources.py +0 -0
  114. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/mpcomplete/__init__.py +0 -0
  115. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/mpcomplete/query_operator.py +0 -0
  116. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/mpcomplete/resources.py +0 -0
  117. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/oxidation_states/__init__.py +0 -0
  118. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/oxidation_states/query_operators.py +0 -0
  119. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/oxidation_states/resources.py +0 -0
  120. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/phonon/__init__.py +0 -0
  121. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/phonon/query_operators.py +0 -0
  122. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/phonon/resources.py +0 -0
  123. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/piezo/__init__.py +0 -0
  124. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/piezo/query_operators.py +0 -0
  125. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/piezo/resources.py +0 -0
  126. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/provenance/__init__.py +0 -0
  127. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/provenance/resources.py +0 -0
  128. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/robocrys/__init__.py +0 -0
  129. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/robocrys/query_operators.py +0 -0
  130. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/robocrys/resources.py +0 -0
  131. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/similarity/__init__.py +0 -0
  132. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/similarity/query_operators.py +0 -0
  133. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/similarity/resources.py +0 -0
  134. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/substrates/__init__.py +0 -0
  135. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/substrates/query_operators.py +0 -0
  136. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/substrates/resources.py +0 -0
  137. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/summary/__init__.py +0 -0
  138. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/summary/hint_scheme.py +0 -0
  139. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/summary/query_operators.py +0 -0
  140. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/summary/resources.py +0 -0
  141. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/surface_properties/__init__.py +0 -0
  142. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/surface_properties/query_operators.py +0 -0
  143. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/surface_properties/resources.py +0 -0
  144. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/synthesis/__init__.py +0 -0
  145. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/synthesis/data_adaptor.py +0 -0
  146. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/synthesis/data_adaptor_synpro.py +0 -0
  147. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/synthesis/query_operators.py +0 -0
  148. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/synthesis/resources.py +0 -0
  149. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/synthesis/utils.py +0 -0
  150. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/tasks/__init__.py +0 -0
  151. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/tasks/hint_scheme.py +0 -0
  152. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/tasks/utils.py +0 -0
  153. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/thermo/__init__.py +0 -0
  154. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/thermo/query_operators.py +0 -0
  155. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/thermo/resources.py +0 -0
  156. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/xas/__init__.py +0 -0
  157. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/xas/query_operators.py +0 -0
  158. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/materials/xas/resources.py +0 -0
  159. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/__init__.py +0 -0
  160. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/association/__init__.py +0 -0
  161. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/association/resources.py +0 -0
  162. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/bonds/__init__.py +0 -0
  163. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/bonds/query_operators.py +0 -0
  164. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/bonds/resources.py +0 -0
  165. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/electric/__init__.py +0 -0
  166. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/electric/query_operators.py +0 -0
  167. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/electric/resources.py +0 -0
  168. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/metal_binding/__init__.py +0 -0
  169. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/metal_binding/query_operators.py +0 -0
  170. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/metal_binding/resources.py +0 -0
  171. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/molecules/__init__.py +0 -0
  172. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/molecules/hint_scheme.py +0 -0
  173. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/molecules/query_operators.py +0 -0
  174. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/molecules/resources.py +0 -0
  175. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/orbitals/__init__.py +0 -0
  176. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/orbitals/resources.py +0 -0
  177. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/partial_charges/__init__.py +0 -0
  178. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/partial_charges/resources.py +0 -0
  179. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/partial_spins/__init__.py +0 -0
  180. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/partial_spins/resources.py +0 -0
  181. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/redox/__init__.py +0 -0
  182. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/redox/query_operators.py +0 -0
  183. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/redox/resources.py +0 -0
  184. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/summary/__init__.py +0 -0
  185. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/summary/hint_scheme.py +0 -0
  186. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/summary/query_operators.py +0 -0
  187. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/summary/resources.py +0 -0
  188. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/tasks/__init__.py +0 -0
  189. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/tasks/hint_scheme.py +0 -0
  190. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/tasks/resources.py +0 -0
  191. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/tasks/utils.py +0 -0
  192. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/thermo/__init__.py +0 -0
  193. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/thermo/query_operators.py +0 -0
  194. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/thermo/resources.py +0 -0
  195. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/utils.py +0 -0
  196. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/vibrations/__init__.py +0 -0
  197. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet/api/routes/molecules/vibrations/resources.py +0 -0
  198. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet_api.egg-info/dependency_links.txt +0 -0
  199. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/emmet_api.egg-info/top_level.txt +0 -0
  200. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/healthcheck.sh +0 -0
  201. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/material_resources.py +0 -0
  202. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/molecule_resources.py +0 -0
  203. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/setup.cfg +0 -0
  204. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/start.sh +0 -0
  205. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/__init__.py +0 -0
  206. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/_consumer/__init__.py +0 -0
  207. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/_consumer/test_query_operators.py +0 -0
  208. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/_general_store/__init__.py +0 -0
  209. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/_general_store/test_query_operators.py +0 -0
  210. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/async_mongomock.py +0 -0
  211. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/conftest.py +0 -0
  212. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/core/__init__.py +0 -0
  213. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/core/test_mapi.py +0 -0
  214. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/legacy/molecules/__init__.py +0 -0
  215. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/legacy/molecules/test_query_operators.py +0 -0
  216. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/mapi/test_aggregation_resource.py +0 -0
  217. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/mapi/test_api.py +0 -0
  218. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/mapi/test_post_resource.py +0 -0
  219. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/mapi/test_query_operators.py +0 -0
  220. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/mapi/test_read_resource.py +0 -0
  221. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/mapi/test_submission_resource.py +0 -0
  222. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/bonds/__init__.py +0 -0
  223. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/bonds/test_query_operators.py +0 -0
  224. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/charge_density/__init__.py +0 -0
  225. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/charge_density/test_query_operators.py +0 -0
  226. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/chemenv/__init__.py +0 -0
  227. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/chemenv/test_query_operators.py +0 -0
  228. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/dielectric/__init__.py +0 -0
  229. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/dielectric/test_query_operators.py +0 -0
  230. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/elasticity/__init__.py +0 -0
  231. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/elasticity/test_query_operators.py +0 -0
  232. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/electrodes/__init__.py +0 -0
  233. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/electrodes/test_query_operators.py +0 -0
  234. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/electrodes/test_utils.py +0 -0
  235. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/electronic_structure/__init__.py +0 -0
  236. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/eos/__init__.py +0 -0
  237. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/grain_boundary/__init__.py +0 -0
  238. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/grain_boundary/test_query_operators.py +0 -0
  239. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/magnetism/__init__.py +0 -0
  240. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/magnetism/test_query_operators.py +0 -0
  241. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/materials/__init__.py +0 -0
  242. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/materials/test_query_operators.py +0 -0
  243. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/materials/test_utils.py +0 -0
  244. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/mpcomplete/__init__.py +0 -0
  245. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/mpcomplete/test_query_operators.py +0 -0
  246. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/oxidation_states/__init__.py +0 -0
  247. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/oxidation_states/test_query_operators.py +0 -0
  248. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/piezo/__init__.py +0 -0
  249. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/piezo/test_query_operators.py +0 -0
  250. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/robocrys/__init__.py +0 -0
  251. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/robocrys/test_query_operators.py +0 -0
  252. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/similarity/__init__.py +0 -0
  253. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/similarity/test_query_operators.py +0 -0
  254. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/substrates/__init__.py +0 -0
  255. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/substrates/test_query_operators.py +0 -0
  256. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/summary/__init__.py +0 -0
  257. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/summary/test_hint_scheme.py +0 -0
  258. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/summary/test_query_operators.py +0 -0
  259. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/surface_properties/__init__.py +0 -0
  260. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/surface_properties/test_query_operators.py +0 -0
  261. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/synthesis/__init__.py +0 -0
  262. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/synthesis/test_adaptor.py +0 -0
  263. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/synthesis/test_adaptor_synpro.py +0 -0
  264. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/synthesis/test_query_operators.py +0 -0
  265. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/synthesis/test_utils.py +0 -0
  266. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/tasks/__init__.py +0 -0
  267. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/tasks/test_utils.py +0 -0
  268. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/thermo/__init__.py +0 -0
  269. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/thermo/test_query_operators.py +0 -0
  270. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/xas/__init__.py +0 -0
  271. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/materials/xas/test_query_operators.py +0 -0
  272. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/__init__.py +0 -0
  273. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/bonds/__init__.py +0 -0
  274. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/bonds/test_query_operators.py +0 -0
  275. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/electric/__init__.py +0 -0
  276. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/electric/test_query_operators.py +0 -0
  277. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/metal_binding/__init__.py +0 -0
  278. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/metal_binding/test_query_operators.py +0 -0
  279. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/molecules/__init__.py +0 -0
  280. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/molecules/test_hint_scheme.py +0 -0
  281. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/molecules/test_query_operators.py +0 -0
  282. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/redox/__init__.py +0 -0
  283. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/redox/test_query_operators.py +0 -0
  284. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/summary/__init__.py +0 -0
  285. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/summary/test_query_operators.py +0 -0
  286. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/tasks/__init__.py +0 -0
  287. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/tasks/test_hint_scheme.py +0 -0
  288. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/tasks/test_query_operators.py +0 -0
  289. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/tasks/test_utils.py +0 -0
  290. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/thermo/__init__.py +0 -0
  291. {emmet_api-0.87.0.dev10 → emmet_api-0.87.0.dev11}/tests/molecules/thermo/test_query_operators.py +0 -0
@@ -18,7 +18,8 @@ RUN wget -q https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-
18
18
  chmod +x wait-for-it.sh && mv wait-for-it.sh /root/.local/bin/
19
19
 
20
20
  FROM base
21
- ARG BUILDARCH=x86_64
21
+ ARG BUILDARCH
22
+ ENV BUILDARCH=x86_64
22
23
  COPY --from=builder /root/.local/lib/python3.11/site-packages /root/.local/lib/python3.11/site-packages
23
24
  COPY --from=builder /root/.local/bin /root/.local/bin
24
25
  COPY --from=builder /usr/lib/$BUILDARCH-linux-gnu/libsnappy* /usr/lib/$BUILDARCH-linux-gnu/
@@ -1,17 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emmet-api
3
- Version: 0.87.0.dev10
3
+ Version: 0.87.0.dev11
4
4
  Summary: Emmet API Server
5
5
  Author-email: The Materials Project <feedback@materialsproject.org>
6
- License: Modified BSD
6
+ License-Expression: BSD-3-Clause-LBNL
7
7
  Project-URL: Homepage, https://github.com/materialsproject/emmet/tree/main/emmet-api/
8
8
  Project-URL: Documentation, https://materialsproject.github.io/emmet/
9
9
  Requires-Python: >=3.11
10
- Requires-Dist: emmet-core>=0.86.1
10
+ Requires-Dist: emmet-core<0.87.0,>=0.86.3rc1
11
11
  Requires-Dist: fastapi>=0.123.1
12
- Requires-Dist: gunicorn
12
+ Requires-Dist: gunicorn==24.1.1
13
13
  Requires-Dist: boto3
14
- Requires-Dist: ddtrace
14
+ Requires-Dist: ddtrace==4.3.0
15
15
  Requires-Dist: setproctitle
16
16
  Requires-Dist: shapely
17
17
  Requires-Dist: asgi-logger
@@ -1,6 +1,6 @@
1
1
  from emmet.api.query_operator.core import QueryOperator
2
2
  from emmet.api.query_operator.dynamic import NumericQuery, StringQueryOperator
3
- from emmet.api.query_operator.pagination import PaginationQuery
3
+ from emmet.api.query_operator.pagination import AtlasPaginationQuery, PaginationQuery
4
4
  from emmet.api.query_operator.sorting import SortQuery
5
5
  from emmet.api.query_operator.sparse_fields import SparseFieldsQuery
6
6
  from emmet.api.query_operator.submission import SubmissionQuery
@@ -9,6 +9,7 @@ __all__ = [
9
9
  "QueryOperator",
10
10
  "NumericQuery",
11
11
  "StringQueryOperator",
12
+ "AtlasPaginationQuery",
12
13
  "PaginationQuery",
13
14
  "SortQuery",
14
15
  "SparseFieldsQuery",
@@ -4,6 +4,70 @@ from emmet.api.query_operator import QueryOperator
4
4
  from emmet.api.utils import STORE_PARAMS
5
5
 
6
6
 
7
+ class AtlasPaginationQuery(QueryOperator):
8
+ """Query operators to provides pagination for Atlas Search queries."""
9
+
10
+ def __init__(self, default_limit: int = 100, max_limit: int = 1000):
11
+ """
12
+ Args:
13
+ default_limit: the default number of documents to return
14
+ max_limit: max number of documents to return.
15
+ """
16
+ self.default_limit = default_limit
17
+ self.max_limit = max_limit
18
+
19
+ def query(
20
+ _forward: bool = Query(
21
+ True,
22
+ description="Whether to page forward (True) or backward (False) in the search results.",
23
+ ),
24
+ _pagination_token: str = Query(
25
+ None, description="Pagination token for the next set of results."
26
+ ),
27
+ _skip: int = Query(
28
+ 0,
29
+ description="Number of entries to skip in the search.",
30
+ ),
31
+ _limit: int = Query(
32
+ default_limit,
33
+ description=f"Max number of entries to return in a single query. Limited to {max_limit}.",
34
+ ),
35
+ ) -> STORE_PARAMS:
36
+ """
37
+ Pagination parameters for the API Endpoint.
38
+ """
39
+ if _limit > max_limit:
40
+ raise HTTPException(
41
+ status_code=400,
42
+ detail="Requested more data per query than allowed by this endpoint."
43
+ f" The max limit is {max_limit} entries",
44
+ )
45
+
46
+ if _skip < 0 or _limit < 0:
47
+ raise HTTPException(
48
+ status_code=400,
49
+ detail="Cannot request negative _skip or _limit values",
50
+ )
51
+
52
+ return {
53
+ "forward": _forward,
54
+ "pagination_token": _pagination_token,
55
+ "skip": _skip,
56
+ "limit": _limit,
57
+ }
58
+
59
+ self.query = query # type: ignore
60
+
61
+ def query(self):
62
+ """Stub query function for abstract class."""
63
+
64
+ def meta(self) -> dict:
65
+ """
66
+ Metadata for the pagination params.
67
+ """
68
+ return {"max_limit": self.max_limit}
69
+
70
+
7
71
  class PaginationQuery(QueryOperator):
8
72
  """Query operators to provides Pagination."""
9
73
 
@@ -12,6 +12,7 @@ from emmet.api.resource.aggregation import AggregationResource
12
12
  from emmet.api.resource.post_resource import PostOnlyResource
13
13
  from emmet.api.resource.read_resource import ReadOnlyResource, attach_query_ops
14
14
  from emmet.api.resource.submission import SubmissionResource
15
+ from emmet.api.resource.search_resource import SearchResource
15
16
 
16
17
  __all__ = [
17
18
  "Resource",
@@ -23,4 +24,5 @@ __all__ = [
23
24
  "ReadOnlyResource",
24
25
  "attach_query_ops",
25
26
  "SubmissionResource",
27
+ "SearchResource",
26
28
  ]
@@ -0,0 +1,144 @@
1
+ from inspect import signature
2
+ from typing import Any
3
+
4
+ import orjson
5
+ from fastapi import HTTPException, Request, Response
6
+ from pymongo.errors import NetworkTimeout, PyMongoError
7
+
8
+ from emmet.api.models import Meta
9
+ from emmet.api.query_operator import QueryOperator
10
+ from emmet.api.resource import CollectionResource
11
+ from emmet.api.resource.utils import (
12
+ attach_query_ops,
13
+ generate_atlas_search_pipeline,
14
+ )
15
+ from emmet.api.utils import STORE_PARAMS, merge_atlas_queries, serialization_helper
16
+
17
+
18
+ class SearchResource(CollectionResource):
19
+ """
20
+ Implements a REST Compatible Resource as a GET URL endpoint
21
+ This class provides a number of convenience features
22
+ including full pagination, field projection.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ *args,
28
+ disable_validation: bool = False,
29
+ enable_default_search: bool = True,
30
+ query_to_configure_on_request: QueryOperator | None = None,
31
+ **kwargs,
32
+ ):
33
+ """
34
+ Args:
35
+ disable_validation: Whether to use ORJSON and provide a direct FastAPI response.
36
+ Note this will disable auto JSON serialization and response validation with the
37
+ provided model.
38
+ enable_default_search: Enable default endpoint search behavior.
39
+ query_to_configure_on_request: Query operator to configure on request
40
+ """
41
+ self.disable_validation = disable_validation
42
+ self.enable_default_search = enable_default_search
43
+ self.query_to_configure_on_request = query_to_configure_on_request
44
+
45
+ super().__init__(*args, **kwargs)
46
+
47
+ def prepare_endpoint(self):
48
+ """
49
+ Internal method to prepare the endpoint by setting up default handlers
50
+ for routes.
51
+ """
52
+ if self.enable_default_search:
53
+ self.build_dynamic_model_search()
54
+
55
+ def build_dynamic_model_search(self):
56
+ model_name = self.model.__name__
57
+
58
+ async def search(**queries: dict[str, STORE_PARAMS]) -> dict | Response:
59
+
60
+ request: Request = queries.pop("request") # type: ignore
61
+ temp_response: Response = queries.pop("temp_response") # type: ignore
62
+
63
+ if self.query_to_configure_on_request is not None:
64
+ # give the key name "request", arbitrary choice, as only the value gets merged into the query
65
+ queries["groups"] = self.header_processor.configure_query_on_request( # type: ignore
66
+ request=request, query_operator=self.query_to_configure_on_request
67
+ )
68
+ # allowed query parameters
69
+ query_params = [
70
+ entry
71
+ for _, i in enumerate(self.query_operators)
72
+ for entry in signature(i.query).parameters
73
+ ]
74
+ # check for overlap between allowed query parameters and request query parameters
75
+ overlap = [key for key in request.query_params if key not in query_params]
76
+ if any(overlap):
77
+ if "limit" in overlap or "skip" in overlap:
78
+ raise HTTPException(
79
+ status_code=400,
80
+ detail="'limit' and 'skip' parameters have been renamed. "
81
+ "Please update your API client to the newest version.",
82
+ )
83
+ else:
84
+ raise HTTPException(
85
+ status_code=400,
86
+ detail="Request contains query parameters which cannot be used: {}".format(
87
+ ", ".join(overlap)
88
+ ),
89
+ )
90
+ query: dict[Any, Any] = merge_atlas_queries(list(queries.values())) # type: ignore
91
+
92
+ try:
93
+ pipeline = generate_atlas_search_pipeline(query)
94
+ cursor = await self.collection.aggregate(pipeline)
95
+ data = await cursor.to_list()
96
+ except (NetworkTimeout, PyMongoError) as e:
97
+ raise HTTPException(
98
+ status_code=504 if e.timeout else 500,
99
+ detail=f"Server error: {e}",
100
+ )
101
+
102
+ # results are returned reversed when paginating backwards so we need to fix that
103
+ reverse = any(
104
+ "$search" in p and "searchBefore" in p["$search"] for p in pipeline
105
+ )
106
+ if reverse:
107
+ data = list(reversed(data))
108
+
109
+ operator_meta = {}
110
+
111
+ for operator in self.query_operators:
112
+ data = operator.post_process(data, query)
113
+ operator_meta.update(operator.meta())
114
+
115
+ if data and "meta" in data[0] and data[0]["meta"]:
116
+ meta = Meta(
117
+ total_doc=data[0]["meta"].get("count", {}).get("total", 1),
118
+ facet=data[0]["meta"].get("facet", {}),
119
+ pagination_token=data[-1].get("meta_pagination_token", ""),
120
+ )
121
+ else:
122
+ meta = Meta(total_doc=0)
123
+
124
+ response = {"data": data if data else [], "meta": {**meta.dict(), **operator_meta}} # type: ignore
125
+
126
+ if self.disable_validation:
127
+ response = Response(orjson.dumps(response, default=serialization_helper)) # type: ignore
128
+
129
+ if self.header_processor is not None:
130
+ if self.disable_validation:
131
+ self.header_processor.process_header(response, request)
132
+ else:
133
+ self.header_processor.process_header(temp_response, request)
134
+
135
+ return response
136
+
137
+ self.router.get(
138
+ self.sub_path,
139
+ tags=self.tags,
140
+ summary=f"Get {model_name} documents",
141
+ response_model=self.response_model,
142
+ response_description=f"Search for a {model_name}",
143
+ response_model_exclude_unset=True,
144
+ )(attach_query_ops(search, self.query_operators))
@@ -6,8 +6,6 @@ from emmet.api.query_operator import QueryOperator
6
6
  from emmet.api.utils import STORE_PARAMS, attach_signature
7
7
  from pymongo.asynchronous.collection import AsyncCollection
8
8
 
9
- NON_STORED_SOURCES = ["calcs_reversed", "orig_inputs"]
10
-
11
9
 
12
10
  class CollectionWithKey:
13
11
 
@@ -74,62 +72,62 @@ def generate_query_pipeline(query: dict):
74
72
  def generate_atlas_search_pipeline(query: dict):
75
73
  """
76
74
  Generate the aggregation pipeline for Atlas Search queries.
77
-
78
- Args:
79
- query: Query parameters
80
- store: Store containing endpoint data
81
75
  """
82
76
  pipeline = []
77
+ operator: dict = {}
83
78
 
84
- # generate the operator, if more than one
85
- operator = {
86
- "compound": {
87
- "must": [q for q in query["criteria"] if not q.get("mustNot", False)]
79
+ if not query.get("criteria"):
80
+ operator = {"exists": {"path": "_id"}}
81
+ else:
82
+ operator = {"compound": {"must": [], "mustNot": []}}
83
+
84
+ # Build the must clauses
85
+ for q in query["criteria"]:
86
+ if not q.get("mustNot", False):
87
+ if "must" in q:
88
+ # If q has a "must" key, expand its contents instead of using q directly
89
+ operator["compound"]["must"].extend(q["must"])
90
+ else:
91
+ # Use the query as-is
92
+ operator["compound"]["must"].append(q)
93
+ else:
94
+ operator["compound"]["mustNot"].extend(q["mustNot"])
95
+
96
+ search_base = {
97
+ "$search": {
98
+ "index": "default",
99
+ "returnStoredSource": True,
100
+ "count": {"type": "total"},
88
101
  }
89
102
  }
90
- # append the mustNot criteria to the compound operator
91
- operator["compound"]["mustNot"] = [
92
- q["mustNot"] for q in query["criteria"] if q.get("mustNot", False)
93
- ]
94
-
95
- if query.get("facets", False):
96
- pipeline.append(
97
- {
98
- "$search": {
99
- "index": "default",
100
- "facet": {"operator": operator, "facets": query["facets"]},
101
- }
102
- }
103
- )
104
- else:
105
- pipeline.append({"$search": {"index": "default", **operator}})
106
- # add returnedStoredSource: True if non-stored source are not present in "properties"
107
- # for quicker document retrieval, otherwise, do a full lookup
108
- return_stored_source = not any(
109
- prop in NON_STORED_SOURCES for prop in query.get("properties", [])
110
- )
111
- if return_stored_source:
112
- pipeline[0]["$search"]["returnStoredSource"] = True # type: ignore
113
103
 
114
- sorting = query.get("sort", False)
115
- if sorting:
116
- # no $ sign for atlas search
117
- sort_dict = {"sort": {}} # type: ignore
118
- sort_dict["sort"].update(query["sort"])
119
- # add sort to $search stage
120
- pipeline[0]["$search"].update(sort_dict)
104
+ if p_token := query.get("pagination_token", None):
105
+ search_base["$search"][
106
+ f"search{'After' if query.get('forward', True) else 'Before'}"
107
+ ] = p_token
108
+
109
+ search_base["$search"].update(operator)
110
+
111
+ sort_dict = {"sort": {"_id": 1}}
112
+ if query.get("sort", False):
113
+ sort_dict["sort"] = {**sort_dict["sort"], **query["sort"]}
114
+ # add sort to $search stage
115
+ search_base["$search"].update(sort_dict)
121
116
 
122
- projection_dict = {"_id": 0}
117
+ pipeline.append(search_base)
118
+
119
+ projection_dict = {
120
+ "_id": 0,
121
+ "meta": "$$SEARCH_META",
122
+ "meta_pagination_token": {"$meta": "searchSequenceToken"},
123
+ }
123
124
  if query.get("properties", False):
124
125
  projection_dict.update({p: 1 for p in query["properties"]})
125
- pipeline.insert(1, {"$project": projection_dict}) # type: ignore
126
+ pipeline.append({"$project": projection_dict}) # type: ignore
126
127
 
127
128
  pipeline.append({"$skip": query.get("skip", 0)})
128
129
 
129
130
  if query.get("limit", False):
130
131
  pipeline.append({"$limit": query["limit"]})
131
132
 
132
- if query.get("facets", False):
133
- pipeline.append({"$facet": {"docs": [], "meta": [{"$replaceWith": "$$SEARCH_META"}, {"$limit": 1}]}}) # type: ignore
134
-
135
133
  return pipeline
@@ -10,7 +10,8 @@ from pymatgen.analysis.magnetism.analyzer import Ordering
10
10
  from pymatgen.core.periodic_table import Element
11
11
  from pymatgen.electronic_structure.core import OrbitalType, Spin
12
12
 
13
- from emmet.core.electronic_structure import BSPathType, DOSProjectionType
13
+ from emmet.core.band_theory import BSPathType
14
+ from emmet.core.electronic_structure import DOSProjectionType
14
15
  from emmet.core.mpid import MPID, AlphaID
15
16
 
16
17
 
@@ -0,0 +1,271 @@
1
+ from typing import Any
2
+ from fastapi import HTTPException
3
+ from pymatgen.core import Composition
4
+ from pymatgen.core.periodic_table import DummySpecies
5
+
6
+
7
+ def formula_to_criteria(formulas: str) -> dict:
8
+ """
9
+ Santizes formula into a dictionary to search with wild cards
10
+
11
+ Arguments:
12
+ formula: formula with wildcards in it for unknown elements
13
+
14
+ Returns:
15
+ Mongo style search criteria for this formula
16
+ """
17
+ dummies = "AEGJLMQRXZ"
18
+
19
+ formula_list = [formula.strip() for formula in formulas.split(",")]
20
+
21
+ if "*" in formulas:
22
+ if len(formula_list) > 1:
23
+ raise HTTPException(
24
+ status_code=400,
25
+ detail="Wild cards only supported for single formula queries.",
26
+ )
27
+ else:
28
+ # Wild card in formula
29
+ nstars = formulas.count("*")
30
+
31
+ formula_dummies = formulas.replace("*", "{}").format(*dummies[:nstars])
32
+
33
+ try:
34
+ integer_formula = Composition(
35
+ formula_dummies
36
+ ).get_integer_formula_and_factor()[0]
37
+ except ValueError:
38
+ raise HTTPException(
39
+ status_code=400,
40
+ detail="Problem processing formula with wild cards.",
41
+ )
42
+
43
+ comp = Composition(integer_formula).reduced_composition
44
+ crit: dict[str, Any] = {"formula_anonymous": comp.anonymized_formula}
45
+ real_elts = [
46
+ e.name for e in comp.elements if not isinstance(e, DummySpecies)
47
+ ]
48
+ crit.update(
49
+ {
50
+ f"composition_reduced.{el}": n
51
+ for el, n in comp.to_reduced_dict.items()
52
+ if el in real_elts
53
+ }
54
+ )
55
+ return crit
56
+
57
+ else:
58
+ try:
59
+ composition_list = [Composition(formula) for formula in formula_list]
60
+ except ValueError:
61
+ raise HTTPException(
62
+ status_code=400,
63
+ detail="Problem processing one or more provided formulas.",
64
+ )
65
+
66
+ if any(
67
+ isinstance(el, DummySpecies) for comp in composition_list for el in comp
68
+ ):
69
+ # Assume fully anonymized formula
70
+ if len(formula_list) == 1:
71
+ return {"formula_anonymous": composition_list[0].anonymized_formula}
72
+ else:
73
+ return {
74
+ "formula_anonymous": {
75
+ "$in": [comp.anonymized_formula for comp in composition_list]
76
+ }
77
+ }
78
+
79
+ else:
80
+ if len(formula_list) == 1:
81
+ comp = composition_list[0]
82
+ # Paranoia below about floating-point "equality"
83
+ crit = {"nelements": len(comp)}
84
+
85
+ try:
86
+ crit.update(
87
+ {
88
+ f"composition_reduced.{el}": n
89
+ for el, n in comp.to_reduced_dict.items()
90
+ }
91
+ )
92
+ except IndexError:
93
+ raise HTTPException(
94
+ status_code=400,
95
+ detail="Problem processing one or more provided formulas.",
96
+ )
97
+
98
+ return crit
99
+ else:
100
+ return {
101
+ "formula_pretty": {
102
+ "$in": [comp.reduced_formula for comp in composition_list]
103
+ }
104
+ }
105
+
106
+
107
+ def formula_to_atlas_criteria(formulas: str) -> dict:
108
+ """
109
+ Converts formula into Atlas Search query criteria for composition-based searches
110
+
111
+ Arguments:
112
+ formula: formula with wildcards in it for unknown elements
113
+
114
+ Returns:
115
+ Atlas Search style query criteria for this formula
116
+ """
117
+ dummies = "AEGJLMQRXZ"
118
+ formula_list = [formula.strip() for formula in formulas.split(",")]
119
+ must_clauses: list[dict[str, Any]] = []
120
+
121
+ if "*" in formulas:
122
+ if len(formula_list) > 1:
123
+ raise HTTPException(
124
+ status_code=400,
125
+ detail="Wild cards only supported for single formula queries.",
126
+ )
127
+ else:
128
+ # Wild card in formula
129
+ nstars = formulas.count("*")
130
+ formula_dummies = formulas.replace("*", "{}").format(*dummies[:nstars])
131
+
132
+ try:
133
+ integer_formula = Composition(
134
+ formula_dummies
135
+ ).get_integer_formula_and_factor()[0]
136
+ except ValueError:
137
+ raise HTTPException(
138
+ status_code=400,
139
+ detail="Problem processing formula with wild cards.",
140
+ )
141
+
142
+ comp = Composition(integer_formula).reduced_composition
143
+ real_elts = [
144
+ e.name for e in comp.elements if not isinstance(e, DummySpecies)
145
+ ]
146
+
147
+ # Build Atlas Search compound query
148
+ must_clauses = [
149
+ {
150
+ "text": {
151
+ "query": comp.anonymized_formula,
152
+ "path": "formula_anonymous",
153
+ }
154
+ }
155
+ ]
156
+
157
+ # Add element-specific clauses
158
+ must_clauses += [
159
+ {"equals": {"path": f"composition_reduced.{el}", "value": n}}
160
+ for el, n in comp.to_reduced_dict.items()
161
+ if el in real_elts
162
+ ]
163
+
164
+ return {"must": must_clauses}
165
+
166
+ else:
167
+ try:
168
+ composition_list = [Composition(formula) for formula in formula_list]
169
+ except ValueError:
170
+ raise HTTPException(
171
+ status_code=400,
172
+ detail="Problem processing one or more provided formulas.",
173
+ )
174
+
175
+ if any(
176
+ isinstance(el, DummySpecies) for comp in composition_list for el in comp
177
+ ):
178
+ # Assume fully anonymized formula
179
+ anonymized_formulas = [comp.anonymized_formula for comp in composition_list]
180
+
181
+ if len(formula_list) == 1:
182
+ return {
183
+ "text": {
184
+ "query": anonymized_formulas[0],
185
+ "path": "formula_anonymous",
186
+ }
187
+ }
188
+ else:
189
+ return {
190
+ "in": {
191
+ "path": "formula_anonymous",
192
+ "value": anonymized_formulas,
193
+ }
194
+ }
195
+
196
+ else:
197
+ if len(formula_list) == 1:
198
+ comp = composition_list[0]
199
+
200
+ # Build compound query for exact composition match
201
+ must_clauses = [{"equals": {"path": "nelements", "value": len(comp)}}]
202
+
203
+ try:
204
+ must_clauses += [
205
+ {
206
+ "equals": {
207
+ "path": f"composition_reduced.{el}",
208
+ "value": n,
209
+ }
210
+ }
211
+ for el, n in comp.to_reduced_dict.items()
212
+ ]
213
+ except IndexError:
214
+ raise HTTPException(
215
+ status_code=400,
216
+ detail="Problem processing one or more provided formulas.",
217
+ )
218
+
219
+ return {"must": must_clauses}
220
+ else:
221
+ # Multiple formulas - use pretty formula matching
222
+ pretty_formulas = [comp.reduced_formula for comp in composition_list]
223
+ return {"in": {"path": "formula_pretty", "value": pretty_formulas}}
224
+
225
+
226
+ def chemsys_to_criteria(chemsys: str) -> dict:
227
+ """
228
+ Santizes chemsys into a dictionary to search with wild cards
229
+
230
+ Arguments:
231
+ chemsys: A comma delimited string list of chemical systems
232
+ with wildcards in it for unknown elements
233
+
234
+ Returns:
235
+ Mongo style search criteria for this formula
236
+ """
237
+
238
+ crit = {} # type: dict
239
+
240
+ chemsys_list = [chemsys_val.strip() for chemsys_val in chemsys.split(",")]
241
+
242
+ if "*" in chemsys:
243
+ if len(chemsys_list) > 1:
244
+ raise HTTPException(
245
+ status_code=400,
246
+ detail="Wild cards only supported for single chemsys queries.",
247
+ )
248
+ else:
249
+ eles = chemsys_list[0].split("-")
250
+
251
+ crit["nelements"] = len(eles)
252
+ crit.update(
253
+ {
254
+ f"composition_reduced.{el}": {"$exists": True}
255
+ for el in eles
256
+ if el != "*"
257
+ }
258
+ )
259
+
260
+ return crit
261
+ else:
262
+ query_vals = [
263
+ "-".join(sorted(chemsys_val.split("-"))) for chemsys_val in chemsys_list
264
+ ]
265
+
266
+ if len(query_vals) == 1:
267
+ crit["chemsys"] = query_vals[0]
268
+ else:
269
+ crit["chemsys"] = {"$in": query_vals}
270
+
271
+ return crit