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,453 @@
1
+ """DRF serializers for the ``/me/credentials/`` viewset (ADR-0049 §3).
2
+
3
+ Three serializers, all built around the rule that the encrypted PAT
4
+ ciphertext is **never** rendered back to the client — not even to the
5
+ authenticated owner. The list response is intentionally a derived view
6
+ ("does a row exist? when was it last used? when does it expire?"); read
7
+ of the underlying secret only happens server-side when the git-aware
8
+ tasks refresh endpoint (#637) needs it.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any
14
+ from urllib.parse import urlparse
15
+
16
+ from django.core.exceptions import ValidationError as DjangoValidationError
17
+ from django.core.validators import URLValidator
18
+ from rest_framework import serializers
19
+
20
+ from .models import IntegrationCredential, TaskLink
21
+ from .registry import TASK_LINK_PROVIDERS
22
+
23
+
24
+ class CredentialSummarySerializer(serializers.Serializer[Any]):
25
+ """One row per provider in the list response.
26
+
27
+ Surfaces:
28
+ - ``provider`` (machine key) and ``name`` (human label) for the UI header
29
+ - ``exists`` — drives the Connected / Not connected pill
30
+ - ``base_url`` for self-hosted users
31
+ - ``created_at`` / ``updated_at`` / ``last_used_at`` for the audit trail
32
+ - ``expires_at`` for the "expires in X days" hint
33
+ - ``requires_credential`` so the UI can hide the Connect button for the
34
+ ``generic`` provider when no PAT is needed
35
+
36
+ Notably absent: any field derived from ``secret_ciphertext``.
37
+ """
38
+
39
+ provider = serializers.CharField()
40
+ name = serializers.CharField()
41
+ exists = serializers.BooleanField()
42
+ base_url = serializers.CharField(allow_blank=True, required=False)
43
+ created_at = serializers.DateTimeField(allow_null=True)
44
+ updated_at = serializers.DateTimeField(allow_null=True)
45
+ last_used_at = serializers.DateTimeField(allow_null=True)
46
+ expires_at = serializers.DateTimeField(allow_null=True)
47
+ requires_credential = serializers.BooleanField()
48
+
49
+
50
+ class CredentialUpsertSerializer(serializers.Serializer[Any]):
51
+ """Payload for ``POST /me/credentials/{provider}/`` (connect or rotate).
52
+
53
+ Validation rules:
54
+ - ``secret`` is required and non-empty (the encryption helper refuses
55
+ empty plaintext anyway; rejecting here gives a clean 400).
56
+ - ``secret`` is write-only — never echoed back.
57
+ - ``base_url`` is optional. When present it must be an ``http://`` or
58
+ ``https://`` URL; ``file://`` / ``javascript:`` / ``gopher://`` /
59
+ similar schemes are rejected here. This serializer is the cheap first
60
+ line of defense; the resolver-level SSRF guard (block RFC1918, link-local,
61
+ cloud metadata at connect time) lands with #677's PAT verification in
62
+ ``integrations.http`` and is reused by #637's git-link refresh.
63
+ - ``expires_at`` is optional and informational only.
64
+ """
65
+
66
+ secret = serializers.CharField(
67
+ write_only=True, min_length=1, max_length=4096, trim_whitespace=False
68
+ )
69
+ base_url = serializers.CharField(required=False, allow_blank=True, max_length=512)
70
+ expires_at = serializers.DateTimeField(required=False, allow_null=True)
71
+
72
+ def validate_secret(self, value: str) -> str:
73
+ # trim_whitespace=False above lets us catch the all-whitespace case
74
+ # explicitly so the error message is clear.
75
+ if not value.strip():
76
+ raise serializers.ValidationError("Secret must not be blank.")
77
+ return value
78
+
79
+ def validate_base_url(self, value: str) -> str:
80
+ if not value:
81
+ return value
82
+ if "://" not in value:
83
+ raise serializers.ValidationError(
84
+ "Host URL must include a scheme (https://example.com)."
85
+ )
86
+ parsed = urlparse(value)
87
+ if parsed.scheme.lower() not in ("http", "https"):
88
+ raise serializers.ValidationError(
89
+ f"Host URL scheme {parsed.scheme.lower()!r} is not allowed. Use http or https."
90
+ )
91
+ # A host URL is a base — a path (self-hosted GitLab relative root, e.g.
92
+ # https://example.com/gitlab) is fine, but a query string or fragment is
93
+ # never meaningful and would corrupt the constructed ``…/api/v4/user``
94
+ # verify URL (e.g. https://host#frag/api/v4/user). Reject them here.
95
+ if parsed.query or parsed.fragment:
96
+ raise serializers.ValidationError(
97
+ "Host URL must not contain a query string or fragment."
98
+ )
99
+ return value
100
+
101
+
102
+ class CredentialVerificationErrorSerializer(serializers.Serializer[Any]):
103
+ """422 body when live PAT verification fails (#677).
104
+
105
+ Documents the shape the Connected Accounts page reads on a rejected
106
+ connect/rotate: a human ``detail``, the stable ``code``
107
+ ``provider_verification_failed``, and a machine-readable ``reason`` the
108
+ client can branch on (``invalid_token``, ``provider_unreachable``,
109
+ ``provider_timeout``, ``blocked_host``).
110
+ """
111
+
112
+ detail = serializers.CharField()
113
+ code = serializers.CharField()
114
+ reason = serializers.CharField(allow_null=True)
115
+
116
+
117
+ class TaskLinkSerializer(serializers.ModelSerializer[TaskLink]):
118
+ """A git/PM link on a task (ADR-0049 §3, #637; redesign #970).
119
+
120
+ Client-writable: ``url``, ``custom_title``, ``labels``, ``display_order``.
121
+ ``provider`` is resolved server-side from the URL + the user's connected
122
+ hosts, and ``status``/``title``/``fetched_at`` are populated by the refresh
123
+ endpoint — never trusted from the client. ``server_version`` is exposed so
124
+ offline clients can reconcile against the sync delta.
125
+ """
126
+
127
+ # Declared as a plain CharField (not the auto ModelSerializer URLField) so a
128
+ # bare host like "github.com/acme/api" survives field validation and reaches
129
+ # ``validate_url``, which prepends the scheme before validating (#970). A
130
+ # strict URLField would 400 it before normalization could run.
131
+ url = serializers.CharField(max_length=2048, trim_whitespace=True)
132
+ # Optional free-text tags; entries are trimmed/de-duped/capped in
133
+ # ``validate_labels``. ``allow_blank`` on the child so a stray "" is dropped
134
+ # rather than 400-ing the whole request.
135
+ labels = serializers.ListField(
136
+ child=serializers.CharField(max_length=40, allow_blank=True),
137
+ required=False,
138
+ )
139
+
140
+ class Meta:
141
+ model = TaskLink
142
+ fields = [
143
+ "id",
144
+ "url",
145
+ "provider",
146
+ "title",
147
+ "custom_title",
148
+ "labels",
149
+ "status",
150
+ "fetched_at",
151
+ # Cloud-file preview cache (#571, ADR-0163) — server-owned, populated
152
+ # by a file provider's OpenGraph unfurl on refresh; read-only.
153
+ "description",
154
+ "thumbnail_url",
155
+ "preview_type",
156
+ "display_order",
157
+ "server_version",
158
+ ]
159
+ read_only_fields = [
160
+ "id",
161
+ "provider",
162
+ "title",
163
+ "status",
164
+ "fetched_at",
165
+ "description",
166
+ "thumbnail_url",
167
+ "preview_type",
168
+ "server_version",
169
+ ]
170
+
171
+ def validate_url(self, value: str) -> str:
172
+ """Normalize a scheme-less URL to ``https://`` then validate (#970).
173
+
174
+ Users routinely paste a bare ``github.com/acme/api/pull/5`` — prepend
175
+ ``https://`` rather than reject it. Anything with an explicit scheme is
176
+ left as-is so a deliberate ``ftp://`` / ``javascript:`` is still caught
177
+ by the http(s)-only ``URLValidator``. The resolver-level SSRF guard runs
178
+ separately at refresh time when the URL is actually fetched.
179
+ """
180
+ raw = (value or "").strip()
181
+ if not raw:
182
+ raise serializers.ValidationError("Enter a URL.")
183
+ if "://" not in raw:
184
+ raw = "https://" + raw.lstrip("/")
185
+ try:
186
+ URLValidator(schemes=["http", "https"])(raw)
187
+ except DjangoValidationError:
188
+ raise serializers.ValidationError("Enter a valid http(s) web address.") from None
189
+ return raw
190
+
191
+ def validate_labels(self, value: list[str]) -> list[str]:
192
+ """Trim, drop blanks, de-dupe (case-insensitive), and cap at 12 (#970)."""
193
+ cleaned: list[str] = []
194
+ seen: set[str] = set()
195
+ for raw in value:
196
+ label = (raw or "").strip()
197
+ if not label:
198
+ continue
199
+ key = label.casefold()
200
+ if key in seen:
201
+ continue
202
+ seen.add(key)
203
+ cleaned.append(label)
204
+ if len(cleaned) > 12:
205
+ raise serializers.ValidationError("A link can have at most 12 labels.")
206
+ return cleaned
207
+
208
+ def validate_custom_title(self, value: str) -> str:
209
+ return (value or "").strip()
210
+
211
+
212
+ class TaskLinkCredentialRequiredSerializer(serializers.Serializer[Any]):
213
+ """422 body when refresh needs a PAT the caller hasn't connected (#637).
214
+
215
+ Lets the task-detail UI swap the status badge for a "Connect {provider}"
216
+ affordance pointing at ``/me/settings/connected-accounts`` instead of
217
+ leaving the link stuck at ``unknown``.
218
+ """
219
+
220
+ detail = serializers.CharField()
221
+ code = serializers.CharField()
222
+ provider = serializers.CharField()
223
+ requires_credential = serializers.BooleanField()
224
+
225
+
226
+ def serialize_credential_summaries(
227
+ user: Any,
228
+ queryset: list[IntegrationCredential],
229
+ ) -> list[dict[str, Any]]:
230
+ """Build the list-response payload for ``GET /me/credentials/``.
231
+
232
+ Iterates the registered providers (so the response always includes one
233
+ entry per provider, whether the user has a row or not) and joins the
234
+ user's existing rows by ``provider`` key. This is the shape the
235
+ Connected Accounts page consumes — it never has to reason about
236
+ "missing rows" vs "no providers registered."
237
+ """
238
+ rows_by_provider: dict[str, IntegrationCredential] = {row.provider: row for row in queryset}
239
+ out: list[dict[str, Any]] = []
240
+ for key in TASK_LINK_PROVIDERS:
241
+ handler = TASK_LINK_PROVIDERS.get(key)
242
+ if handler is None: # pragma: no cover — keys() iterates known
243
+ continue
244
+ # Skip providers that never store a credential (cloud-file preview
245
+ # providers, #571) — the Connected Accounts page is a PAT manager, and
246
+ # an account with nothing to connect would only confuse.
247
+ if not getattr(handler, "connectable", True):
248
+ continue
249
+ row = rows_by_provider.get(key)
250
+ out.append(
251
+ {
252
+ "provider": key,
253
+ "name": getattr(handler, "label", key),
254
+ "exists": row is not None,
255
+ "base_url": row.base_url if row else "",
256
+ "created_at": row.created_at if row else None,
257
+ "updated_at": row.updated_at if row else None,
258
+ "last_used_at": row.last_used_at if row else None,
259
+ "expires_at": row.expires_at if row else None,
260
+ "requires_credential": getattr(handler, "requires_credential", True),
261
+ }
262
+ )
263
+ return out
264
+
265
+
266
+ #: Outcomes a **verified** delivery can produce (#2882). Published as an ``enum`` on
267
+ #: ``GitAutomationConfig.last_delivery_outcome`` so an SDK consumer can discover the
268
+ #: vocabulary the web client renders as user-facing copy. Kept as a module constant,
269
+ #: not re-typed inline, because the receiver and the schema must not drift.
270
+ GIT_DELIVERY_OUTCOMES = (
271
+ "malformed_payload",
272
+ "ignored",
273
+ "draft",
274
+ "duplicate",
275
+ "no_url",
276
+ "no_link",
277
+ "noop_forward_only",
278
+ "opened_review",
279
+ "merged_complete",
280
+ )
281
+
282
+ #: Outcomes a **refused** (pre-verification) delivery can produce. ``no_automation``
283
+ #: is absent on purpose: that refusal has no ``BoardAutomation`` row to write to, so
284
+ #: it can only ever reach the receiver log — publishing it as a readable value would
285
+ #: be advertising a field state that cannot occur.
286
+ GIT_REFUSAL_OUTCOMES = (
287
+ "automation_disabled",
288
+ "no_secret",
289
+ "unknown_provider",
290
+ "secret_unreadable",
291
+ "bad_signature",
292
+ "project_archived",
293
+ )
294
+
295
+
296
+ class GitAutomationConfigSerializer(serializers.Serializer[Any]):
297
+ """Read view of a project's Git-event card automation (#329, ADR-0158).
298
+
299
+ Never renders the secret — only whether one is set. ``webhook_url`` is the
300
+ per-project endpoint the admin pastes into GitHub/GitLab; the secret travels
301
+ in the provider's signature header, never in the URL.
302
+ """
303
+
304
+ enabled = serializers.BooleanField()
305
+ secret_set = serializers.BooleanField()
306
+ webhook_url = serializers.CharField()
307
+ configured_by = serializers.UUIDField(allow_null=True)
308
+ secret_set_at = serializers.DateTimeField(allow_null=True)
309
+ updated_at = serializers.DateTimeField(allow_null=True)
310
+ # Delivery diagnostics (#2882). Before these, a webhook that failed or matched
311
+ # nothing was invisible on every surface an operator can reach: the receiver
312
+ # logged nothing, every non-auth outcome was a 200 so the provider showed a green
313
+ # check, and the settings card had no error state at all. These are what the
314
+ # "Last delivery" and "Last refused delivery" rows on that card read.
315
+ #
316
+ # ``ChoiceField``, not ``CharField``, and not for validation — these are
317
+ # read-only outputs. It is so the vocabulary reaches ``openapi.json`` as an
318
+ # ``enum``. The same MR gives the receiver's own ``reason`` a documented enum, and
319
+ # publishing its superset as a bare ``string`` here would leave an SDK consumer no
320
+ # way to discover the values, while the web client maps every one of them to
321
+ # user-facing copy. A token the server can emit and a client cannot look up is the
322
+ # declared-vs-actual gap this issue is about.
323
+ last_delivery_at = serializers.DateTimeField(allow_null=True)
324
+ last_delivery_outcome = serializers.ChoiceField(
325
+ choices=GIT_DELIVERY_OUTCOMES,
326
+ allow_blank=True,
327
+ help_text=(
328
+ "Outcome of the last delivery whose signature VERIFIED. Empty until one "
329
+ "arrives. Trustworthy: the caller proved it holds the project's secret."
330
+ ),
331
+ )
332
+ last_delivery_provider = serializers.CharField(allow_blank=True)
333
+ # Deliberately separate from the fields above — see BoardAutomation. A refusal is
334
+ # recorded before verification, so an unauthenticated caller holding the project
335
+ # UUID can write here at will; keeping it out of ``last_delivery_*`` is what stops
336
+ # that caller erasing a genuine outcome or provoking a needless secret rotation.
337
+ last_refusal_at = serializers.DateTimeField(allow_null=True)
338
+ last_refusal_outcome = serializers.ChoiceField(
339
+ choices=GIT_REFUSAL_OUTCOMES,
340
+ allow_blank=True,
341
+ help_text=(
342
+ "Why the last REFUSED delivery was refused. The caller saw only an opaque "
343
+ "404. Diagnostic, not evidence: the endpoint is public, so this may record "
344
+ "unauthenticated traffic rather than your provider."
345
+ ),
346
+ )
347
+ last_refusal_provider = serializers.CharField(allow_blank=True)
348
+
349
+
350
+ class GitAutomationUpdateSerializer(serializers.Serializer[Any]):
351
+ """Write payload for toggling Git-event automation (project-admin only)."""
352
+
353
+ enabled = serializers.BooleanField()
354
+
355
+
356
+ class GitAutomationSecretSerializer(serializers.Serializer[Any]):
357
+ """One-time rotate-secret response (``201``) for ``.../rotate-secret/``.
358
+
359
+ Declared so the published schema stops describing this endpoint as
360
+ "200: No response body" — it returns **201** with a plaintext secret that is
361
+ never retrievable again, which is precisely the shape an integrator has to know
362
+ in advance (#2882).
363
+ """
364
+
365
+ secret = serializers.CharField()
366
+ webhook_url = serializers.CharField()
367
+ secret_set_at = serializers.DateTimeField(allow_null=True)
368
+
369
+
370
+ #: Raw OpenAPI schema for the inbound receiver's **request** body (#3364).
371
+ #:
372
+ #: The receiver published no ``requestBody`` at all (``request=None``), which told a
373
+ #: generated client the endpoint accepts nothing — while the handler reads
374
+ #: ``request.data`` and refuses a non-object with a 400. There is no serializer to
375
+ #: name here and there should not be: the body is the *provider's* pull/merge-request
376
+ #: event, whose shape GitHub and GitLab own and change without asking us. Declaring a
377
+ #: fixed schema would publish a contract we do not control and cannot keep true.
378
+ #:
379
+ #: So this declares exactly what the receiver actually requires — a JSON **object**,
380
+ #: any keys — and says in prose where the real shape is defined. ``parse_envelope``
381
+ #: reads a handful of provider-specific keys out of it and ignores the rest, which is
382
+ #: why "an object" is not a cop-out but the honest ceiling on what we can promise.
383
+ GIT_WEBHOOK_PAYLOAD_SCHEMA: dict[str, Any] = {
384
+ "type": "object",
385
+ "additionalProperties": True,
386
+ "description": (
387
+ "The provider's own webhook event payload, forwarded verbatim — GitHub's "
388
+ "`pull_request` event or GitLab's `merge_request` event. Its shape is defined "
389
+ "by the provider, not by TruePPM, so it is typed here only as far as this "
390
+ "receiver actually constrains it: a JSON object. A non-object body is refused "
391
+ "with a 400 once the signature has verified. The signature is computed over "
392
+ "these exact bytes, so the body must be sent unmodified."
393
+ ),
394
+ }
395
+
396
+
397
+ #: Raw OpenAPI schema for the inbound receiver's ``200`` body (#2882).
398
+ #:
399
+ #: The published schema declared all four Git endpoints as "200: No response body",
400
+ #: while the receiver actually returns four distinct shapes and a ``reason`` enum that
401
+ #: was discoverable only by reading Python. Expressed as a raw schema rather than a
402
+ #: ``Serializer`` for one blunt reason: the body carries a ``from`` key, and ``from``
403
+ #: is a Python keyword, so it cannot be a serializer class attribute. Renaming the
404
+ #: wire field to suit the tooling would be the tail wagging the dog.
405
+ GIT_WEBHOOK_RESULT_SCHEMA: dict[str, Any] = {
406
+ "type": "object",
407
+ "properties": {
408
+ "matched": {
409
+ "type": "boolean",
410
+ "description": "A task in this project is linked to the PR/MR in the payload.",
411
+ },
412
+ "moved": {"type": "boolean", "description": "The linked card's status changed."},
413
+ "task": {
414
+ "type": "string",
415
+ "format": "uuid",
416
+ "nullable": True,
417
+ "description": "The matched task, when there was one.",
418
+ },
419
+ "from": {"type": "string", "nullable": True, "description": "Board status before."},
420
+ "to": {"type": "string", "nullable": True, "description": "Board status after."},
421
+ "ignored": {
422
+ "type": "string",
423
+ "description": (
424
+ "The provider's own event name, present when the event is not one this "
425
+ "receiver acts on."
426
+ ),
427
+ },
428
+ "reason": {
429
+ "type": "string",
430
+ "enum": [
431
+ "draft",
432
+ "duplicate",
433
+ "no_url",
434
+ "no_link",
435
+ "noop_forward_only",
436
+ "opened_review",
437
+ "merged_complete",
438
+ ],
439
+ "description": (
440
+ "draft: a draft/WIP pull/merge request opened — deliberately does not "
441
+ "promote the card, the promotion happens when it is marked ready for "
442
+ "review. duplicate: a provider redelivery of an event already "
443
+ "processed. no_url: the payload carried no pull/merge request URL. "
444
+ "no_link: no task in this project has a link pointing at this PR/MR, so "
445
+ "there is no card to move — this is the most common reason a correctly "
446
+ "configured webhook moves nothing. noop_forward_only: the card is "
447
+ "already at or past the target status. opened_review / merged_complete: "
448
+ "a card moved."
449
+ ),
450
+ },
451
+ },
452
+ "required": ["matched", "moved"],
453
+ }
@@ -0,0 +1,171 @@
1
+ """Service layer for user-scoped external task sync (ADR-0097 §4).
2
+
3
+ ``enqueue_external_sync`` is the single entry point that queues a read-only pull
4
+ of one ``(user, source)`` connection. It follows the canonical transactional
5
+ outbox shape (ADR-0017/0019): write an ``ExternalSyncRequest`` row inside a
6
+ transaction, then a best-effort ``transaction.on_commit`` dispatch of the
7
+ ``external_sync`` Celery task. If the broker is down the row stays ``PENDING``
8
+ and the 300 s ``drain-external-sync`` Beat task recovers it.
9
+
10
+ Never call ``external_sync.delay()`` directly from a view — the outbox row is
11
+ what makes the pull survive a broker outage (ADR-0097 §Durable Execution #4).
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ import uuid
18
+
19
+ from django.db import IntegrityError, transaction
20
+ from django.utils import timezone
21
+
22
+ from .external_sources import EXTERNAL_TASK_SOURCES
23
+ from .models import (
24
+ ExternalSyncRequest,
25
+ ExternalSyncRequestReason,
26
+ ExternalSyncRequestStatus,
27
+ )
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+ # Minimum spacing between *manual* refreshes of one connection (ADR-0097
32
+ # §Resolution #5 "per-user cooldown ≥60 s"). The partial-unique constraint
33
+ # already coalesces a burst onto one in-flight pull; this outer bound also
34
+ # rate-limits back-to-back refreshes *after* a pull completes, so a user
35
+ # hammering the button cannot walk Jira's rate budget. The opt-in poll and the
36
+ # on-open refresh are exempt — they are already low-frequency by construction.
37
+ MANUAL_SYNC_COOLDOWN_SECONDS = 60
38
+
39
+
40
+ class SyncCooldownActive(Exception):
41
+ """A manual refresh was requested inside the per-connection cooldown window.
42
+
43
+ Carries ``retry_after`` (seconds) so the view can answer ``429`` with a
44
+ ``Retry-After`` header rather than silently swallowing the request.
45
+ """
46
+
47
+ def __init__(self, retry_after: int) -> None:
48
+ self.retry_after = retry_after
49
+ super().__init__(f"Sync cooldown active — retry in {retry_after}s")
50
+
51
+
52
+ def enqueue_external_sync(
53
+ user_id: int,
54
+ source: str,
55
+ *,
56
+ reason: ExternalSyncRequestReason = ExternalSyncRequestReason.MANUAL,
57
+ ) -> ExternalSyncRequest | None:
58
+ """Queue a read-only pull of one external-source connection (outbox + dispatch).
59
+
60
+ Idempotent: if a ``PENDING`` row already exists for ``(user, source)`` it is
61
+ adopted (coalescing a burst of triggers onto one pull) rather than stacking a
62
+ second Jira fetch — mirrors ``scheduling.enqueue_recalculate``.
63
+
64
+ Args:
65
+ user_id: Owner of the connection (rows are strictly per-user).
66
+ source: An ``EXTERNAL_TASK_SOURCES`` key (e.g. ``"jira"``).
67
+ reason: Why the pull was queued — forensics only.
68
+
69
+ Returns:
70
+ The queued (or adopted) ``ExternalSyncRequest``, or ``None`` if a race
71
+ transitioned the pending row out from under the adopt path.
72
+
73
+ Raises:
74
+ ValueError: ``source`` is not a registered external task source.
75
+ SyncCooldownActive: ``reason`` is ``MANUAL`` and the last request for this
76
+ connection is inside :data:`MANUAL_SYNC_COOLDOWN_SECONDS`.
77
+ """
78
+ if EXTERNAL_TASK_SOURCES.get(source) is None:
79
+ raise ValueError(f"Unknown external task source {source!r}.")
80
+
81
+ now = timezone.now()
82
+
83
+ # Manual-refresh cooldown (ADR-0097 §Resolution #5). Measured from the most
84
+ # recent request's ``requested_at`` regardless of its terminal state, so a
85
+ # completed pull still blocks an immediate re-trigger. Poll / on-open reasons
86
+ # skip this — they are already spaced by their Beat cadence.
87
+ if reason == ExternalSyncRequestReason.MANUAL:
88
+ last = (
89
+ ExternalSyncRequest.objects.filter(user_id=user_id, source=source)
90
+ .order_by("-requested_at")
91
+ .first()
92
+ )
93
+ if last is not None:
94
+ elapsed = (now - last.requested_at).total_seconds()
95
+ if elapsed < MANUAL_SYNC_COOLDOWN_SECONDS:
96
+ raise SyncCooldownActive(
97
+ retry_after=max(1, int(MANUAL_SYNC_COOLDOWN_SECONDS - elapsed))
98
+ )
99
+
100
+ req: ExternalSyncRequest
101
+ try:
102
+ with transaction.atomic():
103
+ req = ExternalSyncRequest.objects.create(user_id=user_id, source=source, reason=reason)
104
+ except IntegrityError:
105
+ # A PENDING row already exists (partial-unique constraint). Adopt it — it
106
+ # may be stranded (its own dispatch failed on a broker blip) and every
107
+ # subsequent trigger would otherwise pile up behind it. Re-dispatching now
108
+ # collapses them into one pull with the latest Jira data.
109
+ existing = ExternalSyncRequest.objects.filter(
110
+ user_id=user_id,
111
+ source=source,
112
+ status=ExternalSyncRequestStatus.PENDING,
113
+ ).first()
114
+ if existing is None:
115
+ # Race: the row left PENDING between the insert and this lookup — the
116
+ # other writer already dispatched it, so there is nothing to do.
117
+ return None
118
+ req = existing
119
+
120
+ # An adopted row may be a rate-limited pull sitting out the clock the
121
+ # source itself gave us (``_requeue_rate_limited`` puts it back in PENDING
122
+ # with ``next_attempt_at``). Dispatching it now would re-hit the source
123
+ # inside its own ``Retry-After`` and earn another 429 — the ADR-0097 §4
124
+ # "backoff that respects Retry-After" promise, broken by the trigger rather
125
+ # than by the backoff.
126
+ #
127
+ # This became routine with the background-poll opt-in (#3104): the poll
128
+ # ticks every 15 minutes and ``_RATE_LIMIT_MAX_BACKOFF`` is also 15
129
+ # minutes, so an always-on connection reliably ticks inside an active
130
+ # backoff window with nobody touching the UI. ``drain_external_sync``
131
+ # already honors the same clock and re-dispatches on its own 300 s cadence,
132
+ # so leaving the row alone loses nothing — the pull still happens, just
133
+ # when the source said it could.
134
+ if req.next_attempt_at is not None and req.next_attempt_at > now:
135
+ return req
136
+
137
+ _dispatch_on_commit(req.id)
138
+ return req
139
+
140
+
141
+ def _dispatch_on_commit(request_id: uuid.UUID) -> None:
142
+ """Register a best-effort ``external_sync`` dispatch for after commit.
143
+
144
+ The dispatch is deferred to ``transaction.on_commit`` so the outbox row is
145
+ durably committed before the worker reads it. A broker error is swallowed —
146
+ the row stays ``PENDING`` and ``drain_external_sync`` re-dispatches it.
147
+ """
148
+
149
+ def _dispatch() -> None:
150
+ from .tasks import external_sync
151
+
152
+ try:
153
+ result = external_sync.delay(str(request_id))
154
+ except Exception:
155
+ logger.exception(
156
+ "enqueue_external_sync: immediate dispatch failed for request %s "
157
+ "— drain_external_sync will retry",
158
+ request_id,
159
+ )
160
+ return
161
+ # Flip to DISPATCHED only if still PENDING, so a concurrent drain that
162
+ # already dispatched this row is not clobbered.
163
+ ExternalSyncRequest.objects.filter(
164
+ id=request_id, status=ExternalSyncRequestStatus.PENDING
165
+ ).update(
166
+ status=ExternalSyncRequestStatus.DISPATCHED,
167
+ celery_task_id=result.id,
168
+ dispatched_at=timezone.now(),
169
+ )
170
+
171
+ transaction.on_commit(_dispatch)