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.
- trueppm_api/__init__.py +14 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +164 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +167 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +132 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +2837 -0
- trueppm_api/apps/access/serializers.py +946 -0
- trueppm_api/apps/access/services.py +667 -0
- trueppm_api/apps/access/signals.py +103 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +95 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1931 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +108 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +418 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +177 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +837 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +403 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +622 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +519 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +731 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1106 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +277 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +500 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1231 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +436 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +447 -0
- trueppm_api/apps/notifications/email_backend.py +330 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +294 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1197 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +961 -0
- trueppm_api/apps/notifications/services.py +1451 -0
- trueppm_api/apps/notifications/tasks.py +711 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +904 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +551 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +969 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +523 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +466 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +449 -0
- trueppm_api/apps/projects/backfill.py +287 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +204 -0
- trueppm_api/apps/projects/config_notice.py +1325 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +93 -0
- trueppm_api/apps/projects/decisions_views.py +94 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +353 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +247 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +44 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +61 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +62 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +8946 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +575 -0
- trueppm_api/apps/projects/program_schedule.py +745 -0
- trueppm_api/apps/projects/program_views.py +3220 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +493 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/exporter.py +1644 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2390 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +126 -0
- trueppm_api/apps/projects/seed/replay.py +1691 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1149 -0
- trueppm_api/apps/projects/serializers.py +12310 -0
- trueppm_api/apps/projects/services.py +6966 -0
- trueppm_api/apps/projects/share_serializers.py +132 -0
- trueppm_api/apps/projects/share_services.py +325 -0
- trueppm_api/apps/projects/share_views.py +374 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +909 -0
- trueppm_api/apps/projects/signal_privacy_views.py +476 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +266 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1152 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1917 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +258 -0
- trueppm_api/apps/projects/template_views.py +799 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1011 -0
- trueppm_api/apps/projects/utilization.py +885 -0
- trueppm_api/apps/projects/views.py +20572 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1749 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +119 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +516 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +283 -0
- trueppm_api/apps/scheduling/serializers.py +564 -0
- trueppm_api/apps/scheduling/services.py +1182 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +1984 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2564 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +437 -0
- trueppm_api/apps/sso/services.py +1385 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +320 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +471 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +225 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +371 -0
- trueppm_api/apps/sync/serializers.py +687 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +501 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +930 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +142 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +434 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +117 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +526 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +144 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +90 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +372 -0
- trueppm_api/apps/webhooks/serializers.py +318 -0
- trueppm_api/apps/webhooks/tasks.py +663 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +383 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +368 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +242 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +919 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +555 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1502 -0
- trueppm_api/asgi.py +42 -0
- trueppm_api/celery.py +31 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +916 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +949 -0
- trueppm_api/core/password_reset.py +500 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +884 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +344 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2455 -0
- trueppm_api/settings/dev.py +273 -0
- trueppm_api/settings/prod.py +228 -0
- trueppm_api/urls.py +153 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0b2.dist-info/METADATA +70 -0
- trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
- trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""Views for the program backlog (ADR-0069 Erratum, #737 / #739).
|
|
2
|
+
|
|
3
|
+
``BacklogItem`` is a program-scoped CRUD resource nested under a program, plus a
|
|
4
|
+
``pull`` action that converts a PROPOSED item into a project-backlog Task. The
|
|
5
|
+
project-backlog *read* (Task with ``status=BACKLOG``, ``sprint=NULL``) is served
|
|
6
|
+
by the existing ``TaskViewSet`` filters (``?status=BACKLOG&sprint=none``) — no
|
|
7
|
+
separate endpoint is added here.
|
|
8
|
+
|
|
9
|
+
Permission matrix (ADR-0069 Erratum §4):
|
|
10
|
+
list / retrieve — IsProgramMember (Viewer+)
|
|
11
|
+
create / update / destroy — IsProgramEditor (Team Member+)
|
|
12
|
+
pull — IsProgramEditor **and** write role on the target project
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from django.contrib.postgres.search import TrigramSimilarity
|
|
18
|
+
from django.core.exceptions import ValidationError as DjangoValidationError
|
|
19
|
+
from django.db.models import QuerySet
|
|
20
|
+
from django.shortcuts import get_object_or_404
|
|
21
|
+
from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer
|
|
22
|
+
from rest_framework import mixins, serializers, status, viewsets
|
|
23
|
+
from rest_framework.decorators import action
|
|
24
|
+
from rest_framework.permissions import BasePermission, IsAuthenticated
|
|
25
|
+
from rest_framework.request import Request
|
|
26
|
+
from rest_framework.response import Response
|
|
27
|
+
from rest_framework.serializers import BaseSerializer
|
|
28
|
+
|
|
29
|
+
from trueppm_api.apps.access.models import Role
|
|
30
|
+
from trueppm_api.apps.access.permissions import (
|
|
31
|
+
IsProgramEditor,
|
|
32
|
+
IsProgramMember,
|
|
33
|
+
IsProgramNotClosed,
|
|
34
|
+
McpReadableViewMixin,
|
|
35
|
+
McpScope,
|
|
36
|
+
_membership_role,
|
|
37
|
+
assert_project_not_archived,
|
|
38
|
+
)
|
|
39
|
+
from trueppm_api.apps.idempotency.mixins import IdempotencyMixin
|
|
40
|
+
from trueppm_api.apps.projects.backlog_services import (
|
|
41
|
+
BacklogItemNotPullable,
|
|
42
|
+
CrossProgramPullError,
|
|
43
|
+
pull_to_project_backlog,
|
|
44
|
+
)
|
|
45
|
+
from trueppm_api.apps.projects.models import (
|
|
46
|
+
BacklogItem,
|
|
47
|
+
BacklogItemStatus,
|
|
48
|
+
Program,
|
|
49
|
+
Project,
|
|
50
|
+
)
|
|
51
|
+
from trueppm_api.apps.projects.serializers import BacklogItemSerializer, TaskSerializer
|
|
52
|
+
from trueppm_api.core.request_body import object_body
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class BacklogItemViewSet(
|
|
56
|
+
McpReadableViewMixin,
|
|
57
|
+
IdempotencyMixin,
|
|
58
|
+
mixins.ListModelMixin,
|
|
59
|
+
mixins.CreateModelMixin,
|
|
60
|
+
mixins.RetrieveModelMixin,
|
|
61
|
+
mixins.UpdateModelMixin,
|
|
62
|
+
mixins.DestroyModelMixin,
|
|
63
|
+
viewsets.GenericViewSet[BacklogItem],
|
|
64
|
+
):
|
|
65
|
+
"""CRUD + pull for program backlog items.
|
|
66
|
+
|
|
67
|
+
URL: ``/api/v1/programs/<program_pk>/backlog-items/[<pk>/]``
|
|
68
|
+
``/api/v1/programs/<program_pk>/backlog-items/<pk>/pull/``
|
|
69
|
+
|
|
70
|
+
``McpReadableViewMixin`` (ADR-0186 §E) additively exposes the list/retrieve
|
|
71
|
+
reads to a personal ``mcp:read`` API token — it powers the MCP
|
|
72
|
+
``list_program_backlog`` tool. Because this viewset overrides
|
|
73
|
+
``get_permissions`` with a per-action list, the MCP token guards are appended
|
|
74
|
+
from that wrapper (via ``mcp_token_guards()``) rather than the mixin's default
|
|
75
|
+
``get_permissions``, so no branch — including the write branches — can leak a
|
|
76
|
+
token past ``TokenReadOnlyMethods`` / ``TokenHasScope`` / ``TokenIsOwnerScoped``.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# ADR-0678 (#2482): program-level intake pool. BacklogItem has no project FK,
|
|
80
|
+
# so the central filter reaches it through the `program` branch — a child
|
|
81
|
+
# project's opt-out does not withhold intake data the project does not own.
|
|
82
|
+
mcp_scope = McpScope.AGGREGATE
|
|
83
|
+
|
|
84
|
+
serializer_class = BacklogItemSerializer
|
|
85
|
+
|
|
86
|
+
def get_permissions(self) -> list[BasePermission]:
|
|
87
|
+
# ADR-0186 §E: append the read-only MCP token guards around the
|
|
88
|
+
# action-specific RBAC list so a mcp:read token is confined to safe
|
|
89
|
+
# methods on every action (no write-branch leak); human auth passes both.
|
|
90
|
+
return [*self._rbac_permissions(), *self.mcp_token_guards()]
|
|
91
|
+
|
|
92
|
+
def _rbac_permissions(self) -> list[BasePermission]:
|
|
93
|
+
if self.action in ("list", "retrieve"):
|
|
94
|
+
return [IsAuthenticated(), IsProgramMember(), IsProgramNotClosed()]
|
|
95
|
+
# create / update / partial_update / destroy / pull all require program
|
|
96
|
+
# write (Team Member+). The pull action additionally checks project-write
|
|
97
|
+
# on the resolved target inside the handler.
|
|
98
|
+
return [IsAuthenticated(), IsProgramEditor(), IsProgramNotClosed()]
|
|
99
|
+
|
|
100
|
+
def get_queryset(self) -> QuerySet[BacklogItem]:
|
|
101
|
+
"""Program-scoped backlog.
|
|
102
|
+
|
|
103
|
+
The list view layers on item_type / status / tags / ``?q=`` filters and
|
|
104
|
+
defaults ``status`` to PROPOSED (the active pool). Those filters are
|
|
105
|
+
**list-only** — detail operations (retrieve / update / destroy) must be
|
|
106
|
+
able to reach an item in *any* status (e.g. PATCH an item to ARCHIVED,
|
|
107
|
+
or fetch a PULLED item), so they get the unfiltered program-scoped set.
|
|
108
|
+
"""
|
|
109
|
+
program_pk = str(self.kwargs["program_pk"])
|
|
110
|
+
qs = BacklogItem.objects.filter(program_id=program_pk, is_deleted=False).select_related(
|
|
111
|
+
# pulled_task__project powers the serializer's pulled_task_project_*
|
|
112
|
+
# wayfinding fields (#1994) without an extra query per pulled row.
|
|
113
|
+
"pulled_task__project",
|
|
114
|
+
"created_by",
|
|
115
|
+
"pulled_by",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if self.action != "list":
|
|
119
|
+
return qs
|
|
120
|
+
|
|
121
|
+
item_type = self.request.query_params.get("item_type")
|
|
122
|
+
if item_type:
|
|
123
|
+
qs = qs.filter(item_type=item_type)
|
|
124
|
+
|
|
125
|
+
# status: explicit value filters; a present-but-empty value means "all";
|
|
126
|
+
# absent means the default active pool (PROPOSED).
|
|
127
|
+
if "status" in self.request.query_params:
|
|
128
|
+
status_filter = self.request.query_params.get("status")
|
|
129
|
+
if status_filter:
|
|
130
|
+
qs = qs.filter(status=status_filter)
|
|
131
|
+
else:
|
|
132
|
+
qs = qs.filter(status=BacklogItemStatus.PROPOSED)
|
|
133
|
+
|
|
134
|
+
# tags: repeatable ?tags=foo&tags=bar — match items carrying ALL given tags.
|
|
135
|
+
tags = self.request.query_params.getlist("tags")
|
|
136
|
+
for tag in tags:
|
|
137
|
+
if tag:
|
|
138
|
+
qs = qs.filter(tags__contains=[tag])
|
|
139
|
+
|
|
140
|
+
# Trigram fuzzy search on title (#739). Combines with the filters above;
|
|
141
|
+
# an empty/absent q is a no-op (other filters + default ordering apply).
|
|
142
|
+
#
|
|
143
|
+
# The FILTER uses the ``%`` operator (``__trigram_similar``) — this is the
|
|
144
|
+
# form the ``gin_trgm_ops`` GIN index (backlogitem_title_trgm) accelerates,
|
|
145
|
+
# so search scales as the ADR intends rather than seq-scanning a computed
|
|
146
|
+
# similarity per row. The ``%`` operator honours pg_trgm's
|
|
147
|
+
# ``similarity_threshold`` (default 0.3). We additionally annotate the
|
|
148
|
+
# whole-string similarity and order by it desc so the closest title ranks
|
|
149
|
+
# first, overriding the model's default priority_rank ordering for searches.
|
|
150
|
+
q = (self.request.query_params.get("q") or "").strip()
|
|
151
|
+
if q:
|
|
152
|
+
qs = (
|
|
153
|
+
qs.annotate(similarity=TrigramSimilarity("title", q))
|
|
154
|
+
.filter(title__trigram_similar=q)
|
|
155
|
+
.order_by("-similarity")
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return qs
|
|
159
|
+
|
|
160
|
+
def _resolve_program(self) -> Program:
|
|
161
|
+
program = get_object_or_404(Program, pk=self.kwargs["program_pk"], is_deleted=False)
|
|
162
|
+
# DRF skips has_object_permission on create/custom actions — enforce the
|
|
163
|
+
# program role gate against the resolved program before any write.
|
|
164
|
+
self.check_object_permissions(self.request, program)
|
|
165
|
+
return program
|
|
166
|
+
|
|
167
|
+
def perform_create(self, serializer: BaseSerializer[BacklogItem]) -> None:
|
|
168
|
+
program = self._resolve_program()
|
|
169
|
+
# status is forced to PROPOSED on create regardless of payload — a new
|
|
170
|
+
# item always enters the active pool; PULLED/ARCHIVED are reached later
|
|
171
|
+
# via the pull action / archive PATCH.
|
|
172
|
+
serializer.save(
|
|
173
|
+
program=program,
|
|
174
|
+
created_by=self.request.user,
|
|
175
|
+
status=BacklogItemStatus.PROPOSED,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def perform_destroy(self, instance: BacklogItem) -> None:
|
|
179
|
+
# Soft-delete so the sync protocol ships a tombstone rather than the row
|
|
180
|
+
# vanishing silently (VersionedModel contract).
|
|
181
|
+
instance.soft_delete()
|
|
182
|
+
|
|
183
|
+
# Surfaced by the #2840 conformance guard, which scans every module under
|
|
184
|
+
# ``apps/`` rather than ``views.py`` alone. Every part of the inferred contract
|
|
185
|
+
# was wrong: the fallback declared a full BacklogItemRequest body (the real
|
|
186
|
+
# body is ``{"project_id": …}``) and a bare ``200 BacklogItem`` response (the
|
|
187
|
+
# handler returns ``201`` with a two-key envelope carrying the created Task
|
|
188
|
+
# *and* the transitioned item).
|
|
189
|
+
@extend_schema(
|
|
190
|
+
summary="Pull a backlog item into a project's backlog",
|
|
191
|
+
request=inline_serializer(
|
|
192
|
+
name="BacklogItemPullRequest",
|
|
193
|
+
fields={"project_id": serializers.UUIDField()},
|
|
194
|
+
),
|
|
195
|
+
responses={
|
|
196
|
+
201: inline_serializer(
|
|
197
|
+
name="BacklogItemPullResult",
|
|
198
|
+
fields={
|
|
199
|
+
"task": TaskSerializer(),
|
|
200
|
+
"backlog_item": BacklogItemSerializer(),
|
|
201
|
+
},
|
|
202
|
+
),
|
|
203
|
+
400: OpenApiResponse(
|
|
204
|
+
description="project_id missing, or the target project is not in this program."
|
|
205
|
+
),
|
|
206
|
+
403: OpenApiResponse(
|
|
207
|
+
description=(
|
|
208
|
+
"Caller lacks Team Member+ on the target project, **or** the target "
|
|
209
|
+
"project is archived. The archived case is not a role problem and no "
|
|
210
|
+
"role clears it — unarchive the project and retry (#3354)."
|
|
211
|
+
)
|
|
212
|
+
),
|
|
213
|
+
409: OpenApiResponse(description="The item is no longer PROPOSED."),
|
|
214
|
+
},
|
|
215
|
+
)
|
|
216
|
+
@action(detail=True, methods=["post"], url_path="pull")
|
|
217
|
+
def pull(self, request: Request, program_pk: str, pk: str) -> Response:
|
|
218
|
+
"""Pull a PROPOSED item into a project's backlog (ADR-0069 Erratum §5).
|
|
219
|
+
|
|
220
|
+
Body: ``{"project_id": "<uuid>"}``. Creates a Task (status=BACKLOG,
|
|
221
|
+
sprint=NULL) in the target project and transitions the item to PULLED.
|
|
222
|
+
Requires program-write (gated by ``get_permissions``) **and** write role
|
|
223
|
+
on the target project (checked here). Never assigns a sprint.
|
|
224
|
+
|
|
225
|
+
Returns ``201`` with the created task and the updated backlog item.
|
|
226
|
+
``400`` if project_id is missing / not in this program; ``403`` if the
|
|
227
|
+
caller lacks project-write **or the target project is archived** (the
|
|
228
|
+
latter clears for no role — the project must be unarchived); ``409`` if
|
|
229
|
+
the item is no longer PROPOSED.
|
|
230
|
+
"""
|
|
231
|
+
# Confirm the program exists and the caller holds program-write.
|
|
232
|
+
self._resolve_program()
|
|
233
|
+
|
|
234
|
+
project_id = object_body(request).get("project_id")
|
|
235
|
+
if not project_id:
|
|
236
|
+
return Response(
|
|
237
|
+
{"detail": "project_id is required."},
|
|
238
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Resolve the target project, scoped to this program. A project_id that
|
|
242
|
+
# is not in the program returns 404-as-400 (cross-program pull rejected).
|
|
243
|
+
try:
|
|
244
|
+
project = Project.objects.get(pk=project_id, program_id=program_pk, is_deleted=False)
|
|
245
|
+
except (Project.DoesNotExist, ValueError, DjangoValidationError):
|
|
246
|
+
return Response(
|
|
247
|
+
{"detail": "Target project not found in this program."},
|
|
248
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Project-level write gate: program-write alone is not enough to drop a
|
|
252
|
+
# Task into a project — the caller must also be Team Member+ on it.
|
|
253
|
+
role = _membership_role(request, project.pk)
|
|
254
|
+
if role is None or role < Role.MEMBER:
|
|
255
|
+
return Response(
|
|
256
|
+
{"detail": "You need at least Team Member role on the target project."},
|
|
257
|
+
status=status.HTTP_403_FORBIDDEN,
|
|
258
|
+
)
|
|
259
|
+
# Archived target projects take no writes, and a pull creates a Task (#3354).
|
|
260
|
+
# `IsProgramNotClosed` in `get_permissions` gates the *program*; nothing was
|
|
261
|
+
# checking the project's own lifecycle. `IsProjectNotArchived` cannot cover
|
|
262
|
+
# this route: the target project comes from the request body, so there is no
|
|
263
|
+
# `project_pk` kwarg for `has_permission` and `get_object()` returns the
|
|
264
|
+
# `BacklogItem` — the same blind spot as `ProjectTemplateViewSet.apply`, and
|
|
265
|
+
# it needs the same explicit assert. Placed after the role check so a caller
|
|
266
|
+
# without project-write still gets the role refusal first.
|
|
267
|
+
assert_project_not_archived(project.pk)
|
|
268
|
+
|
|
269
|
+
try:
|
|
270
|
+
task = pull_to_project_backlog(item_id=str(pk), project=project, actor=request.user)
|
|
271
|
+
except BacklogItem.DoesNotExist:
|
|
272
|
+
return Response(
|
|
273
|
+
{"detail": "Backlog item not found."},
|
|
274
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
275
|
+
)
|
|
276
|
+
except CrossProgramPullError as exc:
|
|
277
|
+
# False positive: domain exception with a curated, client-safe message
|
|
278
|
+
# (single-program pull boundary, ADR-0069) — no server internals.
|
|
279
|
+
return Response(
|
|
280
|
+
{"detail": str(exc)}, # codeql[py/stack-trace-exposure]
|
|
281
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
282
|
+
)
|
|
283
|
+
except BacklogItemNotPullable as exc:
|
|
284
|
+
# False positive: curated domain message (item no longer PROPOSED).
|
|
285
|
+
return Response(
|
|
286
|
+
{"detail": str(exc)}, # codeql[py/stack-trace-exposure]
|
|
287
|
+
status=status.HTTP_409_CONFLICT,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Re-fetch with the FK joins the serializer reads (pulled_task__project,
|
|
291
|
+
# created_by, pulled_by) so the pull response doesn't fire lazy per-FK
|
|
292
|
+
# queries — including the pulled_task_project_* wayfinding fields (#1994).
|
|
293
|
+
item = BacklogItem.objects.select_related(
|
|
294
|
+
"pulled_task__project", "created_by", "pulled_by"
|
|
295
|
+
).get(pk=pk)
|
|
296
|
+
return Response(
|
|
297
|
+
{
|
|
298
|
+
"task": TaskSerializer(task).data,
|
|
299
|
+
"backlog_item": BacklogItemSerializer(item).data,
|
|
300
|
+
},
|
|
301
|
+
status=status.HTTP_201_CREATED,
|
|
302
|
+
)
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Read + undo endpoints for the schedule outline's batch operation ledgers (ADR-0810, #2756).
|
|
2
|
+
|
|
3
|
+
Mirrors ``template_views.py``'s ``TemplateApplicationViewSet`` shape exactly:
|
|
4
|
+
read-only (these are server-written ledger rows, no client create/update/delete
|
|
5
|
+
door), scoped to the caller's project memberships, one ``undo`` POST action per
|
|
6
|
+
viewset. See ``task_batch_services.py`` for the actual undo logic — these views
|
|
7
|
+
are a thin RBAC + status-guard layer over it, same division of labor as the
|
|
8
|
+
template application endpoint.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any, cast
|
|
14
|
+
|
|
15
|
+
from django.db.models import QuerySet
|
|
16
|
+
from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_field
|
|
17
|
+
from rest_framework import serializers, status, viewsets
|
|
18
|
+
from rest_framework.decorators import action
|
|
19
|
+
from rest_framework.exceptions import PermissionDenied, ValidationError
|
|
20
|
+
from rest_framework.permissions import IsAuthenticated
|
|
21
|
+
from rest_framework.request import Request
|
|
22
|
+
from rest_framework.response import Response
|
|
23
|
+
|
|
24
|
+
from trueppm_api.apps.access.permissions import (
|
|
25
|
+
IsProjectNotArchived,
|
|
26
|
+
_membership_role,
|
|
27
|
+
role_can_undo_batch_operation,
|
|
28
|
+
)
|
|
29
|
+
from trueppm_api.apps.idempotency.mixins import IdempotencyMixin
|
|
30
|
+
from trueppm_api.apps.projects.models import (
|
|
31
|
+
CascadeClassificationOperation,
|
|
32
|
+
PasteManyOperation,
|
|
33
|
+
SyncBatchOperationStatus,
|
|
34
|
+
)
|
|
35
|
+
from trueppm_api.apps.projects.task_batch_services import (
|
|
36
|
+
undo_cascade_classification_operation,
|
|
37
|
+
undo_paste_many_operation,
|
|
38
|
+
)
|
|
39
|
+
from trueppm_api.core.openapi import state_refusal_400, undo_summary_schema
|
|
40
|
+
|
|
41
|
+
#: Both ``undo`` actions refuse for two unrelated reasons that share a status code.
|
|
42
|
+
#: Spelling them out is the difference between a client retrying usefully and a
|
|
43
|
+
#: client retrying forever (#3354).
|
|
44
|
+
ARCHIVED_403 = (
|
|
45
|
+
"Two causes, and a client must tell them apart: the caller is below the role "
|
|
46
|
+
"floor for this action, **or** the project is archived. The archived case clears "
|
|
47
|
+
"for no role — including Owner — so escalating the caller will never succeed; "
|
|
48
|
+
"the project has to be unarchived (#3354)."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _caller_project_ids(request: Request) -> QuerySet[Any]:
|
|
53
|
+
from trueppm_api.apps.access.models import ProjectMembership
|
|
54
|
+
|
|
55
|
+
return ProjectMembership.objects.filter(
|
|
56
|
+
user=cast("Any", request.user), is_deleted=False
|
|
57
|
+
).values_list("project_id", flat=True)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _require_admin(request: Request, project_id: Any) -> None:
|
|
61
|
+
"""⌘Z on a batch write is Admin+, the same bar as undoing a template apply
|
|
62
|
+
(ADR-0773's "who may reverse a plan-shaping batch write" matrix). A plain
|
|
63
|
+
Member may have authored the paste/cascade under Author mode and still not
|
|
64
|
+
clear this floor.
|
|
65
|
+
|
|
66
|
+
**The floor is open at #3355 and the reason once given for it was wrong.** This
|
|
67
|
+
docstring used to say undo "removes work other collaborators may already be
|
|
68
|
+
building on top of". It does not: ``undo_paste_many_operation`` and its cascade
|
|
69
|
+
and import-fix siblings (all in ``projects.task_batch_services``) partition on
|
|
70
|
+
``server_version`` via ``_partition_touched`` and act only on the untouched rows,
|
|
71
|
+
returning the rest as ``kept``. Nothing another collaborator wrote is removed.
|
|
72
|
+
ADR-0880 §4 rejects the same premise for structural undo and implements
|
|
73
|
+
actor-or-Admin instead.
|
|
74
|
+
|
|
75
|
+
Defers the comparison to :func:`role_can_undo_batch_operation` rather than
|
|
76
|
+
testing the ordinal here, because the cascade's apply endpoint reports the
|
|
77
|
+
same rule to the client as ``can_undo`` and the two must not drift (#3304).
|
|
78
|
+
"""
|
|
79
|
+
if not role_can_undo_batch_operation(_membership_role(request, project_id)):
|
|
80
|
+
raise PermissionDenied("You need at least Project Manager role to undo this.")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class PasteManyOperationSerializer(serializers.ModelSerializer[PasteManyOperation]):
|
|
84
|
+
class Meta:
|
|
85
|
+
model = PasteManyOperation
|
|
86
|
+
fields = [ # noqa: RUF012
|
|
87
|
+
"id",
|
|
88
|
+
"project",
|
|
89
|
+
"applied_by",
|
|
90
|
+
"status",
|
|
91
|
+
"created_task_versions",
|
|
92
|
+
"result_summary",
|
|
93
|
+
"created_at",
|
|
94
|
+
"undone_at",
|
|
95
|
+
]
|
|
96
|
+
read_only_fields = fields
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class PasteManyOperationUndoSerializer(PasteManyOperationSerializer):
|
|
100
|
+
"""The ledger row **plus** the ``undo`` summary the action returns (#3416).
|
|
101
|
+
|
|
102
|
+
The handler builds its body as ``data = serializer.data`` then ``data["undo"] =
|
|
103
|
+
summary``, so declaring the bare ``PasteManyOperationSerializer`` as the ``200``
|
|
104
|
+
would publish a document with no ``undo`` field — the one value the endpoint
|
|
105
|
+
exists to report, missing from every generated SDK. See
|
|
106
|
+
:func:`undo_summary_schema` for why that is worse than declaring nothing.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
undo = serializers.SerializerMethodField()
|
|
110
|
+
|
|
111
|
+
class Meta(PasteManyOperationSerializer.Meta):
|
|
112
|
+
fields = [*PasteManyOperationSerializer.Meta.fields, "undo"] # noqa: RUF012
|
|
113
|
+
|
|
114
|
+
@extend_schema_field(
|
|
115
|
+
undo_summary_schema(
|
|
116
|
+
"What the undo did to the rows this paste created.",
|
|
117
|
+
deleted="Rows removed — created by the paste and untouched since.",
|
|
118
|
+
kept="Rows left in place because someone has edited them since the paste.",
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
def get_undo(self, obj: PasteManyOperation) -> dict[str, int]:
|
|
122
|
+
"""The persisted summary, for a caller that serializes a row outside the action.
|
|
123
|
+
|
|
124
|
+
The action itself returns the summary the service just computed rather than
|
|
125
|
+
this — see its handler. They agree on every path that persists one.
|
|
126
|
+
"""
|
|
127
|
+
return dict((obj.result_summary or {}).get("undo") or {})
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class PasteManyOperationViewSet(
|
|
131
|
+
IdempotencyMixin, viewsets.ReadOnlyModelViewSet[PasteManyOperation]
|
|
132
|
+
):
|
|
133
|
+
serializer_class = PasteManyOperationSerializer
|
|
134
|
+
# `undo` hard-deletes task rows, so this route is a write path and carries the
|
|
135
|
+
# archived floor every sibling in the family carries (`StructuralOperationViewSet`,
|
|
136
|
+
# `CsvImportUndoView`, `TaskBulkView`, `TaskClassificationView`). Reads are
|
|
137
|
+
# unaffected — `IsProjectNotArchived` passes every SAFE_METHOD, so polling a
|
|
138
|
+
# ledger row on an archived project still works (#3354).
|
|
139
|
+
#
|
|
140
|
+
# It resolves the project from the *object*, not a URL kwarg: this viewset is
|
|
141
|
+
# router-registered at the top level, so `has_permission` finds no `project_pk`
|
|
142
|
+
# and returns True, and the real check runs in `has_object_permission` off the
|
|
143
|
+
# operation's `project_id` during `get_object()`. Role authority stays in
|
|
144
|
+
# `_require_admin` below — this class enforces lifecycle state only.
|
|
145
|
+
permission_classes = [IsAuthenticated, IsProjectNotArchived] # noqa: RUF012
|
|
146
|
+
|
|
147
|
+
def get_queryset(self) -> QuerySet[PasteManyOperation]:
|
|
148
|
+
qs = PasteManyOperation.objects.filter(project_id__in=_caller_project_ids(self.request))
|
|
149
|
+
project_id = self.request.query_params.get("project")
|
|
150
|
+
if project_id:
|
|
151
|
+
qs = qs.filter(project_id=project_id)
|
|
152
|
+
return qs
|
|
153
|
+
|
|
154
|
+
@extend_schema(
|
|
155
|
+
request=None,
|
|
156
|
+
responses={
|
|
157
|
+
200: PasteManyOperationUndoSerializer,
|
|
158
|
+
400: state_refusal_400(
|
|
159
|
+
"This batch has already been undone. Verified against the status "
|
|
160
|
+
"guard in ``undo`` (#3319)."
|
|
161
|
+
),
|
|
162
|
+
403: OpenApiResponse(description=ARCHIVED_403),
|
|
163
|
+
},
|
|
164
|
+
description=(
|
|
165
|
+
"Undo this paste-many batch — removes the rows it created that nobody has edited."
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
@action(detail=True, methods=["post"], url_path="undo")
|
|
169
|
+
def undo(self, request: Request, pk: str | None = None) -> Response:
|
|
170
|
+
operation = self.get_object()
|
|
171
|
+
_require_admin(request, operation.project_id)
|
|
172
|
+
if operation.status == SyncBatchOperationStatus.UNDONE:
|
|
173
|
+
raise ValidationError({"detail": "This batch has already been undone."})
|
|
174
|
+
summary = undo_paste_many_operation(operation)
|
|
175
|
+
operation.refresh_from_db()
|
|
176
|
+
data: dict[str, Any] = self.get_serializer(operation).data
|
|
177
|
+
data["undo"] = summary
|
|
178
|
+
return Response(data, status=status.HTTP_200_OK)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class CascadeClassificationOperationSerializer(
|
|
182
|
+
serializers.ModelSerializer[CascadeClassificationOperation]
|
|
183
|
+
):
|
|
184
|
+
class Meta:
|
|
185
|
+
model = CascadeClassificationOperation
|
|
186
|
+
fields = [ # noqa: RUF012
|
|
187
|
+
"id",
|
|
188
|
+
"project",
|
|
189
|
+
"applied_by",
|
|
190
|
+
"subtree_id",
|
|
191
|
+
"status",
|
|
192
|
+
"task_snapshots",
|
|
193
|
+
"result_summary",
|
|
194
|
+
"created_at",
|
|
195
|
+
"undone_at",
|
|
196
|
+
]
|
|
197
|
+
read_only_fields = fields
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class CascadeClassificationOperationUndoSerializer(CascadeClassificationOperationSerializer):
|
|
201
|
+
"""The ledger row **plus** the ``undo`` summary the action returns (#3416).
|
|
202
|
+
|
|
203
|
+
Same defect and same fix as :class:`PasteManyOperationUndoSerializer`; the counts
|
|
204
|
+
differ because a cascade undo writes prior values back rather than deleting rows.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
undo = serializers.SerializerMethodField()
|
|
208
|
+
|
|
209
|
+
class Meta(CascadeClassificationOperationSerializer.Meta):
|
|
210
|
+
fields = [*CascadeClassificationOperationSerializer.Meta.fields, "undo"] # noqa: RUF012
|
|
211
|
+
|
|
212
|
+
@extend_schema_field(
|
|
213
|
+
undo_summary_schema(
|
|
214
|
+
"What the undo did to the rows this cascade reclassified.",
|
|
215
|
+
reverted="Rows restored to their pre-cascade classification.",
|
|
216
|
+
kept="Rows left as the cascade set them because someone has edited them since.",
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
def get_undo(self, obj: CascadeClassificationOperation) -> dict[str, int]:
|
|
220
|
+
"""The persisted summary — see :meth:`PasteManyOperationUndoSerializer.get_undo`."""
|
|
221
|
+
return dict((obj.result_summary or {}).get("undo") or {})
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class CascadeClassificationOperationViewSet(
|
|
225
|
+
IdempotencyMixin, viewsets.ReadOnlyModelViewSet[CascadeClassificationOperation]
|
|
226
|
+
):
|
|
227
|
+
serializer_class = CascadeClassificationOperationSerializer
|
|
228
|
+
# Same reasoning as `PasteManyOperationViewSet` above — `undo` writes
|
|
229
|
+
# classification fields back onto existing rows, so it is a write path and takes
|
|
230
|
+
# the archived floor (#3354).
|
|
231
|
+
permission_classes = [IsAuthenticated, IsProjectNotArchived] # noqa: RUF012
|
|
232
|
+
|
|
233
|
+
def get_queryset(self) -> QuerySet[CascadeClassificationOperation]:
|
|
234
|
+
qs = CascadeClassificationOperation.objects.filter(
|
|
235
|
+
project_id__in=_caller_project_ids(self.request)
|
|
236
|
+
)
|
|
237
|
+
project_id = self.request.query_params.get("project")
|
|
238
|
+
if project_id:
|
|
239
|
+
qs = qs.filter(project_id=project_id)
|
|
240
|
+
return qs
|
|
241
|
+
|
|
242
|
+
@extend_schema(
|
|
243
|
+
request=None,
|
|
244
|
+
responses={
|
|
245
|
+
200: CascadeClassificationOperationUndoSerializer,
|
|
246
|
+
400: state_refusal_400(
|
|
247
|
+
"This cascade has already been undone. Verified against the status "
|
|
248
|
+
"guard in ``undo`` (#3319)."
|
|
249
|
+
),
|
|
250
|
+
403: OpenApiResponse(description=ARCHIVED_403),
|
|
251
|
+
},
|
|
252
|
+
description="Undo this cascade — restores each untouched row's prior classification.",
|
|
253
|
+
)
|
|
254
|
+
@action(detail=True, methods=["post"], url_path="undo")
|
|
255
|
+
def undo(self, request: Request, pk: str | None = None) -> Response:
|
|
256
|
+
operation = self.get_object()
|
|
257
|
+
_require_admin(request, operation.project_id)
|
|
258
|
+
if operation.status == SyncBatchOperationStatus.UNDONE:
|
|
259
|
+
raise ValidationError({"detail": "This cascade has already been undone."})
|
|
260
|
+
summary = undo_cascade_classification_operation(operation)
|
|
261
|
+
operation.refresh_from_db()
|
|
262
|
+
data: dict[str, Any] = self.get_serializer(operation).data
|
|
263
|
+
data["undo"] = summary
|
|
264
|
+
return Response(data, status=status.HTTP_200_OK)
|