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
trueppm_api/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""TruePPM Django REST API."""
|
|
2
|
+
|
|
3
|
+
# Loading the Celery app here is the standard Django+Celery integration:
|
|
4
|
+
# it forces ``app.set_default()`` at Django startup so ``shared_task.delay()``
|
|
5
|
+
# resolves to the configured trueppm_api app rather than an unconfigured
|
|
6
|
+
# default Celery instance with ``broker_url=None``. Without this,
|
|
7
|
+
# ``recalculate_schedule.delay()`` (and every other shared_task) raises
|
|
8
|
+
# ``OperationalError: Connection refused`` and the outbox row is silently
|
|
9
|
+
# left in PENDING — which is exactly what produced the cascade-not-firing
|
|
10
|
+
# regression in #314.
|
|
11
|
+
from .celery import app as celery_app
|
|
12
|
+
|
|
13
|
+
__all__ = ("celery_app",)
|
|
14
|
+
__version__ = "0.1.0"
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""App config for access (RBAC)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.apps import AppConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AccessConfig(AppConfig):
|
|
9
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
10
|
+
name = "trueppm_api.apps.access"
|
|
11
|
+
|
|
12
|
+
def ready(self) -> None:
|
|
13
|
+
# Imports for side effect. The access app is always installed, so it is the
|
|
14
|
+
# registration point for app-wide hooks that have no better home:
|
|
15
|
+
# - signals: ProjectMembership revocation evicts live WS sockets (#813).
|
|
16
|
+
# - security_checks: registers the SECRET_KEY system check (#566).
|
|
17
|
+
# - valkey_checks: registers the Valkey topology system check (#2554).
|
|
18
|
+
from trueppm_api.apps.access import signals # noqa: F401
|
|
19
|
+
from trueppm_api.core import security_checks, valkey_checks # noqa: F401
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"""Auto-group resolver for @mention fan-out (ADR-0075 §C).
|
|
2
|
+
|
|
3
|
+
Resolves auto-group keys to a list of user IDs at the moment of write. The
|
|
4
|
+
resolver is intentionally snapshot-based: new members joining after a
|
|
5
|
+
mention do NOT receive retroactive notifications, and departed members are
|
|
6
|
+
not re-pinged. This matches Slack / GitHub / Jira semantics.
|
|
7
|
+
|
|
8
|
+
Auto-groups (0.2):
|
|
9
|
+
@owners Project members with Owner role
|
|
10
|
+
@admins Project members with Admin OR Owner role
|
|
11
|
+
@schedulers Project members with Scheduler+ role
|
|
12
|
+
@members Project members with Member role
|
|
13
|
+
@viewers Project members with Viewer role
|
|
14
|
+
@all Every project member
|
|
15
|
+
@scrum-team Members assigned to tasks in the active sprint(s)
|
|
16
|
+
|
|
17
|
+
ADR-0075 locked constraints enforced by this module:
|
|
18
|
+
@all cardinality cap: 200 (raises GroupTooLargeError above)
|
|
19
|
+
@all role gate: ADMIN+ (enforced by the parser, not this module)
|
|
20
|
+
|
|
21
|
+
Program auto-groups (#514, ADR-0075 §C extension):
|
|
22
|
+
@program-pms Owner/Admin across every project in the mention's program
|
|
23
|
+
@program-schedulers Scheduler+ across the program
|
|
24
|
+
@program-stakeholders Viewer-role (exact) across the program
|
|
25
|
+
@program-all Every member of every project in the program
|
|
26
|
+
|
|
27
|
+
Program groups resolve against the program that contains the project the
|
|
28
|
+
mention was written in (``Project.program``). They draw from the UNION of
|
|
29
|
+
``ProjectMembership`` across the program's projects — the people actually
|
|
30
|
+
working the program's projects — not from program-level ``ProgramMembership``.
|
|
31
|
+
|
|
32
|
+
User-defined groups (#515, ADR-0212) are curated per project and resolved by
|
|
33
|
+
the separate ``resolve_user_defined_group_members`` path below — distinct from
|
|
34
|
+
the RBAC-derived auto-groups above.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import uuid
|
|
40
|
+
from typing import TYPE_CHECKING, Any, NamedTuple, cast
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from collections.abc import Iterable
|
|
44
|
+
|
|
45
|
+
from django.db.models import QuerySet
|
|
46
|
+
|
|
47
|
+
from trueppm_api.apps.access.models import ExternalStakeholder, ProjectMembership
|
|
48
|
+
|
|
49
|
+
# ADR-0075 locked constraint #1 — @all resolve cardinality cap.
|
|
50
|
+
ALL_GROUP_HARD_CAP: int = 200
|
|
51
|
+
|
|
52
|
+
# Project-scoped auto-group keys recognized since 0.2.
|
|
53
|
+
KNOWN_GROUP_KEYS: frozenset[str] = frozenset(
|
|
54
|
+
{
|
|
55
|
+
"owners",
|
|
56
|
+
"admins",
|
|
57
|
+
"schedulers",
|
|
58
|
+
"members",
|
|
59
|
+
"viewers",
|
|
60
|
+
"all",
|
|
61
|
+
"scrum-team",
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Program-scoped auto-group keys (#514). Resolved from the union of
|
|
66
|
+
# ``ProjectMembership`` across every project in the mention's program.
|
|
67
|
+
PROGRAM_GROUP_KEYS: frozenset[str] = frozenset(
|
|
68
|
+
{
|
|
69
|
+
"program-pms",
|
|
70
|
+
"program-schedulers",
|
|
71
|
+
"program-stakeholders",
|
|
72
|
+
"program-all",
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Every recognized auto-group key (project + program scope). The mention parser
|
|
77
|
+
# classifies a ``@name`` as a group when it is in this set, and the user-defined
|
|
78
|
+
# group validator reserves the whole set so a curated group can never shadow an
|
|
79
|
+
# auto-group.
|
|
80
|
+
ALL_AUTO_GROUP_KEYS: frozenset[str] = KNOWN_GROUP_KEYS | PROGRAM_GROUP_KEYS
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class InvalidGroupKeyError(ValueError):
|
|
84
|
+
"""Raised when a @mention references an unknown group key."""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class GroupTooLargeError(ValueError):
|
|
88
|
+
"""Raised when a @all resolution exceeds ALL_GROUP_HARD_CAP.
|
|
89
|
+
|
|
90
|
+
The mention parser surfaces this to the user as a structured 400 with
|
|
91
|
+
a friendly message ("@all would notify {n} people — limit is 200").
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(self, key: str, count: int, cap: int = ALL_GROUP_HARD_CAP) -> None:
|
|
95
|
+
super().__init__(
|
|
96
|
+
f"@{key} resolves to {count} users; cap is {cap}. "
|
|
97
|
+
"Use a smaller group or split the message."
|
|
98
|
+
)
|
|
99
|
+
self.key = key
|
|
100
|
+
self.count = count
|
|
101
|
+
self.cap = cap
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def resolve_group_members(
|
|
105
|
+
project_id: uuid.UUID | str,
|
|
106
|
+
group_key: str,
|
|
107
|
+
) -> list[uuid.UUID]:
|
|
108
|
+
"""Snapshot-resolve an auto-group key to a list of user UUIDs.
|
|
109
|
+
|
|
110
|
+
Inputs are validated; output is deduplicated.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
InvalidGroupKeyError: group_key is not in ALL_AUTO_GROUP_KEYS, or a
|
|
114
|
+
``@program-*`` key was used on a standalone project (no program).
|
|
115
|
+
GroupTooLargeError: @all / @program-all expands to more than
|
|
116
|
+
ALL_GROUP_HARD_CAP users
|
|
117
|
+
"""
|
|
118
|
+
# Local imports to avoid app-loading ordering surprises during AppConfig
|
|
119
|
+
# ready() — resolver is only ever called from request paths.
|
|
120
|
+
from trueppm_api.apps.access.models import ProjectMembership, Role
|
|
121
|
+
from trueppm_api.apps.projects.models import Sprint, SprintState, Task
|
|
122
|
+
|
|
123
|
+
key = group_key.strip().lower()
|
|
124
|
+
if key not in ALL_AUTO_GROUP_KEYS:
|
|
125
|
+
raise InvalidGroupKeyError(group_key)
|
|
126
|
+
|
|
127
|
+
# Program-scoped groups (#514) fan out across the mention's whole program;
|
|
128
|
+
# they resolve differently (join through Project.program) so branch early.
|
|
129
|
+
if key in PROGRAM_GROUP_KEYS:
|
|
130
|
+
return _resolve_program_group_members(project_id, key)
|
|
131
|
+
|
|
132
|
+
# Role-bounded groups. Each "bucket" returns members at or above the
|
|
133
|
+
# named role floor (band-boundary semantics per ADR-0072). E.g. @admins
|
|
134
|
+
# includes OWNER; @schedulers includes ADMIN and OWNER.
|
|
135
|
+
role_floors: dict[str, int] = {
|
|
136
|
+
"owners": Role.OWNER,
|
|
137
|
+
"admins": Role.ADMIN,
|
|
138
|
+
"schedulers": Role.SCHEDULER,
|
|
139
|
+
"members": Role.MEMBER,
|
|
140
|
+
"viewers": Role.VIEWER,
|
|
141
|
+
}
|
|
142
|
+
# django-stubs infers values_list("user_id", flat=True) as int even when
|
|
143
|
+
# the underlying field is UUID; cast at the boundary to satisfy mypy
|
|
144
|
+
# without polluting the call sites.
|
|
145
|
+
if key in role_floors:
|
|
146
|
+
rows = list(
|
|
147
|
+
ProjectMembership.objects.filter(
|
|
148
|
+
project_id=project_id,
|
|
149
|
+
role__gte=role_floors[key],
|
|
150
|
+
is_deleted=False,
|
|
151
|
+
)
|
|
152
|
+
.values_list("user_id", flat=True)
|
|
153
|
+
.distinct()
|
|
154
|
+
)
|
|
155
|
+
return cast(list[uuid.UUID], list(dict.fromkeys(rows)))
|
|
156
|
+
|
|
157
|
+
if key == "all":
|
|
158
|
+
rows = list(
|
|
159
|
+
ProjectMembership.objects.filter(project_id=project_id, is_deleted=False)
|
|
160
|
+
.values_list("user_id", flat=True)
|
|
161
|
+
.distinct()
|
|
162
|
+
)
|
|
163
|
+
result = cast(list[uuid.UUID], list(dict.fromkeys(rows)))
|
|
164
|
+
if len(result) > ALL_GROUP_HARD_CAP:
|
|
165
|
+
raise GroupTooLargeError(key, len(result))
|
|
166
|
+
return result
|
|
167
|
+
|
|
168
|
+
if key == "scrum-team":
|
|
169
|
+
active_sprint_ids = list(
|
|
170
|
+
Sprint.objects.filter(
|
|
171
|
+
project_id=project_id,
|
|
172
|
+
state=SprintState.ACTIVE,
|
|
173
|
+
is_deleted=False,
|
|
174
|
+
).values_list("id", flat=True)
|
|
175
|
+
)
|
|
176
|
+
scrum_rows = list(
|
|
177
|
+
Task.objects.filter(
|
|
178
|
+
project_id=project_id,
|
|
179
|
+
is_deleted=False,
|
|
180
|
+
assignee__isnull=False,
|
|
181
|
+
sprint_id__in=active_sprint_ids,
|
|
182
|
+
)
|
|
183
|
+
.values_list("assignee_id", flat=True)
|
|
184
|
+
.distinct()
|
|
185
|
+
)
|
|
186
|
+
return cast(list[uuid.UUID], list(dict.fromkeys(scrum_rows)))
|
|
187
|
+
|
|
188
|
+
# Unreachable — ALL_AUTO_GROUP_KEYS membership was checked above and program
|
|
189
|
+
# keys were dispatched earlier.
|
|
190
|
+
raise InvalidGroupKeyError(group_key) # pragma: no cover
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _program_membership_base_qs(
|
|
194
|
+
program_id: uuid.UUID | str,
|
|
195
|
+
) -> QuerySet[ProjectMembership]:
|
|
196
|
+
"""Every live ``ProjectMembership`` across a program's live projects.
|
|
197
|
+
|
|
198
|
+
The one base every ``@program-*`` key narrows, and the one the reach counter
|
|
199
|
+
narrows too. It exists so a recipient-narrowing filter added here — excluding
|
|
200
|
+
deactivated users, a privacy suppression gate — cannot be inherited by three of
|
|
201
|
+
the four group keys and silently skipped by the fourth. Add such filters
|
|
202
|
+
**here**, never in a per-key branch.
|
|
203
|
+
|
|
204
|
+
**Draft projects are deliberately kept** (#3144; the reasoning is recorded in
|
|
205
|
+
``projects.lifecycle``, not repeated here). A member whose only membership in
|
|
206
|
+
the program is on a project still in ``ProjectLifecycle.DRAFT`` is still
|
|
207
|
+
reached. The decisive reason is the paragraph above: a draft exclusion belongs
|
|
208
|
+
*here* by that rule, and here it would also cut the draft's own Admin out of
|
|
209
|
+
``@program-pms`` and its Scheduler out of ``@program-schedulers`` — the people
|
|
210
|
+
authoring the plan, dropped from program-wide coordination. The absence of
|
|
211
|
+
:func:`~trueppm_api.apps.projects.lifecycle.exclude_draft_projects` in this
|
|
212
|
+
function is a decision, not an oversight.
|
|
213
|
+
"""
|
|
214
|
+
from trueppm_api.apps.access.models import ProjectMembership
|
|
215
|
+
|
|
216
|
+
return ProjectMembership.objects.filter(
|
|
217
|
+
project__program_id=program_id,
|
|
218
|
+
project__is_deleted=False,
|
|
219
|
+
is_deleted=False,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _program_stakeholder_membership_qs(
|
|
224
|
+
program_id: uuid.UUID | str,
|
|
225
|
+
) -> QuerySet[ProjectMembership]:
|
|
226
|
+
"""The ``ProjectMembership`` rows the ``@program-stakeholders`` alias draws on.
|
|
227
|
+
|
|
228
|
+
Narrows :func:`_program_membership_base_qs` to an **exact** ``Role.VIEWER`` —
|
|
229
|
+
not the ``role__gte`` floor the project-level ``@viewers`` uses, which would
|
|
230
|
+
resolve to everyone (Viewer is the lowest band) and duplicate ``@program-all``.
|
|
231
|
+
|
|
232
|
+
Extracted so the resolver and the reach counter (ADR-0697) cannot drift: a
|
|
233
|
+
change to the role band or the soft-delete rules moves both at once.
|
|
234
|
+
|
|
235
|
+
Note the rows are per-(user, project): a person who is a Viewer on two projects
|
|
236
|
+
in the program has two rows but is **one** recipient. Callers must dedupe on
|
|
237
|
+
``user_id`` — the resolver does, and so does the counter.
|
|
238
|
+
"""
|
|
239
|
+
from trueppm_api.apps.access.models import Role
|
|
240
|
+
|
|
241
|
+
return _program_membership_base_qs(program_id).filter(role=Role.VIEWER)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _program_external_stakeholder_qs(
|
|
245
|
+
program_id: uuid.UUID | str,
|
|
246
|
+
) -> QuerySet[ExternalStakeholder]:
|
|
247
|
+
"""The ``ExternalStakeholder`` rows registered against a program.
|
|
248
|
+
|
|
249
|
+
The non-account arm of the ``@program-stakeholders`` fan-out. Extracted
|
|
250
|
+
alongside :func:`_program_stakeholder_membership_qs` so the resolver and the
|
|
251
|
+
reach counter share one definition (ADR-0697).
|
|
252
|
+
"""
|
|
253
|
+
from trueppm_api.apps.access.models import ExternalStakeholder
|
|
254
|
+
|
|
255
|
+
return ExternalStakeholder.objects.filter(program_id=program_id, is_deleted=False)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class ProgramStakeholderReach(NamedTuple):
|
|
259
|
+
"""The two arms of ``@program-stakeholders``, counted separately (ADR-0697).
|
|
260
|
+
|
|
261
|
+
Deliberately **not** summable into a headline total: the two arms have
|
|
262
|
+
materially different fates today. ``viewer_member_count`` people receive a
|
|
263
|
+
durable in-app notification; ``external_stakeholder_count`` people receive
|
|
264
|
+
nothing at all — they have no ``User`` account and outbound delivery is
|
|
265
|
+
deferred to #1675. A combined number would re-introduce in the UI the same
|
|
266
|
+
union the resolver was changed to stop making.
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
viewer_member_count: int
|
|
270
|
+
external_stakeholder_count: int
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def count_program_stakeholder_reach(
|
|
274
|
+
program_id: uuid.UUID | str,
|
|
275
|
+
exclude_project_ids: Iterable[uuid.UUID] | QuerySet[Any] | None = None,
|
|
276
|
+
) -> ProgramStakeholderReach:
|
|
277
|
+
"""Count both arms of ``@program-stakeholders`` for a program (ADR-0697).
|
|
278
|
+
|
|
279
|
+
Backs ``GET /api/v1/programs/{id}/mention-reach/``. Shares its querysets with
|
|
280
|
+
:func:`_resolve_program_group_members` and :func:`resolve_external_stakeholders`
|
|
281
|
+
so the number a PM reads on the settings page is the number the mention will
|
|
282
|
+
actually fan out to.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
program_id: The program whose alias is being counted.
|
|
286
|
+
exclude_project_ids: Projects to drop from the internal arm — the MCP
|
|
287
|
+
team opt-out intersection (ADR-0678 T1). ``None`` for a human caller,
|
|
288
|
+
which is the common case and leaves the query plan untouched. The
|
|
289
|
+
external arm is program-owned with no project FK, so it is never
|
|
290
|
+
narrowed by a project-level opt-out.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
A :class:`ProgramStakeholderReach` with the two arms counted separately.
|
|
294
|
+
"""
|
|
295
|
+
memberships = _program_stakeholder_membership_qs(program_id)
|
|
296
|
+
if exclude_project_ids is not None:
|
|
297
|
+
memberships = memberships.exclude(project_id__in=exclude_project_ids)
|
|
298
|
+
# Distinct on ``user_id``, never ``.count()`` on the membership rows: a Viewer
|
|
299
|
+
# on two projects in the program is one recipient, and the resolver this
|
|
300
|
+
# mirrors returns distinct users.
|
|
301
|
+
return ProgramStakeholderReach(
|
|
302
|
+
viewer_member_count=memberships.values("user_id").distinct().count(),
|
|
303
|
+
external_stakeholder_count=_program_external_stakeholder_qs(program_id).count(),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _resolve_program_group_members(
|
|
308
|
+
project_id: uuid.UUID | str,
|
|
309
|
+
key: str,
|
|
310
|
+
) -> list[uuid.UUID]:
|
|
311
|
+
"""Snapshot-resolve a ``@program-*`` key to member UUIDs across the program.
|
|
312
|
+
|
|
313
|
+
The mention was written on a task in ``project_id``; the group resolves to
|
|
314
|
+
the program that contains that project (``Project.program``). Membership is
|
|
315
|
+
the *union* of ``ProjectMembership`` across every live project in the
|
|
316
|
+
program, filtered by the role band the key names, deduplicated across
|
|
317
|
+
projects.
|
|
318
|
+
|
|
319
|
+
Raises:
|
|
320
|
+
InvalidGroupKeyError: the project is standalone (``program`` is NULL),
|
|
321
|
+
so there is no program to resolve against. The autocomplete never
|
|
322
|
+
offers ``@program-*`` for a standalone project; this only fires on a
|
|
323
|
+
hand-typed key, and surfaces to the caller as a skipped group.
|
|
324
|
+
GroupTooLargeError: ``@program-all`` exceeds ``ALL_GROUP_HARD_CAP`` — a
|
|
325
|
+
program-wide fan-out is exactly the blast radius ADR-0075 caps.
|
|
326
|
+
"""
|
|
327
|
+
from trueppm_api.apps.access.models import Role
|
|
328
|
+
from trueppm_api.apps.projects.models import Project
|
|
329
|
+
|
|
330
|
+
# The origin project's own lifecycle is deliberately not consulted (#3144): a
|
|
331
|
+
# mention written on a task in a DRAFT project still fans out program-wide,
|
|
332
|
+
# because a PM typing @program-stakeholders into a draft is asking for the
|
|
333
|
+
# review a draft exists to get. The notification sites the draft exclusion list
|
|
334
|
+
# does cover are unattended sweeps, not something a human just typed.
|
|
335
|
+
program_id = Project.objects.filter(pk=project_id).values_list("program_id", flat=True).first()
|
|
336
|
+
if program_id is None:
|
|
337
|
+
raise InvalidGroupKeyError(f"{key} (project has no program)")
|
|
338
|
+
|
|
339
|
+
# Join through the sibling projects in one query rather than materializing
|
|
340
|
+
# their ids first — bounded by (# projects in program × members), no N+1.
|
|
341
|
+
# Exclude soft-deleted sibling projects and soft-deleted memberships.
|
|
342
|
+
memberships = _program_membership_base_qs(program_id)
|
|
343
|
+
if key == "program-pms":
|
|
344
|
+
memberships = memberships.filter(role__gte=Role.ADMIN)
|
|
345
|
+
elif key == "program-schedulers":
|
|
346
|
+
memberships = memberships.filter(role__gte=Role.SCHEDULER)
|
|
347
|
+
elif key == "program-stakeholders":
|
|
348
|
+
# Stakeholders are the view-only audience — an EXACT Viewer role, not the
|
|
349
|
+
# role>=VIEWER floor project-level @viewers uses (which, since Viewer is
|
|
350
|
+
# the lowest band, would resolve to everyone and duplicate @program-all).
|
|
351
|
+
# The AC's "+ external stakeholder list" is resolved separately and
|
|
352
|
+
# additively by ``resolve_external_stakeholders`` (#1658, ADR-0264) — those
|
|
353
|
+
# rows have no User account, so they are never unioned into this User-keyed
|
|
354
|
+
# result; the caller threads them onto a distinct ``external_targets`` field.
|
|
355
|
+
# Narrows the SAME base the other keys narrow — the extracted helper is a
|
|
356
|
+
# composition of it, not a replacement — so a future base-level recipient
|
|
357
|
+
# filter cannot be inherited by three keys and skipped by this one, and the
|
|
358
|
+
# reach counter behind ``/mention-reach/`` stays in lockstep (ADR-0697).
|
|
359
|
+
memberships = memberships.filter(role=Role.VIEWER)
|
|
360
|
+
# program-all: no role filter — every member of every project in the program.
|
|
361
|
+
|
|
362
|
+
rows = list(memberships.values_list("user_id", flat=True).distinct())
|
|
363
|
+
result = cast(list[uuid.UUID], list(dict.fromkeys(rows)))
|
|
364
|
+
if key == "program-all" and len(result) > ALL_GROUP_HARD_CAP:
|
|
365
|
+
raise GroupTooLargeError(key, len(result))
|
|
366
|
+
return result
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def resolve_external_stakeholders(
|
|
370
|
+
project_id: uuid.UUID | str,
|
|
371
|
+
) -> list[ExternalStakeholder]:
|
|
372
|
+
"""Snapshot-resolve the external stakeholders reachable from a project's program.
|
|
373
|
+
|
|
374
|
+
The non-account arm of the ``@program-stakeholders`` fan-out (#1658, ADR-0264).
|
|
375
|
+
The mention was written on a task in ``project_id``; this resolves the program
|
|
376
|
+
that contains that project (``Project.program``) and returns that program's
|
|
377
|
+
live :class:`~trueppm_api.apps.access.models.ExternalStakeholder` rows.
|
|
378
|
+
|
|
379
|
+
These rows have **no** ``User`` account, so they are deliberately *additive and
|
|
380
|
+
separate* from :func:`resolve_group_members` — never unioned into the
|
|
381
|
+
User-keyed group result. The caller threads them onto a distinct
|
|
382
|
+
``external_targets`` field. Snapshot semantics match the auto-group resolvers:
|
|
383
|
+
the list is the registry *at the moment of the mention*.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
project_id: The project the mention was written in.
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
The program's non-deleted external stakeholders, ordered by name. Empty for
|
|
390
|
+
a standalone project (no program) — there is no program registry to draw on.
|
|
391
|
+
"""
|
|
392
|
+
from trueppm_api.apps.projects.models import Project
|
|
393
|
+
|
|
394
|
+
program_id = Project.objects.filter(pk=project_id).values_list("program_id", flat=True).first()
|
|
395
|
+
if program_id is None:
|
|
396
|
+
return []
|
|
397
|
+
# Shares its queryset with the reach counter (ADR-0697) so the registry the PM
|
|
398
|
+
# is shown a count of is exactly the registry a mention resolves against.
|
|
399
|
+
return list(_program_external_stakeholder_qs(program_id).order_by("name", "email"))
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def resolve_user_defined_group_members(
|
|
403
|
+
project_id: uuid.UUID | str,
|
|
404
|
+
name: str,
|
|
405
|
+
) -> list[uuid.UUID] | None:
|
|
406
|
+
"""Snapshot-resolve a user-defined group name to its member user UUIDs.
|
|
407
|
+
|
|
408
|
+
The "separate resolver path" this module's header defers to #515 (ADR-0212).
|
|
409
|
+
Distinct from :func:`resolve_group_members`, which resolves the RBAC-derived
|
|
410
|
+
auto-groups: this looks up an admin-curated
|
|
411
|
+
:class:`~trueppm_api.apps.access.models.UserDefinedMentionGroup` by its
|
|
412
|
+
case-insensitive name within the project and returns its current members,
|
|
413
|
+
filtered to those who are still active project members.
|
|
414
|
+
|
|
415
|
+
Snapshot semantics match the auto-group resolver: the returned list is the
|
|
416
|
+
membership *at the moment of the mention*; later membership changes do not
|
|
417
|
+
retroactively notify.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
project_id: The project the mention was written in.
|
|
421
|
+
name: The group name without the leading ``@`` (case-insensitive).
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
A deduplicated list of member user UUIDs, or ``None`` if no live group
|
|
425
|
+
with that name exists in the project (so the caller can fall through to
|
|
426
|
+
treating the token as an unresolved user mention).
|
|
427
|
+
"""
|
|
428
|
+
from django.db.models.functions import Lower
|
|
429
|
+
|
|
430
|
+
from trueppm_api.apps.access.models import (
|
|
431
|
+
ProjectMembership,
|
|
432
|
+
UserDefinedMentionGroup,
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
key = name.strip().lstrip("@").lower()
|
|
436
|
+
# Filter on Lower(name) rather than name__iexact so the case-insensitive
|
|
437
|
+
# unique index (uniq_mention_group_project_name_ci, on Lower(name)) is usable.
|
|
438
|
+
group = (
|
|
439
|
+
UserDefinedMentionGroup.objects.annotate(name_lower=Lower("name"))
|
|
440
|
+
.filter(
|
|
441
|
+
project_id=project_id,
|
|
442
|
+
name_lower=key,
|
|
443
|
+
is_deleted=False,
|
|
444
|
+
)
|
|
445
|
+
.prefetch_related("members")
|
|
446
|
+
.first()
|
|
447
|
+
)
|
|
448
|
+
if group is None:
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
# Only notify members who still have an active project membership — a member
|
|
452
|
+
# who left the project should not receive the ping even if the M2M row lingers.
|
|
453
|
+
active_member_ids = set(
|
|
454
|
+
ProjectMembership.objects.filter(
|
|
455
|
+
project_id=project_id,
|
|
456
|
+
is_deleted=False,
|
|
457
|
+
).values_list("user_id", flat=True)
|
|
458
|
+
)
|
|
459
|
+
member_ids = [member.pk for member in group.members.all() if member.pk in active_member_ids]
|
|
460
|
+
return cast(list[uuid.UUID], list(dict.fromkeys(member_ids)))
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def resolve_program_user_defined_group_members(
|
|
464
|
+
program_id: uuid.UUID | str,
|
|
465
|
+
name: str,
|
|
466
|
+
) -> list[uuid.UUID] | None:
|
|
467
|
+
"""Snapshot-resolve a program-scoped user-defined group name to member UUIDs.
|
|
468
|
+
|
|
469
|
+
The program sibling of :func:`resolve_user_defined_group_members` (ADR-0248,
|
|
470
|
+
#516). Looks up an owner-curated
|
|
471
|
+
:class:`~trueppm_api.apps.access.models.ProgramUserDefinedMentionGroup` by its
|
|
472
|
+
case-insensitive name within the program and returns its current members,
|
|
473
|
+
filtered to those who still hold a live ``ProjectMembership`` on *some* project
|
|
474
|
+
in the program (the ADR-0248 §2 union — a member who left every project in the
|
|
475
|
+
program no longer receives the ping even if the M2M row lingers).
|
|
476
|
+
|
|
477
|
+
Snapshot semantics match the auto-group and project-group resolvers.
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
program_id: The program that contains the project the mention was written
|
|
481
|
+
in (``Project.program``).
|
|
482
|
+
name: The group name without the leading ``@`` (case-insensitive).
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
A deduplicated list of member user UUIDs, or ``None`` if no live group with
|
|
486
|
+
that name exists in the program (so the caller can fall through to treating
|
|
487
|
+
the token as an unresolved user mention).
|
|
488
|
+
"""
|
|
489
|
+
from django.db.models.functions import Lower
|
|
490
|
+
|
|
491
|
+
from trueppm_api.apps.access.models import (
|
|
492
|
+
ProgramUserDefinedMentionGroup,
|
|
493
|
+
ProjectMembership,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
key = name.strip().lstrip("@").lower()
|
|
497
|
+
group = (
|
|
498
|
+
ProgramUserDefinedMentionGroup.objects.annotate(name_lower=Lower("name"))
|
|
499
|
+
.filter(
|
|
500
|
+
program_id=program_id,
|
|
501
|
+
name_lower=key,
|
|
502
|
+
is_deleted=False,
|
|
503
|
+
)
|
|
504
|
+
.prefetch_related("members")
|
|
505
|
+
.first()
|
|
506
|
+
)
|
|
507
|
+
if group is None:
|
|
508
|
+
return None
|
|
509
|
+
|
|
510
|
+
active_member_ids = set(
|
|
511
|
+
ProjectMembership.objects.filter(
|
|
512
|
+
project__program_id=program_id,
|
|
513
|
+
project__is_deleted=False,
|
|
514
|
+
is_deleted=False,
|
|
515
|
+
).values_list("user_id", flat=True)
|
|
516
|
+
)
|
|
517
|
+
member_ids = [member.pk for member in group.members.all() if member.pk in active_member_ids]
|
|
518
|
+
return cast(list[uuid.UUID], list(dict.fromkeys(member_ids)))
|
|
File without changes
|
|
File without changes
|