lex-app 2.2.0__tar.gz → 2.2.2__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 (685) hide show
  1. lex_app-2.2.2/MANIFEST.in +15 -0
  2. {lex_app-2.2.0/lex_app.egg-info → lex_app-2.2.2}/PKG-INFO +2 -1
  3. lex_app-2.2.2/lex/_version.py +1 -0
  4. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/serializers/base_serializers.py +50 -3
  5. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/List.py +16 -1
  6. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/models/CalculationLog.py +53 -0
  7. lex_app-2.2.2/lex/audit_logging/utils/latest_calculation.py +105 -0
  8. lex_app-2.2.2/lex/core/migrations/0001_bitemporal_activation.py +62 -0
  9. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/models/CalculationModel.py +11 -0
  10. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/services/MetaHistory.py +29 -0
  11. lex_app-2.2.2/lex/core/services/activation_applier.py +167 -0
  12. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/services/bitemporal_signals.py +40 -3
  13. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/settings.py +52 -3
  14. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/__init__.py +3 -1
  15. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/embed.py +333 -15
  16. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/sidebar.py +135 -64
  17. {lex_app-2.2.0 → lex_app-2.2.2}/lex/proxy.py +41 -25
  18. {lex_app-2.2.0 → lex_app-2.2.2}/lex/streamlit_app.py +22 -29
  19. {lex_app-2.2.0/lex/tests/e2e → lex_app-2.2.2/lex/test_project/tests}/_e2e_test_case.py +22 -0
  20. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/models.py +5 -0
  21. lex_app-2.2.2/lex/test_project/tests/calculation_logging/test_15i_latest_run_on_the_row.py +260 -0
  22. lex_app-2.2.2/lex/test_project/tests/calculation_logging/test_15j_failed_run_keeps_its_log.py +95 -0
  23. lex_app-2.2.2/lex/test_project/tests/history/test_5o_activation_applier.py +727 -0
  24. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ad_proxy_assets_and_session_durability.py +49 -9
  25. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1aj_streamlit_sidebar_chrome.py +133 -63
  26. lex_app-2.2.2/lex/test_project/tests/init/test_1an_proxy_http2_safe_headers.py +331 -0
  27. lex_app-2.2.2/lex/test_project/tests/init/test_1ao_flow_composable_steps.py +359 -0
  28. lex_app-2.2.2/lex/test_project/tests/init/test_1ao_frontend_resolution.py +101 -0
  29. {lex_app-2.2.0 → lex_app-2.2.2/lex_app.egg-info}/PKG-INFO +2 -1
  30. {lex_app-2.2.0 → lex_app-2.2.2}/lex_app.egg-info/SOURCES.txt +9 -19
  31. {lex_app-2.2.0 → lex_app-2.2.2}/lex_app.egg-info/requires.txt +1 -0
  32. {lex_app-2.2.0 → lex_app-2.2.2}/pyproject.toml +9 -1
  33. {lex_app-2.2.0 → lex_app-2.2.2}/requirements.txt +10 -0
  34. lex_app-2.2.0/MANIFEST.in +0 -7
  35. lex_app-2.2.0/lex/_version.py +0 -1
  36. lex_app-2.2.0/lex/react/build/.frontend-version.json +0 -6
  37. lex_app-2.2.0/lex/react/build/apple-touch-icon.png +0 -0
  38. lex_app-2.2.0/lex/react/build/assets/SessionAuthGate-BB6V9OL2.js +0 -647
  39. lex_app-2.2.0/lex/react/build/assets/SessionAuthGate-Cpfxv-bc.css +0 -1
  40. lex_app-2.2.0/lex/react/build/assets/embed-CCZwn8AA.js +0 -1
  41. lex_app-2.2.0/lex/react/build/assets/index.es-HIQvQaz0.js +0 -18
  42. lex_app-2.2.0/lex/react/build/assets/main-Cfrrc9ln.js +0 -730
  43. lex_app-2.2.0/lex/react/build/assets/main-D8qVAgsi.css +0 -1
  44. lex_app-2.2.0/lex/react/build/config.js +0 -8
  45. lex_app-2.2.0/lex/react/build/embed.html +0 -65
  46. lex_app-2.2.0/lex/react/build/favicon-16x16.png +0 -0
  47. lex_app-2.2.0/lex/react/build/favicon-32x32.png +0 -0
  48. lex_app-2.2.0/lex/react/build/favicon.ico +0 -0
  49. lex_app-2.2.0/lex/react/build/index.html +0 -291
  50. lex_app-2.2.0/lex/react/build/logo192.png +0 -0
  51. lex_app-2.2.0/lex/react/build/logo512.png +0 -0
  52. lex_app-2.2.0/lex/react/build/manifest.json +0 -25
  53. lex_app-2.2.0/lex/react/build/mockServiceWorker.js +0 -307
  54. lex_app-2.2.0/lex/react/build/robots.txt +0 -3
  55. {lex_app-2.2.0 → lex_app-2.2.2}/LICENSE.txt +0 -0
  56. {lex_app-2.2.0 → lex_app-2.2.2}/README.md +0 -0
  57. {lex_app-2.2.0 → lex_app-2.2.2}/generate_pycharm_configs.py +0 -0
  58. {lex_app-2.2.0 → lex_app-2.2.2}/lex/__init__.py +0 -0
  59. {lex_app-2.2.0 → lex_app-2.2.2}/lex/__main__.py +0 -0
  60. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/__init__.py +0 -0
  61. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/admin.py +0 -0
  62. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/apps.py +0 -0
  63. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/consumers/BackendHealthConsumer.py +0 -0
  64. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/consumers/CalculationLogConsumer.py +0 -0
  65. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/consumers/CalculationsConsumer.py +0 -0
  66. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/consumers/LogConsumer.py +0 -0
  67. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/consumers/ModelDataUpdateConsumer.py +0 -0
  68. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/consumers/UpdateCalculationStatusConsumer.py +0 -0
  69. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/consumers/__init__.py +0 -0
  70. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/filters/FilterTreeNode.py +0 -0
  71. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/filters/GenericFilters.py +0 -0
  72. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/filters/__init__.py +0 -0
  73. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/middleware/__init__.py +0 -0
  74. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/middleware/keycloak_permissions.py +0 -0
  75. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/migrations/__init__.py +0 -0
  76. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/models.py +0 -0
  77. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/serializers/__init__.py +0 -0
  78. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/serializers/sensitive_fields.py +0 -0
  79. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/utils/Context.py +0 -0
  80. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/utils/__init__.py +0 -0
  81. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/utils/api_key_requests.py +0 -0
  82. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/utils/collection_utils.py +0 -0
  83. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/utils/converters.py +0 -0
  84. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/utils/helpers.py +0 -0
  85. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/utils/temporal.py +0 -0
  86. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/LexLoggerView/LexLoggerView.py +0 -0
  87. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/LexLoggerView/__init__.py +0 -0
  88. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/ModelStructureObtainView.py +0 -0
  89. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/__init__.py +0 -0
  90. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/authentication/KeycloakManager.py +0 -0
  91. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/authentication/UserAPIView.py +0 -0
  92. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/authentication/__init__.py +0 -0
  93. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/calculations/ActiveCalculations.py +0 -0
  94. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/calculations/CleanCalculations.py +0 -0
  95. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/calculations/DownloadMarkdownPdf.py +0 -0
  96. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/calculations/InitCalculationLogs.py +0 -0
  97. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/calculations/__init__.py +0 -0
  98. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/file_operations/FileDownload.py +0 -0
  99. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/file_operations/ModelExport.py +0 -0
  100. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/file_operations/__init__.py +0 -0
  101. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/global_search_for_models/Search.py +0 -0
  102. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/global_search_for_models/__init__.py +0 -0
  103. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/lex_api/LexAPI.py +0 -0
  104. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/lex_api/__init__.py +0 -0
  105. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/CalculationLogTreeView.py +0 -0
  106. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/History.py +0 -0
  107. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/Many.py +0 -0
  108. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/One.py +0 -0
  109. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/__init__.py +0 -0
  110. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/filter_backends.py +0 -0
  111. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/mixins/DestroyOneWithPayloadMixin.py +0 -0
  112. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/mixins/ModelEntryProviderMixin.py +0 -0
  113. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/mixins/PermissionAwareSerializerMixin.py +0 -0
  114. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/mixins/__init__.py +0 -0
  115. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/serializers/CalculationLogTreeSerializer.py +0 -0
  116. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_entries/serializers/__init__.py +0 -0
  117. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_info/Fields.py +0 -0
  118. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_info/Widgets.py +0 -0
  119. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/model_info/__init__.py +0 -0
  120. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/pagination.py +0 -0
  121. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/permissions/ModelPermissions.py +0 -0
  122. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/permissions/UserPermission.py +0 -0
  123. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/permissions/__init__.py +0 -0
  124. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/process_flow/CreateOrUpdate.py +0 -0
  125. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/process_flow/__init__.py +0 -0
  126. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/project_info/ProjectInfo.py +0 -0
  127. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/project_info/__init__.py +0 -0
  128. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/rbac/RBACInfo.py +0 -0
  129. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/rbac/__init__.py +0 -0
  130. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/sharepoint/DeleteUnusedFiles.py +0 -0
  131. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/sharepoint/SharePointFileDownload.py +0 -0
  132. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/sharepoint/SharePointPreview.py +0 -0
  133. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/sharepoint/SharePointShareLink.py +0 -0
  134. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/sharepoint/__init__.py +0 -0
  135. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views/streamlit/__init__.py +0 -0
  136. {lex_app-2.2.0 → lex_app-2.2.2}/lex/api/views.py +0 -0
  137. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/dark-lex-logo.svg +0 -0
  138. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/lex-bg.jpg +0 -0
  139. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/lex-logo.png +0 -0
  140. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/lex-logo.svg +0 -0
  141. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/lex-test-report/email.html +0 -0
  142. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/lex-test-report/lex-icon.png +0 -0
  143. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/lex-test-report/lex-logo.png +0 -0
  144. {lex_app-2.2.0 → lex_app-2.2.2}/lex/assets/lex-test-report/pdf.html +0 -0
  145. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/__init__.py +0 -0
  146. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/admin.py +0 -0
  147. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/apps.py +0 -0
  148. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/handlers/ConsoleHandler.py +0 -0
  149. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/handlers/LexLogger.py +0 -0
  150. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/handlers/WebSocketHandler.py +0 -0
  151. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/handlers/__init__.py +0 -0
  152. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/0001_initial.py +0 -0
  153. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/0002_auditlog_created_at_auditlog_edited_at_and_more.py +0 -0
  154. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/0003_alter_auditlog_created_at_alter_auditlog_edited_at_and_more.py +0 -0
  155. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/0004_remove_auditlog_audit_fields.py +0 -0
  156. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/0005_calculationlog_calculationid_index.py +0 -0
  157. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/0006_alter_auditlog_calculation_id.py +0 -0
  158. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/0007_calculationlog_heading.py +0 -0
  159. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/migrations/__init__.py +0 -0
  160. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/mixins/AuditLogMixin.py +0 -0
  161. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/mixins/BulkAuditLogMixin.py +0 -0
  162. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/mixins/__init__.py +0 -0
  163. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/models/AuditLog.py +0 -0
  164. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/models/AuditLogStatus.py +0 -0
  165. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/models/__init__.py +0 -0
  166. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/models.py +0 -0
  167. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/serializers/AuditLogMixinSerializer.py +0 -0
  168. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/serializers/AuditLogSerializer.py +0 -0
  169. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/serializers/CalculationLogSerializer.py +0 -0
  170. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/serializers/__init__.py +0 -0
  171. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/tests/__init__.py +0 -0
  172. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/CacheManager.py +0 -0
  173. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/ContextResolver.py +0 -0
  174. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/DataModels.py +0 -0
  175. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/InitialDataAuditLogger.py +0 -0
  176. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/ModelContext.py +0 -0
  177. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/WebSocketNotifier.py +0 -0
  178. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/__init__.py +0 -0
  179. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/calculation_audit.py +0 -0
  180. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/config.py +0 -0
  181. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/content_types.py +0 -0
  182. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/utils/legacy_audit_payload.py +0 -0
  183. {lex_app-2.2.0 → lex_app-2.2.2}/lex/audit_logging/views.py +0 -0
  184. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/__init__.py +0 -0
  185. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/admin.py +0 -0
  186. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/apps.py +0 -0
  187. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/authentication_backends/BearerMiddlewareAuthentication.py +0 -0
  188. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/authentication_backends/__init__.py +0 -0
  189. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/__init__.py +0 -0
  190. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/commands/__init__.py +0 -0
  191. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/commands/createprofiles.py +0 -0
  192. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/commands/delete_keycloak_resources.py +0 -0
  193. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/commands/keycloak_backup.py +0 -0
  194. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/commands/keycloak_init_bak.py +0 -0
  195. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/commands/keycloak_rollback_bak.py +0 -0
  196. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/management/commands/register_keycloak_resources.py +0 -0
  197. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/middleware.py +0 -0
  198. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/migrations/0001_initial.py +0 -0
  199. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/migrations/__init__.py +0 -0
  200. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/models/Profile.py +0 -0
  201. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/models/__init__.py +0 -0
  202. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/models.py +0 -0
  203. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/utils/__init__.py +0 -0
  204. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/utils/lex_authentication.py +0 -0
  205. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/views/__init__.py +0 -0
  206. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/views/permissions.py +0 -0
  207. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/views/token_views.py +0 -0
  208. {lex_app-2.2.0 → lex_app-2.2.2}/lex/authentication/views/user_api.py +0 -0
  209. {lex_app-2.2.0 → lex_app-2.2.2}/lex/backfill_history_sql.py +0 -0
  210. {lex_app-2.2.0 → lex_app-2.2.2}/lex/bin/__init__.py +0 -0
  211. {lex_app-2.2.0 → lex_app-2.2.2}/lex/bin/lex.py +0 -0
  212. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/__init__.py +0 -0
  213. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/admin.py +0 -0
  214. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/apps.py +0 -0
  215. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/calculated_updates/ObjectsToRecalculateStore.py +0 -0
  216. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/calculated_updates/__init__.py +0 -0
  217. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/calculated_updates/update_handler.py +0 -0
  218. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/cancellation/__init__.py +0 -0
  219. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/cancellation/cluster_cancel_index.py +0 -0
  220. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/config.py +0 -0
  221. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/exceptions.py +0 -0
  222. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/fields/PDF_field.py +0 -0
  223. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/fields/XLSX_field.py +0 -0
  224. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/fields/__init__.py +0 -0
  225. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/management/__init__.py +0 -0
  226. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/management/commands/__init__.py +0 -0
  227. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/management/commands/bootstrap_callback_server.py +0 -0
  228. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/middleware/__init__.py +0 -0
  229. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/middleware/embed_token_auth.py +0 -0
  230. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/middleware/embed_xframe.py +0 -0
  231. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/migrations/__init__.py +0 -0
  232. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/mixins/CalculatedModelMixin.py +0 -0
  233. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/mixins/ModelModificationRestriction.py +0 -0
  234. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/mixins/__init__.py +0 -0
  235. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/models/HTMLReport.py +0 -0
  236. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/models/LexModel.py +0 -0
  237. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/models/Process.py +0 -0
  238. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/models/__init__.py +0 -0
  239. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/models.py +0 -0
  240. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/services/Bitemporal.py +0 -0
  241. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/services/StandardHistory.py +0 -0
  242. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/services/__init__.py +0 -0
  243. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/services/signal_registry.py +0 -0
  244. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/signals/ActiveCalculationStateStore.py +0 -0
  245. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/signals/CalculationSignals.py +0 -0
  246. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/signals/ModelMutationSignal.py +0 -0
  247. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/signals/__init__.py +0 -0
  248. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/tasks/CeleryTaskDispatcher.py +0 -0
  249. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/tasks/__init__.py +0 -0
  250. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/tests/__init__.py +0 -0
  251. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/tests/_bitemporal_test_case.py +0 -0
  252. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/tests/bitemporal_test_settings.py +0 -0
  253. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/tests/check_schema.py +0 -0
  254. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/tests/test_calculation_history_transitions.py +0 -0
  255. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/transactions/__init__.py +0 -0
  256. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/transactions/transactions.py +0 -0
  257. {lex_app-2.2.0 → lex_app-2.2.2}/lex/core/views.py +0 -0
  258. {lex_app-2.2.0 → lex_app-2.2.2}/lex/extract_legacy_logs.py +0 -0
  259. {lex_app-2.2.0 → lex_app-2.2.2}/lex/fix_v1_migration.py +0 -0
  260. {lex_app-2.2.0 → lex_app-2.2.2}/lex/full_migration_workflow.py +0 -0
  261. {lex_app-2.2.0 → lex_app-2.2.2}/lex/helpers/__init__.py +0 -0
  262. {lex_app-2.2.0 → lex_app-2.2.2}/lex/helpers/cache_tables.py +0 -0
  263. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/__init__.py +0 -0
  264. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/admin/__init__.py +0 -0
  265. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/admin/legacy_calculation_id_admin.py +0 -0
  266. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/admin/legacy_calculation_log_admin.py +0 -0
  267. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/admin/legacy_log_admin.py +0 -0
  268. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/admin/legacy_user_change_log_admin.py +0 -0
  269. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/admin/read_only_admin.py +0 -0
  270. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/apps.py +0 -0
  271. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/migrations/0001_initial.py +0 -0
  272. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/migrations/0002_alter_legacycalculationid_options_and_more.py +0 -0
  273. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/migrations/0003_legacylog.py +0 -0
  274. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/migrations/__init__.py +0 -0
  275. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/models/__init__.py +0 -0
  276. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/models/legacy_calculation_id.py +0 -0
  277. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/models/legacy_calculation_log.py +0 -0
  278. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/models/legacy_log.py +0 -0
  279. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/models/legacy_user_change_log.py +0 -0
  280. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/serializers/__init__.py +0 -0
  281. {lex_app-2.2.0 → lex_app-2.2.2}/lex/legacy_data/serializers/legacy_data_serializers.py +0 -0
  282. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/__init__.py +0 -0
  283. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/admin.py +0 -0
  284. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/apps.py +0 -0
  285. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/asgi.py +0 -0
  286. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery.py +0 -0
  287. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_recovery/__init__.py +0 -0
  288. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_recovery/entrypoint.py +0 -0
  289. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_recovery/exceptions.py +0 -0
  290. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_recovery/heartbeat.py +0 -0
  291. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_recovery/redis_keys.py +0 -0
  292. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_recovery/registry.py +0 -0
  293. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_recovery/supervisor.py +0 -0
  294. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/celery_tasks.py +0 -0
  295. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/design_system/__init__.py +0 -0
  296. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/design_system/lex_tokens.py +0 -0
  297. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/fast_health.py +0 -0
  298. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/keycloak_exclusions.py +0 -0
  299. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/__init__.py +0 -0
  300. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/Init2.py +0 -0
  301. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/__init__.py +0 -0
  302. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/backfill_audit_logging.py +0 -0
  303. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/backfill_bitemporal_history.py +0 -0
  304. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/bootstrap_keycloak.py +0 -0
  305. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/capture_db_tables.py +0 -0
  306. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/capture_migration_state.py +0 -0
  307. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/create_db.py +0 -0
  308. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/detect_model_changes.py +0 -0
  309. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/full_migration_workflow.py +0 -0
  310. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/generate_legacy_freeze_manifest.py +0 -0
  311. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/init.py +0 -0
  312. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/lex_migrate.py +0 -0
  313. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/normalize_is_calculated.py +0 -0
  314. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/rebase_incident_datetimes.py +0 -0
  315. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/rollback_migration_state.py +0 -0
  316. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/run_recovery_supervisor.py +0 -0
  317. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/sync_keycloak.py +0 -0
  318. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/tests/__init__.py +0 -0
  319. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/tests/test_backfill_audit_logging_command.py +0 -0
  320. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/tests/test_backfill_audit_logging_payload.py +0 -0
  321. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/tests/test_bootstrap_callback_server.py +0 -0
  322. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/tests/test_init_command.py +0 -0
  323. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/tests/test_lex_pytest_report_flags.py +0 -0
  324. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/management/commands/tests/test_migration_workflow_commands.py +0 -0
  325. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/migrations/0001_initial.py +0 -0
  326. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/migrations/0002_delete_asoftestmodel_and_more.py +0 -0
  327. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/migrations/__init__.py +0 -0
  328. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/models.py +0 -0
  329. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/routing.py +0 -0
  330. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/runtime_health.py +0 -0
  331. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/simple_history_config.py +0 -0
  332. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/Streamlit.py +0 -0
  333. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/_lex_view_component/__init__.py +0 -0
  334. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/_lex_view_component/frontend/index.html +0 -0
  335. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/_widget_host_component/__init__.py +0 -0
  336. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/_widget_host_component/frontend/index.html +0 -0
  337. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/eager_frames.py +0 -0
  338. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/examples/__init__.py +0 -0
  339. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/examples/_streamlit_structure.py +0 -0
  340. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/quackback.py +0 -0
  341. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/theme/__init__.py +0 -0
  342. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/theme/config_writer.py +0 -0
  343. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/theme/mapping.py +0 -0
  344. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/theme/tokens.py +0 -0
  345. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/widgets/__init__.py +0 -0
  346. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/widgets/host.py +0 -0
  347. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/widgets/keys.py +0 -0
  348. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/streamlit/widgets/spec.py +0 -0
  349. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/tests/ProcessAdminTestCase.py +0 -0
  350. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/tests/__init__.py +0 -0
  351. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/tests/create_db.py +0 -0
  352. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/urls.py +0 -0
  353. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/utils.py +0 -0
  354. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/views.py +0 -0
  355. {lex_app-2.2.0 → lex_app-2.2.2}/lex/lex_app/wsgi.py +0 -0
  356. {lex_app-2.2.0 → lex_app-2.2.2}/lex/manage.py +0 -0
  357. {lex_app-2.2.0 → lex_app-2.2.2}/lex/mcp_server/tools/embed.py +0 -0
  358. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/__init__.py +0 -0
  359. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/admin.py +0 -0
  360. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/apps.py +0 -0
  361. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/migrations/__init__.py +0 -0
  362. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/models/ModelCollection.py +0 -0
  363. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/models/ModelContainer.py +0 -0
  364. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/models/ModelProcessAdmin.py +0 -0
  365. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/models/__init__.py +0 -0
  366. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/models/constants.py +0 -0
  367. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/models/utils.py +0 -0
  368. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/models.py +0 -0
  369. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/settings/__init__.py +0 -0
  370. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/settings/process_admin_settings.py +0 -0
  371. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/sites/__init__.py +0 -0
  372. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/sites/process_admin_site.py +0 -0
  373. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/tests/__init__.py +0 -0
  374. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/tests/django_test_settings.py +0 -0
  375. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/tests/urls.py +0 -0
  376. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/utils/__init__.py +0 -0
  377. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/utils/bitemporal_sync.py +0 -0
  378. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/utils/local_scheduler.py +0 -0
  379. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/utils/model_registration.py +0 -0
  380. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/utils/model_structure.py +0 -0
  381. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/utils/model_structure_builder.py +0 -0
  382. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/views/__init__.py +0 -0
  383. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/views/model_relation_views.py +0 -0
  384. {lex_app-2.2.0 → lex_app-2.2.2}/lex/process_admin/views.py +0 -0
  385. {lex_app-2.2.0 → lex_app-2.2.2}/lex/react/admin.py +0 -0
  386. {lex_app-2.2.0 → lex_app-2.2.2}/lex/react/apps.py +0 -0
  387. {lex_app-2.2.0 → lex_app-2.2.2}/lex/react/migrations/__init__.py +0 -0
  388. {lex_app-2.2.0 → lex_app-2.2.2}/lex/react/models.py +0 -0
  389. {lex_app-2.2.0 → lex_app-2.2.2}/lex/react/tests.py +0 -0
  390. {lex_app-2.2.0 → lex_app-2.2.2}/lex/react/views.py +0 -0
  391. {lex_app-2.2.0 → lex_app-2.2.2}/lex/restore_legacy_logs.py +0 -0
  392. {lex_app-2.2.0 → lex_app-2.2.2}/lex/runtime_config.py +0 -0
  393. {lex_app-2.2.0 → lex_app-2.2.2}/lex/sanitize_v2_migrations.py +0 -0
  394. {lex_app-2.2.0 → lex_app-2.2.2}/lex/streamlit_theme.py +0 -0
  395. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/__init__.py +0 -0
  396. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/apps.py +0 -0
  397. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/lex_config.py +0 -0
  398. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/__init__.py +0 -0
  399. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/_authenticated_e2e_test_case.py +0 -0
  400. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/__init__.py +0 -0
  401. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/models.py +0 -0
  402. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10a_crud_endpoints.py +0 -0
  403. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10b_history_endpoint.py +0 -0
  404. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10c_many_endpoint.py +0 -0
  405. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10e_schema_introspection.py +0 -0
  406. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10f_global_search.py +0 -0
  407. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10g_one_endpoint_lifecycle.py +0 -0
  408. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10h_lex_api_sdk.py +0 -0
  409. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10i_fields_view_and_list_ui.py +0 -0
  410. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10j_filter_backends_and_storage.py +0 -0
  411. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10k_destroy_with_payload_mixin.py +0 -0
  412. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10l_file_download_view.py +0 -0
  413. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10m_calculation_log_tree.py +0 -0
  414. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/api_layer/test_10n_active_calculations_endpoint.py +0 -0
  415. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/__init__.py +0 -0
  416. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/extra_models.py +0 -0
  417. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/models.py +0 -0
  418. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6a_api_audit.py +0 -0
  419. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6b_calculation_audit.py +0 -0
  420. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6c_actor_resolution.py +0 -0
  421. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6d_payload_and_gfk.py +0 -0
  422. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6e_bulk_audit.py +0 -0
  423. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6f_resilience.py +0 -0
  424. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6g_immutability.py +0 -0
  425. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6h_cache_manager.py +0 -0
  426. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6i_calculation_via_api.py +0 -0
  427. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6j_audit_utils.py +0 -0
  428. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6k_model_contracts.py +0 -0
  429. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6l_context_and_gfk_resolution.py +0 -0
  430. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6m_lexlogger_and_websocket.py +0 -0
  431. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6n_audit_log_serializers.py +0 -0
  432. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6o_bulk_audit_normalize.py +0 -0
  433. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/audit_logging/test_6q_root_detection_with_headings.py +0 -0
  434. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/__init__.py +0 -0
  435. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15a_builder_api.py +0 -0
  436. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15b_child_node_creation.py +0 -0
  437. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15c_silent_and_mixed.py +0 -0
  438. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15d_three_level_chain.py +0 -0
  439. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15e_combinatorial_pipeline.py +0 -0
  440. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15f_save_boundary.py +0 -0
  441. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15g_heading_context.py +0 -0
  442. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculation_logging/test_15h_pdf_export.py +0 -0
  443. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/__init__.py +0 -0
  444. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/models.py +0 -0
  445. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7a_atomic.py +0 -0
  446. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7b_non_atomic.py +0 -0
  447. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7c_hierarchy.py +0 -0
  448. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7d_state_guards.py +0 -0
  449. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7e_persistence_internals.py +0 -0
  450. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7f_combination_engine.py +0 -0
  451. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7g_create_pipeline.py +0 -0
  452. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7h_celery_dispatch.py +0 -0
  453. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7i_atomicity_matrix.py +0 -0
  454. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7j_three_level_matrix.py +0 -0
  455. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7k_exceptions_restrictions_xlsx.py +0 -0
  456. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7l_recalc_queue.py +0 -0
  457. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7m_calc_signals.py +0 -0
  458. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7n_cancellation.py +0 -0
  459. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7o_fk_violation_abort.py +0 -0
  460. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7p_streaming_expansion.py +0 -0
  461. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7q_worker_default_dispatch.py +0 -0
  462. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/calculations/test_7s_calculation_audit_columns.py +0 -0
  463. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/__init__.py +0 -0
  464. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/models.py +0 -0
  465. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8a_sync_fallback.py +0 -0
  466. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8aa_postrun_shutdown_guard.py +0 -0
  467. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8ab_dispatcher_join_worker_safe.py +0 -0
  468. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8ac_sync_fallback_idempotent.py +0 -0
  469. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8ad_dispatched_task_cancel_marker.py +0 -0
  470. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8b_dispatch_context.py +0 -0
  471. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8c_calculation_audit_columns.py +0 -0
  472. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8g_task_infrastructure.py +0 -0
  473. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8h_eager_end_to_end.py +0 -0
  474. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8i_scope_contracts.py +0 -0
  475. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8j_task_bodies.py +0 -0
  476. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8k_redis_broker_integration.py +0 -0
  477. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8l_celery_dispatcher.py +0 -0
  478. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8m_undecorated_dispatch.py +0 -0
  479. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8u_cancel_revoke.py +0 -0
  480. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8v_cluster_cascade_cancel.py +0 -0
  481. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8w_recovery_terminal_guard.py +0 -0
  482. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8x_startup_reset_recovery_handoff.py +0 -0
  483. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/celery_async/test_8y_beat_recovery_driver.py +0 -0
  484. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/__init__.py +0 -0
  485. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/models.py +0 -0
  486. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2a_create.py +0 -0
  487. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2b_read.py +0 -0
  488. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2c_update.py +0 -0
  489. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2d_delete.py +0 -0
  490. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2e_bulk.py +0 -0
  491. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2f_missing_scenarios.py +0 -0
  492. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2f_ordering.py +0 -0
  493. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2g_filtering.py +0 -0
  494. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2i_cancel_endpoint.py +0 -0
  495. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/crud_api/test_2j_instance_api_key.py +0 -0
  496. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/design_system/__init__.py +0 -0
  497. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/design_system/test_lex_tokens.py +0 -0
  498. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/__init__.py +0 -0
  499. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/models.py +0 -0
  500. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13a_legacy_export.py +0 -0
  501. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13b_ag_grid_export.py +0 -0
  502. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13c_grouped_selected.py +0 -0
  503. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13d_auth_edge.py +0 -0
  504. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13e_streaming_fast_export.py +0 -0
  505. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13f_export_timezone.py +0 -0
  506. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13g_xlsx_field_timezone.py +0 -0
  507. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/exports/test_13h_xlsx_field_binding.py +0 -0
  508. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/fixtures/__init__.py +0 -0
  509. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/gate_selftest/__init__.py +0 -0
  510. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/gate_selftest/test_gate_selftest.py +0 -0
  511. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/__init__.py +0 -0
  512. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/models.py +0 -0
  513. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5_11_fallback_snapshot.py +0 -0
  514. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5a_basic_history.py +0 -0
  515. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5b_calculation_history.py +0 -0
  516. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5c_history_api.py +0 -0
  517. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5g_valid_to_chaining.py +0 -0
  518. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5h_suppression_toolkit.py +0 -0
  519. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5i_api_contract.py +0 -0
  520. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5j_snapshot_actor.py +0 -0
  521. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5k_metahistory.py +0 -0
  522. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5l_future_valid_from.py +0 -0
  523. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/history/test_5m_asof_edit_time.py +0 -0
  524. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/__init__.py +0 -0
  525. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/initial_data/__init__.py +0 -0
  526. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/initial_data/test_1c_initial_data.py +0 -0
  527. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/initial_data/test_1f_seed_idempotency.py +0 -0
  528. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/initial_data/test_1i_initial_data_journey.py +0 -0
  529. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/models.py +0 -0
  530. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1a_lex_setup.py +0 -0
  531. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1aa_embedded_token_renewal.py +0 -0
  532. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ab_ignored_role_policy_cleanup.py +0 -0
  533. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ac_streamlit_session_survival.py +0 -0
  534. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ac_widget_host_manifest.py +0 -0
  535. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ad_streamlit_theme_follower.py +0 -0
  536. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ae_streamlit_theme.py +0 -0
  537. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1af_theme_switch_preserves_brand.py +0 -0
  538. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ag_spa_asset_caching.py +0 -0
  539. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ah_metadata_cache_headers.py +0 -0
  540. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ai_eager_component_frames.py +0 -0
  541. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1ak_widget_keys_and_flow.py +0 -0
  542. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1al_example_dashboard.py +0 -0
  543. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1am_quackback_launcher.py +0 -0
  544. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1b_default_authz.py +0 -0
  545. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1b_lex_init.py +0 -0
  546. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1d_init_helpers.py +0 -0
  547. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1e_keycloak_sync_manager.py +0 -0
  548. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1f_keycloak_drift.py +0 -0
  549. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1g_sync_manager_admin.py +0 -0
  550. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1h_bootstrap_flow.py +0 -0
  551. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1i_rebase_incident_datetimes.py +0 -0
  552. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1j_client_preflight.py +0 -0
  553. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1k_client_preflight_integration.py +0 -0
  554. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1l_full_pipeline_integration.py +0 -0
  555. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1m_cli_commands.py +0 -0
  556. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1n_init_helper_paths.py +0 -0
  557. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1o_lazy_imports_and_helpers.py +0 -0
  558. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1p_settings_urls_views.py +0 -0
  559. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1q_migration_files_complete.py +0 -0
  560. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1r_fetched_datetime_zone.py +0 -0
  561. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1s_log_cleanup_and_lex_debug.py +0 -0
  562. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1t_disable_server_side_cursors.py +0 -0
  563. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1u_fast_health_asgi.py +0 -0
  564. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1v_scheduler_tz_invariant.py +0 -0
  565. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1w_recovery_default_deployment_target.py +0 -0
  566. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1x_runtime_health_metadata.py +0 -0
  567. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1y_ide_run_configs.py +0 -0
  568. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/init/test_1z_proxy_iframe_breakout.py +0 -0
  569. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/journeys/__init__.py +0 -0
  570. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/journeys/models.py +0 -0
  571. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/journeys/test_journey_a_invoice_lifecycle.py +0 -0
  572. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/journeys/test_journey_b_portfolio_calculation.py +0 -0
  573. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/journeys/test_journey_c_role_aware_visibility.py +0 -0
  574. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/journeys/test_journey_d_audit_trail.py +0 -0
  575. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/journeys/test_journey_e_validation_gate.py +0 -0
  576. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/__init__.py +0 -0
  577. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/models.py +0 -0
  578. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4a_field_level.py +0 -0
  579. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4b_action_level.py +0 -0
  580. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4c_keycloak_fallback.py +0 -0
  581. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4d_user_context.py +0 -0
  582. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4e_filter_backend.py +0 -0
  583. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4f_serializer_mixin.py +0 -0
  584. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4g_user_permissions_api.py +0 -0
  585. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4h_full_deny_matrix.py +0 -0
  586. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4i_permission_helpers.py +0 -0
  587. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4j_filter_backends.py +0 -0
  588. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/permissions/test_4m_api_key_middleware.py +0 -0
  589. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/queries/__init__.py +0 -0
  590. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/queries/models.py +0 -0
  591. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/queries/test_14a_query_params.py +0 -0
  592. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/queries/test_14b_ag_post_filter_model.py +0 -0
  593. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/queries/test_14c_ag_post_group_pivot.py +0 -0
  594. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/queries/test_14e_secondary_branches.py +0 -0
  595. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/queries/test_14h_list_query_paths.py +0 -0
  596. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/__init__.py +0 -0
  597. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/models.py +0 -0
  598. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12a_history_meta_scopes.py +0 -0
  599. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12a_read_contract.py +0 -0
  600. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12b_type_roundtrip.py +0 -0
  601. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12c_list_and_many.py +0 -0
  602. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12d_audit_log_payload.py +0 -0
  603. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12e_factory.py +0 -0
  604. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12f_write_paths.py +0 -0
  605. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12g_datetime_tz_ambiguity.py +0 -0
  606. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12h_filefield_clear.py +0 -0
  607. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12i_fk_display_names.py +0 -0
  608. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12j_datetime_roundtrip_convention.py +0 -0
  609. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/serializers/test_12k_sensitive_field_denylist.py +0 -0
  610. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/__init__.py +0 -0
  611. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/models.py +0 -0
  612. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/test_9_7_suppression_guards.py +0 -0
  613. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/test_9a_state_and_broadcast.py +0 -0
  614. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/test_9c_cache_cleanup.py +0 -0
  615. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/test_9d_active_calculation_state_store.py +0 -0
  616. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/test_9e_model_mutation_broadcast.py +0 -0
  617. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/signals_ws/test_9f_core_consumers.py +0 -0
  618. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/__init__.py +0 -0
  619. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/_stress_test_case.py +0 -0
  620. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/models.py +0 -0
  621. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11a_seed_baseline.py +0 -0
  622. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11b_list.py +0 -0
  623. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11c_export.py +0 -0
  624. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11d_period_calc.py +0 -0
  625. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11e_bulk_api.py +0 -0
  626. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11f_growth_and_scale.py +0 -0
  627. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11g_fk_heavy_export.py +0 -0
  628. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11h_calc_log_overhead.py +0 -0
  629. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/stress/test_11i_save_fingerprint.py +0 -0
  630. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/__init__.py +0 -0
  631. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/models.py +0 -0
  632. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/test_3a_pre_validation.py +0 -0
  633. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/test_3b_post_validation.py +0 -0
  634. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/test_3c_hook_ordering.py +0 -0
  635. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/test_3d_recursion_guard.py +0 -0
  636. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/test_3e_snapshot_lifecycle.py +0 -0
  637. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/test_3f_lean_initial_state.py +0 -0
  638. {lex_app-2.2.0 → lex_app-2.2.2}/lex/test_project/tests/validation_hooks/test_3g_aware_datetime_assignment.py +0 -0
  639. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/e2e/__init__.py +0 -0
  640. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/e2e/_authenticated_e2e_test_case.py +0 -0
  641. {lex_app-2.2.0/lex/test_project/tests → lex_app-2.2.2/lex/tests/e2e}/_e2e_test_case.py +0 -0
  642. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/calculation/test_calculated_model_mixin.py +0 -0
  643. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/calculation/test_calculation_wait_contexts.py +0 -0
  644. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/infra/test_ai_asset_mode_sync.py +0 -0
  645. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/infra/test_ai_update_bootstrap.py +0 -0
  646. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/infra/test_ai_windows_parity.py +0 -0
  647. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/infra/test_celery_recovery.py +0 -0
  648. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/infra/test_mcp_server_sdk_compat.py +0 -0
  649. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tests/unit/infra/test_worker_self_termination.py +0 -0
  650. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/__init__.py +0 -0
  651. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/ai_dashboard.py +0 -0
  652. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/ai_faq.py +0 -0
  653. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/ai_issue_report.py +0 -0
  654. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/ai_worktree.py +0 -0
  655. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/mcp_mode_invoke.py +0 -0
  656. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/project_root.py +0 -0
  657. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/setup_with_ai.py +0 -0
  658. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/test_groups.py +0 -0
  659. {lex_app-2.2.0 → lex_app-2.2.2}/lex/tools/verify_ai_assets.py +0 -0
  660. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/__init__.py +0 -0
  661. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/admin.py +0 -0
  662. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/apps.py +0 -0
  663. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/channel_layer.py +0 -0
  664. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/config/__init__.py +0 -0
  665. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/config/generic_app_config.py +0 -0
  666. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/decorators/__init__.py +0 -0
  667. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/decorators/singleton.py +0 -0
  668. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/import_system/__init__.py +0 -0
  669. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/import_system/import_utils.py +0 -0
  670. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/import_system/model_loader.py +0 -0
  671. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/import_system/module_finder.py +0 -0
  672. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/migrations/__init__.py +0 -0
  673. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/models.py +0 -0
  674. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/storage/__init__.py +0 -0
  675. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/storage/custom_storage.py +0 -0
  676. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/utils/__init__.py +0 -0
  677. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/utils/gcs_utils.py +0 -0
  678. {lex_app-2.2.0 → lex_app-2.2.2}/lex/utilities/views.py +0 -0
  679. {lex_app-2.2.0 → lex_app-2.2.2}/lex_app.egg-info/dependency_links.txt +0 -0
  680. {lex_app-2.2.0 → lex_app-2.2.2}/lex_app.egg-info/entry_points.txt +0 -0
  681. {lex_app-2.2.0 → lex_app-2.2.2}/lex_app.egg-info/top_level.txt +0 -0
  682. {lex_app-2.2.0 → lex_app-2.2.2}/migrations_logic/__init__.py +0 -0
  683. {lex_app-2.2.0 → lex_app-2.2.2}/migrations_logic/fix_migrations.py +0 -0
  684. {lex_app-2.2.0 → lex_app-2.2.2}/setup.cfg +0 -0
  685. {lex_app-2.2.0 → lex_app-2.2.2}/setup.py +0 -0
@@ -0,0 +1,15 @@
1
+ recursive-include lex/react *
2
+ # The compiled SPA is NOT shipped in the distribution. It arrives as the
3
+ # lex-app-frontend dependency, pinned in requirements.txt, and shipping a
4
+ # second copy meant every release carried a frozen bundle that nothing served
5
+ # — 57% of the wheel, stale from the day it was built.
6
+ #
7
+ # It stays in the source tree: settings.py falls back to it so a git checkout
8
+ # works without installing the frontend package.
9
+ recursive-exclude lex/react/build *
10
+ recursive-include lex/assets *
11
+ include pyproject.toml
12
+ include requirements*.txt
13
+ include generate_pycharm_configs.py
14
+ global-exclude __pycache__ *.py[cod] .DS_Store
15
+ recursive-exclude * __pycache__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lex-app
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: A Python / Django library to create business applications easily with complex logic
5
5
  Author-email: Melih Sünbül <m.sunbul@excellence-cloud.com>
6
6
  License-Expression: MIT
@@ -60,6 +60,7 @@ Requires-Dist: psycopg2-binary
60
60
  Requires-Dist: django-celery-beat
61
61
  Requires-Dist: pytest
62
62
  Requires-Dist: cryptography
63
+ Requires-Dist: lex-app-frontend~=2.2.0
63
64
  Dynamic: license-file
64
65
 
65
66
  LEX App Documentation - Previously known as DPAG :)
@@ -0,0 +1 @@
1
+ __version__ = "2.2.2"
@@ -7,6 +7,7 @@ from django.db.models import Model, ForeignKey
7
7
  from django.db.models.fields import DateTimeField, DateField, TimeField
8
8
  from lex.api.utils.helpers import can_read_with_default_permission_scope
9
9
  from lex.audit_logging.utils.content_types import safe_get_content_type
10
+ from lex.audit_logging.utils.latest_calculation import is_calculation_model
10
11
  from lex.core.models.LexModel import LexModel, UserContext
11
12
  from rest_framework import serializers, viewsets
12
13
 
@@ -21,6 +22,10 @@ from lex.api.serializers.sensitive_fields import (
21
22
  ID_FIELD_NAME = "id_field"
22
23
  SHORT_DESCR_NAME = "short_description"
23
24
  LEX_SCOPES_NAME = "lex_reserved_scopes"
25
+ # The newest run a calculation row started, and whether it has one. Declared
26
+ # only on serializers of CalculationModel subclasses; see _calculation_run_fields.
27
+ CALCULATION_ID_NAME = "lex_reserved_calculation_id"
28
+ HAS_CALCULATION_LOG_NAME = "lex_reserved_has_calculation_log"
24
29
 
25
30
  # --- MODULE-LEVEL CACHES (populated lazily, persist for process lifetime) ---
26
31
 
@@ -39,6 +44,28 @@ def _get_lexmodel_fields() -> set:
39
44
  return _lexmodel_fields
40
45
 
41
46
 
47
+ def _calculation_run_fields(model) -> dict:
48
+ """The two latest-run fields, for calculation models only.
49
+
50
+ Values are read from attributes the list view sets for a whole page at
51
+ once (``annotate_latest_calculation``). Nothing here queries per row.
52
+ """
53
+ if not is_calculation_model(model):
54
+ return {}
55
+ # Explicit method names, deliberately not the get_<field> defaults:
56
+ # _wrap_custom_serializer builds (LexSerializer, custom_cls), so a method
57
+ # named get_lex_reserved_has_calculation_log on LexSerializer would come
58
+ # first in the MRO and shadow AuditLogDefaultSerializer's own getter.
59
+ return {
60
+ CALCULATION_ID_NAME: serializers.SerializerMethodField(
61
+ method_name="_lex_latest_calculation_id"
62
+ ),
63
+ HAS_CALCULATION_LOG_NAME: serializers.SerializerMethodField(
64
+ method_name="_lex_has_latest_calculation_log"
65
+ ),
66
+ }
67
+
68
+
42
69
  # Cache: model-name -> model-class lookup for _resolve_target_model
43
70
  _model_lookup: dict | None = None
44
71
 
@@ -364,6 +391,17 @@ class LexSerializer(serializers.ModelSerializer):
364
391
 
365
392
  return target
366
393
 
394
+ # ------------------------------------------------------------------
395
+ # Latest calculation run — read through method_name, see _calculation_run_fields
396
+ # ------------------------------------------------------------------
397
+ def _lex_latest_calculation_id(self, instance):
398
+ """The newest run started from this row, when the list resolved it."""
399
+ return getattr(instance, "_lex_calculation_id", None)
400
+
401
+ def _lex_has_latest_calculation_log(self, instance):
402
+ """Whether that run exists. Unannotated rows answer False, never a query."""
403
+ return bool(getattr(instance, "_has_calculation_log", False))
404
+
367
405
  # ------------------------------------------------------------------
368
406
  # Scopes computation
369
407
  # ------------------------------------------------------------------
@@ -680,7 +718,10 @@ class LexSerializer(serializers.ModelSerializer):
680
718
  _SYSTEM_FIELDS = frozenset({
681
719
  'history_id', 'history_date', 'history_type', 'history_user', 'history_change_reason',
682
720
  'valid_from', 'valid_to',
683
- 'calculation_record', 'lex_reserved_scopes', 'id', 'id_field', SHORT_DESCR_NAME
721
+ 'calculation_record', 'lex_reserved_scopes', 'id', 'id_field', SHORT_DESCR_NAME,
722
+ # Without these two, to_representation's visibility filter strips the
723
+ # run fields silently and the log button never appears.
724
+ CALCULATION_ID_NAME, HAS_CALCULATION_LOG_NAME,
684
725
  })
685
726
 
686
727
  def to_representation(self, instance):
@@ -870,13 +911,17 @@ def model2serializer(model, fields=None, name_suffix=""):
870
911
  pk_alias = serializers.ReadOnlyField(default=model._meta.pk.name)
871
912
 
872
913
  # ensure our internal fields are always present
873
- all_fields = list(fields) + [ID_FIELD_NAME, SHORT_DESCR_NAME, "id", LEX_SCOPES_NAME]
914
+ run_fields = _calculation_run_fields(model)
915
+ all_fields = list(fields) + [
916
+ ID_FIELD_NAME, SHORT_DESCR_NAME, "id", LEX_SCOPES_NAME, *run_fields,
917
+ ]
874
918
 
875
919
  return type(
876
920
  class_name,
877
921
  (RestApiModelSerializerTemplate,),
878
922
  {
879
923
  ID_FIELD_NAME: pk_alias,
924
+ **run_fields,
880
925
  "Meta": type(
881
926
  "Meta",
882
927
  (RestApiModelSerializerTemplate.Meta,),
@@ -888,11 +933,12 @@ def model2serializer(model, fields=None, name_suffix=""):
888
933
 
889
934
  def _wrap_custom_serializer(custom_cls, model_class):
890
935
  meta = getattr(custom_cls, "Meta", type("Meta", (), {}))
936
+ run_fields = _calculation_run_fields(model_class)
891
937
  existing_fields = getattr(meta, "fields", "__all__")
892
938
  if existing_fields != "__all__":
893
939
  existing = list(existing_fields)
894
940
  # make sure all internal fields are present, including lex_reserved_scopes
895
- for extra in (ID_FIELD_NAME, SHORT_DESCR_NAME, "id", LEX_SCOPES_NAME):
941
+ for extra in (ID_FIELD_NAME, SHORT_DESCR_NAME, "id", LEX_SCOPES_NAME, *run_fields):
896
942
  if extra not in existing:
897
943
  existing.append(extra)
898
944
  new_fields = existing
@@ -935,6 +981,7 @@ def _wrap_custom_serializer(custom_cls, model_class):
935
981
  ID_FIELD_NAME: serializers.ReadOnlyField(default=model_class._meta.pk.name),
936
982
  SHORT_DESCR_NAME: serializers.SerializerMethodField(),
937
983
  "get_short_description": lambda self, obj: str(obj),
984
+ **run_fields,
938
985
  "Meta": NewMeta,
939
986
  }
940
987
  # Use a bare ``ReadOnlyField()`` (DRF defaults ``source`` to the field
@@ -892,7 +892,22 @@ class ListModelEntries(ModelEntryProviderMixin, ListAPIView):
892
892
 
893
893
  row_count = qs.count()
894
894
  page_qs = qs[start_row:end_row]
895
- serializer = self.get_serializer(page_qs, many=True)
895
+
896
+ from lex.audit_logging.utils.latest_calculation import (
897
+ annotate_latest_calculation,
898
+ is_calculation_model,
899
+ )
900
+
901
+ if is_calculation_model(self._ag_model_class):
902
+ # A calculation row carries only `is_calculated`; the grid needs to
903
+ # know which rows have a finished log to open. Resolved for the
904
+ # whole page in one query and set on the instances the serializer
905
+ # reads — the same shape as the audit log branch above.
906
+ page_rows = list(page_qs)
907
+ annotate_latest_calculation(page_rows, self._ag_model_class)
908
+ serializer = self.get_serializer(page_rows, many=True)
909
+ else:
910
+ serializer = self.get_serializer(page_qs, many=True)
896
911
  return {
897
912
  "rowData": serializer.data,
898
913
  "rowCount": row_count,
@@ -203,6 +203,59 @@ class CalculationLog(models.Model):
203
203
  log_entry.calculation_log = (log_entry.calculation_log or "") + f"\n{message}"
204
204
  log_entry.save(update_fields=["calculation_log"])
205
205
 
206
+ @classmethod
207
+ def keep_rolled_back_log(cls, calculation_id, root_record, root_instance) -> bool:
208
+ """
209
+ Keep what a failed run logged when its transaction took the rows with it.
210
+
211
+ `log` persists on commit, so a calculation that fails inside its
212
+ transaction — every `is_atomic` model — is rolled back with every row it
213
+ wrote: the steps that succeeded vanish with the one that failed. The live
214
+ cache is not transactional. It holds the run's whole log, in order, under
215
+ the root's key until the run ends, so the failure paths call this just
216
+ before they purge it, and it writes that text back as the run's log: one
217
+ row on the root record, the same text the live stream showed.
218
+
219
+ Does nothing when rows for the run survived (a run outside a transaction
220
+ keeps them) or when the cache holds nothing for it. Never raises — a
221
+ failure path must not fail again on its way to reporting the first one.
222
+ """
223
+ logger = logging.getLogger("lex.calclog")
224
+ try:
225
+ if not calculation_id or not root_record or root_instance is None:
226
+ return False
227
+ if cls.objects.filter(calculationId=calculation_id).exists():
228
+ return False
229
+ text = CacheManager.get_message(
230
+ CacheManager.build_cache_key(root_record, calculation_id)
231
+ )
232
+ if not text:
233
+ return False
234
+
235
+ from lex.audit_logging.models.AuditLog import AuditLog
236
+
237
+ # The run's first audit row was written by the request that started
238
+ # it, before the transaction, so it survived; rows the calculation
239
+ # wrote itself went with the rollback.
240
+ audit_log = (
241
+ AuditLog.objects.filter(calculation_id=calculation_id).order_by("id").first()
242
+ )
243
+ cls.objects.create(
244
+ calculationId=calculation_id,
245
+ audit_log=audit_log,
246
+ content_type=ContentType.objects.get_for_model(type(root_instance)),
247
+ object_id=root_instance.pk,
248
+ calculation_log=text,
249
+ )
250
+ return True
251
+ except Exception:
252
+ logger.warning(
253
+ "Could not keep the log of failed calculation %s",
254
+ calculation_id,
255
+ exc_info=True,
256
+ )
257
+ return False
258
+
206
259
  @classmethod
207
260
  def log(cls, message: str):
208
261
  """
@@ -0,0 +1,105 @@
1
+ """The newest calculation run started from each record on a page.
2
+
3
+ A calculation row carries ``is_calculated`` and nothing else, so on its own the
4
+ grid cannot tell which rows have a finished log to open. This resolves, for a
5
+ whole page of rows in one query, the ``calculationId`` of the newest run each
6
+ row started.
7
+
8
+ The rule is the one ``useResolvedCalculationId`` applies in the frontend,
9
+ stated here in Python so the table and every widget open the same run for the
10
+ same row: a run started from record ``pk`` of model ``m`` has an id beginning
11
+ ``f"{m}_{pk}_"`` (the frontend mints ``{m}_{pk}_update_{uuid}``), and the newest
12
+ is the one with the highest ``CalculationLog.id``.
13
+
14
+ The trailing underscore is load-bearing: without it, record 1 would match
15
+ record 11's runs.
16
+
17
+ Served by the index ``calculationId`` already has (migration 0005). On
18
+ PostgreSQL, Django adds a ``text_pattern_ops`` companion index for an indexed
19
+ text field, and that is what serves ``LIKE 'prefix%'``.
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ from functools import reduce
25
+ from operator import or_
26
+ from typing import Dict, Iterable, Tuple
27
+
28
+ from django.db.models import Max, Q
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ def is_calculation_model(model) -> bool:
34
+ """Whether ``model`` is a ``CalculationModel`` subclass.
35
+
36
+ Imported lazily: ``lex.core.models.CalculationModel`` pulls in much of the
37
+ framework, and the serializer layer imports this module.
38
+ """
39
+ from lex.core.models.CalculationModel import CalculationModel
40
+
41
+ return isinstance(model, type) and issubclass(model, CalculationModel)
42
+
43
+
44
+ def run_prefix(model_name: str, pk: object) -> str:
45
+ """The id prefix every run started from this record carries."""
46
+ return f"{model_name}_{pk}_"
47
+
48
+
49
+ def latest_calculation_ids(model_name: str, pks: Iterable[object]) -> Dict[str, str]:
50
+ """Map ``str(pk)`` to the ``calculationId`` of the newest run it started.
51
+
52
+ Records that never started a run are absent. One query whatever the number
53
+ of records: one ``startswith`` per record, ORed, collapsed to one row per
54
+ run before it leaves the database.
55
+ """
56
+ from lex.audit_logging.models.CalculationLog import CalculationLog
57
+
58
+ prefixes = {str(pk): run_prefix(model_name, pk) for pk in pks}
59
+ if not prefixes:
60
+ return {}
61
+
62
+ runs = (
63
+ CalculationLog.objects.filter(
64
+ reduce(or_, (Q(calculationId__startswith=p) for p in prefixes.values()))
65
+ )
66
+ .values("calculationId")
67
+ .annotate(newest=Max("id"))
68
+ )
69
+
70
+ # Longest prefix first. For string keys containing "_", a run id can start
71
+ # with two of the page's prefixes ("m_a_" and "m_a_b_"); the longer one is
72
+ # the record that started it, because record "a"'s own runs begin
73
+ # "m_a_update_". Integer keys cannot collide.
74
+ by_length = sorted(prefixes.items(), key=lambda item: len(item[1]), reverse=True)
75
+ best: Dict[str, Tuple[int, str]] = {}
76
+ for run in runs:
77
+ calculation_id = run["calculationId"]
78
+ for pk, prefix in by_length:
79
+ if calculation_id.startswith(prefix):
80
+ if pk not in best or run["newest"] > best[pk][0]:
81
+ best[pk] = (run["newest"], calculation_id)
82
+ break
83
+ return {pk: calculation_id for pk, (_, calculation_id) in best.items()}
84
+
85
+
86
+ def annotate_latest_calculation(rows, model) -> None:
87
+ """Set ``_lex_calculation_id`` and ``_has_calculation_log`` on each row.
88
+
89
+ Never raises. A failure costs the log button, not the page: every row is
90
+ marked as having no log and the grid still loads.
91
+ """
92
+ try:
93
+ ids = latest_calculation_ids(model._meta.model_name, [row.pk for row in rows])
94
+ except Exception:
95
+ logger.warning(
96
+ "Could not resolve the latest calculation runs for %s; "
97
+ "serving the page without them.",
98
+ model._meta.label,
99
+ exc_info=True,
100
+ )
101
+ ids = {}
102
+ for row in rows:
103
+ calculation_id = ids.get(str(row.pk))
104
+ row._lex_calculation_id = calculation_id
105
+ row._has_calculation_log = calculation_id is not None
@@ -0,0 +1,62 @@
1
+ """
2
+ In-database activation of future-dated bitemporal records.
3
+
4
+ Installs ``lex/core/sql/bitemporal_activation.sql``: the applier's heartbeat table and
5
+ three pl/pgSQL functions:
6
+
7
+ lex_bitemporal_tables() discovery, from the catalog
8
+ lex_pending_activations() visibility: every SCHEDULED row, due or not
9
+ lex_apply_due_activations() the applier, called every minute by pg_cron
10
+
11
+ The SQL is executed through a raw cursor with no parameters: it is full of ``%I`` /
12
+ ``%L`` ``format()`` placeholders that psycopg2 would otherwise try to interpolate.
13
+
14
+ Nothing here is a Django model on purpose. The framework registers every concrete model
15
+ it discovers under ``lex/`` for history tracking and for the model list, and an
16
+ operational heartbeat belongs in neither (see the SQL file). On a non-PostgreSQL backend
17
+ this migration is a no-op: the functions have no equivalent there and the in-process
18
+ fallback keeps working (design §8.13).
19
+
20
+ Design: docs/superpowers/specs/2026-09-16-bitemporal-activation-applier-design.md
21
+ """
22
+
23
+ from pathlib import Path
24
+
25
+ from django.db import migrations
26
+
27
+ SQL_PATH = Path(__file__).resolve().parents[1] / "sql" / "bitemporal_activation.sql"
28
+
29
+ # Drop order: dependants first.
30
+ FUNCTIONS = (
31
+ "lex_apply_due_activations",
32
+ "lex_pending_activations",
33
+ "lex_bitemporal_tables",
34
+ )
35
+ TABLE = "lex_activation_applier_state"
36
+
37
+
38
+ def install(apps, schema_editor):
39
+ if schema_editor.connection.vendor != "postgresql":
40
+ return
41
+ with schema_editor.connection.cursor() as cursor:
42
+ cursor.execute(SQL_PATH.read_text(encoding="utf-8"))
43
+
44
+
45
+ def uninstall(apps, schema_editor):
46
+ if schema_editor.connection.vendor != "postgresql":
47
+ return
48
+ with schema_editor.connection.cursor() as cursor:
49
+ for name in FUNCTIONS:
50
+ cursor.execute(f"DROP FUNCTION IF EXISTS {name}()")
51
+ cursor.execute(f"DROP TABLE IF EXISTS {TABLE}")
52
+
53
+
54
+ class Migration(migrations.Migration):
55
+
56
+ initial = True
57
+
58
+ dependencies = []
59
+
60
+ operations = [
61
+ migrations.RunPython(install, uninstall),
62
+ ]
@@ -1200,6 +1200,12 @@ class CalculationModel(LexModel):
1200
1200
 
1201
1201
  if is_root:
1202
1202
  calc_id = context.calculation_id
1203
+ # A run that did not succeed was rolled back with its log
1204
+ # rows; the cache about to be purged still holds the log.
1205
+ if self.is_calculated != self.SUCCESS:
1206
+ from lex.audit_logging.models.CalculationLog import CalculationLog
1207
+
1208
+ CalculationLog.keep_rolled_back_log(calc_id, context.root_record, self)
1203
1209
  # If we are root, we can clean up everything for this calculation ID
1204
1210
  # or just our specific key. Cleaning everything ensures no orphaned child keys.
1205
1211
  cleanup_result = CacheManager.cleanup_calculation(calculation_id=calc_id)
@@ -1432,6 +1438,11 @@ class CalculationModel(LexModel):
1432
1438
 
1433
1439
  if is_root:
1434
1440
  calc_id = context.calculation_id
1441
+ # Same as in execute_calculation_sync, for a failure that
1442
+ # never reached its cleanup; a no-op once that kept the log.
1443
+ from lex.audit_logging.models.CalculationLog import CalculationLog
1444
+
1445
+ CalculationLog.keep_rolled_back_log(calc_id, context.root_record, self)
1435
1446
  # Clean up all keys associated with this calculation ID
1436
1447
  cleanup_result = CacheManager.cleanup_calculation(calculation_id=calc_id)
1437
1448
 
@@ -12,6 +12,7 @@ The control fields are prefixed with ``meta_`` to avoid collisions with
12
12
  the Level 1 fields that are copied into the MetaHistory model.
13
13
  """
14
14
 
15
+ import hashlib
15
16
  import logging
16
17
 
17
18
  from django.db import models, transaction
@@ -74,6 +75,29 @@ def create_meta_history_record(
74
75
  return history_instance
75
76
 
76
77
 
78
+ def scheduled_activation_index(table_name: str) -> models.Index:
79
+ """
80
+ The partial index both activation paths scan every minute, forever.
81
+
82
+ ``SELECT ... WHERE meta_task_status = 'SCHEDULED'`` runs per meta table per tick —
83
+ from the in-database applier (``lex_apply_due_activations()``), from the pending
84
+ view, and from the reconcile floor. ``SCHEDULED`` rows are transient and rare, so a
85
+ partial index is tiny and serves the scan and the ``DONE`` flip alike. Declared on
86
+ the generated model, so every customer repository picks it up as one ``AddIndex``
87
+ migration per meta table on its next ``makemigrations``. Correctness never depends
88
+ on it; only the cost of the scan does.
89
+
90
+ The name is derived from the table name (Django caps index names at 30 characters
91
+ and they must be unique per database).
92
+ """
93
+ digest = hashlib.md5(table_name.encode("utf-8")).hexdigest()[:12]
94
+ return models.Index(
95
+ fields=["history_object"],
96
+ condition=models.Q(meta_task_status="SCHEDULED"),
97
+ name=f"lexsched_{digest}",
98
+ )
99
+
100
+
77
101
  class MetaLevelHistoricalRecords(HistoricalRecords):
78
102
  """
79
103
  History-on-History provider.
@@ -170,6 +194,11 @@ class MetaLevelHistoricalRecords(HistoricalRecords):
170
194
  meta_fields = super().get_meta_options(model)
171
195
  meta_fields["ordering"] = ("-sys_from", "-meta_history_id")
172
196
  meta_fields["get_latest_by"] = ("sys_from", "meta_history_id")
197
+ table_name = self.table_name or f"{model._meta.db_table}_meta_history"
198
+ meta_fields["indexes"] = [
199
+ *meta_fields.get("indexes", ()),
200
+ scheduled_activation_index(table_name),
201
+ ]
173
202
  return meta_fields
174
203
 
175
204
  # ------------------------------------------------------------------
@@ -0,0 +1,167 @@
1
+ """
2
+ lex-app's side of the in-database activation applier.
3
+
4
+ The applier itself is SQL (``lex/core/sql/bitemporal_activation.sql``), installed by the
5
+ ``lex.core`` migration and called every minute by a pg_cron job the instance controller
6
+ registers. Nothing in this module runs at apply time. What lives here:
7
+
8
+ * :func:`applier_is_alive` — the **write-time liveness gate**. A future-dated save asks
9
+ whether the database applied something recently. If yes, the save leaves only the
10
+ ``SCHEDULED`` meta row: the database will activate it. If no — SQLite, a PostgreSQL
11
+ without pg_cron, an instance whose job is not registered yet, a migration that has
12
+ not run — the save also arms the in-process timer it always has. The decision is
13
+ made from a heartbeat the applier writes on every run, so no configuration can
14
+ claim an applier that is not there, and the lex-app release is safe to deploy in
15
+ any order relative to the infrastructure change.
16
+
17
+ * Thin invokers for tests and operators (:func:`apply_due_activations`,
18
+ :func:`pending_activations`, :func:`bitemporal_tables`, :func:`install_functions`).
19
+ They issue one ``SELECT`` each; they add no logic.
20
+
21
+ Design: docs/superpowers/specs/2026-09-16-bitemporal-activation-applier-design.md (§5.8).
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ import os
28
+ from datetime import timedelta
29
+ from pathlib import Path
30
+ from typing import Any
31
+
32
+ from django.db import DatabaseError, connection as default_connection, transaction
33
+ from django.utils import timezone
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ SQL_PATH = Path(__file__).resolve().parents[1] / "sql" / "bitemporal_activation.sql"
38
+
39
+ HEARTBEAT_TABLE = "lex_activation_applier_state"
40
+
41
+ FUNCTION_NAMES = (
42
+ "lex_bitemporal_tables",
43
+ "lex_pending_activations",
44
+ "lex_apply_due_activations",
45
+ )
46
+
47
+ #: How recent the heartbeat must be for the database applier to count as alive.
48
+ #: pg_cron fires every minute; five minutes absorbs a slow tick or a failover without
49
+ #: arming redundant timers, while a dead applier is noticed within the same window.
50
+ DEFAULT_LIVENESS_WINDOW_SECONDS = 300
51
+ LIVENESS_WINDOW_ENV = "LEX_ACTIVATION_APPLIER_LIVENESS_SECONDS"
52
+
53
+
54
+ def liveness_window() -> timedelta:
55
+ """The heartbeat freshness window, read from the environment on every call."""
56
+ raw = os.getenv(LIVENESS_WINDOW_ENV, "")
57
+ try:
58
+ seconds = int(raw) if raw else DEFAULT_LIVENESS_WINDOW_SECONDS
59
+ except ValueError:
60
+ logger.warning("%s=%r is not an integer; using %s", LIVENESS_WINDOW_ENV, raw,
61
+ DEFAULT_LIVENESS_WINDOW_SECONDS)
62
+ seconds = DEFAULT_LIVENESS_WINDOW_SECONDS
63
+ return timedelta(seconds=max(seconds, 0))
64
+
65
+
66
+ def applier_is_alive(*, now=None, using=None) -> bool:
67
+ """
68
+ True when the database applier has run within :func:`liveness_window`.
69
+
70
+ False on any non-PostgreSQL backend, when the heartbeat table is missing (the
71
+ migration has not run), when it is empty (the applier has never run), or when the
72
+ last run is older than the window. Never raises: the query runs in a savepoint so a
73
+ missing table cannot poison the caller's transaction, and a database error simply
74
+ means "not alive", which falls back to the behaviour lex-app has always had.
75
+ """
76
+ conn = default_connection if using is None else transaction.get_connection(using)
77
+ if conn.vendor != "postgresql":
78
+ return False
79
+ try:
80
+ with transaction.atomic(using=conn.alias), conn.cursor() as cursor:
81
+ cursor.execute(f"SELECT last_run_at FROM {HEARTBEAT_TABLE} WHERE id = 1")
82
+ row = cursor.fetchone()
83
+ except DatabaseError as exc: # table not migrated yet, connection trouble, ...
84
+ logger.debug("Activation applier liveness unknown (%s); treating as not alive", exc)
85
+ return False
86
+ if row is None:
87
+ return False
88
+ now = now or timezone.now()
89
+ return (now - row[0]) <= liveness_window()
90
+
91
+
92
+ def _rows(cursor) -> list[dict[str, Any]]:
93
+ columns = [col[0] for col in cursor.description]
94
+ return [dict(zip(columns, row)) for row in cursor.fetchall()]
95
+
96
+
97
+ # ── heartbeat access (operators and tests; the applier itself writes it in SQL) ──
98
+
99
+
100
+ def read_heartbeat(using=None) -> dict[str, Any] | None:
101
+ """The heartbeat row as a dict, or None when the applier has never run."""
102
+ conn = default_connection if using is None else transaction.get_connection(using)
103
+ with conn.cursor() as cursor:
104
+ cursor.execute(
105
+ f"SELECT last_run_at, last_run_applied, last_run_failed, last_run_duration_ms "
106
+ f"FROM {HEARTBEAT_TABLE} WHERE id = 1"
107
+ )
108
+ rows = _rows(cursor)
109
+ return rows[0] if rows else None
110
+
111
+
112
+ def record_heartbeat(*, last_run_at=None, applied: int = 0, failed: int = 0,
113
+ duration_ms: int = 0, using=None) -> None:
114
+ """Write the heartbeat row as the applier would. Used to simulate a live applier."""
115
+ conn = default_connection if using is None else transaction.get_connection(using)
116
+ with conn.cursor() as cursor:
117
+ cursor.execute(
118
+ f"INSERT INTO {HEARTBEAT_TABLE} (id, last_run_at, last_run_applied, last_run_failed, last_run_duration_ms) "
119
+ f"VALUES (1, %s, %s, %s, %s) "
120
+ f"ON CONFLICT (id) DO UPDATE SET last_run_at = EXCLUDED.last_run_at, "
121
+ f"last_run_applied = EXCLUDED.last_run_applied, last_run_failed = EXCLUDED.last_run_failed, "
122
+ f"last_run_duration_ms = EXCLUDED.last_run_duration_ms",
123
+ [last_run_at or timezone.now(), applied, failed, duration_ms],
124
+ )
125
+
126
+
127
+ def clear_heartbeat(using=None) -> None:
128
+ """Remove the heartbeat row: the state before the applier has ever run."""
129
+ conn = default_connection if using is None else transaction.get_connection(using)
130
+ with conn.cursor() as cursor:
131
+ cursor.execute(f"DELETE FROM {HEARTBEAT_TABLE} WHERE id = 1")
132
+
133
+
134
+ # ── thin invokers ──────────────────────────────────────────────────────────────
135
+
136
+
137
+ def install_functions(using=None) -> None:
138
+ """(Re)install the SQL functions from :data:`SQL_PATH`. PostgreSQL only."""
139
+ conn = default_connection if using is None else transaction.get_connection(using)
140
+ if conn.vendor != "postgresql":
141
+ raise RuntimeError("The activation applier is PostgreSQL-only")
142
+ with conn.cursor() as cursor:
143
+ cursor.execute(SQL_PATH.read_text(encoding="utf-8"))
144
+
145
+
146
+ def apply_due_activations(using=None) -> int:
147
+ """Run one applier tick. Returns the number of records converged."""
148
+ conn = default_connection if using is None else transaction.get_connection(using)
149
+ with conn.cursor() as cursor:
150
+ cursor.execute("SELECT lex_apply_due_activations()")
151
+ return int(cursor.fetchone()[0])
152
+
153
+
154
+ def pending_activations(using=None) -> list[dict[str, Any]]:
155
+ """Every SCHEDULED meta row across every bitemporal table, due or not."""
156
+ conn = default_connection if using is None else transaction.get_connection(using)
157
+ with conn.cursor() as cursor:
158
+ cursor.execute("SELECT * FROM lex_pending_activations()")
159
+ return _rows(cursor)
160
+
161
+
162
+ def bitemporal_tables(using=None) -> list[dict[str, Any]]:
163
+ """The (main, history, meta, pk) triples the applier discovers right now."""
164
+ conn = default_connection if using is None else transaction.get_connection(using)
165
+ with conn.cursor() as cursor:
166
+ cursor.execute("SELECT * FROM lex_bitemporal_tables()")
167
+ return _rows(cursor)