trueppm-api 0.4.0b2__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 (759) hide show
  1. trueppm_api/__init__.py +14 -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 +164 -0
  8. trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
  9. trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -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 +167 -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_userdefinedmentiongroup.py +82 -0
  24. trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
  25. trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
  26. trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
  27. trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +132 -0
  28. trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
  29. trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
  30. trueppm_api/apps/access/migrations/__init__.py +0 -0
  31. trueppm_api/apps/access/models.py +588 -0
  32. trueppm_api/apps/access/permissions.py +2837 -0
  33. trueppm_api/apps/access/serializers.py +946 -0
  34. trueppm_api/apps/access/services.py +667 -0
  35. trueppm_api/apps/access/signals.py +103 -0
  36. trueppm_api/apps/access/tasks.py +50 -0
  37. trueppm_api/apps/access/throttles.py +95 -0
  38. trueppm_api/apps/access/urls.py +191 -0
  39. trueppm_api/apps/access/views.py +1931 -0
  40. trueppm_api/apps/agents/__init__.py +6 -0
  41. trueppm_api/apps/agents/apps.py +11 -0
  42. trueppm_api/apps/agents/canonical.py +73 -0
  43. trueppm_api/apps/agents/deferred.py +108 -0
  44. trueppm_api/apps/agents/management/__init__.py +0 -0
  45. trueppm_api/apps/agents/management/commands/__init__.py +0 -0
  46. trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
  47. trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
  48. trueppm_api/apps/agents/middleware.py +44 -0
  49. trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
  50. trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
  51. trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
  52. trueppm_api/apps/agents/migrations/__init__.py +0 -0
  53. trueppm_api/apps/agents/models.py +371 -0
  54. trueppm_api/apps/agents/refusal.py +103 -0
  55. trueppm_api/apps/agents/serializers.py +85 -0
  56. trueppm_api/apps/agents/services.py +397 -0
  57. trueppm_api/apps/agents/signals.py +30 -0
  58. trueppm_api/apps/agents/urls.py +15 -0
  59. trueppm_api/apps/agents/views.py +108 -0
  60. trueppm_api/apps/csvimport/__init__.py +12 -0
  61. trueppm_api/apps/csvimport/apps.py +11 -0
  62. trueppm_api/apps/csvimport/mapping.py +363 -0
  63. trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
  64. trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
  65. trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
  66. trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
  67. trueppm_api/apps/csvimport/models.py +111 -0
  68. trueppm_api/apps/csvimport/parser.py +1825 -0
  69. trueppm_api/apps/csvimport/services.py +48 -0
  70. trueppm_api/apps/csvimport/tasks.py +418 -0
  71. trueppm_api/apps/csvimport/template.py +37 -0
  72. trueppm_api/apps/csvimport/urls.py +50 -0
  73. trueppm_api/apps/csvimport/views.py +793 -0
  74. trueppm_api/apps/history/__init__.py +0 -0
  75. trueppm_api/apps/history/apps.py +12 -0
  76. trueppm_api/apps/history/changelog.py +359 -0
  77. trueppm_api/apps/history/diff_policy.py +177 -0
  78. trueppm_api/apps/history/serializers.py +104 -0
  79. trueppm_api/apps/history/signals.py +82 -0
  80. trueppm_api/apps/history/tasks.py +105 -0
  81. trueppm_api/apps/history/urls.py +38 -0
  82. trueppm_api/apps/history/views.py +443 -0
  83. trueppm_api/apps/idempotency/__init__.py +0 -0
  84. trueppm_api/apps/idempotency/apps.py +11 -0
  85. trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
  86. trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
  87. trueppm_api/apps/idempotency/mixins.py +226 -0
  88. trueppm_api/apps/idempotency/models.py +75 -0
  89. trueppm_api/apps/idempotency/tasks.py +61 -0
  90. trueppm_api/apps/integrations/__init__.py +13 -0
  91. trueppm_api/apps/integrations/apps.py +59 -0
  92. trueppm_api/apps/integrations/connections.py +806 -0
  93. trueppm_api/apps/integrations/encryption.py +92 -0
  94. trueppm_api/apps/integrations/external_sources.py +837 -0
  95. trueppm_api/apps/integrations/git_automation_services.py +231 -0
  96. trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
  97. trueppm_api/apps/integrations/http.py +403 -0
  98. trueppm_api/apps/integrations/me_work.py +218 -0
  99. trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
  100. trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
  101. trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
  102. trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
  103. trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
  104. trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
  105. trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
  106. trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
  107. trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
  108. trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
  109. trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
  110. trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
  111. trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
  112. trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
  113. trueppm_api/apps/integrations/migrations/__init__.py +0 -0
  114. trueppm_api/apps/integrations/models.py +622 -0
  115. trueppm_api/apps/integrations/notification_channels.py +57 -0
  116. trueppm_api/apps/integrations/opengraph.py +229 -0
  117. trueppm_api/apps/integrations/outgoing.py +201 -0
  118. trueppm_api/apps/integrations/providers.py +519 -0
  119. trueppm_api/apps/integrations/registry.py +345 -0
  120. trueppm_api/apps/integrations/serializers.py +453 -0
  121. trueppm_api/apps/integrations/services.py +171 -0
  122. trueppm_api/apps/integrations/tasks.py +731 -0
  123. trueppm_api/apps/integrations/throttles.py +165 -0
  124. trueppm_api/apps/integrations/urls.py +77 -0
  125. trueppm_api/apps/integrations/views.py +1106 -0
  126. trueppm_api/apps/jiraimport/__init__.py +8 -0
  127. trueppm_api/apps/jiraimport/apps.py +11 -0
  128. trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
  129. trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
  130. trueppm_api/apps/jiraimport/models.py +70 -0
  131. trueppm_api/apps/jiraimport/parser.py +350 -0
  132. trueppm_api/apps/jiraimport/services.py +46 -0
  133. trueppm_api/apps/jiraimport/tasks.py +277 -0
  134. trueppm_api/apps/jiraimport/urls.py +15 -0
  135. trueppm_api/apps/jiraimport/views.py +153 -0
  136. trueppm_api/apps/msproject/__init__.py +0 -0
  137. trueppm_api/apps/msproject/apps.py +11 -0
  138. trueppm_api/apps/msproject/dataclasses.py +153 -0
  139. trueppm_api/apps/msproject/exporter.py +500 -0
  140. trueppm_api/apps/msproject/extended_attributes.py +77 -0
  141. trueppm_api/apps/msproject/importer.py +1095 -0
  142. trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
  143. trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
  144. trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
  145. trueppm_api/apps/msproject/migrations/__init__.py +0 -0
  146. trueppm_api/apps/msproject/models.py +81 -0
  147. trueppm_api/apps/msproject/parser.py +1231 -0
  148. trueppm_api/apps/msproject/serializers.py +136 -0
  149. trueppm_api/apps/msproject/services.py +58 -0
  150. trueppm_api/apps/msproject/tasks.py +436 -0
  151. trueppm_api/apps/msproject/urls.py +39 -0
  152. trueppm_api/apps/msproject/views.py +531 -0
  153. trueppm_api/apps/notifications/__init__.py +7 -0
  154. trueppm_api/apps/notifications/apps.py +15 -0
  155. trueppm_api/apps/notifications/backfill.py +79 -0
  156. trueppm_api/apps/notifications/categories.py +112 -0
  157. trueppm_api/apps/notifications/delivery_limits.py +258 -0
  158. trueppm_api/apps/notifications/digests.py +447 -0
  159. trueppm_api/apps/notifications/email_backend.py +330 -0
  160. trueppm_api/apps/notifications/email_health.py +128 -0
  161. trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
  162. trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +294 -0
  163. trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
  164. trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
  165. trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
  166. trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
  167. trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
  168. trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
  169. trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
  170. trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
  171. trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
  172. trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
  173. trueppm_api/apps/notifications/migrations/__init__.py +0 -0
  174. trueppm_api/apps/notifications/models.py +1197 -0
  175. trueppm_api/apps/notifications/receivers.py +155 -0
  176. trueppm_api/apps/notifications/schema_migrations.py +56 -0
  177. trueppm_api/apps/notifications/serializers.py +961 -0
  178. trueppm_api/apps/notifications/services.py +1451 -0
  179. trueppm_api/apps/notifications/tasks.py +711 -0
  180. trueppm_api/apps/notifications/throttles.py +98 -0
  181. trueppm_api/apps/notifications/urls.py +97 -0
  182. trueppm_api/apps/notifications/views.py +904 -0
  183. trueppm_api/apps/observability/__init__.py +0 -0
  184. trueppm_api/apps/observability/apps.py +31 -0
  185. trueppm_api/apps/observability/logging.py +213 -0
  186. trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
  187. trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
  188. trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
  189. trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
  190. trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
  191. trueppm_api/apps/observability/migrations/__init__.py +0 -0
  192. trueppm_api/apps/observability/models.py +141 -0
  193. trueppm_api/apps/observability/otel/__init__.py +45 -0
  194. trueppm_api/apps/observability/otel/attributes.py +247 -0
  195. trueppm_api/apps/observability/otel/export_health.py +533 -0
  196. trueppm_api/apps/observability/otel/instrumentation.py +351 -0
  197. trueppm_api/apps/observability/otel/metrics.py +551 -0
  198. trueppm_api/apps/observability/otel/provider.py +511 -0
  199. trueppm_api/apps/observability/otel/request_attributes.py +204 -0
  200. trueppm_api/apps/observability/purge_registry.py +124 -0
  201. trueppm_api/apps/observability/retention.py +139 -0
  202. trueppm_api/apps/observability/selectors.py +969 -0
  203. trueppm_api/apps/observability/serializers.py +142 -0
  204. trueppm_api/apps/observability/services.py +407 -0
  205. trueppm_api/apps/observability/tasks.py +266 -0
  206. trueppm_api/apps/observability/urls.py +27 -0
  207. trueppm_api/apps/observability/views.py +523 -0
  208. trueppm_api/apps/profiles/__init__.py +0 -0
  209. trueppm_api/apps/profiles/apps.py +15 -0
  210. trueppm_api/apps/profiles/constants.py +54 -0
  211. trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
  212. trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
  213. trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
  214. trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
  215. trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
  216. trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
  217. trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
  218. trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
  219. trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
  220. trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
  221. trueppm_api/apps/profiles/migrations/__init__.py +0 -0
  222. trueppm_api/apps/profiles/models.py +276 -0
  223. trueppm_api/apps/profiles/serializers.py +199 -0
  224. trueppm_api/apps/profiles/services.py +466 -0
  225. trueppm_api/apps/profiles/views.py +124 -0
  226. trueppm_api/apps/projects/__init__.py +0 -0
  227. trueppm_api/apps/projects/actual_date_rules.py +163 -0
  228. trueppm_api/apps/projects/amend.py +133 -0
  229. trueppm_api/apps/projects/apps.py +38 -0
  230. trueppm_api/apps/projects/asset_feed.py +459 -0
  231. trueppm_api/apps/projects/asset_views.py +317 -0
  232. trueppm_api/apps/projects/attachment_policy.py +278 -0
  233. trueppm_api/apps/projects/authentication.py +449 -0
  234. trueppm_api/apps/projects/backfill.py +287 -0
  235. trueppm_api/apps/projects/backlog_services.py +324 -0
  236. trueppm_api/apps/projects/backlog_views.py +302 -0
  237. trueppm_api/apps/projects/batch_operation_views.py +264 -0
  238. trueppm_api/apps/projects/blocker_services.py +510 -0
  239. trueppm_api/apps/projects/board_activity.py +462 -0
  240. trueppm_api/apps/projects/board_activity_views.py +186 -0
  241. trueppm_api/apps/projects/board_lanes.py +134 -0
  242. trueppm_api/apps/projects/bulk_settings.py +194 -0
  243. trueppm_api/apps/projects/bundled_templates.py +215 -0
  244. trueppm_api/apps/projects/calendar_settings.py +180 -0
  245. trueppm_api/apps/projects/ceremony_views.py +244 -0
  246. trueppm_api/apps/projects/commit_moment.py +204 -0
  247. trueppm_api/apps/projects/config_notice.py +1325 -0
  248. trueppm_api/apps/projects/custom_field_values.py +266 -0
  249. trueppm_api/apps/projects/decisions_services.py +93 -0
  250. trueppm_api/apps/projects/decisions_views.py +94 -0
  251. trueppm_api/apps/projects/estimation_scale.py +126 -0
  252. trueppm_api/apps/projects/export_bundle.py +353 -0
  253. trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
  254. trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
  255. trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
  256. trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
  257. trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -0
  258. trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
  259. trueppm_api/apps/projects/inbound_sync.py +555 -0
  260. trueppm_api/apps/projects/iteration_label.py +85 -0
  261. trueppm_api/apps/projects/lifecycle.py +191 -0
  262. trueppm_api/apps/projects/management/__init__.py +0 -0
  263. trueppm_api/apps/projects/management/commands/__init__.py +0 -0
  264. trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
  265. trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
  266. trueppm_api/apps/projects/management/commands/export_program.py +50 -0
  267. trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
  268. trueppm_api/apps/projects/management/commands/load_sample_project.py +247 -0
  269. trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
  270. trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -0
  271. trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
  272. trueppm_api/apps/projects/mcp_settings.py +305 -0
  273. trueppm_api/apps/projects/methodology.py +178 -0
  274. trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
  275. trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -0
  276. trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
  277. trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
  278. trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
  279. trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
  280. trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
  281. trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
  282. trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
  283. trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
  284. trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
  285. trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
  286. trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
  287. trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
  288. trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
  289. trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
  290. trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
  291. trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
  292. trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
  293. trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
  294. trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
  295. trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
  296. trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
  297. trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
  298. trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
  299. trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
  300. trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
  301. trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
  302. trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
  303. trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
  304. trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
  305. trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
  306. trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
  307. trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
  308. trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
  309. trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
  310. trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
  311. trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
  312. trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
  313. trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
  314. trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
  315. trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
  316. trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
  317. trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
  318. trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
  319. trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
  320. trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
  321. trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
  322. trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
  323. trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
  324. trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
  325. trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
  326. trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
  327. trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
  328. trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
  329. trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
  330. trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
  331. trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
  332. trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
  333. trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
  334. trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
  335. trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
  336. trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
  337. trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
  338. trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
  339. trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
  340. trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
  341. trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
  342. trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
  343. trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
  344. trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
  345. trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
  346. trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
  347. trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
  348. trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
  349. trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
  350. trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
  351. trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
  352. trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
  353. trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
  354. trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
  355. trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
  356. trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
  357. trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
  358. trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
  359. trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
  360. trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
  361. trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
  362. trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
  363. trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
  364. trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
  365. trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
  366. trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
  367. trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
  368. trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +44 -0
  369. trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
  370. trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
  371. trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
  372. trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
  373. trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
  374. trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
  375. trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
  376. trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
  377. trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
  378. trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
  379. trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +61 -0
  380. trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
  381. trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
  382. trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
  383. trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
  384. trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
  385. trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +62 -0
  386. trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
  387. trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
  388. trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
  389. trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
  390. trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
  391. trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
  392. trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
  393. trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
  394. trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
  395. trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
  396. trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
  397. trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
  398. trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
  399. trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
  400. trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
  401. trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
  402. trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
  403. trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
  404. trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
  405. trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
  406. trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
  407. trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
  408. trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
  409. trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
  410. trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
  411. trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
  412. trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
  413. trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
  414. trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
  415. trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
  416. trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
  417. trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
  418. trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
  419. trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
  420. trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
  421. trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
  422. trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
  423. trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
  424. trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
  425. trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
  426. trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
  427. trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
  428. trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
  429. trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
  430. trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
  431. trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
  432. trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
  433. trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
  434. trueppm_api/apps/projects/migrations/__init__.py +0 -0
  435. trueppm_api/apps/projects/models.py +8946 -0
  436. trueppm_api/apps/projects/poker_services.py +154 -0
  437. trueppm_api/apps/projects/poker_views.py +398 -0
  438. trueppm_api/apps/projects/product_backlog_services.py +450 -0
  439. trueppm_api/apps/projects/program_label_services.py +94 -0
  440. trueppm_api/apps/projects/program_label_views.py +189 -0
  441. trueppm_api/apps/projects/program_rollup.py +575 -0
  442. trueppm_api/apps/projects/program_schedule.py +745 -0
  443. trueppm_api/apps/projects/program_views.py +3220 -0
  444. trueppm_api/apps/projects/project_templates.py +422 -0
  445. trueppm_api/apps/projects/receivers.py +234 -0
  446. trueppm_api/apps/projects/refusal_codes.py +140 -0
  447. trueppm_api/apps/projects/reorder_services.py +145 -0
  448. trueppm_api/apps/projects/restructure_hooks.py +54 -0
  449. trueppm_api/apps/projects/retro_board_services.py +368 -0
  450. trueppm_api/apps/projects/retro_services.py +285 -0
  451. trueppm_api/apps/projects/risk_import.py +396 -0
  452. trueppm_api/apps/projects/row_identity.py +113 -0
  453. trueppm_api/apps/projects/schema_migrations.py +228 -0
  454. trueppm_api/apps/projects/schemas/seed_v1.json +493 -0
  455. trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
  456. trueppm_api/apps/projects/seed/__init__.py +61 -0
  457. trueppm_api/apps/projects/seed/exporter.py +1644 -0
  458. trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
  459. trueppm_api/apps/projects/seed/importer.py +2390 -0
  460. trueppm_api/apps/projects/seed/reanchor.py +551 -0
  461. trueppm_api/apps/projects/seed/reldates.py +126 -0
  462. trueppm_api/apps/projects/seed/replace.py +126 -0
  463. trueppm_api/apps/projects/seed/replay.py +1691 -0
  464. trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
  465. trueppm_api/apps/projects/seed/samples.py +427 -0
  466. trueppm_api/apps/projects/seed/validation.py +1149 -0
  467. trueppm_api/apps/projects/serializers.py +12310 -0
  468. trueppm_api/apps/projects/services.py +6966 -0
  469. trueppm_api/apps/projects/share_serializers.py +132 -0
  470. trueppm_api/apps/projects/share_services.py +325 -0
  471. trueppm_api/apps/projects/share_views.py +374 -0
  472. trueppm_api/apps/projects/sharing_settings.py +187 -0
  473. trueppm_api/apps/projects/signal_privacy_services.py +909 -0
  474. trueppm_api/apps/projects/signal_privacy_views.py +476 -0
  475. trueppm_api/apps/projects/signals.py +132 -0
  476. trueppm_api/apps/projects/slip_conflict.py +266 -0
  477. trueppm_api/apps/projects/sprint_cadence.py +453 -0
  478. trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
  479. trueppm_api/apps/projects/standup.py +304 -0
  480. trueppm_api/apps/projects/standup_views.py +99 -0
  481. trueppm_api/apps/projects/structural_operation_services.py +727 -0
  482. trueppm_api/apps/projects/structural_operation_views.py +311 -0
  483. trueppm_api/apps/projects/surface_visibility.py +126 -0
  484. trueppm_api/apps/projects/task_batch_services.py +379 -0
  485. trueppm_api/apps/projects/task_bulk.py +1152 -0
  486. trueppm_api/apps/projects/task_classification.py +561 -0
  487. trueppm_api/apps/projects/task_classification_defaults.py +95 -0
  488. trueppm_api/apps/projects/task_duration_settings.py +142 -0
  489. trueppm_api/apps/projects/task_grouping.py +754 -0
  490. trueppm_api/apps/projects/tasks.py +1917 -0
  491. trueppm_api/apps/projects/template_divergence.py +168 -0
  492. trueppm_api/apps/projects/template_services.py +160 -0
  493. trueppm_api/apps/projects/template_tasks.py +258 -0
  494. trueppm_api/apps/projects/template_views.py +799 -0
  495. trueppm_api/apps/projects/throttles.py +311 -0
  496. trueppm_api/apps/projects/urls.py +1011 -0
  497. trueppm_api/apps/projects/utilization.py +885 -0
  498. trueppm_api/apps/projects/views.py +20572 -0
  499. trueppm_api/apps/projects/wbs_paths.py +119 -0
  500. trueppm_api/apps/resources/__init__.py +0 -0
  501. trueppm_api/apps/resources/apps.py +8 -0
  502. trueppm_api/apps/resources/capacity.py +121 -0
  503. trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
  504. trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
  505. trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
  506. trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
  507. trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
  508. trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
  509. trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
  510. trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
  511. trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
  512. trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
  513. trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
  514. trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
  515. trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
  516. trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
  517. trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
  518. trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
  519. trueppm_api/apps/resources/migrations/__init__.py +0 -0
  520. trueppm_api/apps/resources/models.py +298 -0
  521. trueppm_api/apps/resources/serializers.py +426 -0
  522. trueppm_api/apps/resources/services.py +465 -0
  523. trueppm_api/apps/resources/urls.py +28 -0
  524. trueppm_api/apps/resources/views.py +1749 -0
  525. trueppm_api/apps/scheduling/__init__.py +0 -0
  526. trueppm_api/apps/scheduling/apps.py +153 -0
  527. trueppm_api/apps/scheduling/calendars.py +119 -0
  528. trueppm_api/apps/scheduling/deadletter.py +91 -0
  529. trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
  530. trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
  531. trueppm_api/apps/scheduling/graph_guard.py +263 -0
  532. trueppm_api/apps/scheduling/management/__init__.py +0 -0
  533. trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
  534. trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
  535. trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
  536. trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
  537. trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
  538. trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
  539. trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
  540. trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
  541. trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
  542. trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
  543. trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
  544. trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
  545. trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
  546. trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
  547. trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
  548. trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
  549. trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
  550. trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
  551. trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
  552. trueppm_api/apps/scheduling/models.py +516 -0
  553. trueppm_api/apps/scheduling/receivers.py +160 -0
  554. trueppm_api/apps/scheduling/risk_premium.py +283 -0
  555. trueppm_api/apps/scheduling/serializers.py +564 -0
  556. trueppm_api/apps/scheduling/services.py +1182 -0
  557. trueppm_api/apps/scheduling/signals.py +56 -0
  558. trueppm_api/apps/scheduling/tasks.py +1984 -0
  559. trueppm_api/apps/scheduling/telemetry.py +80 -0
  560. trueppm_api/apps/scheduling/units.py +35 -0
  561. trueppm_api/apps/scheduling/urls.py +57 -0
  562. trueppm_api/apps/scheduling/views.py +2564 -0
  563. trueppm_api/apps/sso/__init__.py +0 -0
  564. trueppm_api/apps/sso/apps.py +19 -0
  565. trueppm_api/apps/sso/extensions.py +130 -0
  566. trueppm_api/apps/sso/management/__init__.py +0 -0
  567. trueppm_api/apps/sso/management/commands/__init__.py +0 -0
  568. trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
  569. trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
  570. trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
  571. trueppm_api/apps/sso/migrations/__init__.py +0 -0
  572. trueppm_api/apps/sso/models.py +148 -0
  573. trueppm_api/apps/sso/serializers.py +437 -0
  574. trueppm_api/apps/sso/services.py +1385 -0
  575. trueppm_api/apps/sso/urls.py +47 -0
  576. trueppm_api/apps/sso/views.py +935 -0
  577. trueppm_api/apps/sync/__init__.py +0 -0
  578. trueppm_api/apps/sync/apps.py +15 -0
  579. trueppm_api/apps/sync/backfill.py +208 -0
  580. trueppm_api/apps/sync/broadcast.py +320 -0
  581. trueppm_api/apps/sync/conflict.py +262 -0
  582. trueppm_api/apps/sync/consumers.py +471 -0
  583. trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
  584. trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
  585. trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
  586. trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
  587. trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
  588. trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
  589. trueppm_api/apps/sync/migrations/__init__.py +0 -0
  590. trueppm_api/apps/sync/models.py +225 -0
  591. trueppm_api/apps/sync/pagination.py +231 -0
  592. trueppm_api/apps/sync/sequence.py +371 -0
  593. trueppm_api/apps/sync/serializers.py +687 -0
  594. trueppm_api/apps/sync/tasks.py +260 -0
  595. trueppm_api/apps/sync/throttles.py +175 -0
  596. trueppm_api/apps/sync/upload.py +501 -0
  597. trueppm_api/apps/sync/urls.py +17 -0
  598. trueppm_api/apps/sync/views.py +930 -0
  599. trueppm_api/apps/sync/ws_auth.py +193 -0
  600. trueppm_api/apps/taskruns/__init__.py +0 -0
  601. trueppm_api/apps/taskruns/apps.py +11 -0
  602. trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
  603. trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
  604. trueppm_api/apps/taskruns/models.py +68 -0
  605. trueppm_api/apps/taskruns/serializers.py +92 -0
  606. trueppm_api/apps/taskruns/tasks.py +60 -0
  607. trueppm_api/apps/taskruns/tracker.py +286 -0
  608. trueppm_api/apps/taskruns/urls.py +46 -0
  609. trueppm_api/apps/taskruns/views.py +195 -0
  610. trueppm_api/apps/teams/__init__.py +8 -0
  611. trueppm_api/apps/teams/apps.py +16 -0
  612. trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
  613. trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +142 -0
  614. trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
  615. trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
  616. trueppm_api/apps/teams/migrations/__init__.py +0 -0
  617. trueppm_api/apps/teams/models.py +145 -0
  618. trueppm_api/apps/teams/permissions.py +149 -0
  619. trueppm_api/apps/teams/serializers.py +95 -0
  620. trueppm_api/apps/teams/services.py +434 -0
  621. trueppm_api/apps/teams/signals.py +61 -0
  622. trueppm_api/apps/teams/urls.py +34 -0
  623. trueppm_api/apps/teams/views.py +201 -0
  624. trueppm_api/apps/timetracking/__init__.py +0 -0
  625. trueppm_api/apps/timetracking/apps.py +9 -0
  626. trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
  627. trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
  628. trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
  629. trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
  630. trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
  631. trueppm_api/apps/timetracking/models.py +178 -0
  632. trueppm_api/apps/timetracking/serializers.py +175 -0
  633. trueppm_api/apps/timetracking/services.py +117 -0
  634. trueppm_api/apps/timetracking/urls.py +47 -0
  635. trueppm_api/apps/timetracking/views.py +526 -0
  636. trueppm_api/apps/webhooks/__init__.py +0 -0
  637. trueppm_api/apps/webhooks/apps.py +8 -0
  638. trueppm_api/apps/webhooks/backfill.py +90 -0
  639. trueppm_api/apps/webhooks/dispatch.py +144 -0
  640. trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
  641. trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -0
  642. trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
  643. trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
  644. trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
  645. trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
  646. trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
  647. trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
  648. trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
  649. trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +90 -0
  650. trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
  651. trueppm_api/apps/webhooks/models.py +372 -0
  652. trueppm_api/apps/webhooks/serializers.py +318 -0
  653. trueppm_api/apps/webhooks/tasks.py +663 -0
  654. trueppm_api/apps/webhooks/urls.py +22 -0
  655. trueppm_api/apps/webhooks/views.py +383 -0
  656. trueppm_api/apps/workflow_engine/__init__.py +0 -0
  657. trueppm_api/apps/workflow_engine/apps.py +23 -0
  658. trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
  659. trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
  660. trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
  661. trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
  662. trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
  663. trueppm_api/apps/workflow_engine/models.py +265 -0
  664. trueppm_api/apps/workflow_engine/tasks.py +368 -0
  665. trueppm_api/apps/workspace/__init__.py +0 -0
  666. trueppm_api/apps/workspace/apps.py +10 -0
  667. trueppm_api/apps/workspace/backfill.py +111 -0
  668. trueppm_api/apps/workspace/export.py +242 -0
  669. trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
  670. trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
  671. trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
  672. trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
  673. trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
  674. trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
  675. trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
  676. trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
  677. trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
  678. trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
  679. trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
  680. trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
  681. trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
  682. trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
  683. trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
  684. trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
  685. trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
  686. trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
  687. trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
  688. trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
  689. trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
  690. trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
  691. trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
  692. trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
  693. trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
  694. trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
  695. trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
  696. trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
  697. trueppm_api/apps/workspace/migrations/__init__.py +0 -0
  698. trueppm_api/apps/workspace/models.py +857 -0
  699. trueppm_api/apps/workspace/permissions.py +187 -0
  700. trueppm_api/apps/workspace/serializers.py +590 -0
  701. trueppm_api/apps/workspace/services.py +919 -0
  702. trueppm_api/apps/workspace/signals.py +37 -0
  703. trueppm_api/apps/workspace/tasks.py +555 -0
  704. trueppm_api/apps/workspace/urls.py +113 -0
  705. trueppm_api/apps/workspace/views.py +1502 -0
  706. trueppm_api/asgi.py +42 -0
  707. trueppm_api/celery.py +31 -0
  708. trueppm_api/core/__init__.py +0 -0
  709. trueppm_api/core/admin_site.py +206 -0
  710. trueppm_api/core/auth_views.py +916 -0
  711. trueppm_api/core/constant_time.py +51 -0
  712. trueppm_api/core/csp.py +54 -0
  713. trueppm_api/core/db.py +87 -0
  714. trueppm_api/core/db_session.py +37 -0
  715. trueppm_api/core/exception_handlers.py +157 -0
  716. trueppm_api/core/export_downloads.py +56 -0
  717. trueppm_api/core/extension_providers.py +56 -0
  718. trueppm_api/core/extension_signals.py +147 -0
  719. trueppm_api/core/idempotent.py +260 -0
  720. trueppm_api/core/metadata.py +42 -0
  721. trueppm_api/core/middleware.py +51 -0
  722. trueppm_api/core/openapi.py +949 -0
  723. trueppm_api/core/password_reset.py +500 -0
  724. trueppm_api/core/protect_conflict.py +100 -0
  725. trueppm_api/core/ratelimit.py +148 -0
  726. trueppm_api/core/redis_throttle.py +82 -0
  727. trueppm_api/core/refresh_cookie_policy.py +156 -0
  728. trueppm_api/core/request_body.py +86 -0
  729. trueppm_api/core/security_checks.py +884 -0
  730. trueppm_api/core/storage_config.py +105 -0
  731. trueppm_api/core/text_decode.py +168 -0
  732. trueppm_api/core/throttling.py +344 -0
  733. trueppm_api/core/valkey.py +214 -0
  734. trueppm_api/core/valkey_cache.py +66 -0
  735. trueppm_api/core/valkey_checks.py +104 -0
  736. trueppm_api/core/valkey_config.py +39 -0
  737. trueppm_api/core/worker_broker_wait.py +149 -0
  738. trueppm_api/core/worker_heartbeat.py +134 -0
  739. trueppm_api/fields.py +36 -0
  740. trueppm_api/permissions.py +30 -0
  741. trueppm_api/py.typed +0 -0
  742. trueppm_api/routing.py +11 -0
  743. trueppm_api/settings/__init__.py +0 -0
  744. trueppm_api/settings/base.py +2455 -0
  745. trueppm_api/settings/dev.py +273 -0
  746. trueppm_api/settings/prod.py +228 -0
  747. trueppm_api/urls.py +153 -0
  748. trueppm_api/workflows/__init__.py +38 -0
  749. trueppm_api/workflows/backends/__init__.py +6 -0
  750. trueppm_api/workflows/backends/default.py +297 -0
  751. trueppm_api/workflows/consumers/__init__.py +11 -0
  752. trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
  753. trueppm_api/workflows/interface.py +148 -0
  754. trueppm_api/workflows/registry.py +106 -0
  755. trueppm_api/workflows/services.py +90 -0
  756. trueppm_api/wsgi.py +11 -0
  757. trueppm_api-0.4.0b2.dist-info/METADATA +70 -0
  758. trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
  759. trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
@@ -0,0 +1,476 @@
1
+ """Signal-privacy policy endpoints (ADR-0104 §1.1 + Amendment A / #930).
2
+
3
+ Surface:
4
+ GET /api/v1/projects/<project_pk>/signal-privacy/ — read posture (+ open proposals)
5
+ PATCH /api/v1/projects/<project_pk>/signal-privacy/ — set one audience
6
+ POST .../signal-privacy/raise-ceiling/ — raise → propose; lower → apply
7
+ POST /api/v1/projects/<project_pk>/signal-privacy/ratchet-down/
8
+ GET /api/v1/projects/<project_pk>/signal-privacy/ceiling-proposals/ — list (team-readable)
9
+ POST .../signal-privacy/ceiling-proposals/<proposal_pk>/vote/ — cast/change a vote
10
+ POST .../signal-privacy/ceiling-proposals/<proposal_pk>/withdraw/ — proposer cancels
11
+
12
+ Write gates:
13
+ * set-audience / ratchet — the facilitator's dial (Scrum-Master facet, ADR-0078
14
+ / #927) OR a project Admin (role >= ADMIN). Bounded by the ceiling, so it can
15
+ never widen exposure past what the team authorized.
16
+ * raise-ceiling — the team-owned act (ADR-0104 §1.1 + Amendment A / #930).
17
+ A *raise* opens a ratification proposal (facilitator/Admin may propose); the
18
+ ceiling applies only once the team ratifies (strict majority of the team roster).
19
+ A *lower* and all set-audience moves stay immediate single actions. There is no
20
+ management bypass — that would reopen the §2 back-door the model closes.
21
+ * vote — any active team member (TeamMembership of the default
22
+ team). A non-team project Admin/PM cannot vote (Amendment A.2).
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from typing import Any
28
+
29
+ from drf_spectacular.utils import OpenApiResponse, extend_schema
30
+ from rest_framework import serializers, status
31
+ from rest_framework.exceptions import PermissionDenied
32
+ from rest_framework.generics import get_object_or_404
33
+ from rest_framework.permissions import IsAuthenticated
34
+ from rest_framework.request import Request
35
+ from rest_framework.response import Response
36
+ from rest_framework.views import APIView
37
+
38
+ from trueppm_api.apps.access.models import Role
39
+ from trueppm_api.apps.access.permissions import (
40
+ IsProjectMember,
41
+ IsProjectNotArchived,
42
+ _membership_role,
43
+ )
44
+ from trueppm_api.apps.projects.models import (
45
+ CeilingRaiseStatus,
46
+ Project,
47
+ ProjectSignalPrivacyPolicy,
48
+ SignalAudience,
49
+ SignalCeilingRaiseProposal,
50
+ )
51
+ from trueppm_api.apps.projects.signal_privacy_services import (
52
+ SIGNAL_KEYS,
53
+ CeilingProposalConflict,
54
+ cast_ceiling_vote,
55
+ get_or_create_policy,
56
+ live_open_proposals,
57
+ proposal_tally,
58
+ propose_or_apply_ceiling_change,
59
+ ratchet_down_to_team,
60
+ requester_signal_tier,
61
+ set_signal_audience,
62
+ withdraw_ceiling_proposal,
63
+ )
64
+ from trueppm_api.apps.teams.services import (
65
+ has_team_facet,
66
+ is_team_member,
67
+ team_member_user_ids,
68
+ )
69
+
70
+ # ---------------------------------------------------------------------------
71
+ # Serializers (declared here; drf-spectacular discovers them for the schema)
72
+ # ---------------------------------------------------------------------------
73
+
74
+
75
+ class _SignalPairSerializer(serializers.Serializer[Any]):
76
+ audience = serializers.ChoiceField(choices=SignalAudience.choices)
77
+ ceiling = serializers.ChoiceField(choices=SignalAudience.choices)
78
+
79
+
80
+ class CeilingVoteSerializer(serializers.Serializer[Any]):
81
+ """One team member's vote on a ceiling-raise proposal (team-readable)."""
82
+
83
+ voter = serializers.CharField()
84
+ choice = serializers.CharField()
85
+ created_at = serializers.DateTimeField()
86
+
87
+
88
+ class CeilingProposalSerializer(serializers.Serializer[Any]):
89
+ """A ceiling-raise ratification proposal with its live tally (ADR-0104 Amendment A)."""
90
+
91
+ id = serializers.CharField()
92
+ signal = serializers.CharField()
93
+ from_ceiling = serializers.ChoiceField(choices=SignalAudience.choices)
94
+ to_ceiling = serializers.ChoiceField(choices=SignalAudience.choices)
95
+ status = serializers.ChoiceField(choices=CeilingRaiseStatus.choices)
96
+ proposed_by = serializers.CharField(allow_null=True)
97
+ created_at = serializers.DateTimeField()
98
+ expires_at = serializers.DateTimeField()
99
+ resolved_at = serializers.DateTimeField(allow_null=True)
100
+ approve_count = serializers.IntegerField()
101
+ reject_count = serializers.IntegerField()
102
+ eligible_count = serializers.IntegerField()
103
+ threshold = serializers.IntegerField()
104
+ # Amendment C: eligibility is evaluated at tally time, so the counts above can move
105
+ # when the roster does. This says how many cast votes are no longer counted, so a
106
+ # shrinking tally is explainable rather than mysterious. Normally 0.
107
+ disregarded_vote_count = serializers.IntegerField()
108
+ your_vote = serializers.CharField(allow_null=True)
109
+ can_vote = serializers.BooleanField()
110
+ votes = CeilingVoteSerializer(many=True)
111
+
112
+
113
+ class SignalPrivacyPolicySerializer(serializers.Serializer[Any]):
114
+ """Read shape: the resolved per-signal posture + what the requester may do."""
115
+
116
+ signals = serializers.DictField(child=_SignalPairSerializer())
117
+ requester_tier = serializers.ChoiceField(choices=SignalAudience.choices, allow_null=True)
118
+ can_set_audience = serializers.BooleanField()
119
+ can_raise_ceiling = serializers.BooleanField()
120
+ can_vote = serializers.BooleanField()
121
+ # Per-signal live ratification proposal (Amendment A) — Sarah's pending indicator.
122
+ open_proposals = serializers.DictField(child=CeilingProposalSerializer())
123
+
124
+
125
+ class SetAudienceSerializer(serializers.Serializer[Any]):
126
+ """PATCH body — move one signal's audience within [TEAM, ceiling]."""
127
+
128
+ signal = serializers.ChoiceField(choices=[(k, k) for k in SIGNAL_KEYS])
129
+ audience = serializers.ChoiceField(choices=SignalAudience.choices)
130
+
131
+
132
+ class RaiseCeilingSerializer(serializers.Serializer[Any]):
133
+ """POST body — set one signal's ceiling (the team-owned authorization)."""
134
+
135
+ signal = serializers.ChoiceField(choices=[(k, k) for k in SIGNAL_KEYS])
136
+ ceiling = serializers.ChoiceField(choices=SignalAudience.choices)
137
+
138
+
139
+ class CeilingVoteRequestSerializer(serializers.Serializer[Any]):
140
+ """POST body — cast or change a vote on a ceiling-raise proposal."""
141
+
142
+ choice = serializers.ChoiceField(choices=[("approve", "approve"), ("reject", "reject")])
143
+
144
+
145
+ # ---------------------------------------------------------------------------
146
+ # Shared helpers
147
+ # ---------------------------------------------------------------------------
148
+
149
+
150
+ def _is_facilitator_or_admin(request: Request, project_id: Any) -> bool:
151
+ """Facilitator (Scrum-Master facet) OR project Admin — the propose/set-audience gate."""
152
+ role = _membership_role(request, project_id)
153
+ if role is not None and role >= Role.ADMIN:
154
+ return True
155
+ return has_team_facet(request.user, project_id, "is_scrum_master")
156
+
157
+
158
+ def _serialize_proposal(
159
+ proposal: SignalCeilingRaiseProposal,
160
+ request: Request,
161
+ voter_ids: set[Any] | None = None,
162
+ ) -> dict[str, Any]:
163
+ """Build the team-readable proposal payload + live tally (Amendment A.6).
164
+
165
+ Pass ``voter_ids`` (the project's roster, computed once) when serializing a list so
166
+ the tally and the ``can_vote`` derivation reuse it instead of querying per proposal.
167
+
168
+ Per-voter privacy (ADR-0104 §2 + Amendment A.6/B.2, issue 1553): the individual
169
+ ``votes`` list (which member voted which way) is a *team* signal — team-readable,
170
+ never project-wide. A requester who is not on the default-team roster (e.g. a
171
+ non-team project Admin/PM or a Viewer) is structurally outside the ladder for this
172
+ signal, so the per-voter choices are redacted for them. They still receive the
173
+ governance aggregate the policy-GET pending indicator needs — the tally counts,
174
+ ``threshold``, ``eligible_count``, ``to_ceiling``, ``expires_at`` and their own
175
+ ``your_vote`` (exactly the A.6 ``open_proposals`` shape) — but never the individual
176
+ choices. Team members receive the full per-voter list. This mirrors the write-side
177
+ ``is_team_member`` gate on voting (Amendment A.2 / A.5: no management back-door).
178
+ """
179
+ if voter_ids is None:
180
+ voter_ids = team_member_user_ids(proposal.project_id)
181
+ tally = proposal_tally(proposal, voter_ids=voter_ids)
182
+ votes = list(proposal.votes.all())
183
+ user = request.user
184
+ uid = user.pk if getattr(user, "is_authenticated", False) else None
185
+ your_vote: str | None = (
186
+ next((v.choice for v in votes if v.voter_id == uid), None) if uid is not None else None
187
+ )
188
+ # Team membership drives both can_vote and per-voter-detail visibility; derived
189
+ # from the roster set already in hand (no extra is_team_member query).
190
+ is_team_reader = uid is not None and uid in voter_ids
191
+ can_vote = proposal.status == CeilingRaiseStatus.OPEN and is_team_reader
192
+ # Per-voter choices stay team-scoped (issue 1553): redact for non-team readers so a
193
+ # project-level Admin/Viewer cannot see how each teammate voted, while keeping the
194
+ # aggregate tally that management legitimately reads for the pending indicator.
195
+ voter_detail = (
196
+ [{"voter": str(v.voter_id), "choice": v.choice, "created_at": v.created_at} for v in votes]
197
+ if is_team_reader
198
+ else []
199
+ )
200
+ return {
201
+ "id": str(proposal.id),
202
+ "signal": proposal.signal_key,
203
+ "from_ceiling": proposal.from_ceiling,
204
+ "to_ceiling": proposal.to_ceiling,
205
+ "status": proposal.status,
206
+ "proposed_by": str(proposal.proposed_by_id) if proposal.proposed_by_id else None,
207
+ "created_at": proposal.created_at,
208
+ "expires_at": proposal.expires_at,
209
+ "resolved_at": proposal.resolved_at,
210
+ "your_vote": your_vote,
211
+ "can_vote": can_vote,
212
+ "votes": voter_detail,
213
+ **tally,
214
+ }
215
+
216
+
217
+ def _serialize_policy(
218
+ request: Request, project_id: Any, policy: ProjectSignalPrivacyPolicy
219
+ ) -> dict[str, Any]:
220
+ # The reader band (TEAM for the team incl. the SM; TEAM_SM_PM for the PM) does
221
+ # not encode the SM *write* capability, so the write gate is checked directly:
222
+ # facilitator (SM facet) OR project Admin.
223
+ can_write = _is_facilitator_or_admin(request, project_id)
224
+ # One roster fetch drives both can_vote and every open-proposal tally below.
225
+ voter_ids = team_member_user_ids(project_id)
226
+ user = request.user
227
+ can_vote = getattr(user, "is_authenticated", False) and user.pk in voter_ids
228
+ open_proposals: dict[str, Any] = {
229
+ key: _serialize_proposal(proposal, request, voter_ids=voter_ids)
230
+ for key, proposal in live_open_proposals(project_id).items()
231
+ }
232
+ return {
233
+ "signals": {key: policy.resolved(key) for key in SIGNAL_KEYS},
234
+ "requester_tier": requester_signal_tier(request, project_id),
235
+ # set-audience (immediate) and propose-raise share the facilitator/Admin gate;
236
+ # casting a ratification vote is the broader team-member gate.
237
+ "can_set_audience": can_write,
238
+ "can_raise_ceiling": can_write,
239
+ "can_vote": can_vote,
240
+ "open_proposals": open_proposals,
241
+ }
242
+
243
+
244
+ class _SignalPrivacyBase(APIView):
245
+ """Common project resolution + member gate for every signal-privacy endpoint."""
246
+
247
+ permission_classes = [IsAuthenticated, IsProjectMember, IsProjectNotArchived] # noqa: RUF012
248
+
249
+ def _project(self, project_pk: str) -> Project:
250
+ project = get_object_or_404(Project, pk=project_pk, is_deleted=False)
251
+ self.check_object_permissions(self.request, project)
252
+ return project
253
+
254
+ def _require_writer(self, project_id: Any) -> None:
255
+ if not _is_facilitator_or_admin(self.request, project_id):
256
+ raise PermissionDenied(
257
+ "Only the Scrum Master or a project Admin can change signal privacy."
258
+ )
259
+
260
+
261
+ # ---------------------------------------------------------------------------
262
+ # Endpoints
263
+ # ---------------------------------------------------------------------------
264
+
265
+
266
+ class SignalPrivacyPolicyView(_SignalPrivacyBase):
267
+ """GET the posture; PATCH one signal's audience (within its ceiling)."""
268
+
269
+ @extend_schema(
270
+ summary="Read the team-signal privacy posture",
271
+ responses=SignalPrivacyPolicySerializer,
272
+ )
273
+ def get(self, request: Request, project_pk: str) -> Response:
274
+ project = self._project(project_pk)
275
+ policy = get_or_create_policy(project)
276
+ return Response(_serialize_policy(request, project.pk, policy))
277
+
278
+ @extend_schema(
279
+ summary="Set one signal's audience within its ceiling",
280
+ request=SetAudienceSerializer,
281
+ responses=SignalPrivacyPolicySerializer,
282
+ )
283
+ def patch(self, request: Request, project_pk: str) -> Response:
284
+ project = self._project(project_pk)
285
+ self._require_writer(project.pk)
286
+ body = SetAudienceSerializer(data=request.data)
287
+ body.is_valid(raise_exception=True)
288
+ policy = get_or_create_policy(project)
289
+ policy = set_signal_audience(
290
+ policy,
291
+ body.validated_data["signal"],
292
+ body.validated_data["audience"],
293
+ actor=request.user,
294
+ )
295
+ return Response(_serialize_policy(request, project.pk, policy))
296
+
297
+
298
+ class SignalPrivacyRaiseCeilingView(_SignalPrivacyBase):
299
+ """POST — raise (→ opens a team-ratification proposal) or lower (immediate) a ceiling."""
300
+
301
+ @extend_schema(
302
+ summary="Raise (team-ratified) or lower (immediate) a signal's ceiling",
303
+ request=RaiseCeilingSerializer,
304
+ responses={
305
+ 200: OpenApiResponse(SignalPrivacyPolicySerializer, description="Lower/no-op applied."),
306
+ 202: OpenApiResponse(
307
+ CeilingProposalSerializer,
308
+ description="Raise opened a ratification proposal (or ratified immediately).",
309
+ ),
310
+ 409: OpenApiResponse(
311
+ description=(
312
+ "A ceiling-raise proposal is already open for this signal. Body: "
313
+ '``{code: "proposal_already_open", detail: ...}``.'
314
+ )
315
+ ),
316
+ },
317
+ )
318
+ def post(self, request: Request, project_pk: str) -> Response:
319
+ project = self._project(project_pk)
320
+ self._require_writer(project.pk)
321
+ body = RaiseCeilingSerializer(data=request.data)
322
+ body.is_valid(raise_exception=True)
323
+ policy = get_or_create_policy(project)
324
+ try:
325
+ proposal = propose_or_apply_ceiling_change(
326
+ policy,
327
+ body.validated_data["signal"],
328
+ body.validated_data["ceiling"],
329
+ actor=request.user,
330
+ )
331
+ except CeilingProposalConflict as exc:
332
+ return Response(
333
+ {"code": exc.code, "detail": exc.detail}, status=status.HTTP_409_CONFLICT
334
+ )
335
+ if proposal is None:
336
+ # A lower / no-op applied immediately — return the refreshed policy.
337
+ policy = get_or_create_policy(project)
338
+ return Response(_serialize_policy(request, project.pk, policy))
339
+ # A raise opened a ratification proposal (still OPEN, or RATIFIED for a solo team).
340
+ return Response(_serialize_proposal(proposal, request), status=status.HTTP_202_ACCEPTED)
341
+
342
+
343
+ class SignalPrivacyRatchetDownView(_SignalPrivacyBase):
344
+ """POST — set every signal's audience to TEAM in one call (the SM panic button)."""
345
+
346
+ @extend_schema(
347
+ summary="Ratchet every signal's audience down to team-only",
348
+ request=None,
349
+ responses={200: OpenApiResponse(SignalPrivacyPolicySerializer)},
350
+ )
351
+ def post(self, request: Request, project_pk: str) -> Response:
352
+ project = self._project(project_pk)
353
+ self._require_writer(project.pk)
354
+ policy = get_or_create_policy(project)
355
+ policy = ratchet_down_to_team(policy, actor=request.user)
356
+ return Response(_serialize_policy(request, project.pk, policy), status=status.HTTP_200_OK)
357
+
358
+
359
+ class SignalCeilingProposalListView(_SignalPrivacyBase):
360
+ """GET — list ceiling-raise proposals (open + recently resolved).
361
+
362
+ Every project member may read a proposal's governance aggregate (status, tally,
363
+ threshold, to_ceiling, expiry). The per-voter ``votes`` detail is team-scoped and
364
+ redacted for non-team readers by ``_serialize_proposal`` (ADR-0104 §2 / issue 1553).
365
+ """
366
+
367
+ # How many resolved proposals to surface alongside the open ones — the audit tail.
368
+ _RESOLVED_LIMIT = 20
369
+
370
+ @extend_schema(
371
+ summary="List ceiling-raise ratification proposals (open + recent)",
372
+ responses=CeilingProposalSerializer(many=True),
373
+ )
374
+ def get(self, request: Request, project_pk: str) -> Response:
375
+ project = self._project(project_pk)
376
+ # Open first, then the most-recent resolved (audit tail). Prefetch votes so the
377
+ # per-proposal tally/serialize reads hit the cache (no N+1 over votes).
378
+ base = SignalCeilingRaiseProposal.objects.filter(project_id=project.pk).prefetch_related(
379
+ "votes"
380
+ )
381
+ open_qs = list(base.filter(status=CeilingRaiseStatus.OPEN).order_by("-created_at"))
382
+ resolved_qs = list(
383
+ base.exclude(status=CeilingRaiseStatus.OPEN).order_by("-resolved_at")[
384
+ : self._RESOLVED_LIMIT
385
+ ]
386
+ )
387
+ # One roster fetch for the whole list (the roster is identical across a
388
+ # project's proposals) — no per-proposal team-membership query.
389
+ voter_ids = team_member_user_ids(project.pk)
390
+ payload = [
391
+ _serialize_proposal(p, request, voter_ids=voter_ids) for p in (*open_qs, *resolved_qs)
392
+ ]
393
+ return Response(payload)
394
+
395
+
396
+ class SignalCeilingProposalVoteView(_SignalPrivacyBase):
397
+ """POST — cast or change a vote on an open ceiling-raise proposal (team members only)."""
398
+
399
+ @extend_schema(
400
+ summary="Vote on a ceiling-raise proposal",
401
+ request=CeilingVoteRequestSerializer,
402
+ responses={
403
+ 200: OpenApiResponse(CeilingProposalSerializer),
404
+ 409: OpenApiResponse(
405
+ description=(
406
+ "The proposal is no longer open for voting (already resolved, or "
407
+ "lazily expired under the lock; body: "
408
+ '``{code: "proposal_closed", detail: ...}``), or — a narrow race '
409
+ "since this view already confirmed the proposal exists — it was "
410
+ "deleted between that check and the locked re-read (``{code: "
411
+ '"not_found", detail: ...}``).'
412
+ )
413
+ ),
414
+ },
415
+ )
416
+ def post(self, request: Request, project_pk: str, proposal_pk: str) -> Response:
417
+ project = self._project(project_pk)
418
+ # IDOR-safe: the proposal must belong to this project (404 otherwise).
419
+ proposal = get_object_or_404(
420
+ SignalCeilingRaiseProposal, pk=proposal_pk, project_id=project.pk
421
+ )
422
+ # Only an active team member may vote — a non-team project Admin/PM cannot
423
+ # (Amendment A.2: the ratification is the team's, not management's).
424
+ if not is_team_member(request.user, project.pk):
425
+ raise PermissionDenied("Only a team member can vote on a signal-ceiling proposal.")
426
+ body = CeilingVoteRequestSerializer(data=request.data)
427
+ body.is_valid(raise_exception=True)
428
+ try:
429
+ proposal = cast_ceiling_vote(proposal.pk, request.user, body.validated_data["choice"])
430
+ except CeilingProposalConflict as exc:
431
+ return Response(
432
+ {"code": exc.code, "detail": exc.detail}, status=status.HTTP_409_CONFLICT
433
+ )
434
+ return Response(_serialize_proposal(proposal, request))
435
+
436
+
437
+ class SignalCeilingProposalWithdrawView(_SignalPrivacyBase):
438
+ """POST — withdraw an open proposal (the proposer or a facilitator/Admin cancels it)."""
439
+
440
+ @extend_schema(
441
+ summary="Withdraw an open ceiling-raise proposal",
442
+ request=None,
443
+ responses={
444
+ 200: OpenApiResponse(CeilingProposalSerializer),
445
+ 409: OpenApiResponse(
446
+ description=(
447
+ "The proposal is no longer open (body: "
448
+ '``{code: "proposal_closed", detail: ...}``), or — a narrow race '
449
+ "since this view already confirmed the proposal exists — it was "
450
+ "deleted between that check and the locked re-read (``{code: "
451
+ '"not_found", detail: ...}``).'
452
+ )
453
+ ),
454
+ },
455
+ )
456
+ def post(self, request: Request, project_pk: str, proposal_pk: str) -> Response:
457
+ project = self._project(project_pk)
458
+ proposal = get_object_or_404(
459
+ SignalCeilingRaiseProposal, pk=proposal_pk, project_id=project.pk
460
+ )
461
+ # Withdraw is the proposer's cancel; a facilitator/Admin may also clear it.
462
+ is_proposer = (
463
+ getattr(request.user, "is_authenticated", False)
464
+ and proposal.proposed_by_id == request.user.pk
465
+ )
466
+ if not (is_proposer or _is_facilitator_or_admin(request, project.pk)):
467
+ raise PermissionDenied(
468
+ "Only the proposer or a facilitator/Admin can withdraw this proposal."
469
+ )
470
+ try:
471
+ proposal = withdraw_ceiling_proposal(proposal.pk, request.user)
472
+ except CeilingProposalConflict as exc:
473
+ return Response(
474
+ {"code": exc.code, "detail": exc.detail}, status=status.HTTP_409_CONFLICT
475
+ )
476
+ return Response(_serialize_proposal(proposal, request))
@@ -0,0 +1,132 @@
1
+ """Django signals for the projects app.
2
+
3
+ The ``risk_changed`` signal is the OSS extension point for Enterprise
4
+ portfolio risk rollup. Enterprise receivers connect via their own
5
+ AppConfig.ready() without modifying OSS code.
6
+
7
+ Usage (Enterprise side, never imported by OSS)::
8
+
9
+ # trueppm_enterprise/portfolio_risks/apps.py
10
+ def ready(self):
11
+ from trueppm_api.apps.projects.signals import risk_changed
12
+ risk_changed.connect(portfolio_risk_rollup_receiver)
13
+
14
+ .. warning::
15
+ The signal is emitted synchronously inside the database transaction.
16
+ Enterprise receivers that perform I/O (HTTP calls, cross-DB writes) **must**
17
+ defer those side-effects with ``transaction.on_commit()`` to avoid blocking
18
+ the transaction or writing to external systems on a rolled-back save.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import django.dispatch
24
+
25
+ # Sent after a Risk is saved or soft-deleted.
26
+ #
27
+ # Keyword arguments:
28
+ # sender — the Risk class
29
+ # risk — the Risk instance (post-save state)
30
+ # action — "saved" | "deleted"
31
+ #
32
+ # Only emitted when probability, impact, or status changes on save.
33
+ # For soft-deletes, ``action="deleted"`` is sent by Risk.soft_delete()
34
+ # and the save() emission is suppressed (is_deleted is already True).
35
+ risk_changed = django.dispatch.Signal()
36
+
37
+ # Sent after a Task's status field changes (board card drag or direct API update).
38
+ #
39
+ # Keyword arguments:
40
+ # sender — the Task class
41
+ # task — the Task instance (post-save state)
42
+ # old_status — previous TaskStatus value (None on first save / INSERT)
43
+ # new_status — new TaskStatus value
44
+ #
45
+ # Only emitted when the status value actually changes. CPM bulk_update bypasses
46
+ # Task.save() so this signal never fires for scheduling engine writes.
47
+ # Enterprise receivers must use transaction.on_commit() for any I/O side-effects.
48
+ task_status_changed = django.dispatch.Signal()
49
+
50
+ # Sent after a SprintScopeChange row is created (ADR-0060 #308, generalized in
51
+ # ADR-0101 §5 / ADR-0102). Fires when *any* task is linked to a task's ACTIVE
52
+ # sprint mid-sprint — the original subtask-spawn path, plus the direct
53
+ # "assign existing task to the active sprint" path. The row enters the pending
54
+ # state (ADR-0102): excluded from commitment/burndown until a team member with
55
+ # the sprint-lifecycle gate accepts or rejects it.
56
+ #
57
+ # Keyword arguments:
58
+ # sender — the SprintScopeChange class
59
+ # scope_change — the SprintScopeChange instance (post-save)
60
+ # task — the Task whose sprint membership changed (the injected item)
61
+ #
62
+ # OSS only emits; Enterprise registers receivers via AppConfig.ready().
63
+ # Receivers that perform I/O must use transaction.on_commit() to avoid
64
+ # blocking the transaction or writing on rollback.
65
+ #
66
+ # ADR-0102 §3 invariant: this signal is *notify-only*. No receiver may flip a
67
+ # SprintScopeChange.status away from PENDING — accept/reject are exclusively the
68
+ # human-invoked service functions (accept_scope_change / reject_scope_change).
69
+ sprint_scope_changed = django.dispatch.Signal()
70
+
71
+ # Sent after a bound milestone is reforecast (ADR-0106 §6, #860). This is the
72
+ # one OSS read-only seam the Enterprise cross-team forecast mirrors (#140/#141/
73
+ # #142, 1.0) register against. OSS itself never aggregates across teams.
74
+ #
75
+ # Keyword arguments (band + dates ONLY — never the velocity series, never any
76
+ # per-contributor data):
77
+ # sender — the ForecastSnapshot class
78
+ # project_id — the project UUID (str)
79
+ # milestone_id — the bound milestone Task UUID (str)
80
+ # cpm_finish — ISO date str | None (the deterministic CPM spine)
81
+ # p50 — ISO date str | None
82
+ # p80 — ISO date str | None
83
+ # confidence — "high" | "medium" | "low"
84
+ # unmodeled_dependency — bool (§4 cheap predecessor heuristic)
85
+ #
86
+ # Privacy (ADR-0106 §6): the signal fires for OSS's own forecast-history needs
87
+ # unconditionally, but the *cross-team-eligible* projection of it is supplied
88
+ # only through the consent-respecting provider in the Unified Team-Signal Privacy
89
+ # Model — the Enterprise receiver must consult the consent record; OSS never does
90
+ # the aggregation. Receivers performing I/O must use transaction.on_commit().
91
+ milestone_forecast_recomputed = django.dispatch.Signal()
92
+
93
+
94
+ # Emitted when a team changes how far one of its signals may travel — an audience
95
+ # move or a ceiling raise/lower on ProjectSignalPrivacyPolicy (ADR-0104 §3).
96
+ #
97
+ # Keyword arguments:
98
+ # sender — the ProjectSignalPrivacyPolicy class
99
+ # project_id — the project whose policy changed
100
+ # signal_key — the signal that moved ("velocity" / "throughput_rollup" / "pulse")
101
+ # change — "audience" or "ceiling"
102
+ # old — prior SignalAudience value
103
+ # new — new SignalAudience value
104
+ #
105
+ # Supply-only extension point: OSS only emits. Enterprise registers a receiver in
106
+ # AppConfig.ready() to invalidate its cross-team rollup cache / capture the upward-
107
+ # share decision in its immutable audit. Receivers that perform I/O must use
108
+ # transaction.on_commit(). No receiver may write signal_visibility — the only
109
+ # writers are the human-invoked, member-gated services (set_signal_audience /
110
+ # raise_signal_ceiling / ratchet_down_to_team).
111
+ team_signal_consent_changed = django.dispatch.Signal()
112
+
113
+
114
+ # Emitted on every status transition of a ceiling-raise ratification proposal
115
+ # (ADR-0104 Amendment A / #930) — opened, ratified, rejected, expired, superseded.
116
+ #
117
+ # Keyword arguments:
118
+ # sender — the SignalCeilingRaiseProposal class
119
+ # project_id — the project whose signal ceiling is under ratification
120
+ # signal_key — the signal being raised ("velocity" / "throughput_rollup" / ...)
121
+ # proposal_id — the proposal's UUID
122
+ # status — the new CeilingRaiseStatus value
123
+ #
124
+ # Supply-only extension point, same contract as team_signal_consent_changed:
125
+ # projects only emits; consumers register a receiver in AppConfig.ready(). Since
126
+ # #1275 the OSS notifications app consumes it (notifications/receivers.py →
127
+ # in-app inbox row for eligible voters, ADR-0104 Amendment B); Enterprise may
128
+ # register its own receiver to capture the ratification lifecycle in its immutable
129
+ # audit. The signal is already deferred via transaction.on_commit (see
130
+ # _emit_proposal_changed), so receivers run post-commit and read committed state.
131
+ # Email stays opt-in OFF (Priya's hard-NO is on un-opted email, not the inbox).
132
+ team_signal_ceiling_proposal_changed = django.dispatch.Signal()