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,191 @@
1
+ """The draft exclusion list (#2962).
2
+
3
+ A draft is a plan nobody has agreed to yet. It is fully writable — that is the
4
+ difference from ``is_archived`` — but it must not appear in anything that
5
+ *aggregates*, because a half-built plan inside a rollup makes that rollup a
6
+ guess with a chart around it.
7
+
8
+ **One module, not N filters.** The exclusion has to hold across program rollup,
9
+ portfolio health, search and notifications, and those live in different modules
10
+ maintained at different times. A `.exclude(lifecycle=DRAFT)` written out per
11
+ surface is a place to forget it per surface; this module is one place to get it
12
+ right and one place to grep for.
13
+
14
+ It says *module* rather than *helper* because #3128 found the single-helper
15
+ framing was itself part of why the list leaked. :func:`visible_projects` takes a
16
+ ``Project`` queryset, and half the surfaces that must honor the exclusion never
17
+ build one — omni-search and the cross-project dependency picker filter ``Task``
18
+ rows, and the Programs directory counts projects inside a ``Count(filter=...)``
19
+ annotation on ``Program``. Faced with a helper that did not fit, those sites
20
+ wrote nothing at all. So there are three shapes of the same predicate —
21
+ :func:`visible_projects`, :func:`exclude_draft_projects`, :func:`not_draft_q` —
22
+ and the rule is: **no surface writes its own draft-exclusion predicate.** If a
23
+ new one fits none of the three, add a fourth here rather than inlining it there.
24
+
25
+ Be exact about what that rule does and does not say, because the tempting
26
+ stronger version ("``ProjectLifecycle.DRAFT`` appears in this file and nowhere
27
+ else") is *false* and would be a trap to write down. ``commit_moment.py`` reads
28
+ it to decide whether a project is committable, and ``amend.py`` reads its
29
+ sibling ``ACTIVE`` — those are state *transitions*, which are none of this
30
+ module's business. The rule covers exclusion filters only, and nothing enforces
31
+ it mechanically today: a gate would have to tell a transition check apart from
32
+ an exclusion filter, which is not a grep. Treat it as a convention this module
33
+ argues for, not a guarantee something checks (#3128).
34
+
35
+ **The MCP read surface is deliberately NOT on this list**, which is a correction
36
+ to the design rather than an omission. Hiding a project from an agent's reads is
37
+ indistinguishable from a 404 or a token-scope miss: the agent cannot tell "not
38
+ committed yet" from "does not exist" and has no way to debug the difference.
39
+ ``lifecycle`` is returned as an ordinary field instead, so a client can filter on
40
+ a stated fact. Filtering is something a caller can do; recovering from silence is
41
+ not.
42
+
43
+ **Two more surfaces are deliberately NOT on this list** (#3144). The #3128 audit
44
+ found both leaking and left both, because filtering them is a decision about
45
+ people and about their own files rather than a correctness fix about plans. Both
46
+ decisions are KEEP, and the reasoning is recorded here so the next audit reads it
47
+ instead of re-deriving it.
48
+
49
+ *The mention fan-out* (``access.groups._program_membership_base_qs``). The
50
+ strongest reason is structural rather than a values call. That queryset is the
51
+ one base **all four** ``@program-*`` keys narrow, and its own docstring forbids a
52
+ per-key filter. So a draft exclusion has no legal placement: written where it
53
+ belongs it also drops the draft's Admin out of ``@program-pms`` and its Scheduler
54
+ out of ``@program-schedulers`` — the people *authoring the draft*, cut out of
55
+ program-wide coordination — and written per key it breaks the invariant the
56
+ helper was extracted to hold. The values argument agrees: draft-ness is a
57
+ property of the plan someone was added to, not of their standing in the program,
58
+ and the failure mode of dropping them is silence neither they nor the sender can
59
+ see, exactly as for the MCP surface above. Two further consequences, both
60
+ deliberate:
61
+
62
+ * ``@program-stakeholders`` has a second arm. ``ExternalStakeholder`` is
63
+ program-owned with **no project FK**, so no project-level narrowing can reach
64
+ it. Excluding the internal arm would report ``viewer_member_count: 0`` beside a
65
+ non-zero external count on a program whose projects are all drafts — an
66
+ incoherence between the arms that does not exist today.
67
+ * A mention *written in* a draft project still fans out program-wide. That is the
68
+ other leak direction, and it is kept too: #3128's three notification sites were
69
+ unattended machine sweeps over a project population, whereas a PM typing
70
+ ``@program-stakeholders`` into a draft is deliberately asking for review, which
71
+ is what a draft is for.
72
+
73
+ The cost of keeping, named so a later report traces here: draft memberships
74
+ consume ``@program-all``'s ``ALL_GROUP_HARD_CAP`` slots, so enough drafts can trip
75
+ ``GroupTooLargeError`` on the strength of plans nobody committed to. Accepted —
76
+ the cap is a blast-radius guard over real people, who are genuinely reached.
77
+
78
+ Keeping also holds ``count_program_stakeholder_reach`` in lockstep with the
79
+ resolver (ADR-0697) for free: the count and the fan-out cannot drift if neither
80
+ moves. The program-settings docs say the alias reaches "members holding the Viewer
81
+ role on any project inside the program", which stays true under KEEP; that
82
+ sentence becomes wrong, and must gain the gap, only if this decision flips.
83
+
84
+ *The cross-project asset feeds* (``projects.asset_feed``, behind
85
+ ``GET /programs/{pk}/assets/`` and ``GET /assets/``) are the member's own browse
86
+ list, which this module exempts, at two scopes — the workspace view is literally
87
+ the program view with the ``project__program=`` clause dropped, and both narrow to
88
+ the caller's own ``ProjectMembership``. ``?q=`` makes them look like the
89
+ omni-search palette this list does name, but the line under omni-search and the
90
+ dependency picker is that a returned row is *acted on across a boundary*: the
91
+ palette navigates into a project, the picker mints an edge that moves a real
92
+ project's dates. Nothing downstream of an asset row changes a schedule, and the
93
+ feed emits no count, health band or ranking that an exclusion would make truer.
94
+ What exclusion would produce instead is a visibility discontinuity at the commit
95
+ moment: a file you attached to your own draft disappears from the surface built to
96
+ find it and returns when somebody else commits the plan.
97
+
98
+ ``GET /assets/`` is additionally a declared MCP read surface
99
+ (``McpReadableViewMixin``), which the paragraph above exempts outright; note this
100
+ covers only the workspace tier — ``ProgramAssetsView`` is not MCP-readable, and
101
+ the browse-list argument is what carries it. An operator who wants a project out
102
+ of an agent's index already has ``mcp_settings.mcp_excluded_project_ids``, a
103
+ stated and auditable lever orthogonal to lifecycle.
104
+ """
105
+
106
+ from __future__ import annotations
107
+
108
+ from typing import Any, TypeVar
109
+
110
+ from django.db.models import Model, Q, QuerySet
111
+
112
+ from .models import Project, ProjectLifecycle
113
+
114
+ _M = TypeVar("_M", bound=Model)
115
+
116
+
117
+ def visible_projects(qs: QuerySet[Project]) -> QuerySet[Project]:
118
+ """Drop drafts from an aggregate.
119
+
120
+ Apply to any queryset feeding a rollup, a health figure, a search result or an
121
+ **unattended** notification fan-out over a project population. Do **not** apply
122
+ it to a direct read of one project by id, to the project list a member browses,
123
+ to the MCP read surface, or to a mention resolver, where a human named the
124
+ audience at write time (#3144) — a draft is not a secret, it is merely
125
+ uncommitted.
126
+ """
127
+ return qs.exclude(lifecycle=ProjectLifecycle.DRAFT)
128
+
129
+
130
+ def exclude_draft_projects(qs: QuerySet[_M], path: str = "project") -> QuerySet[_M]:
131
+ """Drop rows whose owning project is a draft, from a *non*-Project queryset.
132
+
133
+ The same exclusion as :func:`visible_projects`, reached across a relation.
134
+ Two of the four surfaces the exclusion list names — omni-search and the
135
+ cross-project dependency picker — never build a ``Project`` queryset at all;
136
+ they filter ``Task`` rows by their project. Without this, those sites would
137
+ each have to hand-write ``exclude(project__lifecycle="draft")``, which is
138
+ exactly the "four places to forget it" the module docstring rejects.
139
+
140
+ ``path`` must be **single-valued** (a chain of forward FKs / one-to-ones), and
141
+ that is enforced below rather than documented and hoped for. On a single-valued
142
+ path Django appends a bare column predicate to a join it already has. On a
143
+ multi-valued one (``Program.projects``) the same call compiles to a correlated
144
+ ``NOT EXISTS`` and, far worse, silently *inverts the question*: it drops every
145
+ row whose related set contains **any** draft, rather than dropping draft rows.
146
+ That is a wrong answer, not a slow one, and nothing downstream would show it —
147
+ so it fails loudly here instead. For an aggregate over a multi-valued relation
148
+ use :func:`not_draft_q` inside the aggregate's own ``filter=``.
149
+
150
+ Args:
151
+ qs: Any queryset with a single-valued relation reaching ``Project``.
152
+ path: The ORM lookup path from ``qs``'s model to that ``Project``
153
+ (``"project"`` for ``Task``, ``"task__project"`` for a task's children).
154
+
155
+ Raises:
156
+ ValueError: If ``path`` traverses a multi-valued relation.
157
+ """
158
+ model: Any = qs.model
159
+ for part in path.split("__"):
160
+ field = model._meta.get_field(part)
161
+ if field.many_to_many or field.one_to_many:
162
+ raise ValueError(
163
+ f"exclude_draft_projects: path {path!r} traverses the multi-valued "
164
+ f"relation {part!r} on {model.__name__}. That would drop every row "
165
+ "whose related set contains ANY draft, not the draft rows — use "
166
+ "not_draft_q() inside the aggregate's filter= instead."
167
+ )
168
+ model = field.related_model
169
+ lookup: dict[str, Any] = {f"{path}__lifecycle": ProjectLifecycle.DRAFT}
170
+ return qs.exclude(**lookup)
171
+
172
+
173
+ def not_draft_q(path: str = "") -> Q:
174
+ """The same exclusion as a ``Q``, for an aggregate's ``filter=`` argument.
175
+
176
+ ``Count(..., filter=...)`` takes a ``Q``, not a queryset, so the one place
177
+ that counts projects inside an annotation on ``Program`` cannot call either
178
+ helper above. It gets this instead of hand-writing the predicate, so the
179
+ exclusion still has one definition rather than one per surface.
180
+
181
+ Args:
182
+ path: The ORM lookup path to the ``Project``, or ``""`` when the
183
+ queryset's own model is ``Project`` (``"projects"`` from ``Program``).
184
+ """
185
+ field = f"{path}__lifecycle" if path else "lifecycle"
186
+ return ~Q(**{field: ProjectLifecycle.DRAFT})
187
+
188
+
189
+ def is_draft(project: Project) -> bool:
190
+ """True when nobody has committed to this plan yet."""
191
+ return project.lifecycle == ProjectLifecycle.DRAFT
File without changes
@@ -0,0 +1,92 @@
1
+ """Backfill the date-gated NOT_STARTED → IN_PROGRESS auto-transition (#336).
2
+
3
+ Before #336 landed, setting `planned_start` on a NOT_STARTED task did not
4
+ auto-transition status across most entry points (only the gutter-promote
5
+ action did, briefly, on the frontend). This command finds rows that *should*
6
+ have been IN_PROGRESS under the new rule and corrects them in one pass so
7
+ the unified data model holds for existing data, not just new mutations.
8
+
9
+ Targeted rows: ``status=NOT_STARTED`` AND ``planned_start <= today``.
10
+
11
+ For each, the command:
12
+
13
+ - Sets ``status = IN_PROGRESS``.
14
+ - Sets ``actual_start = planned_start`` (preserving the historical date —
15
+ same logic as ``TaskSerializer.update`` for past-dated planned_start).
16
+
17
+ Idempotent: a second run finds zero matches because the first run moved them
18
+ out of NOT_STARTED. Safe to run repeatedly during deployment rollout.
19
+
20
+ Usage::
21
+
22
+ python manage.py backfill_in_progress_status # apply
23
+ python manage.py backfill_in_progress_status --dry-run # preview only
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import logging
29
+ from typing import Any
30
+
31
+ from django.core.management.base import BaseCommand
32
+ from django.db import transaction
33
+ from django.utils import timezone
34
+
35
+ from trueppm_api.apps.projects.models import Task, TaskStatus
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class Command(BaseCommand):
41
+ help = (
42
+ "Backfill NOT_STARTED tasks with planned_start ≤ today to IN_PROGRESS, "
43
+ "pinning actual_start to planned_start (#336)."
44
+ )
45
+
46
+ def add_arguments(self, parser: Any) -> None:
47
+ parser.add_argument(
48
+ "--dry-run",
49
+ action="store_true",
50
+ help="Print the rows that would be updated without writing.",
51
+ )
52
+
53
+ def handle(self, *args: Any, **options: Any) -> None:
54
+ today = timezone.localdate()
55
+ candidates = Task.objects.filter(
56
+ status=TaskStatus.NOT_STARTED,
57
+ planned_start__lte=today,
58
+ ).only("id", "name", "planned_start", "actual_start")
59
+
60
+ count = candidates.count()
61
+ if count == 0:
62
+ self.stdout.write(self.style.SUCCESS("No tasks to backfill."))
63
+ return
64
+
65
+ self.stdout.write(
66
+ f"Found {count} NOT_STARTED task(s) with planned_start ≤ {today.isoformat()}."
67
+ )
68
+
69
+ if options["dry_run"]:
70
+ for t in candidates[:50]:
71
+ self.stdout.write(f" • {t.id} '{t.name}' planned_start={t.planned_start}")
72
+ if count > 50:
73
+ self.stdout.write(f" … and {count - 50} more")
74
+ self.stdout.write(self.style.WARNING("--dry-run: no changes written."))
75
+ return
76
+
77
+ # Bulk update inside a transaction; preserve actual_start when it's
78
+ # already set (don't trample historical data) and never overwrite a
79
+ # status that has somehow drifted out of NOT_STARTED between the
80
+ # candidate query and the write.
81
+ updated = 0
82
+ with transaction.atomic():
83
+ for t in candidates.select_for_update():
84
+ if t.status != TaskStatus.NOT_STARTED:
85
+ continue # raced; another writer moved this row already
86
+ t.status = TaskStatus.IN_PROGRESS
87
+ if not t.actual_start:
88
+ t.actual_start = t.planned_start
89
+ t.save(update_fields=["status", "actual_start"])
90
+ updated += 1
91
+
92
+ self.stdout.write(self.style.SUCCESS(f"Backfilled {updated} task(s)."))
@@ -0,0 +1,368 @@
1
+ """Mint (or pin) the public read-only demo share links (issues #1487, #2440).
2
+
3
+ The public demo (``try.trueppm.com``) dogfoods the product's own tokenized,
4
+ read-only share links (#283, extended to schedules by #1486) rather than a
5
+ bespoke read-only mode: no login, no write path, near-zero abuse surface. This
6
+ command binds :class:`~trueppm_api.apps.projects.models.ShareLink` rows to the
7
+ seeded demo project and prints their public URLs, so both the demo compose stack
8
+ (``docker-compose.demo.yml``) and the Helm demo-seed hook (ADR-0658) can log
9
+ ready-to-share links on startup.
10
+
11
+ **Two content kinds.** A ``SCHEDULE`` link is always minted. A ``BOARD`` link is
12
+ minted *only when a board token is supplied* (``--token-board`` /
13
+ ``TRUEPPM_DEMO_SHARE_TOKEN_BOARD``). That asymmetry is deliberate back-compat:
14
+ ``docker-compose.demo.yml`` predates board sharing and supplies only the schedule
15
+ token, so it keeps its existing single-link behaviour untouched, while the Helm
16
+ path (which requires both tokens) gets both links. A board link is only worth
17
+ having if it is stable, so there is no generated-token mode for it.
18
+
19
+ Each kind needs its *own* token — ``ShareLink.token_hash`` is globally unique, so
20
+ one raw token cannot back two links.
21
+
22
+ **Two modes** (schedule only):
23
+
24
+ * **Pinned (required for a hosted demo).** Set ``TRUEPPM_DEMO_SHARE_TOKEN``
25
+ (or pass ``--token``) to a fixed value. The command upserts a share link whose
26
+ hash matches that token — idempotent and, crucially, *reprintable*: the same
27
+ stable URL is emitted on every restart, so the demo has one deep-linkable
28
+ address that survives redeploys. Under Helm this is not optional: the seeder is
29
+ destructive and ``ShareLink.project`` is ``on_delete=CASCADE``, so an unpinned
30
+ link changes its URL on every upgrade.
31
+ * **Generated (one-shot).** With no token supplied, a random token is minted
32
+ once and its URL printed a single time (the raw token is never stored, only its
33
+ hash — so it cannot be reprinted). Re-running without a pinned token reuses the
34
+ existing link and reminds the operator to pin a token for a stable URL.
35
+
36
+ Read-only posture: this never touches the ``--with-personas`` /
37
+ ``TRUEPPM_DEMO_PASSWORD`` path (#1350). The demo is reachable *only* through the
38
+ unauthenticated share endpoints.
39
+
40
+ **Minting and serving are two different gates** (#2781). ``mint_share_link()``
41
+ writes a row; the *read* additionally requires the ADR-0135 "Public sharing"
42
+ policy to resolve true for the project. Because ``Workspace.public_sharing``
43
+ defaults to ``False``, a demo built from default state minted links that every
44
+ recipient saw as ``410 Gone`` — while this command printed a confident URL. So
45
+ the command now turns sharing on for the project it publishes, and verifies the
46
+ *effective* policy before printing, rather than assuming the mint implies a
47
+ working link.
48
+ """
49
+
50
+ from __future__ import annotations
51
+
52
+ import os
53
+ from typing import Any
54
+
55
+ from django.core.management.base import BaseCommand, CommandError
56
+
57
+ from trueppm_api.apps.projects.authentication import sha256_hex
58
+ from trueppm_api.apps.projects.models import Project, ShareContentKind, ShareLink
59
+ from trueppm_api.apps.projects.share_services import mint_share_link
60
+ from trueppm_api.apps.projects.sharing_settings import (
61
+ public_sharing_instance_enabled,
62
+ resolve_effective_sharing,
63
+ )
64
+
65
+ # The project this publishes by default. It used to be imported from
66
+ # ``seed_demo_project``, which #3098 retired when the Python seeders converged
67
+ # into the JSON fixture format; the hosted demo now loads the bundled Atlas
68
+ # sample (``DEFAULT_SAMPLE``) and publishes its flagship project. Overridable
69
+ # with ``--project``.
70
+ PROJECT_NAME = "Platform Core"
71
+
72
+ # Idempotency key: a demo link is matched (and reused) by its exact label so
73
+ # repeated runs never sprawl a fresh link per restart. The two kinds carry
74
+ # *different* labels because the generated-mode lookup filters on label — a shared
75
+ # label would make the two kinds collide on it.
76
+ #
77
+ # The SCHEDULE label is frozen at its original #1487 text on purpose: existing demo
78
+ # deployments already hold rows carrying it, and renaming it would make the
79
+ # generated-mode lookup miss them and mint a duplicate on the next run.
80
+ DEMO_LABEL = "Public read-only demo (#1487)"
81
+ DEMO_LABEL_BOARD = "Public read-only demo — board (#2440)"
82
+
83
+ LABEL_BY_KIND = {
84
+ ShareContentKind.SCHEDULE: DEMO_LABEL,
85
+ ShareContentKind.BOARD: DEMO_LABEL_BOARD,
86
+ }
87
+
88
+ # URL path segment per kind — mirrors the frontend routes /share/schedule/<token>
89
+ # and /share/board/<token>, and the API's own share/ URL patterns.
90
+ PATH_BY_KIND = {
91
+ ShareContentKind.SCHEDULE: "schedule",
92
+ ShareContentKind.BOARD: "board",
93
+ }
94
+
95
+ TOKEN_ENV = "TRUEPPM_DEMO_SHARE_TOKEN"
96
+ TOKEN_BOARD_ENV = "TRUEPPM_DEMO_SHARE_TOKEN_BOARD"
97
+ BASE_URL_ENV = "TRUEPPM_DEMO_BASE_URL"
98
+ # The printed URL is the demo's only entry point (there are no accounts), so the
99
+ # fallback must name a host the operator running the command can actually open.
100
+ # It is therefore the loopback origin the local `docker-compose.demo.yml` stack
101
+ # publishes on — NOT the hosted demo's domain, which would hand a local evaluator
102
+ # a link that resolves to somebody else's instance where their token does not
103
+ # exist (#2801). Every hosted path sets the env var explicitly; the Helm chart
104
+ # fails its render when `demo.baseUrl` is empty rather than falling back here.
105
+ DEFAULT_BASE_URL = "http://localhost"
106
+
107
+
108
+ class Command(BaseCommand):
109
+ """Mint or pin the public read-only demo share links (#1487, #2440)."""
110
+
111
+ help = "Create (or pin) the public read-only demo share links and print their URLs."
112
+
113
+ def add_arguments(self, parser: Any) -> None:
114
+ parser.add_argument(
115
+ "--project",
116
+ default=PROJECT_NAME,
117
+ help=(
118
+ f"Demo project name to share (default: {PROJECT_NAME!r}, "
119
+ "seeded by load_sample_project)."
120
+ ),
121
+ )
122
+ parser.add_argument(
123
+ "--token",
124
+ default=None,
125
+ help=(
126
+ f"Pin a fixed raw token for a stable, reprintable SCHEDULE URL. "
127
+ f"Falls back to the {TOKEN_ENV} env var. When omitted a random "
128
+ "token is minted once."
129
+ ),
130
+ )
131
+ parser.add_argument(
132
+ "--token-board",
133
+ default=None,
134
+ help=(
135
+ f"Pin a fixed raw token for a stable BOARD URL. Falls back to the "
136
+ f"{TOKEN_BOARD_ENV} env var. When omitted no board link is minted "
137
+ "(a board link is only useful if stable, so there is no generated "
138
+ "mode). Must differ from the schedule token."
139
+ ),
140
+ )
141
+ parser.add_argument(
142
+ "--base-url",
143
+ default=None,
144
+ help=(
145
+ f"Public base URL of the demo host (default: {BASE_URL_ENV} env "
146
+ f"var, else {DEFAULT_BASE_URL!r})."
147
+ ),
148
+ )
149
+
150
+ def handle(self, *args: object, **options: object) -> None:
151
+ project_name = str(options["project"])
152
+ base_url_opt = options.get("base_url")
153
+ base_url = (
154
+ str(base_url_opt) if base_url_opt else os.environ.get(BASE_URL_ENV) or DEFAULT_BASE_URL
155
+ ).rstrip("/")
156
+
157
+ token_opt = options.get("token")
158
+ schedule_token = str(token_opt) if token_opt else os.environ.get(TOKEN_ENV)
159
+ token_board_opt = options.get("token_board")
160
+ board_token = str(token_board_opt) if token_board_opt else os.environ.get(TOKEN_BOARD_ENV)
161
+
162
+ # Guard the collision early with an actionable message. Without this the
163
+ # second upsert would silently resolve the *first* kind's row by token_hash
164
+ # and report success while minting nothing.
165
+ if schedule_token and board_token and schedule_token == board_token:
166
+ raise CommandError(
167
+ "The schedule and board tokens are identical. ShareLink.token_hash is "
168
+ "globally unique, so each kind needs its own token — generate a second "
169
+ "one with: openssl rand -base64 32 | tr -d '=+/'"
170
+ )
171
+
172
+ try:
173
+ # Scoped to is_sample so this publishes the seeder's project and never
174
+ # an operator's same-named real one — Project.name is non-unique, and
175
+ # since #2476 the seeder no longer deletes a real project that
176
+ # collides with the demo name, so it can legitimately coexist here.
177
+ project = Project.objects.get(name=project_name, is_sample=True, is_deleted=False)
178
+ except Project.DoesNotExist as exc:
179
+ raise CommandError(
180
+ f"Demo project {project_name!r} not found. Run "
181
+ "`python manage.py load_sample_project` first."
182
+ ) from exc
183
+
184
+ # Turn sharing on *before* minting, so the links this run prints resolve
185
+ # for the recipient rather than 410-ing (#2781).
186
+ self._enable_public_sharing(project)
187
+
188
+ results: list[tuple[str, ShareLink, str]] = []
189
+
190
+ scheduled = self._resolve_link(project, ShareContentKind.SCHEDULE, schedule_token)
191
+ if scheduled is not None:
192
+ results.append(scheduled)
193
+
194
+ # Board is opt-in via its token — see the module docstring for why.
195
+ if board_token:
196
+ board = self._resolve_link(project, ShareContentKind.BOARD, board_token)
197
+ if board is not None:
198
+ results.append(board)
199
+
200
+ if not results:
201
+ return
202
+
203
+ # Re-read the posture the *recipient* will hit. Enabling sharing above is
204
+ # necessary but not sufficient: the operator kill switch sits above every
205
+ # policy, and an Enterprise ENFORCE lock outranks the project override. If
206
+ # either still says no, say so instead of printing a URL that 404s/410s.
207
+ self._warn_if_links_will_not_resolve(project)
208
+
209
+ self.stdout.write(self.style.SUCCESS("=" * 60))
210
+ self.stdout.write(self.style.SUCCESS(" Public read-only demo share links"))
211
+ self.stdout.write(self.style.SUCCESS(f" Project: {project.name!r}"))
212
+ for action, link, token in results:
213
+ kind_path = PATH_BY_KIND[ShareContentKind(link.content_kind)]
214
+ url = f"{base_url}/share/{kind_path}/{token}"
215
+ self.stdout.write(self.style.SUCCESS(""))
216
+ self.stdout.write(self.style.SUCCESS(f" {action} {link.content_kind} link"))
217
+ self.stdout.write(self.style.SUCCESS(f" Link id: {link.id}"))
218
+ self.stdout.write(self.style.SUCCESS(f" URL: {url}"))
219
+ self.stdout.write(self.style.SUCCESS("=" * 60))
220
+
221
+ def _enable_public_sharing(self, project: Any) -> None:
222
+ """Set the demo project's own ADR-0135 ``public_sharing`` override to True.
223
+
224
+ The override is written at **project** scope, not on the workspace, and
225
+ that is the whole point of the choice: ``Workspace.public_sharing`` is the
226
+ admin-facing default for *every* project on the instance, and a management
227
+ command has no business flipping it. The demo project is public by
228
+ definition; nothing else on the instance is, and a self-hoster who seeds
229
+ the sample onto a real install must not find their workspace default
230
+ silently loosened as a side effect.
231
+
232
+ Safe by scoping: ``handle()`` resolves the project with ``is_sample=True``,
233
+ so the only row this can ever touch is one the seeder created.
234
+
235
+ Idempotent — a second run finds the override already true and writes
236
+ nothing, which keeps ``server_version`` (and the history row each save
237
+ writes) from advancing once per container restart.
238
+ """
239
+ if project.public_sharing:
240
+ return
241
+ project.public_sharing = True
242
+ project.save(update_fields=["public_sharing"])
243
+ self.stdout.write(
244
+ self.style.WARNING(
245
+ f"Enabled public sharing on demo project {project.name!r} "
246
+ "(project-scoped override; the workspace default is untouched)."
247
+ )
248
+ )
249
+
250
+ def _warn_if_links_will_not_resolve(self, project: Any) -> None:
251
+ """Warn when the printed URLs will not actually serve, and say why.
252
+
253
+ The mint and the read are separate gates, and this command historically
254
+ only exercised the first — which is how a demo shipped links that every
255
+ recipient saw as ``410 Gone`` while startup logs showed a confident URL
256
+ (#2781). Two conditions survive :meth:`_enable_public_sharing`:
257
+
258
+ * the operator kill switch (``TRUEPPM_PUBLIC_BOARD_SHARING_ENABLED=false``),
259
+ which outranks every policy and makes the share endpoints answer 404;
260
+ * an Enterprise ``ENFORCE`` sharing lock with the workspace value off,
261
+ under which the project override is ignored and the read answers 410.
262
+
263
+ Neither is this command's to override — an operator or a workspace admin
264
+ set them deliberately. Reporting them is the honest response.
265
+ """
266
+ if not public_sharing_instance_enabled():
267
+ self.stdout.write(
268
+ self.style.ERROR(
269
+ "TRUEPPM_PUBLIC_BOARD_SHARING_ENABLED is false — this instance "
270
+ "serves no public share links, so the URLs below will 404. Set it "
271
+ "to true to run a public demo."
272
+ )
273
+ )
274
+ return
275
+ if not resolve_effective_sharing(project, "public_sharing"):
276
+ self.stdout.write(
277
+ self.style.ERROR(
278
+ "Public sharing still resolves to off for this project despite the "
279
+ "project-scoped override — a workspace ENFORCE policy is locking it. "
280
+ "The URLs below will return 410 until a workspace admin turns "
281
+ "'Public sharing' on or relaxes the override policy."
282
+ )
283
+ )
284
+
285
+ def _resolve_link(
286
+ self,
287
+ project: Any,
288
+ kind: ShareContentKind,
289
+ pinned_token: str | None,
290
+ ) -> tuple[str, ShareLink, str] | None:
291
+ """Return ``(action, link, raw_token)`` for one content kind.
292
+
293
+ Returns ``None`` when there is nothing printable — today that is only the
294
+ generated-mode case where a prior link exists but its raw token cannot be
295
+ recovered, which is a warning rather than an error.
296
+ """
297
+ if pinned_token:
298
+ link, created = self._upsert_pinned_link(project, kind, pinned_token)
299
+ return ("Created" if created else "Reused"), link, pinned_token
300
+
301
+ existing = (
302
+ ShareLink.objects.filter(
303
+ project=project,
304
+ content_kind=kind,
305
+ label=LABEL_BY_KIND[kind],
306
+ revoked_at__isnull=True,
307
+ )
308
+ .order_by("-created_at")
309
+ .first()
310
+ )
311
+ if existing is not None:
312
+ env_var = TOKEN_ENV if kind == ShareContentKind.SCHEDULE else TOKEN_BOARD_ENV
313
+ self.stdout.write(
314
+ self.style.WARNING(
315
+ f"A generated demo {kind} share link already exists but its raw token "
316
+ f"cannot be recovered. Set {env_var} to pin a stable, "
317
+ "reprintable URL, or revoke it in the sharing UI and re-run."
318
+ )
319
+ )
320
+ return None
321
+
322
+ link, token = mint_share_link(
323
+ project,
324
+ user=None,
325
+ label=LABEL_BY_KIND[kind],
326
+ show_assignees=False,
327
+ content_kind=kind,
328
+ )
329
+ return "Created", link, token
330
+
331
+ def _upsert_pinned_link(
332
+ self,
333
+ project: Any,
334
+ kind: ShareContentKind,
335
+ token: str,
336
+ ) -> tuple[ShareLink, bool]:
337
+ """Return ``(link, created)`` for the pinned token, creating it if absent.
338
+
339
+ Matched by ``token_hash`` (the only stored representation of the token),
340
+ so a fixed token always resolves the same row and the URL is reprintable
341
+ across restarts — and across the destructive re-seed that a Helm upgrade
342
+ performs, which cascades the previous rows away entirely.
343
+ """
344
+ token_hash = sha256_hex(token)
345
+ link = ShareLink.objects.filter(token_hash=token_hash).first()
346
+ if link is not None:
347
+ # A token already bound to the *other* kind must not be silently
348
+ # reported as reused: the caller would see "Reused" and exit 0 while
349
+ # the link they asked for was never minted. The same-run guard in
350
+ # handle() only catches both-tokens-equal; this catches the cross-run
351
+ # case (reusing a previously-pinned schedule token as a board token).
352
+ if link.content_kind != kind:
353
+ raise CommandError(
354
+ f"That token is already bound to a {link.content_kind} share link "
355
+ f"(id {link.id}); it cannot also back a {kind} link because "
356
+ "ShareLink.token_hash is globally unique. Use a different token."
357
+ )
358
+ return link, False
359
+ link = ShareLink.objects.create(
360
+ project=project,
361
+ content_kind=kind,
362
+ token_prefix=token[:12],
363
+ token_hash=token_hash,
364
+ label=LABEL_BY_KIND[kind],
365
+ show_assignees=False,
366
+ created_by=None,
367
+ )
368
+ return link, True