GeneralManager 0.59.2__tar.gz → 0.60.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 (258) hide show
  1. {generalmanager-0.59.2/src/GeneralManager.egg-info → generalmanager-0.60.0}/PKG-INFO +1 -1
  2. {generalmanager-0.59.2 → generalmanager-0.60.0}/pyproject.toml +1 -1
  3. {generalmanager-0.59.2 → generalmanager-0.60.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/GeneralManager.egg-info/SOURCES.txt +3 -0
  5. generalmanager-0.60.0/src/general_manager/_types/dataframes.py +31 -0
  6. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/cache_tracker.py +5 -0
  7. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/dependency_matching.py +6 -0
  8. generalmanager-0.60.0/src/general_manager/dataframes/__init__.py +29 -0
  9. generalmanager-0.60.0/src/general_manager/dataframes/measurements.py +398 -0
  10. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/base_interface.py +132 -49
  11. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/core/observability.py +36 -20
  12. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/manager/general_manager.py +12 -10
  13. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/manager/meta.py +16 -4
  14. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/measurement/measurement.py +31 -8
  15. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/public_api_registry.py +35 -0
  16. {generalmanager-0.59.2 → generalmanager-0.60.0}/LICENSE +0 -0
  17. {generalmanager-0.59.2 → generalmanager-0.60.0}/README.md +0 -0
  18. {generalmanager-0.59.2 → generalmanager-0.60.0}/setup.cfg +0 -0
  19. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  20. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/GeneralManager.egg-info/requires.txt +0 -0
  21. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
  22. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/__init__.py +0 -0
  23. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/__init__.py +0 -0
  24. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/api.py +0 -0
  25. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/bucket.py +0 -0
  26. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/cache.py +0 -0
  27. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/factory.py +0 -0
  28. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/general_manager.py +0 -0
  29. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/interface.py +0 -0
  30. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/manager.py +0 -0
  31. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/measurement.py +0 -0
  32. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/permission.py +0 -0
  33. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/rule.py +0 -0
  34. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/search.py +0 -0
  35. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/_types/utils.py +0 -0
  36. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/__init__.py +0 -0
  37. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql.py +0 -0
  38. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_errors.py +0 -0
  39. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_mutations.py +0 -0
  40. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_prefetch.py +0 -0
  41. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_resolvers.py +0 -0
  42. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_search.py +0 -0
  43. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  44. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
  45. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_view.py +0 -0
  46. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_warmup.py +0 -0
  47. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  48. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  49. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/mutation.py +0 -0
  50. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/property.py +0 -0
  51. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/registry.py +0 -0
  52. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/remote_api.py +0 -0
  53. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/remote_invalidation.py +0 -0
  54. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
  55. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/apps.py +0 -0
  56. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bootstrap.py +0 -0
  57. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bucket/__init__.py +0 -0
  58. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bucket/base_bucket.py +0 -0
  59. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
  60. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bucket/database_bucket.py +0 -0
  61. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bucket/group_bucket.py +0 -0
  62. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bucket/indexing.py +0 -0
  63. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/bucket/request_bucket.py +0 -0
  64. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/__init__.py +0 -0
  65. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/cache_decorator.py +0 -0
  66. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/dependency_cache.py +0 -0
  67. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/dependency_index.py +0 -0
  68. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/dependency_publish.py +0 -0
  69. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/dependency_shards.py +0 -0
  70. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
  71. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/run_context.py +0 -0
  72. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/cache/signals.py +0 -0
  73. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/__init__.py +0 -0
  74. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/audit.py +0 -0
  75. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/bootstrap.py +0 -0
  76. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/checks.py +0 -0
  77. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/consumer.py +0 -0
  78. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/errors.py +0 -0
  79. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/__init__.py +0 -0
  80. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/__main__.py +0 -0
  81. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/baseline.py +0 -0
  82. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/diagnostics.py +0 -0
  83. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/fingerprints.py +0 -0
  84. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/fixtures.py +0 -0
  85. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  86. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  87. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/judges/contract.py +0 -0
  88. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  89. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  90. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/readiness.py +0 -0
  91. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/runner.py +0 -0
  92. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/evals/traces.py +0 -0
  93. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/grounding.py +0 -0
  94. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/models.py +0 -0
  95. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/providers/__init__.py +0 -0
  96. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/providers/_shared.py +0 -0
  97. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/providers/anthropic.py +0 -0
  98. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/providers/base.py +0 -0
  99. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/providers/google.py +0 -0
  100. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/providers/ollama.py +0 -0
  101. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/providers/openai.py +0 -0
  102. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/rate_limits.py +0 -0
  103. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/routing.py +0 -0
  104. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/schema_index.py +0 -0
  105. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/settings.py +0 -0
  106. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/signals.py +0 -0
  107. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/system_prompt.py +0 -0
  108. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/tool_metadata.py +0 -0
  109. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/tools.py +0 -0
  110. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/chat/views.py +0 -0
  111. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/conf.py +0 -0
  112. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/factory/__init__.py +0 -0
  113. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/factory/auto_factory.py +0 -0
  114. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/factory/factories.py +0 -0
  115. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/factory/factory_methods.py +0 -0
  116. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/__init__.py +0 -0
  117. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/bundles/__init__.py +0 -0
  118. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/bundles/calculation.py +0 -0
  119. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/bundles/database.py +0 -0
  120. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  121. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/bundles/request.py +0 -0
  122. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
  123. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/base.py +0 -0
  124. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
  125. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  126. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  127. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  128. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
  129. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  130. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  131. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  132. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  133. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  134. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/factory.py +0 -0
  135. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  136. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  137. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  138. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  139. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
  140. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  141. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  142. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  143. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  144. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  145. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  146. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  147. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  148. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  149. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  150. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/registry.py +0 -0
  151. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  152. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  153. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  154. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  155. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  156. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
  157. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
  158. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/interfaces/database.py +0 -0
  159. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  160. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
  161. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  162. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/interfaces/request.py +0 -0
  163. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/manifests/__init__.py +0 -0
  164. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  165. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  166. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
  167. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/orm_interface.py +0 -0
  168. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/requests.py +0 -0
  169. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/utils/__init__.py +0 -0
  170. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  171. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/utils/errors.py +0 -0
  172. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/interface/utils/models.py +0 -0
  173. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/logging.py +0 -0
  174. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  175. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  176. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  177. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/search_index.py +0 -0
  178. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/search_reconcile.py +0 -0
  179. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  180. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/shell.py +0 -0
  181. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  182. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  183. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/manager/__init__.py +0 -0
  184. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/manager/group_manager.py +0 -0
  185. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/manager/input.py +0 -0
  186. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/measurement/__init__.py +0 -0
  187. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/measurement/measurement_field.py +0 -0
  188. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/metrics/__init__.py +0 -0
  189. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/metrics/graphql.py +0 -0
  190. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/0001_initial.py +0 -0
  191. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  192. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  193. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  194. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  195. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  196. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  197. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/migrations/__init__.py +0 -0
  198. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/models.py +0 -0
  199. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/__init__.py +0 -0
  200. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/audit.py +0 -0
  201. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/base_permission.py +0 -0
  202. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
  203. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/manager_based_permission.py +0 -0
  204. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/mutation_permission.py +0 -0
  205. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/permission_checks.py +0 -0
  206. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/permission_data_manager.py +0 -0
  207. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/permission/utils.py +0 -0
  208. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/py.typed +0 -0
  209. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/rule/__init__.py +0 -0
  210. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/rule/handler.py +0 -0
  211. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/rule/rule.py +0 -0
  212. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/__init__.py +0 -0
  213. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/async_tasks.py +0 -0
  214. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/backend.py +0 -0
  215. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/backend_registry.py +0 -0
  216. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/backends/__init__.py +0 -0
  217. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/backends/dev.py +0 -0
  218. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/backends/meilisearch.py +0 -0
  219. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/backends/opensearch.py +0 -0
  220. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/backends/typesense.py +0 -0
  221. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/config.py +0 -0
  222. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/indexer.py +0 -0
  223. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/models.py +0 -0
  224. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/reconciliation.py +0 -0
  225. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/registry.py +0 -0
  226. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/tasks.py +0 -0
  227. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/search/utils.py +0 -0
  228. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/seeding/__init__.py +0 -0
  229. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/seeding/manager_landscape.py +0 -0
  230. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/__init__.py +0 -0
  231. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
  232. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/filter_parser.py +0 -0
  233. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/format_string.py +0 -0
  234. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/json_encoder.py +0 -0
  235. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/make_cache_key.py +0 -0
  236. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/none_to_zero.py +0 -0
  237. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/path_mapping.py +0 -0
  238. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/public_api.py +0 -0
  239. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/testing.py +0 -0
  240. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/utils/type_checks.py +0 -0
  241. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/__init__.py +0 -0
  242. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/actions.py +0 -0
  243. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/backend_registry.py +0 -0
  244. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/backends/__init__.py +0 -0
  245. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/backends/celery.py +0 -0
  246. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/backends/local.py +0 -0
  247. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/backends/n8n.py +0 -0
  248. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/config.py +0 -0
  249. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/engine.py +0 -0
  250. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/event_registry.py +0 -0
  251. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/events.py +0 -0
  252. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/models.py +0 -0
  253. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/signal_bridge.py +0 -0
  254. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/tasks.py +0 -0
  255. {generalmanager-0.59.2 → generalmanager-0.60.0}/src/general_manager/workflow/telemetry.py +0 -0
  256. {generalmanager-0.59.2 → generalmanager-0.60.0}/tests/test_settings.py +0 -0
  257. {generalmanager-0.59.2 → generalmanager-0.60.0}/tests/test_urls.py +0 -0
  258. {generalmanager-0.59.2 → generalmanager-0.60.0}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.59.2
3
+ Version: 0.60.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.59.2"
7
+ version = "0.60.0"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.59.2
3
+ Version: 0.60.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -18,6 +18,7 @@ src/general_manager/_types/__init__.py
18
18
  src/general_manager/_types/api.py
19
19
  src/general_manager/_types/bucket.py
20
20
  src/general_manager/_types/cache.py
21
+ src/general_manager/_types/dataframes.py
21
22
  src/general_manager/_types/factory.py
22
23
  src/general_manager/_types/general_manager.py
23
24
  src/general_manager/_types/interface.py
@@ -102,6 +103,8 @@ src/general_manager/chat/providers/base.py
102
103
  src/general_manager/chat/providers/google.py
103
104
  src/general_manager/chat/providers/ollama.py
104
105
  src/general_manager/chat/providers/openai.py
106
+ src/general_manager/dataframes/__init__.py
107
+ src/general_manager/dataframes/measurements.py
105
108
  src/general_manager/factory/__init__.py
106
109
  src/general_manager/factory/auto_factory.py
107
110
  src/general_manager/factory/factories.py
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ """Type-only imports for public API re-exports."""
4
+
5
+ __all__ = [
6
+ "DataFrameMeasurementError",
7
+ "InvalidDataFrameMeasurementValueError",
8
+ "MeasurementDataFrameColumnCollisionError",
9
+ "MissingMeasurementDataFrameColumnError",
10
+ "PandasNotInstalledError",
11
+ "collapse_measurements",
12
+ "expand_measurements",
13
+ "from_dataframe",
14
+ "to_dataframe",
15
+ ]
16
+
17
+ from general_manager.dataframes.measurements import DataFrameMeasurementError
18
+ from general_manager.dataframes.measurements import (
19
+ InvalidDataFrameMeasurementValueError,
20
+ )
21
+ from general_manager.dataframes.measurements import (
22
+ MeasurementDataFrameColumnCollisionError,
23
+ )
24
+ from general_manager.dataframes.measurements import (
25
+ MissingMeasurementDataFrameColumnError,
26
+ )
27
+ from general_manager.dataframes.measurements import PandasNotInstalledError
28
+ from general_manager.dataframes.measurements import collapse_measurements
29
+ from general_manager.dataframes.measurements import expand_measurements
30
+ from general_manager.dataframes.measurements import from_dataframe
31
+ from general_manager.dataframes.measurements import to_dataframe
@@ -142,6 +142,11 @@ class DependencyTracker:
142
142
  ]:
143
143
  dep_set.add((class_name, operation, identifier))
144
144
 
145
+ @staticmethod
146
+ def is_active() -> bool:
147
+ """Return whether dependency tracking is active for this execution context."""
148
+ return _dependency_storage.depth >= 0
149
+
145
150
  @staticmethod
146
151
  def reset_thread_local_storage() -> None:
147
152
  """Clear all dependency tracking data for the current thread.
@@ -101,6 +101,12 @@ def normalize_dependency_value(value: object) -> NormalizedDependencyValue:
101
101
 
102
102
  def serialize_normalized_value(value: object) -> str:
103
103
  """Serialize dependency values in the canonical dependency format."""
104
+ if isinstance(value, str):
105
+ return json.dumps(value)
106
+ if value is None or isinstance(value, (int, float, bool)):
107
+ return json.dumps(value)
108
+ if isinstance(value, date):
109
+ return json.dumps(value.isoformat())
104
110
  return json.dumps(normalize_dependency_value(value), sort_keys=True)
105
111
 
106
112
 
@@ -0,0 +1,29 @@
1
+ """Dataframe import/export helpers for GeneralManager values."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from general_manager.public_api_registry import DATAFRAME_EXPORTS
8
+ from general_manager.utils.public_api import build_module_dir, resolve_export
9
+
10
+ __all__ = list(DATAFRAME_EXPORTS)
11
+
12
+ _MODULE_MAP = DATAFRAME_EXPORTS
13
+
14
+ if TYPE_CHECKING:
15
+ from general_manager._types.dataframes import * # noqa: F403
16
+
17
+
18
+ def __getattr__(name: str) -> object:
19
+ """Dynamically resolve dataframe helper exports."""
20
+ return resolve_export(
21
+ name,
22
+ module_all=__all__,
23
+ module_map=_MODULE_MAP,
24
+ module_globals=globals(),
25
+ )
26
+
27
+
28
+ def __dir__() -> list[str]:
29
+ return build_module_dir(module_all=__all__, module_globals=globals())
@@ -0,0 +1,398 @@
1
+ """Helpers for expanding row-level Measurement values into dataframe columns."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable, Mapping
6
+ from decimal import Decimal, InvalidOperation
7
+ import importlib
8
+ import math
9
+ from typing import Any, Protocol, cast
10
+
11
+ import pint
12
+
13
+ from general_manager.measurement import Measurement
14
+
15
+ Row = Mapping[str, object]
16
+ MutableRow = dict[str, object]
17
+
18
+ __all__ = [
19
+ "DataFrameMeasurementError",
20
+ "InvalidDataFrameMeasurementValueError",
21
+ "MeasurementDataFrameColumnCollisionError",
22
+ "MissingMeasurementDataFrameColumnError",
23
+ "PandasNotInstalledError",
24
+ "collapse_measurements",
25
+ "expand_measurements",
26
+ "from_dataframe",
27
+ "to_dataframe",
28
+ ]
29
+
30
+
31
+ class DataFrameLike(Protocol):
32
+ """Minimal dataframe interface needed to collapse measurement columns."""
33
+
34
+ def to_dict(self, orient: str = "dict") -> Any:
35
+ """Return dataframe rows as dictionaries."""
36
+
37
+
38
+ class DataFrameMeasurementError(ValueError):
39
+ """Base error for dataframe measurement expansion failures."""
40
+
41
+
42
+ class PandasNotInstalledError(ImportError):
43
+ """Raised when pandas-specific dataframe helpers are used without pandas."""
44
+
45
+ def __init__(self) -> None:
46
+ super().__init__(
47
+ "pandas is required to create dataframes; install pandas to use "
48
+ "to_dataframe()."
49
+ )
50
+
51
+
52
+ class _InvalidDataFrameRecordsError(TypeError):
53
+ """Raised when dataframe records are not list[Mapping[str, object]]."""
54
+
55
+ def __init__(self) -> None:
56
+ super().__init__(
57
+ "dataframe.to_dict(orient='records') must return a list of mapping rows."
58
+ )
59
+
60
+
61
+ class InvalidDataFrameMeasurementValueError(DataFrameMeasurementError):
62
+ """Raised when a configured measurement field contains an invalid value."""
63
+
64
+ def __init__(
65
+ self,
66
+ field: str,
67
+ *,
68
+ value_type: str | None = None,
69
+ allows_strings: bool = False,
70
+ ) -> None:
71
+ expected = "Measurement values or nulls"
72
+ if allows_strings:
73
+ expected = "Measurement values, parseable measurement strings, or nulls"
74
+ details = f"; got {value_type}" if value_type else ""
75
+ super().__init__(f"Field {field!r} must contain {expected}{details}.")
76
+
77
+
78
+ class MeasurementDataFrameColumnCollisionError(DataFrameMeasurementError):
79
+ """Raised when measurement expansion or collapse would overwrite row data."""
80
+
81
+ def __init__(
82
+ self,
83
+ field: str,
84
+ generated_field: str,
85
+ *,
86
+ operation: str = "Expanding",
87
+ ) -> None:
88
+ super().__init__(
89
+ f"{operation} measurement field "
90
+ f"{field!r} would overwrite existing column {generated_field!r}."
91
+ )
92
+
93
+
94
+ class MissingMeasurementDataFrameColumnError(DataFrameMeasurementError):
95
+ """Raised when a generated measurement column is required but absent."""
96
+
97
+ def __init__(self, column: str) -> None:
98
+ super().__init__(
99
+ f"Collapsing measurement dataframe rows requires column {column!r}."
100
+ )
101
+
102
+
103
+ def expand_measurements(
104
+ rows: Iterable[Row],
105
+ *,
106
+ measurement_fields: Iterable[str] | None = None,
107
+ ) -> list[MutableRow]:
108
+ """Expand Measurement values in mapping-like rows into value/unit columns."""
109
+
110
+ copied_rows = [dict(row) for row in rows]
111
+ explicit_fields = _ordered_unique(
112
+ () if measurement_fields is None else measurement_fields
113
+ )
114
+ inferred_fields = _infer_measurement_fields(copied_rows)
115
+ expanded_fields = _ordered_unique((*explicit_fields, *inferred_fields))
116
+ if not expanded_fields:
117
+ return copied_rows
118
+
119
+ _check_generated_column_collisions(copied_rows, expanded_fields)
120
+ explicit_field_names = set(explicit_fields)
121
+ expanded_field_names = set(expanded_fields)
122
+ return [
123
+ _expand_row(
124
+ row,
125
+ expanded_fields=expanded_fields,
126
+ expanded_field_names=expanded_field_names,
127
+ explicit_field_names=explicit_field_names,
128
+ )
129
+ for row in copied_rows
130
+ ]
131
+
132
+
133
+ def collapse_measurements(
134
+ rows: Iterable[Row],
135
+ *,
136
+ measurement_fields: Iterable[str],
137
+ ) -> list[MutableRow]:
138
+ """Collapse value/unit dataframe columns back into Measurement values."""
139
+
140
+ collapsed_fields = _ordered_unique(measurement_fields)
141
+ value_columns: dict[str, str] = {}
142
+ unit_columns: set[str] = set()
143
+ for field in collapsed_fields:
144
+ value_field, unit_field = _generated_field_names(field)
145
+ value_columns[value_field] = field
146
+ unit_columns.add(unit_field)
147
+
148
+ return [
149
+ _collapse_row(
150
+ dict(row),
151
+ collapsed_fields=collapsed_fields,
152
+ value_columns=value_columns,
153
+ unit_columns=unit_columns,
154
+ )
155
+ for row in rows
156
+ ]
157
+
158
+
159
+ def to_dataframe(
160
+ rows: Iterable[Row],
161
+ *,
162
+ measurement_fields: Iterable[str] | None = None,
163
+ **dataframe_kwargs: object,
164
+ ) -> Any:
165
+ """Expand measurement values and build a pandas DataFrame."""
166
+
167
+ expanded_rows = expand_measurements(rows, measurement_fields=measurement_fields)
168
+ pandas = _import_pandas()
169
+ return pandas.DataFrame(expanded_rows, **dataframe_kwargs)
170
+
171
+
172
+ def from_dataframe(
173
+ dataframe: DataFrameLike,
174
+ *,
175
+ measurement_fields: Iterable[str],
176
+ ) -> list[MutableRow]:
177
+ """Collapse measurement value/unit columns from a dataframe-like object."""
178
+
179
+ records = dataframe.to_dict(orient="records")
180
+ if not isinstance(records, list):
181
+ raise _InvalidDataFrameRecordsError
182
+
183
+ row_records: list[Row] = []
184
+ for record in records:
185
+ if not isinstance(record, Mapping):
186
+ raise _InvalidDataFrameRecordsError
187
+ row_records.append(record)
188
+
189
+ return collapse_measurements(row_records, measurement_fields=measurement_fields)
190
+
191
+
192
+ def _import_pandas() -> Any:
193
+ try:
194
+ return importlib.import_module("pandas")
195
+ except ModuleNotFoundError as error:
196
+ if error.name != "pandas":
197
+ raise
198
+ raise PandasNotInstalledError from error
199
+
200
+
201
+ def _ordered_unique(fields: Iterable[str]) -> tuple[str, ...]:
202
+ seen: set[str] = set()
203
+ ordered_fields: list[str] = []
204
+ for field in fields:
205
+ if field in seen:
206
+ continue
207
+ seen.add(field)
208
+ ordered_fields.append(field)
209
+ return tuple(ordered_fields)
210
+
211
+
212
+ def _infer_measurement_fields(rows: Iterable[Row]) -> tuple[str, ...]:
213
+ return _ordered_unique(
214
+ field
215
+ for row in rows
216
+ for field, value in row.items()
217
+ if isinstance(value, Measurement)
218
+ )
219
+
220
+
221
+ def _check_generated_column_collisions(
222
+ rows: Iterable[Row], measurement_fields: Iterable[str]
223
+ ) -> None:
224
+ for field in measurement_fields:
225
+ generated_fields = _generated_field_names(field)
226
+ for row in rows:
227
+ for generated_field in generated_fields:
228
+ if generated_field in row and generated_field != field:
229
+ raise MeasurementDataFrameColumnCollisionError(
230
+ field, generated_field
231
+ )
232
+
233
+
234
+ def _generated_field_names(field: str) -> tuple[str, str]:
235
+ return (f"{field}_value", f"{field}_unit")
236
+
237
+
238
+ def _expand_row(
239
+ row: Row,
240
+ *,
241
+ expanded_fields: tuple[str, ...],
242
+ expanded_field_names: set[str],
243
+ explicit_field_names: set[str],
244
+ ) -> MutableRow:
245
+ expanded_row: MutableRow = {}
246
+ seen_measurement_fields: set[str] = set()
247
+
248
+ for field, value in row.items():
249
+ if field not in expanded_field_names:
250
+ expanded_row[field] = value
251
+ continue
252
+
253
+ seen_measurement_fields.add(field)
254
+ value_field, unit_field = _generated_field_names(field)
255
+ measurement = _coerce_measurement_value(
256
+ field,
257
+ value,
258
+ explicit_field_names=explicit_field_names,
259
+ )
260
+ expanded_row[value_field] = (
261
+ measurement.magnitude if measurement is not None else None
262
+ )
263
+ expanded_row[unit_field] = measurement.unit if measurement is not None else None
264
+
265
+ for field in expanded_fields:
266
+ if field in seen_measurement_fields:
267
+ continue
268
+ value_field, unit_field = _generated_field_names(field)
269
+ expanded_row[value_field] = None
270
+ expanded_row[unit_field] = None
271
+
272
+ return expanded_row
273
+
274
+
275
+ def _collapse_row(
276
+ row: MutableRow,
277
+ *,
278
+ collapsed_fields: tuple[str, ...],
279
+ value_columns: Mapping[str, str],
280
+ unit_columns: set[str],
281
+ ) -> MutableRow:
282
+ for field in collapsed_fields:
283
+ value_field, unit_field = _generated_field_names(field)
284
+ if value_field not in row:
285
+ raise MissingMeasurementDataFrameColumnError(value_field)
286
+ if unit_field not in row:
287
+ raise MissingMeasurementDataFrameColumnError(unit_field)
288
+ if field in row:
289
+ raise MeasurementDataFrameColumnCollisionError(
290
+ field,
291
+ field,
292
+ operation="Collapsing",
293
+ )
294
+
295
+ collapsed_row: MutableRow = {}
296
+ for column, value in row.items():
297
+ if column in value_columns:
298
+ field = value_columns[column]
299
+ _value_field, unit_field = _generated_field_names(field)
300
+ collapsed_row[field] = _build_collapsed_measurement(
301
+ field,
302
+ value,
303
+ row[unit_field],
304
+ )
305
+ continue
306
+ if column in unit_columns:
307
+ continue
308
+ collapsed_row[column] = value
309
+
310
+ return collapsed_row
311
+
312
+
313
+ def _coerce_measurement_value(
314
+ field: str,
315
+ value: object,
316
+ *,
317
+ explicit_field_names: set[str],
318
+ ) -> Measurement | None:
319
+ if _is_null_measurement_value(value):
320
+ return None
321
+ if isinstance(value, Measurement):
322
+ return value
323
+ if isinstance(value, str) and field in explicit_field_names:
324
+ try:
325
+ return Measurement.from_string(value)
326
+ except ValueError as error:
327
+ raise InvalidDataFrameMeasurementValueError(
328
+ field, allows_strings=True
329
+ ) from error
330
+
331
+ raise InvalidDataFrameMeasurementValueError(field, value_type=type(value).__name__)
332
+
333
+
334
+ def _build_collapsed_measurement(
335
+ field: str,
336
+ value: object,
337
+ unit: object,
338
+ ) -> Measurement | None:
339
+ value_is_null = _is_null_measurement_value(value)
340
+ unit_is_null = _is_null_measurement_value(unit)
341
+ if value_is_null and unit_is_null:
342
+ return None
343
+ if value_is_null or unit_is_null:
344
+ raise InvalidDataFrameMeasurementValueError(field)
345
+ if not isinstance(unit, str):
346
+ raise InvalidDataFrameMeasurementValueError(
347
+ field,
348
+ value_type=type(unit).__name__,
349
+ )
350
+
351
+ try:
352
+ magnitude = Decimal(str(value))
353
+ except (InvalidOperation, TypeError, ValueError) as error:
354
+ raise InvalidDataFrameMeasurementValueError(
355
+ field,
356
+ value_type=type(value).__name__,
357
+ ) from error
358
+ try:
359
+ return Measurement(magnitude, unit)
360
+ except pint.errors.PintError as error:
361
+ raise InvalidDataFrameMeasurementValueError(
362
+ field,
363
+ value_type=type(unit).__name__,
364
+ ) from error
365
+ except (TypeError, ValueError) as error:
366
+ raise InvalidDataFrameMeasurementValueError(
367
+ field,
368
+ value_type=type(value).__name__,
369
+ ) from error
370
+
371
+
372
+ def _is_null_measurement_value(value: object) -> bool:
373
+ if value is None:
374
+ return True
375
+ if _is_nan_like(value):
376
+ return True
377
+ return _is_pandas_null_sentinel(value)
378
+
379
+
380
+ def _is_nan_like(value: object) -> bool:
381
+ try:
382
+ if math.isnan(cast(Any, value)):
383
+ return True
384
+ except (TypeError, ValueError):
385
+ pass
386
+
387
+ try:
388
+ return bool(value != value)
389
+ except (TypeError, ValueError):
390
+ return False
391
+
392
+
393
+ def _is_pandas_null_sentinel(value: object) -> bool:
394
+ value_type = type(value)
395
+ return value_type.__name__ in {
396
+ "NAType",
397
+ "NaTType",
398
+ } and value_type.__module__.startswith("pandas.")