trueppm-api 0.4.0__py3-none-any.whl

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 (799) hide show
  1. trueppm_api/__init__.py +27 -0
  2. trueppm_api/apps/access/__init__.py +0 -0
  3. trueppm_api/apps/access/apps.py +19 -0
  4. trueppm_api/apps/access/groups.py +518 -0
  5. trueppm_api/apps/access/management/__init__.py +0 -0
  6. trueppm_api/apps/access/management/commands/__init__.py +0 -0
  7. trueppm_api/apps/access/management/commands/create_admin.py +346 -0
  8. trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
  9. trueppm_api/apps/access/management/commands/revoke_api_tokens.py +483 -0
  10. trueppm_api/apps/access/migrations/0001_initial.py +61 -0
  11. trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +184 -0
  12. trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
  13. trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
  14. trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
  15. trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
  16. trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
  17. trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
  18. trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
  19. trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
  20. trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
  21. trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
  22. trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
  23. trueppm_api/apps/access/migrations/0013_squashed_0019_release_0_4.py +302 -0
  24. trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
  25. trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
  26. trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
  27. trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
  28. trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +143 -0
  29. trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
  30. trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
  31. trueppm_api/apps/access/migrations/__init__.py +0 -0
  32. trueppm_api/apps/access/minter_role.py +38 -0
  33. trueppm_api/apps/access/models.py +588 -0
  34. trueppm_api/apps/access/permissions.py +3263 -0
  35. trueppm_api/apps/access/serializers.py +1007 -0
  36. trueppm_api/apps/access/services.py +878 -0
  37. trueppm_api/apps/access/signals.py +171 -0
  38. trueppm_api/apps/access/tasks.py +50 -0
  39. trueppm_api/apps/access/throttles.py +96 -0
  40. trueppm_api/apps/access/urls.py +191 -0
  41. trueppm_api/apps/access/views.py +1965 -0
  42. trueppm_api/apps/agents/__init__.py +6 -0
  43. trueppm_api/apps/agents/apps.py +11 -0
  44. trueppm_api/apps/agents/canonical.py +73 -0
  45. trueppm_api/apps/agents/deferred.py +108 -0
  46. trueppm_api/apps/agents/management/__init__.py +0 -0
  47. trueppm_api/apps/agents/management/commands/__init__.py +0 -0
  48. trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
  49. trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
  50. trueppm_api/apps/agents/middleware.py +44 -0
  51. trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
  52. trueppm_api/apps/agents/migrations/0001_squashed_0003_release_0_4.py +344 -0
  53. trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
  54. trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
  55. trueppm_api/apps/agents/migrations/__init__.py +0 -0
  56. trueppm_api/apps/agents/models.py +371 -0
  57. trueppm_api/apps/agents/refusal.py +103 -0
  58. trueppm_api/apps/agents/serializers.py +85 -0
  59. trueppm_api/apps/agents/services.py +397 -0
  60. trueppm_api/apps/agents/signals.py +30 -0
  61. trueppm_api/apps/agents/urls.py +15 -0
  62. trueppm_api/apps/agents/views.py +109 -0
  63. trueppm_api/apps/csvimport/__init__.py +12 -0
  64. trueppm_api/apps/csvimport/apps.py +11 -0
  65. trueppm_api/apps/csvimport/mapping.py +363 -0
  66. trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
  67. trueppm_api/apps/csvimport/migrations/0001_squashed_0003_release_0_4.py +90 -0
  68. trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
  69. trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
  70. trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
  71. trueppm_api/apps/csvimport/models.py +111 -0
  72. trueppm_api/apps/csvimport/parser.py +1825 -0
  73. trueppm_api/apps/csvimport/services.py +48 -0
  74. trueppm_api/apps/csvimport/tasks.py +459 -0
  75. trueppm_api/apps/csvimport/template.py +37 -0
  76. trueppm_api/apps/csvimport/urls.py +50 -0
  77. trueppm_api/apps/csvimport/views.py +793 -0
  78. trueppm_api/apps/history/__init__.py +0 -0
  79. trueppm_api/apps/history/apps.py +12 -0
  80. trueppm_api/apps/history/changelog.py +359 -0
  81. trueppm_api/apps/history/diff_policy.py +178 -0
  82. trueppm_api/apps/history/serializers.py +104 -0
  83. trueppm_api/apps/history/signals.py +82 -0
  84. trueppm_api/apps/history/tasks.py +105 -0
  85. trueppm_api/apps/history/urls.py +38 -0
  86. trueppm_api/apps/history/views.py +443 -0
  87. trueppm_api/apps/idempotency/__init__.py +0 -0
  88. trueppm_api/apps/idempotency/apps.py +11 -0
  89. trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
  90. trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
  91. trueppm_api/apps/idempotency/mixins.py +226 -0
  92. trueppm_api/apps/idempotency/models.py +75 -0
  93. trueppm_api/apps/idempotency/tasks.py +61 -0
  94. trueppm_api/apps/integrations/__init__.py +13 -0
  95. trueppm_api/apps/integrations/apps.py +59 -0
  96. trueppm_api/apps/integrations/connections.py +806 -0
  97. trueppm_api/apps/integrations/encryption.py +92 -0
  98. trueppm_api/apps/integrations/external_sources.py +863 -0
  99. trueppm_api/apps/integrations/git_automation_services.py +231 -0
  100. trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
  101. trueppm_api/apps/integrations/http.py +597 -0
  102. trueppm_api/apps/integrations/me_work.py +218 -0
  103. trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
  104. trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
  105. trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
  106. trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
  107. trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
  108. trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
  109. trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
  110. trueppm_api/apps/integrations/migrations/0007_squashed_0014_release_0_4.py +225 -0
  111. trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
  112. trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
  113. trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
  114. trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
  115. trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
  116. trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
  117. trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
  118. trueppm_api/apps/integrations/migrations/0014_tasklink_created_by.py +27 -0
  119. trueppm_api/apps/integrations/migrations/__init__.py +0 -0
  120. trueppm_api/apps/integrations/models.py +635 -0
  121. trueppm_api/apps/integrations/notification_channels.py +57 -0
  122. trueppm_api/apps/integrations/opengraph.py +229 -0
  123. trueppm_api/apps/integrations/outgoing.py +201 -0
  124. trueppm_api/apps/integrations/providers.py +528 -0
  125. trueppm_api/apps/integrations/registry.py +345 -0
  126. trueppm_api/apps/integrations/serializers.py +453 -0
  127. trueppm_api/apps/integrations/services.py +171 -0
  128. trueppm_api/apps/integrations/tasks.py +734 -0
  129. trueppm_api/apps/integrations/throttles.py +165 -0
  130. trueppm_api/apps/integrations/urls.py +77 -0
  131. trueppm_api/apps/integrations/views.py +1158 -0
  132. trueppm_api/apps/jiraimport/__init__.py +8 -0
  133. trueppm_api/apps/jiraimport/apps.py +11 -0
  134. trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
  135. trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
  136. trueppm_api/apps/jiraimport/models.py +70 -0
  137. trueppm_api/apps/jiraimport/parser.py +350 -0
  138. trueppm_api/apps/jiraimport/services.py +46 -0
  139. trueppm_api/apps/jiraimport/tasks.py +280 -0
  140. trueppm_api/apps/jiraimport/urls.py +15 -0
  141. trueppm_api/apps/jiraimport/views.py +153 -0
  142. trueppm_api/apps/msproject/__init__.py +0 -0
  143. trueppm_api/apps/msproject/apps.py +11 -0
  144. trueppm_api/apps/msproject/dataclasses.py +153 -0
  145. trueppm_api/apps/msproject/exporter.py +551 -0
  146. trueppm_api/apps/msproject/extended_attributes.py +77 -0
  147. trueppm_api/apps/msproject/importer.py +1095 -0
  148. trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
  149. trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
  150. trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
  151. trueppm_api/apps/msproject/migrations/__init__.py +0 -0
  152. trueppm_api/apps/msproject/models.py +81 -0
  153. trueppm_api/apps/msproject/parser.py +1276 -0
  154. trueppm_api/apps/msproject/serializers.py +136 -0
  155. trueppm_api/apps/msproject/services.py +58 -0
  156. trueppm_api/apps/msproject/tasks.py +439 -0
  157. trueppm_api/apps/msproject/urls.py +39 -0
  158. trueppm_api/apps/msproject/views.py +531 -0
  159. trueppm_api/apps/notifications/__init__.py +7 -0
  160. trueppm_api/apps/notifications/apps.py +15 -0
  161. trueppm_api/apps/notifications/backfill.py +79 -0
  162. trueppm_api/apps/notifications/categories.py +112 -0
  163. trueppm_api/apps/notifications/delivery_limits.py +258 -0
  164. trueppm_api/apps/notifications/digests.py +462 -0
  165. trueppm_api/apps/notifications/email_backend.py +350 -0
  166. trueppm_api/apps/notifications/email_health.py +128 -0
  167. trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
  168. trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +299 -0
  169. trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
  170. trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
  171. trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
  172. trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
  173. trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
  174. trueppm_api/apps/notifications/migrations/0007_squashed_0011_release_0_4.py +204 -0
  175. trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
  176. trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
  177. trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
  178. trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
  179. trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
  180. trueppm_api/apps/notifications/migrations/__init__.py +0 -0
  181. trueppm_api/apps/notifications/models.py +1148 -0
  182. trueppm_api/apps/notifications/receivers.py +155 -0
  183. trueppm_api/apps/notifications/schema_migrations.py +56 -0
  184. trueppm_api/apps/notifications/serializers.py +1094 -0
  185. trueppm_api/apps/notifications/services.py +1489 -0
  186. trueppm_api/apps/notifications/tasks.py +762 -0
  187. trueppm_api/apps/notifications/throttles.py +98 -0
  188. trueppm_api/apps/notifications/urls.py +97 -0
  189. trueppm_api/apps/notifications/views.py +919 -0
  190. trueppm_api/apps/observability/__init__.py +0 -0
  191. trueppm_api/apps/observability/apps.py +31 -0
  192. trueppm_api/apps/observability/logging.py +213 -0
  193. trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
  194. trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
  195. trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
  196. trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
  197. trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
  198. trueppm_api/apps/observability/migrations/__init__.py +0 -0
  199. trueppm_api/apps/observability/models.py +141 -0
  200. trueppm_api/apps/observability/otel/__init__.py +45 -0
  201. trueppm_api/apps/observability/otel/attributes.py +247 -0
  202. trueppm_api/apps/observability/otel/export_health.py +533 -0
  203. trueppm_api/apps/observability/otel/instrumentation.py +351 -0
  204. trueppm_api/apps/observability/otel/metrics.py +566 -0
  205. trueppm_api/apps/observability/otel/provider.py +511 -0
  206. trueppm_api/apps/observability/otel/request_attributes.py +204 -0
  207. trueppm_api/apps/observability/purge_registry.py +124 -0
  208. trueppm_api/apps/observability/retention.py +139 -0
  209. trueppm_api/apps/observability/selectors.py +993 -0
  210. trueppm_api/apps/observability/serializers.py +142 -0
  211. trueppm_api/apps/observability/services.py +407 -0
  212. trueppm_api/apps/observability/tasks.py +266 -0
  213. trueppm_api/apps/observability/urls.py +27 -0
  214. trueppm_api/apps/observability/views.py +537 -0
  215. trueppm_api/apps/profiles/__init__.py +0 -0
  216. trueppm_api/apps/profiles/apps.py +15 -0
  217. trueppm_api/apps/profiles/constants.py +54 -0
  218. trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
  219. trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
  220. trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
  221. trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
  222. trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
  223. trueppm_api/apps/profiles/migrations/0005_squashed_0009_release_0_4.py +128 -0
  224. trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
  225. trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
  226. trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
  227. trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
  228. trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
  229. trueppm_api/apps/profiles/migrations/__init__.py +0 -0
  230. trueppm_api/apps/profiles/models.py +276 -0
  231. trueppm_api/apps/profiles/serializers.py +199 -0
  232. trueppm_api/apps/profiles/services.py +541 -0
  233. trueppm_api/apps/profiles/views.py +124 -0
  234. trueppm_api/apps/projects/__init__.py +0 -0
  235. trueppm_api/apps/projects/actual_date_rules.py +163 -0
  236. trueppm_api/apps/projects/amend.py +133 -0
  237. trueppm_api/apps/projects/apps.py +38 -0
  238. trueppm_api/apps/projects/asset_feed.py +459 -0
  239. trueppm_api/apps/projects/asset_views.py +317 -0
  240. trueppm_api/apps/projects/attachment_policy.py +278 -0
  241. trueppm_api/apps/projects/authentication.py +458 -0
  242. trueppm_api/apps/projects/backfill.py +459 -0
  243. trueppm_api/apps/projects/backlog_services.py +324 -0
  244. trueppm_api/apps/projects/backlog_views.py +302 -0
  245. trueppm_api/apps/projects/batch_operation_views.py +264 -0
  246. trueppm_api/apps/projects/blocker_services.py +510 -0
  247. trueppm_api/apps/projects/board_activity.py +462 -0
  248. trueppm_api/apps/projects/board_activity_views.py +186 -0
  249. trueppm_api/apps/projects/board_lanes.py +134 -0
  250. trueppm_api/apps/projects/bulk_settings.py +194 -0
  251. trueppm_api/apps/projects/bundled_templates.py +215 -0
  252. trueppm_api/apps/projects/calendar_settings.py +180 -0
  253. trueppm_api/apps/projects/ceremony_views.py +244 -0
  254. trueppm_api/apps/projects/commit_moment.py +212 -0
  255. trueppm_api/apps/projects/config_notice.py +1330 -0
  256. trueppm_api/apps/projects/custom_field_values.py +266 -0
  257. trueppm_api/apps/projects/decisions_services.py +132 -0
  258. trueppm_api/apps/projects/decisions_views.py +97 -0
  259. trueppm_api/apps/projects/estimation_scale.py +126 -0
  260. trueppm_api/apps/projects/export_bundle.py +423 -0
  261. trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4278 -0
  262. trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2001 -0
  263. trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1827 -0
  264. trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1909 -0
  265. trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1410 -0
  266. trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
  267. trueppm_api/apps/projects/inbound_sync.py +555 -0
  268. trueppm_api/apps/projects/iteration_label.py +85 -0
  269. trueppm_api/apps/projects/key_views.py +468 -0
  270. trueppm_api/apps/projects/keys.py +205 -0
  271. trueppm_api/apps/projects/lifecycle.py +191 -0
  272. trueppm_api/apps/projects/management/__init__.py +0 -0
  273. trueppm_api/apps/projects/management/commands/__init__.py +0 -0
  274. trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
  275. trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
  276. trueppm_api/apps/projects/management/commands/export_program.py +50 -0
  277. trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
  278. trueppm_api/apps/projects/management/commands/load_sample_project.py +514 -0
  279. trueppm_api/apps/projects/management/commands/seed_capacity.py +406 -0
  280. trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +192 -0
  281. trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
  282. trueppm_api/apps/projects/mcp_settings.py +305 -0
  283. trueppm_api/apps/projects/methodology.py +178 -0
  284. trueppm_api/apps/projects/migration_helpers.py +75 -0
  285. trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
  286. trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5659 -0
  287. trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
  288. trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
  289. trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
  290. trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
  291. trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
  292. trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
  293. trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
  294. trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
  295. trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
  296. trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
  297. trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
  298. trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
  299. trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
  300. trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
  301. trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
  302. trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
  303. trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
  304. trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
  305. trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
  306. trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
  307. trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
  308. trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
  309. trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
  310. trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
  311. trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
  312. trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
  313. trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
  314. trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
  315. trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
  316. trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
  317. trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
  318. trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
  319. trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
  320. trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
  321. trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
  322. trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
  323. trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
  324. trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
  325. trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
  326. trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
  327. trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
  328. trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
  329. trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
  330. trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
  331. trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
  332. trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
  333. trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
  334. trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
  335. trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
  336. trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
  337. trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
  338. trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
  339. trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
  340. trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
  341. trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
  342. trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
  343. trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
  344. trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
  345. trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
  346. trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
  347. trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
  348. trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
  349. trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
  350. trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
  351. trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
  352. trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
  353. trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
  354. trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
  355. trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
  356. trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
  357. trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
  358. trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
  359. trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
  360. trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
  361. trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
  362. trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
  363. trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
  364. trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
  365. trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
  366. trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
  367. trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
  368. trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
  369. trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
  370. trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
  371. trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
  372. trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
  373. trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
  374. trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
  375. trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
  376. trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
  377. trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
  378. trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
  379. trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +59 -0
  380. trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
  381. trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
  382. trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
  383. trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
  384. trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
  385. trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
  386. trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
  387. trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
  388. trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
  389. trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
  390. trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +75 -0
  391. trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
  392. trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
  393. trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
  394. trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
  395. trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
  396. trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +76 -0
  397. trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
  398. trueppm_api/apps/projects/migrations/0106_squashed_0158_release_0_4.py +2357 -0
  399. trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
  400. trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
  401. trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
  402. trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
  403. trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
  404. trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
  405. trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
  406. trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
  407. trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
  408. trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
  409. trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
  410. trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
  411. trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
  412. trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
  413. trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
  414. trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
  415. trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
  416. trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
  417. trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
  418. trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
  419. trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
  420. trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
  421. trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
  422. trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
  423. trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
  424. trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
  425. trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
  426. trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
  427. trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
  428. trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
  429. trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
  430. trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
  431. trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
  432. trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
  433. trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
  434. trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
  435. trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
  436. trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
  437. trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
  438. trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
  439. trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
  440. trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
  441. trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
  442. trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
  443. trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
  444. trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
  445. trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
  446. trueppm_api/apps/projects/migrations/0154_object_keys.py +184 -0
  447. trueppm_api/apps/projects/migrations/0155_task_milestone_at_day_end.py +20 -0
  448. trueppm_api/apps/projects/migrations/0156_program_import_job_replaced_project_ids.py +17 -0
  449. trueppm_api/apps/projects/migrations/0157_forecastsnapshot_cpm_finish_at_day_start.py +17 -0
  450. trueppm_api/apps/projects/migrations/0158_baselinetask_finish_at_day_start.py +17 -0
  451. trueppm_api/apps/projects/migrations/__init__.py +0 -0
  452. trueppm_api/apps/projects/models.py +9209 -0
  453. trueppm_api/apps/projects/poker_services.py +154 -0
  454. trueppm_api/apps/projects/poker_views.py +398 -0
  455. trueppm_api/apps/projects/product_backlog_services.py +450 -0
  456. trueppm_api/apps/projects/program_label_services.py +94 -0
  457. trueppm_api/apps/projects/program_label_views.py +189 -0
  458. trueppm_api/apps/projects/program_rollup.py +903 -0
  459. trueppm_api/apps/projects/program_schedule.py +767 -0
  460. trueppm_api/apps/projects/program_views.py +3445 -0
  461. trueppm_api/apps/projects/project_templates.py +422 -0
  462. trueppm_api/apps/projects/receivers.py +234 -0
  463. trueppm_api/apps/projects/refusal_codes.py +140 -0
  464. trueppm_api/apps/projects/reorder_services.py +145 -0
  465. trueppm_api/apps/projects/restructure_hooks.py +54 -0
  466. trueppm_api/apps/projects/retro_board_services.py +368 -0
  467. trueppm_api/apps/projects/retro_services.py +285 -0
  468. trueppm_api/apps/projects/risk_import.py +396 -0
  469. trueppm_api/apps/projects/row_identity.py +113 -0
  470. trueppm_api/apps/projects/schema_migrations.py +228 -0
  471. trueppm_api/apps/projects/schemas/seed_v1.json +492 -0
  472. trueppm_api/apps/projects/schemas/seed_v2.json +891 -0
  473. trueppm_api/apps/projects/seed/__init__.py +61 -0
  474. trueppm_api/apps/projects/seed/demo_landing_overlay.py +532 -0
  475. trueppm_api/apps/projects/seed/exporter.py +1717 -0
  476. trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
  477. trueppm_api/apps/projects/seed/importer.py +2764 -0
  478. trueppm_api/apps/projects/seed/reanchor.py +551 -0
  479. trueppm_api/apps/projects/seed/reldates.py +126 -0
  480. trueppm_api/apps/projects/seed/replace.py +128 -0
  481. trueppm_api/apps/projects/seed/replay.py +1695 -0
  482. trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
  483. trueppm_api/apps/projects/seed/samples.py +427 -0
  484. trueppm_api/apps/projects/seed/validation.py +1169 -0
  485. trueppm_api/apps/projects/serializers.py +12904 -0
  486. trueppm_api/apps/projects/services.py +7525 -0
  487. trueppm_api/apps/projects/share_serializers.py +142 -0
  488. trueppm_api/apps/projects/share_services.py +373 -0
  489. trueppm_api/apps/projects/share_views.py +395 -0
  490. trueppm_api/apps/projects/sharing_settings.py +187 -0
  491. trueppm_api/apps/projects/signal_privacy_services.py +910 -0
  492. trueppm_api/apps/projects/signal_privacy_views.py +485 -0
  493. trueppm_api/apps/projects/signals.py +132 -0
  494. trueppm_api/apps/projects/slip_conflict.py +288 -0
  495. trueppm_api/apps/projects/sprint_cadence.py +453 -0
  496. trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
  497. trueppm_api/apps/projects/standup.py +304 -0
  498. trueppm_api/apps/projects/standup_views.py +99 -0
  499. trueppm_api/apps/projects/structural_operation_services.py +727 -0
  500. trueppm_api/apps/projects/structural_operation_views.py +311 -0
  501. trueppm_api/apps/projects/surface_visibility.py +126 -0
  502. trueppm_api/apps/projects/task_batch_services.py +379 -0
  503. trueppm_api/apps/projects/task_bulk.py +1244 -0
  504. trueppm_api/apps/projects/task_classification.py +561 -0
  505. trueppm_api/apps/projects/task_classification_defaults.py +95 -0
  506. trueppm_api/apps/projects/task_duration_settings.py +142 -0
  507. trueppm_api/apps/projects/task_grouping.py +754 -0
  508. trueppm_api/apps/projects/tasks.py +1979 -0
  509. trueppm_api/apps/projects/template_divergence.py +168 -0
  510. trueppm_api/apps/projects/template_services.py +160 -0
  511. trueppm_api/apps/projects/template_tasks.py +261 -0
  512. trueppm_api/apps/projects/template_views.py +829 -0
  513. trueppm_api/apps/projects/throttles.py +311 -0
  514. trueppm_api/apps/projects/urls.py +1016 -0
  515. trueppm_api/apps/projects/utilization.py +909 -0
  516. trueppm_api/apps/projects/views.py +21225 -0
  517. trueppm_api/apps/projects/wbs_paths.py +119 -0
  518. trueppm_api/apps/resources/__init__.py +0 -0
  519. trueppm_api/apps/resources/apps.py +8 -0
  520. trueppm_api/apps/resources/capacity.py +121 -0
  521. trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
  522. trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
  523. trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
  524. trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
  525. trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
  526. trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
  527. trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
  528. trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
  529. trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
  530. trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
  531. trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
  532. trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
  533. trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
  534. trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
  535. trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
  536. trueppm_api/apps/resources/migrations/0014_squashed_0015_release_0_4.py +47 -0
  537. trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
  538. trueppm_api/apps/resources/migrations/__init__.py +0 -0
  539. trueppm_api/apps/resources/models.py +298 -0
  540. trueppm_api/apps/resources/serializers.py +426 -0
  541. trueppm_api/apps/resources/services.py +465 -0
  542. trueppm_api/apps/resources/urls.py +28 -0
  543. trueppm_api/apps/resources/views.py +1752 -0
  544. trueppm_api/apps/scheduling/__init__.py +0 -0
  545. trueppm_api/apps/scheduling/apps.py +153 -0
  546. trueppm_api/apps/scheduling/calendars.py +144 -0
  547. trueppm_api/apps/scheduling/deadletter.py +91 -0
  548. trueppm_api/apps/scheduling/finish_reading.py +198 -0
  549. trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
  550. trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
  551. trueppm_api/apps/scheduling/graph_guard.py +263 -0
  552. trueppm_api/apps/scheduling/management/__init__.py +0 -0
  553. trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
  554. trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
  555. trueppm_api/apps/scheduling/mc_admission.py +181 -0
  556. trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
  557. trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
  558. trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
  559. trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
  560. trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
  561. trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
  562. trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
  563. trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
  564. trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
  565. trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
  566. trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
  567. trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
  568. trueppm_api/apps/scheduling/migrations/0010_squashed_0016_release_0_4.py +106 -0
  569. trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
  570. trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
  571. trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
  572. trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
  573. trueppm_api/apps/scheduling/migrations/0015_projectforecastsnapshot_cpm_finish_at_day_start.py +17 -0
  574. trueppm_api/apps/scheduling/migrations/0016_montecarlorun_finish_readings.py +32 -0
  575. trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
  576. trueppm_api/apps/scheduling/models.py +542 -0
  577. trueppm_api/apps/scheduling/receivers.py +160 -0
  578. trueppm_api/apps/scheduling/risk_premium.py +334 -0
  579. trueppm_api/apps/scheduling/serializers.py +723 -0
  580. trueppm_api/apps/scheduling/services.py +1251 -0
  581. trueppm_api/apps/scheduling/signals.py +56 -0
  582. trueppm_api/apps/scheduling/tasks.py +2248 -0
  583. trueppm_api/apps/scheduling/telemetry.py +80 -0
  584. trueppm_api/apps/scheduling/units.py +35 -0
  585. trueppm_api/apps/scheduling/urls.py +57 -0
  586. trueppm_api/apps/scheduling/views.py +2908 -0
  587. trueppm_api/apps/sso/__init__.py +0 -0
  588. trueppm_api/apps/sso/apps.py +19 -0
  589. trueppm_api/apps/sso/extensions.py +130 -0
  590. trueppm_api/apps/sso/management/__init__.py +0 -0
  591. trueppm_api/apps/sso/management/commands/__init__.py +0 -0
  592. trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
  593. trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
  594. trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
  595. trueppm_api/apps/sso/migrations/__init__.py +0 -0
  596. trueppm_api/apps/sso/models.py +148 -0
  597. trueppm_api/apps/sso/serializers.py +446 -0
  598. trueppm_api/apps/sso/services.py +1473 -0
  599. trueppm_api/apps/sso/urls.py +47 -0
  600. trueppm_api/apps/sso/views.py +935 -0
  601. trueppm_api/apps/sync/__init__.py +0 -0
  602. trueppm_api/apps/sync/apps.py +15 -0
  603. trueppm_api/apps/sync/backfill.py +208 -0
  604. trueppm_api/apps/sync/broadcast.py +369 -0
  605. trueppm_api/apps/sync/conflict.py +262 -0
  606. trueppm_api/apps/sync/consumers.py +548 -0
  607. trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
  608. trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
  609. trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
  610. trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
  611. trueppm_api/apps/sync/migrations/0003_squashed_0005_release_0_4.py +77 -0
  612. trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
  613. trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
  614. trueppm_api/apps/sync/migrations/0006_boardevent_per_project_seq.py +77 -0
  615. trueppm_api/apps/sync/migrations/__init__.py +0 -0
  616. trueppm_api/apps/sync/models.py +272 -0
  617. trueppm_api/apps/sync/pagination.py +231 -0
  618. trueppm_api/apps/sync/sequence.py +488 -0
  619. trueppm_api/apps/sync/serializers.py +688 -0
  620. trueppm_api/apps/sync/tasks.py +260 -0
  621. trueppm_api/apps/sync/throttles.py +175 -0
  622. trueppm_api/apps/sync/upload.py +556 -0
  623. trueppm_api/apps/sync/urls.py +17 -0
  624. trueppm_api/apps/sync/views.py +957 -0
  625. trueppm_api/apps/sync/ws_auth.py +193 -0
  626. trueppm_api/apps/taskruns/__init__.py +0 -0
  627. trueppm_api/apps/taskruns/apps.py +11 -0
  628. trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
  629. trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
  630. trueppm_api/apps/taskruns/models.py +68 -0
  631. trueppm_api/apps/taskruns/serializers.py +92 -0
  632. trueppm_api/apps/taskruns/tasks.py +60 -0
  633. trueppm_api/apps/taskruns/tracker.py +286 -0
  634. trueppm_api/apps/taskruns/urls.py +46 -0
  635. trueppm_api/apps/taskruns/views.py +195 -0
  636. trueppm_api/apps/teams/__init__.py +8 -0
  637. trueppm_api/apps/teams/apps.py +16 -0
  638. trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
  639. trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +159 -0
  640. trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
  641. trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
  642. trueppm_api/apps/teams/migrations/0004_backfill_default_teams_on_upgrade.py +49 -0
  643. trueppm_api/apps/teams/migrations/__init__.py +0 -0
  644. trueppm_api/apps/teams/models.py +145 -0
  645. trueppm_api/apps/teams/permissions.py +149 -0
  646. trueppm_api/apps/teams/serializers.py +95 -0
  647. trueppm_api/apps/teams/services.py +440 -0
  648. trueppm_api/apps/teams/signals.py +61 -0
  649. trueppm_api/apps/teams/urls.py +34 -0
  650. trueppm_api/apps/teams/views.py +201 -0
  651. trueppm_api/apps/timetracking/__init__.py +0 -0
  652. trueppm_api/apps/timetracking/apps.py +9 -0
  653. trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
  654. trueppm_api/apps/timetracking/migrations/0001_squashed_0004_release_0_4.py +167 -0
  655. trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
  656. trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
  657. trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
  658. trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
  659. trueppm_api/apps/timetracking/models.py +178 -0
  660. trueppm_api/apps/timetracking/serializers.py +175 -0
  661. trueppm_api/apps/timetracking/services.py +211 -0
  662. trueppm_api/apps/timetracking/urls.py +47 -0
  663. trueppm_api/apps/timetracking/views.py +597 -0
  664. trueppm_api/apps/webhooks/__init__.py +0 -0
  665. trueppm_api/apps/webhooks/apps.py +8 -0
  666. trueppm_api/apps/webhooks/backfill.py +90 -0
  667. trueppm_api/apps/webhooks/dispatch.py +279 -0
  668. trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
  669. trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +249 -0
  670. trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
  671. trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
  672. trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
  673. trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
  674. trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
  675. trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
  676. trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
  677. trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +100 -0
  678. trueppm_api/apps/webhooks/migrations/0010_alter_webhook_disabled_at.py +21 -0
  679. trueppm_api/apps/webhooks/migrations/0011_webhookeditor.py +48 -0
  680. trueppm_api/apps/webhooks/migrations/0012_alter_webhook_disabled_at.py +21 -0
  681. trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
  682. trueppm_api/apps/webhooks/models.py +437 -0
  683. trueppm_api/apps/webhooks/serializers.py +326 -0
  684. trueppm_api/apps/webhooks/tasks.py +680 -0
  685. trueppm_api/apps/webhooks/urls.py +22 -0
  686. trueppm_api/apps/webhooks/views.py +533 -0
  687. trueppm_api/apps/workflow_engine/__init__.py +0 -0
  688. trueppm_api/apps/workflow_engine/apps.py +23 -0
  689. trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
  690. trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
  691. trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
  692. trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
  693. trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
  694. trueppm_api/apps/workflow_engine/models.py +265 -0
  695. trueppm_api/apps/workflow_engine/tasks.py +371 -0
  696. trueppm_api/apps/workspace/__init__.py +0 -0
  697. trueppm_api/apps/workspace/apps.py +10 -0
  698. trueppm_api/apps/workspace/backfill.py +111 -0
  699. trueppm_api/apps/workspace/export.py +254 -0
  700. trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
  701. trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
  702. trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
  703. trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
  704. trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
  705. trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
  706. trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
  707. trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
  708. trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
  709. trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
  710. trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
  711. trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
  712. trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
  713. trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
  714. trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
  715. trueppm_api/apps/workspace/migrations/0015_squashed_0027_release_0_4.py +199 -0
  716. trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
  717. trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
  718. trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
  719. trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
  720. trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
  721. trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
  722. trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
  723. trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
  724. trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
  725. trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
  726. trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
  727. trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
  728. trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
  729. trueppm_api/apps/workspace/migrations/__init__.py +0 -0
  730. trueppm_api/apps/workspace/models.py +857 -0
  731. trueppm_api/apps/workspace/permissions.py +187 -0
  732. trueppm_api/apps/workspace/serializers.py +590 -0
  733. trueppm_api/apps/workspace/services.py +967 -0
  734. trueppm_api/apps/workspace/signals.py +37 -0
  735. trueppm_api/apps/workspace/tasks.py +616 -0
  736. trueppm_api/apps/workspace/urls.py +113 -0
  737. trueppm_api/apps/workspace/views.py +1581 -0
  738. trueppm_api/asgi.py +49 -0
  739. trueppm_api/celery.py +40 -0
  740. trueppm_api/core/__init__.py +0 -0
  741. trueppm_api/core/admin_site.py +206 -0
  742. trueppm_api/core/auth_views.py +934 -0
  743. trueppm_api/core/constant_time.py +51 -0
  744. trueppm_api/core/csp.py +54 -0
  745. trueppm_api/core/db.py +87 -0
  746. trueppm_api/core/db_session.py +37 -0
  747. trueppm_api/core/demo_read_only.py +314 -0
  748. trueppm_api/core/exception_handlers.py +157 -0
  749. trueppm_api/core/export_downloads.py +56 -0
  750. trueppm_api/core/extension_providers.py +56 -0
  751. trueppm_api/core/extension_signals.py +147 -0
  752. trueppm_api/core/idempotent.py +260 -0
  753. trueppm_api/core/metadata.py +42 -0
  754. trueppm_api/core/middleware.py +51 -0
  755. trueppm_api/core/openapi.py +1221 -0
  756. trueppm_api/core/password_reset.py +513 -0
  757. trueppm_api/core/protect_conflict.py +100 -0
  758. trueppm_api/core/ratelimit.py +148 -0
  759. trueppm_api/core/redis_throttle.py +82 -0
  760. trueppm_api/core/refresh_cookie_policy.py +156 -0
  761. trueppm_api/core/request_body.py +86 -0
  762. trueppm_api/core/security_checks.py +1034 -0
  763. trueppm_api/core/storage_config.py +105 -0
  764. trueppm_api/core/text_decode.py +168 -0
  765. trueppm_api/core/throttling.py +514 -0
  766. trueppm_api/core/valkey.py +214 -0
  767. trueppm_api/core/valkey_cache.py +66 -0
  768. trueppm_api/core/valkey_checks.py +104 -0
  769. trueppm_api/core/valkey_config.py +39 -0
  770. trueppm_api/core/worker_broker_wait.py +149 -0
  771. trueppm_api/core/worker_heartbeat.py +134 -0
  772. trueppm_api/core/worker_selfheal.py +205 -0
  773. trueppm_api/fields.py +36 -0
  774. trueppm_api/permissions.py +30 -0
  775. trueppm_api/py.typed +0 -0
  776. trueppm_api/routing.py +11 -0
  777. trueppm_api/settings/__init__.py +0 -0
  778. trueppm_api/settings/base.py +2605 -0
  779. trueppm_api/settings/dev.py +296 -0
  780. trueppm_api/settings/prod.py +265 -0
  781. trueppm_api/urls.py +217 -0
  782. trueppm_api/workflows/__init__.py +38 -0
  783. trueppm_api/workflows/backends/__init__.py +6 -0
  784. trueppm_api/workflows/backends/default.py +297 -0
  785. trueppm_api/workflows/consumers/__init__.py +11 -0
  786. trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
  787. trueppm_api/workflows/interface.py +148 -0
  788. trueppm_api/workflows/registry.py +106 -0
  789. trueppm_api/workflows/services.py +90 -0
  790. trueppm_api/wsgi.py +11 -0
  791. trueppm_api-0.4.0.dist-info/METADATA +273 -0
  792. trueppm_api-0.4.0.dist-info/RECORD +799 -0
  793. trueppm_api-0.4.0.dist-info/WHEEL +4 -0
  794. trueppm_api-0.4.0.dist-info/licenses/LICENSE +201 -0
  795. trueppm_api-0.4.0.dist-info/licenses/NOTICE +7 -0
  796. trueppm_api-0.4.0.dist-info/licenses/NOTICES +104 -0
  797. trueppm_api-0.4.0.dist-info/licenses/licenses/GPL-3.0.txt +674 -0
  798. trueppm_api-0.4.0.dist-info/licenses/licenses/LGPL-3.0.txt +165 -0
  799. trueppm_api-0.4.0.dist-info/licenses/licenses/MPL-2.0.txt +373 -0
@@ -0,0 +1,100 @@
1
+ # Generated by Django 5.2.16 on 2026-08-17 14:09
2
+ #
3
+ # Hand-ordered (#2885): the generated migration emitted RemoveField(secret) first,
4
+ # which would have dropped every signing secret in the install. The AddField must
5
+ # come first, the RunPython copies the plaintext into the new encrypted column
6
+ # while both exist, and only then is the plaintext column dropped. The data step
7
+ # lives in webhooks/backfill.py so a test can exercise it without importing this
8
+ # module by name (CLAUDE.md migration rule 3).
9
+
10
+ from django.db import migrations, models
11
+
12
+ from trueppm_api.apps.webhooks.backfill import (
13
+ encrypt_webhook_secrets,
14
+ reverse_encrypt_webhook_secrets,
15
+ )
16
+
17
+
18
+ class Migration(migrations.Migration):
19
+ dependencies = [
20
+ ("webhooks", "0008_risk_baseline_comment_events"),
21
+ ]
22
+
23
+ operations = [
24
+ migrations.AddField(
25
+ model_name="webhook",
26
+ name="secret_ciphertext",
27
+ field=models.BinaryField(blank=True, default=b""),
28
+ ),
29
+ # elidable: safe for the optimizer to drop IF this migration's originals
30
+ # ship and run in a released version before the squash that replaces them
31
+ # is generated — a squash only guarantees an empty table for ops whose
32
+ # originals already applied in an earlier release (#4320 correction; see
33
+ # CLAUDE.md migration-discipline rule 1/6 and access/migrations/
34
+ # 0017_viewer_ordinal_one.py for the upgrade-path counterexample this was
35
+ # wrong about). As of this writing 0009 is UNRELEASED, so that condition
36
+ # does not yet hold: whichever 0.x squash first spans this migration will
37
+ # run it directly against populated tables on upgrade, exactly like the
38
+ # access squash did. Re-check this comment, and verify with a test in the
39
+ # shape of tests/apps/access/test_viewer_ordinal_upgrade.py, before that
40
+ # squash is generated — do not assume elision is still safe by then.
41
+ # Leaving it non-elidable would make squashmigrations keep it as an
42
+ # optimizer barrier, pinning the add-copy-drop dance for a column no model
43
+ # has — plus a hard import of webhooks.backfill — into the baseline.
44
+ migrations.RunPython(
45
+ encrypt_webhook_secrets,
46
+ reverse_encrypt_webhook_secrets,
47
+ elidable=True,
48
+ ),
49
+ # State-only, and a no-op forward: `blank`/`default` are not DDL in Django.
50
+ # It exists for the REVERSE direction. RemoveField's backwards pass re-adds
51
+ # the field from the previous state, where `secret` was CharField(max_length=255)
52
+ # with no default and blank=False — so Django emits a bare
53
+ # `ADD COLUMN "secret" varchar(255) NOT NULL`, which PostgreSQL rejects on any
54
+ # table with rows ("contains null values"). That aborts the reverse *before*
55
+ # reverse_encrypt_webhook_secrets can put the plaintext back, on exactly the
56
+ # databases that have webhooks. Declaring blank/default here makes the
57
+ # re-added column carry `DEFAULT ''`. (Passing `field=` to RemoveField would
58
+ # not work: its database_backwards reads the field off to_state.)
59
+ migrations.AlterField(
60
+ model_name="webhook",
61
+ name="secret",
62
+ field=models.CharField(blank=True, default="", max_length=255),
63
+ ),
64
+ migrations.RemoveField(
65
+ model_name="webhook",
66
+ name="secret",
67
+ ),
68
+ migrations.AddField(
69
+ model_name="webhook",
70
+ name="consecutive_failures",
71
+ field=models.PositiveIntegerField(
72
+ default=0,
73
+ help_text="Terminal delivery failures since the last success. Resets to 0 on any successful delivery.",
74
+ ),
75
+ ),
76
+ migrations.AddField(
77
+ model_name="webhook",
78
+ name="disabled_at",
79
+ field=models.DateTimeField(
80
+ blank=True,
81
+ help_text="When the automatic failure guard deactivated this subscription. Null when it is active, or when an admin deactivated it by hand.",
82
+ null=True,
83
+ ),
84
+ ),
85
+ migrations.AddField(
86
+ model_name="webhook",
87
+ name="disabled_reason",
88
+ field=models.CharField(blank=True, default="", max_length=255),
89
+ ),
90
+ migrations.AddField(
91
+ model_name="webhook",
92
+ name="last_failure_at",
93
+ field=models.DateTimeField(blank=True, null=True),
94
+ ),
95
+ migrations.AddField(
96
+ model_name="webhook",
97
+ name="last_failure_reason",
98
+ field=models.CharField(blank=True, default="", max_length=255),
99
+ ),
100
+ ]
@@ -0,0 +1,21 @@
1
+ # Generated by Django 5.2.17 on 2026-10-17 11:36
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("webhooks", "0009_encrypt_webhook_secret_and_failure_counters"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AlterField(
13
+ model_name="webhook",
14
+ name="disabled_at",
15
+ field=models.DateTimeField(
16
+ blank=True,
17
+ help_text="When the automatic failure guard, or a credential-revocation sweep (password reset, off-boarding, or revoke_api_tokens --all), deactivated this subscription. Null when it is active, or when an admin deactivated it by hand.",
18
+ null=True,
19
+ ),
20
+ ),
21
+ ]
@@ -0,0 +1,48 @@
1
+ # Generated by Django 5.2.17 on 2026-10-17 12:42
2
+
3
+ import uuid
4
+
5
+ import django.db.models.deletion
6
+ from django.conf import settings
7
+ from django.db import migrations, models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+ dependencies = [
12
+ ("webhooks", "0010_alter_webhook_disabled_at"),
13
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.CreateModel(
18
+ name="WebhookEditor",
19
+ fields=[
20
+ (
21
+ "id",
22
+ models.UUIDField(
23
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False
24
+ ),
25
+ ),
26
+ ("set_at", models.DateTimeField(auto_now=True)),
27
+ (
28
+ "user",
29
+ models.ForeignKey(
30
+ on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
31
+ ),
32
+ ),
33
+ (
34
+ "webhook",
35
+ models.ForeignKey(
36
+ on_delete=django.db.models.deletion.CASCADE,
37
+ related_name="editors",
38
+ to="webhooks.webhook",
39
+ ),
40
+ ),
41
+ ],
42
+ options={
43
+ "constraints": [
44
+ models.UniqueConstraint(fields=("webhook", "user"), name="uniq_webhook_editor")
45
+ ],
46
+ },
47
+ ),
48
+ ]
@@ -0,0 +1,21 @@
1
+ # Generated by Django 5.2.17 on 2026-10-19 14:51
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("webhooks", "0011_webhookeditor"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AlterField(
13
+ model_name="webhook",
14
+ name="disabled_at",
15
+ field=models.DateTimeField(
16
+ blank=True,
17
+ help_text="When the automatic failure guard, or a credential-revocation sweep (password reset, off-boarding, or revoke_api_tokens --user/--all-personal/--all), deactivated this subscription. Null when it is active, or when an admin deactivated it by hand.",
18
+ null=True,
19
+ ),
20
+ ),
21
+ ]
File without changes
@@ -0,0 +1,437 @@
1
+ """Models for outbound webhook subscriptions and delivery tracking."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+ from typing import Any
7
+
8
+ from django.conf import settings
9
+ from django.contrib.postgres.fields import ArrayField
10
+ from django.db import models, transaction
11
+ from django.utils import timezone
12
+
13
+ from trueppm_api.apps.integrations.encryption import decrypt_secret, encrypt_secret
14
+
15
+
16
+ class WebhookEventType(models.TextChoices):
17
+ TASK_CREATED = "task.created", "Task Created"
18
+ TASK_UPDATED = "task.updated", "Task Updated"
19
+ TASK_DELETED = "task.deleted", "Task Deleted"
20
+ DEPENDENCY_CREATED = "dependency.created", "Dependency Created"
21
+ DEPENDENCY_DELETED = "dependency.deleted", "Dependency Deleted"
22
+ SCHEDULE_RECALCULATED = "schedule.recalculated", "Schedule Recalculated"
23
+ PROJECT_CREATED = "project.created", "Project Created"
24
+ # Four new task events added in #638 (ADR-0083). task.due_date_changed
25
+ # currently fires on planned_start changes — #690 rebinds it to a dedicated
26
+ # planned_finish deadline field.
27
+ TASK_ASSIGNED = "task.assigned", "Task Assigned"
28
+ TASK_ASSIGNEE_CHANGED = "task.assignee_changed", "Task Assignee Changed"
29
+ TASK_MENTIONED = "task.mentioned", "Task Mentioned"
30
+ TASK_DUE_DATE_CHANGED = "task.due_date_changed", "Task Due Date Changed"
31
+ # Agile trio added in #1073 (ADR-0147). First-party domain events — they make
32
+ # the sprint-sovereignty story (ADR-0102/0104) observable to external tooling.
33
+ # sprint.closed's completion snapshot is velocity and is privacy-gated in its
34
+ # payload builder per ADR-0104 (see _sprint_closed_webhook_payload).
35
+ SPRINT_ACTIVATED = "sprint.activated", "Sprint Activated"
36
+ SPRINT_CLOSED = "sprint.closed", "Sprint Closed"
37
+ SPRINT_SCOPE_CHANGED = "sprint.scope_changed", "Sprint Scope Changed"
38
+ # Risk / baseline / comment domain events added in #1082 (ADR-0206). First-party
39
+ # domain events that extend the catalog beyond agile so external reporting and
40
+ # alerting tooling can observe the risk register, baseline captures, and the
41
+ # comment thread. risk.escalated fires when computed severity (probability ×
42
+ # impact) increases; risk.closed on the transition into CLOSED. comment.created
43
+ # carries no comment body (privacy-conservative) and is distinct from
44
+ # task.mentioned, which fires only when a comment mentions someone.
45
+ RISK_OPENED = "risk.opened", "Risk Opened"
46
+ RISK_ESCALATED = "risk.escalated", "Risk Escalated"
47
+ RISK_CLOSED = "risk.closed", "Risk Closed"
48
+ BASELINE_CAPTURED = "baseline.captured", "Baseline Captured"
49
+ COMMENT_CREATED = "comment.created", "Comment Created"
50
+
51
+
52
+ ALL_WEBHOOK_EVENTS = [e.value for e in WebhookEventType]
53
+
54
+ # Hard cap on the number of OSS webhook event types (ADR-0083, raised 11→14 by
55
+ # ADR-0147 for the agile trio, then 14→19 by ADR-0206 for the risk/baseline/comment
56
+ # domain events). Adding a 20th event requires its own ADR — this is the gate
57
+ # against the per-customer event proliferation that is the explicit Enterprise
58
+ # upsell. These are all first-party domain events, not custom/user-defined events,
59
+ # so the upsell line is unaffected. ``test_event_type_cap`` fails loudly if
60
+ # WebhookEventType drifts from this number.
61
+ OSS_WEBHOOK_EVENT_CAP = 19
62
+
63
+ # Reserved, non-domain event type used by the "Send test" action (#2884). It is
64
+ # deliberately NOT a WebhookEventType member: no subscription can select it and
65
+ # no domain mutation emits it, but a test ping is a real delivery — it renders
66
+ # through the subscription's provider, allocates a sequence number, and carries
67
+ # the ``_meta`` envelope like any other delivery, so a consumer doing in-body gap
68
+ # detection does not see a phantom jump when an admin clicks Test.
69
+ PING_EVENT_TYPE = "ping"
70
+
71
+ # Consecutive terminal delivery failures after which a subscription is
72
+ # automatically deactivated (#2884). Before this existed, a revoked receiver
73
+ # answering 404/410 was retried forever with no state change: `is_active` was
74
+ # only ever written by a human PATCH, so the queue and the receiver both burned
75
+ # indefinitely. Five is one working day's worth of noise for a low-traffic
76
+ # project and about a minute for a busy one — high enough that a brief receiver
77
+ # outage does not trip it (a transient 5xx recovers within the retry chain and
78
+ # resets the counter on the next success), low enough that a permanently broken
79
+ # endpoint stops being retried.
80
+ AUTO_DISABLE_CONSECUTIVE_FAILURES = 5
81
+
82
+
83
+ class Webhook(models.Model):
84
+ """A registered outbound webhook subscription.
85
+
86
+ Polymorphically scoped to either a Project or a Program (ADR-0076).
87
+ A program-scoped webhook fires for events on any project within the
88
+ program; a project-scoped webhook fires only for that project. The DB
89
+ constraint enforces XOR — exactly one of project/program is set.
90
+ """
91
+
92
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
93
+ project = models.ForeignKey(
94
+ "projects.Project",
95
+ on_delete=models.CASCADE,
96
+ related_name="webhooks",
97
+ null=True,
98
+ blank=True,
99
+ help_text="Set when the webhook fires for events on a single project. "
100
+ "Exactly one of project/program is non-null (DB constraint).",
101
+ )
102
+ program = models.ForeignKey(
103
+ "projects.Program",
104
+ on_delete=models.CASCADE,
105
+ related_name="webhooks",
106
+ null=True,
107
+ blank=True,
108
+ help_text="Set when the webhook fires for events on any project within "
109
+ "this program. Exactly one of project/program is non-null (DB constraint).",
110
+ )
111
+ url = models.URLField(max_length=2048)
112
+ # Fernet ciphertext of the HMAC signing secret (#2885, ADR-0049 §3). This was
113
+ # the last plaintext secret in the product; every sibling secret
114
+ # (IntegrationCredential PAT, inbound git webhook secret, SSO client_secret,
115
+ # workspace SMTP password) already used this at-rest pattern, so a pg_dump or
116
+ # a read-only SQL finding yielded signing keys that let an attacker forge
117
+ # accepted deliveries at every registered receiver.
118
+ #
119
+ # Encrypted rather than hashed: signing needs the plaintext at delivery time
120
+ # (the same reason BoardAutomation.secret_ciphertext is encrypted and
121
+ # ApiToken's is hashed). Read and written through the ``secret`` property
122
+ # below, which keeps ``Webhook.objects.create(secret=...)`` and
123
+ # ``webhook.secret`` working — Django's ``Model.__init__`` routes an unknown
124
+ # kwarg to a same-named property with a setter.
125
+ secret_ciphertext = models.BinaryField(default=b"", blank=True, editable=False)
126
+ events = ArrayField(
127
+ models.CharField(max_length=30, choices=WebhookEventType.choices),
128
+ help_text="List of event types this webhook subscribes to.",
129
+ )
130
+ # Outgoing payload format (#638, ADR-0049/0083). NOT a TextChoices — validated
131
+ # at write time against OUTGOING_CHANNEL_PROVIDERS.keys() in the serializer, so
132
+ # Enterprise can register slack_app/teams without an OSS migration. Existing
133
+ # rows backfill to "generic" (the historical pass-through behavior).
134
+ format = models.CharField(max_length=32, default="generic")
135
+ is_active = models.BooleanField(default=True)
136
+ # Per-subscription delivery-health counters (#2884). Before these existed the
137
+ # only evidence of a failing endpoint was the per-webhook delivery log, which
138
+ # is Admin-only and purges terminal rows after TRUEPPM_WEBHOOK_RETENTION_DAYS
139
+ # (default 7) — so an outage older than a week left no trace anywhere.
140
+ #
141
+ # ``consecutive_failures`` counts *terminal* failures (a delivery that will
142
+ # never be attempted again), not individual attempts, and resets to 0 on the
143
+ # next success. ``disabled_at``/``disabled_reason`` are set by the automatic
144
+ # guard OR by ``revoke_personal_durable_grants`` (#4461) — never by a human
145
+ # PATCH — so the UI can distinguish "an admin paused this" from "TruePPM gave
146
+ # up on this" from "the registrant's credentials were revoked".
147
+ consecutive_failures = models.PositiveIntegerField(
148
+ default=0,
149
+ help_text="Terminal delivery failures since the last success. Resets to 0 "
150
+ "on any successful delivery.",
151
+ )
152
+ last_failure_at = models.DateTimeField(null=True, blank=True)
153
+ last_failure_reason = models.CharField(max_length=255, blank=True, default="")
154
+ disabled_at = models.DateTimeField(
155
+ null=True,
156
+ blank=True,
157
+ help_text="When the automatic failure guard, or a credential-revocation "
158
+ "sweep (password reset, off-boarding, or revoke_api_tokens --user/"
159
+ "--all-personal/--all), deactivated this subscription. Null when it is "
160
+ "active, or when an admin deactivated it by hand.",
161
+ )
162
+ disabled_reason = models.CharField(max_length=255, blank=True, default="")
163
+ # Per-subscription monotonic counter for outgoing deliveries (#664). Lives on
164
+ # the subscription, NOT derived from WebhookDelivery rows, because the
165
+ # retention purge (ADR-0081) deletes terminal deliveries — deriving the next
166
+ # value from the delivery table would reuse sequence numbers after a purge
167
+ # and silently corrupt the consumer-side gap-detection contract.
168
+ delivery_sequence = models.BigIntegerField(default=0, editable=False)
169
+ created_at = models.DateTimeField(auto_now_add=True)
170
+ created_by = models.ForeignKey(
171
+ settings.AUTH_USER_MODEL,
172
+ on_delete=models.SET_NULL,
173
+ null=True,
174
+ blank=True,
175
+ )
176
+
177
+ class Meta:
178
+ indexes = [
179
+ models.Index(fields=["project", "is_active"], name="webhook_project_active_idx"),
180
+ models.Index(fields=["program", "is_active"], name="webhook_program_active_idx"),
181
+ ]
182
+ constraints = [
183
+ models.CheckConstraint(
184
+ condition=(
185
+ models.Q(project__isnull=False, program__isnull=True)
186
+ | models.Q(project__isnull=True, program__isnull=False)
187
+ ),
188
+ name="webhook_scope_xor",
189
+ ),
190
+ ]
191
+
192
+ def __str__(self) -> str:
193
+ return f"Webhook {self.id} → {self.url}"
194
+
195
+ @property
196
+ def is_program_scoped(self) -> bool:
197
+ """True when this webhook fires for events on any project in a program."""
198
+ return self.program_id is not None
199
+
200
+ @property
201
+ def secret(self) -> str:
202
+ """The plaintext HMAC signing secret, decrypted from ``secret_ciphertext``.
203
+
204
+ Server-side only — it is never serialized to a client (the API echoes an
205
+ auto-generated secret exactly once at create time and a ``secret_set``
206
+ boolean thereafter). Returns ``""`` when no secret is stored, which the
207
+ delivery task treats as a configuration error rather than signing with an
208
+ empty key.
209
+ """
210
+ if not self.secret_ciphertext:
211
+ return ""
212
+ return decrypt_secret(self.secret_ciphertext)
213
+
214
+ @secret.setter
215
+ def secret(self, plaintext: str) -> None:
216
+ """Fernet-encrypt ``plaintext`` into ``secret_ciphertext`` (#2885).
217
+
218
+ A setter rather than an explicit ``set_secret()`` method so every existing
219
+ write path — ``Webhook.objects.create(secret=...)``, DRF's
220
+ ``setattr``-based ``ModelSerializer.update``, and fixtures — keeps working
221
+ against the encrypted column with no call-site change. Writing a new value
222
+ is the rotation path: the previous ciphertext is overwritten.
223
+ """
224
+ self.secret_ciphertext = encrypt_secret(plaintext) if plaintext else b""
225
+
226
+ @property
227
+ def secret_set(self) -> bool:
228
+ """Whether a signing secret is stored (the only thing the API exposes)."""
229
+ return bool(self.secret_ciphertext)
230
+
231
+ def record_delivery_success(self) -> None:
232
+ """Clear the failure counters after a delivery succeeds (#2884).
233
+
234
+ Guarded on a non-zero counter so the common case (a healthy webhook
235
+ delivering thousands of events) issues no UPDATE at all. Written with a
236
+ queryset update rather than ``save()`` because the caller holds a delivery
237
+ row, not a lock on the subscription, and two concurrent deliveries to the
238
+ same webhook would otherwise race on a stale in-memory copy.
239
+ """
240
+ if not (self.consecutive_failures or self.last_failure_at or self.disabled_at):
241
+ return
242
+ Webhook.objects.filter(pk=self.pk).update(
243
+ consecutive_failures=0,
244
+ last_failure_at=None,
245
+ last_failure_reason="",
246
+ )
247
+ self.consecutive_failures = 0
248
+ self.last_failure_at = None
249
+ self.last_failure_reason = ""
250
+
251
+ def record_delivery_failure(self, reason: str) -> bool:
252
+ """Count one *terminal* delivery failure; auto-disable past the threshold.
253
+
254
+ Called only when a delivery will never be attempted again — a permanent
255
+ client error, an exhausted retry chain, or an SSRF block. Attempts inside a
256
+ live retry chain must not be counted here, or a single event's five
257
+ attempts would trip the guard on their own.
258
+
259
+ The increment uses ``F()`` so concurrent fan-out to the same subscription
260
+ cannot lose a count, then re-reads the committed value to decide whether to
261
+ deactivate. Returns ``True`` when this call deactivated the subscription,
262
+ so the caller can log the transition once.
263
+ """
264
+ now = timezone.now()
265
+ Webhook.objects.filter(pk=self.pk).update(
266
+ consecutive_failures=models.F("consecutive_failures") + 1,
267
+ last_failure_at=now,
268
+ last_failure_reason=reason[:255],
269
+ )
270
+ failures: int = (
271
+ Webhook.objects.filter(pk=self.pk)
272
+ .values_list("consecutive_failures", flat=True)
273
+ .first()
274
+ or 0
275
+ )
276
+ self.consecutive_failures = failures
277
+ self.last_failure_at = now
278
+ self.last_failure_reason = reason[:255]
279
+
280
+ if failures < AUTO_DISABLE_CONSECUTIVE_FAILURES:
281
+ return False
282
+ # Only the first call across the threshold flips the flag, so a webhook
283
+ # already deactivated (by the guard or by an admin) is not re-stamped.
284
+ disabled_reason = (
285
+ f"Automatically deactivated after {failures} consecutive failed "
286
+ f"deliveries. Last failure: {reason}"
287
+ )[:255]
288
+ updated = Webhook.objects.filter(pk=self.pk, is_active=True).update(
289
+ is_active=False,
290
+ disabled_at=now,
291
+ disabled_reason=disabled_reason,
292
+ )
293
+ if updated:
294
+ self.is_active = False
295
+ self.disabled_at = now
296
+ self.disabled_reason = disabled_reason
297
+ return bool(updated)
298
+
299
+
300
+ class WebhookEditor(models.Model):
301
+ """Every user who has ever set a webhook's `url`/`secret`, or re-enabled it
302
+ while disabled (#4461).
303
+
304
+ **Additive by design — never overwritten.** `revoke_personal_durable_grants`'s
305
+ sweep must deactivate a webhook for every account whose credential compromise
306
+ could have put the webhook's *current* URL/secret in place, not only the one
307
+ who most recently touched it. A single mutable "last modified by" field looked
308
+ like the obvious shape and was tried first, and it broke on the very next
309
+ edit: a project webhook's update has no registrant check, so any project Admin
310
+ may edit any project webhook (ADR-0076's creator-only rule is program-scoped
311
+ only, #4310). Given a hijacked session (Admin B) repoints `url` to an
312
+ attacker's host, a single mutable field fails two ways once a *different*,
313
+ legitimate admin (A) later touches the row at all:
314
+
315
+ - A's own later secret rotation overwrites the stamp to A — B's own password
316
+ reset/off-boarding no longer matches, and the row survives it.
317
+ - Even a PUT that only changes `events` carries the (unchanged, still evil)
318
+ `url` in its body — DRF's serializer treats a present-but-unchanged field
319
+ as "set" — so the stamp flips to A on a write that touched neither field in
320
+ substance.
321
+
322
+ A row here is therefore inserted once per (webhook, user) and never deleted
323
+ or reassigned to someone else; a later editor can only ADD their own row, not
324
+ remove this one. `set_at` is refreshed on every qualifying edit by the same
325
+ user (informational — "last confirmed", not "only"), via `update_or_create`.
326
+ The sweep matches `created_by=user OR user in webhook.editors` — both legs,
327
+ because `created_by` is the registrant even if they never edited the row at
328
+ all, and this table is editors, not registrants.
329
+
330
+ A row is also inserted when a user flips a disabled webhook's `is_active`
331
+ back to `True` (``WebhookViewSet.perform_update``), even with `url`/`secret`
332
+ untouched: a webhook that is disabled still carries whatever `url`/`secret`
333
+ it had, so re-enabling it restarts delivery to wherever it currently
334
+ points. A second hijacked session that does nothing but re-enable the row
335
+ must be recorded, or its own future containment would miss it despite
336
+ being the one that restarted the leak. *Disabling* is deliberately NOT
337
+ recorded — it only shrinks what is live, the opposite shape.
338
+
339
+ Mirrors the shape of ``TaskLabel``/``CommentReaction`` (plain through model,
340
+ UUID PK, ``unique(webhook, user)``) rather than a ``ManyToManyField`` so the
341
+ upsert (``update_or_create``) and the `set_at` timestamp have somewhere to
342
+ live.
343
+ """
344
+
345
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
346
+ webhook = models.ForeignKey(Webhook, on_delete=models.CASCADE, related_name="editors")
347
+ # CASCADE: a deleted user cannot be the subject of a future credential sweep
348
+ # anyway — unlike Webhook.created_by (SET_NULL), there is no "unattributed but
349
+ # still relevant" state to preserve here.
350
+ user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
351
+ set_at = models.DateTimeField(auto_now=True)
352
+
353
+ class Meta:
354
+ constraints = [
355
+ models.UniqueConstraint(fields=["webhook", "user"], name="uniq_webhook_editor"),
356
+ ]
357
+
358
+ def __str__(self) -> str:
359
+ return f"WebhookEditor({self.webhook_id}, {self.user_id})"
360
+
361
+
362
+ def _next_delivery_sequence(webhook_id: uuid.UUID | str) -> int:
363
+ """Atomically allocate the next per-subscription delivery sequence number.
364
+
365
+ Locks the Webhook row with ``select_for_update`` and increments its
366
+ ``delivery_sequence`` counter inside an explicit transaction. The lock is
367
+ held until commit, so concurrent deliveries to the same subscription receive
368
+ strictly increasing, *contiguous* numbers — a gap at the consumer therefore
369
+ signals a genuinely lost event, which is the whole point of the gap-detection
370
+ contract (#664).
371
+
372
+ The explicit ``transaction.atomic()`` is required because ``dispatch_webhooks``
373
+ runs from a ``transaction.on_commit`` callback (autocommit context); without
374
+ the surrounding transaction the increment + read-back would race.
375
+ """
376
+ with transaction.atomic():
377
+ webhook = Webhook.objects.select_for_update().get(pk=webhook_id)
378
+ webhook.delivery_sequence += 1
379
+ webhook.save(update_fields=["delivery_sequence"])
380
+ return webhook.delivery_sequence
381
+
382
+
383
+ class DeliveryStatus(models.TextChoices):
384
+ PENDING = "pending", "Pending"
385
+ SUCCESS = "success", "Success"
386
+ FAILED = "failed", "Failed"
387
+
388
+
389
+ class WebhookDelivery(models.Model):
390
+ """Record of a single webhook delivery attempt (or series of retries)."""
391
+
392
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
393
+ webhook = models.ForeignKey(
394
+ Webhook,
395
+ on_delete=models.CASCADE,
396
+ related_name="deliveries",
397
+ )
398
+ # ``choices`` documents the subscribable domain events; it is validation-only
399
+ # (no DB constraint), which is what lets the reserved PING_EVENT_TYPE — not a
400
+ # WebhookEventType member because nothing can subscribe to it — be recorded
401
+ # here as a real delivery.
402
+ event_type = models.CharField(max_length=30, choices=WebhookEventType.choices)
403
+ payload = models.JSONField()
404
+ # Monotonic per-subscription sequence, allocated once on INSERT and never
405
+ # changed across retries (the same row keeps its number). Sent to the
406
+ # consumer in the X-TruePPM-Webhook-Sequence header so it can detect gaps
407
+ # and reorder out-of-order events. The default of 0 is only a placeholder
408
+ # for the add-column migration; every row created through save() is assigned
409
+ # a real value >= 1 via _next_delivery_sequence().
410
+ sequence_number = models.BigIntegerField(default=0, editable=False)
411
+ status = models.CharField(
412
+ max_length=10,
413
+ choices=DeliveryStatus.choices,
414
+ default=DeliveryStatus.PENDING,
415
+ )
416
+ response_status = models.SmallIntegerField(null=True, blank=True)
417
+ attempt_count = models.SmallIntegerField(default=0)
418
+ created_at = models.DateTimeField(auto_now_add=True)
419
+ completed_at = models.DateTimeField(null=True, blank=True)
420
+
421
+ class Meta:
422
+ indexes = [
423
+ models.Index(fields=["webhook", "created_at"], name="delivery_webhook_created_idx"),
424
+ models.Index(fields=["status", "created_at"], name="delivery_status_created_idx"),
425
+ ]
426
+ ordering = ["-created_at"]
427
+
428
+ def __str__(self) -> str:
429
+ return f"Delivery {self.id} [{self.status}]"
430
+
431
+ def save(self, *args: Any, **kwargs: Any) -> None:
432
+ # Allocate the per-subscription sequence on INSERT only, so the number is
433
+ # stable across the Celery retry chain (deliver_webhook re-saves the same
434
+ # row on every attempt and must not re-number it).
435
+ if self._state.adding and not self.sequence_number:
436
+ self.sequence_number = _next_delivery_sequence(self.webhook_id)
437
+ super().save(*args, **kwargs)