trueppm-api 0.4.0__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 +27 -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 +346 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +483 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +184 -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_squashed_0019_release_0_4.py +302 -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 +143 -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/minter_role.py +38 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +3263 -0
- trueppm_api/apps/access/serializers.py +1007 -0
- trueppm_api/apps/access/services.py +878 -0
- trueppm_api/apps/access/signals.py +171 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +96 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1965 -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/0001_squashed_0003_release_0_4.py +344 -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 +109 -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/0001_squashed_0003_release_0_4.py +90 -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 +459 -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 +178 -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 +863 -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 +597 -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_squashed_0014_release_0_4.py +225 -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/0014_tasklink_created_by.py +27 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +635 -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 +528 -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 +734 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1158 -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 +280 -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 +551 -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 +1276 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +439 -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 +462 -0
- trueppm_api/apps/notifications/email_backend.py +350 -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 +299 -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_squashed_0011_release_0_4.py +204 -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 +1148 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +1094 -0
- trueppm_api/apps/notifications/services.py +1489 -0
- trueppm_api/apps/notifications/tasks.py +762 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +919 -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 +566 -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 +993 -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 +537 -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_squashed_0009_release_0_4.py +128 -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 +541 -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 +458 -0
- trueppm_api/apps/projects/backfill.py +459 -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 +212 -0
- trueppm_api/apps/projects/config_notice.py +1330 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +132 -0
- trueppm_api/apps/projects/decisions_views.py +97 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +423 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4278 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2001 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1827 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1909 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1410 -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/key_views.py +468 -0
- trueppm_api/apps/projects/keys.py +205 -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 +514 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +406 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +192 -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/migration_helpers.py +75 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5659 -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 +59 -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 +75 -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 +76 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0106_squashed_0158_release_0_4.py +2357 -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/0154_object_keys.py +184 -0
- trueppm_api/apps/projects/migrations/0155_task_milestone_at_day_end.py +20 -0
- trueppm_api/apps/projects/migrations/0156_program_import_job_replaced_project_ids.py +17 -0
- trueppm_api/apps/projects/migrations/0157_forecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/0158_baselinetask_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +9209 -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 +903 -0
- trueppm_api/apps/projects/program_schedule.py +767 -0
- trueppm_api/apps/projects/program_views.py +3445 -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 +492 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +891 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/demo_landing_overlay.py +532 -0
- trueppm_api/apps/projects/seed/exporter.py +1717 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2764 -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 +128 -0
- trueppm_api/apps/projects/seed/replay.py +1695 -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 +1169 -0
- trueppm_api/apps/projects/serializers.py +12904 -0
- trueppm_api/apps/projects/services.py +7525 -0
- trueppm_api/apps/projects/share_serializers.py +142 -0
- trueppm_api/apps/projects/share_services.py +373 -0
- trueppm_api/apps/projects/share_views.py +395 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +910 -0
- trueppm_api/apps/projects/signal_privacy_views.py +485 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +288 -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 +1244 -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 +1979 -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 +261 -0
- trueppm_api/apps/projects/template_views.py +829 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1016 -0
- trueppm_api/apps/projects/utilization.py +909 -0
- trueppm_api/apps/projects/views.py +21225 -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/0014_squashed_0015_release_0_4.py +47 -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 +1752 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +144 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/finish_reading.py +198 -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/mc_admission.py +181 -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/0010_squashed_0016_release_0_4.py +106 -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/0015_projectforecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/scheduling/migrations/0016_montecarlorun_finish_readings.py +32 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +542 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +334 -0
- trueppm_api/apps/scheduling/serializers.py +723 -0
- trueppm_api/apps/scheduling/services.py +1251 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +2248 -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 +2908 -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 +446 -0
- trueppm_api/apps/sso/services.py +1473 -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 +369 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +548 -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/0003_squashed_0005_release_0_4.py +77 -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/0006_boardevent_per_project_seq.py +77 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +272 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +488 -0
- trueppm_api/apps/sync/serializers.py +688 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +556 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +957 -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 +159 -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/0004_backfill_default_teams_on_upgrade.py +49 -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 +440 -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/0001_squashed_0004_release_0_4.py +167 -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 +211 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +597 -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 +279 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +249 -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 +100 -0
- trueppm_api/apps/webhooks/migrations/0010_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/0011_webhookeditor.py +48 -0
- trueppm_api/apps/webhooks/migrations/0012_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +437 -0
- trueppm_api/apps/webhooks/serializers.py +326 -0
- trueppm_api/apps/webhooks/tasks.py +680 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +533 -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 +371 -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 +254 -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_squashed_0027_release_0_4.py +199 -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 +967 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +616 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1581 -0
- trueppm_api/asgi.py +49 -0
- trueppm_api/celery.py +40 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +934 -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/demo_read_only.py +314 -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 +1221 -0
- trueppm_api/core/password_reset.py +513 -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 +1034 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +514 -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/core/worker_selfheal.py +205 -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 +2605 -0
- trueppm_api/settings/dev.py +296 -0
- trueppm_api/settings/prod.py +265 -0
- trueppm_api/urls.py +217 -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.0.dist-info/METADATA +273 -0
- trueppm_api-0.4.0.dist-info/RECORD +799 -0
- trueppm_api-0.4.0.dist-info/WHEEL +4 -0
- trueppm_api-0.4.0.dist-info/licenses/LICENSE +201 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICE +7 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICES +104 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/GPL-3.0.txt +674 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/LGPL-3.0.txt +165 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/MPL-2.0.txt +373 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
"""Service layer for the teams app (ADR-0078 §Durable Execution §4).
|
|
2
|
+
|
|
3
|
+
Holds the default-team resolution, the auto-membership invariant, and the facet
|
|
4
|
+
resolution helpers that downstream gates (ADR-0104 signal privacy, ADR-0102 scope
|
|
5
|
+
injection, ADR-0073 capacity) call instead of re-implementing the lookup.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
from django.contrib.auth.models import AbstractBaseUser, AnonymousUser
|
|
13
|
+
from django.db.models import Exists, OuterRef, Q
|
|
14
|
+
|
|
15
|
+
from trueppm_api.apps.access.models import ProjectMembership, Role
|
|
16
|
+
from trueppm_api.apps.teams.models import Team, TeamMembership, TeamRole
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Sequence
|
|
20
|
+
|
|
21
|
+
from trueppm_api.apps.projects.models import Project
|
|
22
|
+
|
|
23
|
+
# The two facet flags, exposed as a tuple so gates and serializers share one
|
|
24
|
+
# source of truth for "which booleans are facets" rather than hard-coding strings.
|
|
25
|
+
FACET_FIELDS = ("is_scrum_master", "is_product_owner")
|
|
26
|
+
|
|
27
|
+
# The all-False answer, returned for anonymous users, non-members, and — since
|
|
28
|
+
# #3386 — revoked project members. A module constant rather than a literal at each
|
|
29
|
+
# early return so the three "no facets" paths cannot drift apart.
|
|
30
|
+
_NO_FACETS = {"is_scrum_master": False, "is_product_owner": False}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def live_project_membership_exists() -> Exists:
|
|
34
|
+
"""``Exists`` subquery: the ``TeamMembership`` row's user still has live project access.
|
|
35
|
+
|
|
36
|
+
Correlated on ``team__project_id`` rather than on a caller-supplied project id so
|
|
37
|
+
the predicate stays correct if an outer queryset is ever widened to
|
|
38
|
+
``team__project_id__in=(...)`` for a batched fan-out — the subquery then still
|
|
39
|
+
pairs each team row with its *own* project's membership. Composed into an existing
|
|
40
|
+
queryset so the liveness floor costs no extra round trip.
|
|
41
|
+
|
|
42
|
+
The soft-delete predicate itself comes from :meth:`ProjectMembership.live` rather
|
|
43
|
+
than being restated here (#3411), so this ``Exists`` and the direct membership reads
|
|
44
|
+
on the read paths cannot drift apart about what "live" means.
|
|
45
|
+
|
|
46
|
+
Public (no leading underscore) since #3511: the same predicate floors the team
|
|
47
|
+
roster list and the team member-count annotation in
|
|
48
|
+
:mod:`trueppm_api.apps.teams.views`, not just the service-layer seams below —
|
|
49
|
+
a revoked ``ProjectMembership`` leaves the mirrored ``TeamMembership`` row
|
|
50
|
+
live for the same reason in every case (no ``post_delete`` receiver, no FK a
|
|
51
|
+
cascade could travel), so the roster read must not restate the predicate.
|
|
52
|
+
"""
|
|
53
|
+
return Exists(
|
|
54
|
+
ProjectMembership.live().filter(
|
|
55
|
+
project_id=OuterRef("team__project_id"),
|
|
56
|
+
user_id=OuterRef("user_id"),
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def project_role_to_team_role(project_role: int) -> str:
|
|
62
|
+
"""Map a project ``Role`` ordinal to the coarse team role.
|
|
63
|
+
|
|
64
|
+
Project Admin (300) and Owner (400) become team Admin per ADR-0078 §C step 3;
|
|
65
|
+
everyone else is a team Member. Facets are never inferred — they default False
|
|
66
|
+
and require explicit assignment.
|
|
67
|
+
"""
|
|
68
|
+
return TeamRole.ADMIN if project_role >= Role.ADMIN else TeamRole.MEMBER
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def resolve_default_team(project_id: Any) -> Team | None:
|
|
72
|
+
"""Return the project's default team, or None if it has not been created yet.
|
|
73
|
+
|
|
74
|
+
The invariant (one default team per project, created at migration and by the
|
|
75
|
+
auto-membership signal) means this is non-None for any real project, but
|
|
76
|
+
callers tolerate None so a brand-new project mid-creation never 500s.
|
|
77
|
+
"""
|
|
78
|
+
return Team.objects.filter(project_id=project_id, is_default=True, is_deleted=False).first()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _get_or_create_default_team(project_id: Any, created_by: Any = None) -> Team:
|
|
82
|
+
"""Get-or-create the default team for a project id (idempotent, ADR-0078 §DE §7).
|
|
83
|
+
|
|
84
|
+
Keyed on (project, is_default) so a concurrent create collapses to one row via
|
|
85
|
+
the ``team_one_default_per_project`` constraint. Historical/migration rows and
|
|
86
|
+
this path both stamp server_version=1 so the row is never left at 0.
|
|
87
|
+
"""
|
|
88
|
+
team, _created = Team.objects.get_or_create(
|
|
89
|
+
project_id=project_id,
|
|
90
|
+
is_default=True,
|
|
91
|
+
is_deleted=False,
|
|
92
|
+
defaults={
|
|
93
|
+
"name": "Default Team",
|
|
94
|
+
"short_id": "T01",
|
|
95
|
+
"created_by": created_by,
|
|
96
|
+
"server_version": 1,
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
return team
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def ensure_default_team(project: Project, created_by: Any = None) -> Team:
|
|
103
|
+
"""Get-or-create the project's default team (idempotent, ADR-0078 §DE §7)."""
|
|
104
|
+
return _get_or_create_default_team(project.pk, created_by=created_by)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def ensure_team_membership(*, project_id: Any, user_id: Any, project_role: int) -> None:
|
|
108
|
+
"""Mirror a project membership onto the project's default team (ADR-0078 §F).
|
|
109
|
+
|
|
110
|
+
Permanent invariant, not a one-time migration: a new project member must
|
|
111
|
+
appear on the default team automatically so the facet matrix is complete and
|
|
112
|
+
nobody faces a second "join the team" step. The default team is created on
|
|
113
|
+
demand (it exists for projects that predate this app via the data migration,
|
|
114
|
+
and is materialized here for projects created afterward). The access role is
|
|
115
|
+
mapped to the team role on every call so a project-role change keeps the team
|
|
116
|
+
role in step, but the facets are left untouched — they are explicit,
|
|
117
|
+
user-assigned markers that a role change must never silently flip.
|
|
118
|
+
"""
|
|
119
|
+
team = _get_or_create_default_team(project_id)
|
|
120
|
+
|
|
121
|
+
team_role = project_role_to_team_role(project_role)
|
|
122
|
+
membership, created = TeamMembership.objects.get_or_create(
|
|
123
|
+
team=team,
|
|
124
|
+
user_id=user_id,
|
|
125
|
+
is_deleted=False,
|
|
126
|
+
defaults={"role": team_role},
|
|
127
|
+
)
|
|
128
|
+
if not created and membership.role != team_role:
|
|
129
|
+
membership.role = team_role
|
|
130
|
+
membership.save(update_fields=["role"])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def user_facets(
|
|
134
|
+
user: AbstractBaseUser | AnonymousUser,
|
|
135
|
+
project_id: Any,
|
|
136
|
+
*,
|
|
137
|
+
live_project_members_only: bool = True,
|
|
138
|
+
) -> dict[str, bool]:
|
|
139
|
+
"""Resolve a user's facet flags on a project's default team.
|
|
140
|
+
|
|
141
|
+
Returns ``{"is_scrum_master": bool, "is_product_owner": bool}`` — both False
|
|
142
|
+
for an anonymous user, a non-member, a project whose default team has no
|
|
143
|
+
membership row for them, or (since #3386) a user whose ``ProjectMembership``
|
|
144
|
+
has been revoked. This is the single seam gates read.
|
|
145
|
+
|
|
146
|
+
**The live-membership floor defaults to on, and the default is the whole point
|
|
147
|
+
(#3386).** The ADR-0078 §F mirror (:func:`ensure_team_membership`) only ever
|
|
148
|
+
*creates* team rows — :mod:`trueppm_api.apps.teams.signals` has no ``post_delete``
|
|
149
|
+
receiver, and ``TeamMembership`` has no FK to ``ProjectMembership`` that a cascade
|
|
150
|
+
could travel over — so soft-deleting a ``ProjectMembership`` leaves the mirrored
|
|
151
|
+
``TeamMembership`` live with its facet flags intact. Every gate that reads this
|
|
152
|
+
helper short-circuits to the facet precisely when the role lookup returns ``None``,
|
|
153
|
+
which is exactly what a revoked membership produces: without the floor, revoking
|
|
154
|
+
someone's project access *promotes* their residual facet from a tiebreak into their
|
|
155
|
+
only credential, and five write gates then authorize them
|
|
156
|
+
(:func:`~trueppm_api.apps.access.permissions.can_manage_backlog_with_facet`,
|
|
157
|
+
:func:`~trueppm_api.apps.access.permissions.can_manage_scope_with_facet`,
|
|
158
|
+
:func:`~trueppm_api.apps.projects.services.assert_scope_gate_for_project`,
|
|
159
|
+
``TaskSerializer._enforce_backlog_structural_gate``, and
|
|
160
|
+
``signal_privacy_views._is_facilitator_or_admin``). Those are backstopped today by
|
|
161
|
+
member-scoped querysets that 404 first, but the gates' own docstrings promise the
|
|
162
|
+
boundary "holds even if a view forgets this class" and that both axes resolve to "a
|
|
163
|
+
real, explicitly-assigned membership row" — so the floor belongs in the predicate,
|
|
164
|
+
defaulted **safe**, not in each caller's memory.
|
|
165
|
+
|
|
166
|
+
**The set-shaped counterpart is on the same journey, one MR behind.**
|
|
167
|
+
:func:`facet_holder_user_ids` answers this question for a whole project; #3334 is
|
|
168
|
+
the change that gives it the same floor. Whichever lands first, the two seams
|
|
169
|
+
disagreeing about a revoked holder in the interim is a migration in progress, not
|
|
170
|
+
a design — that disagreement *is* the #2897 defect, and the two are meant to end
|
|
171
|
+
up identical. A new facet cohort written before both have landed should not infer
|
|
172
|
+
a default from either: copy ``config_notice.surface_recipient_ids``, which reads
|
|
173
|
+
live membership and intersects explicitly.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
user: The principal to resolve. Anonymous resolves to no facets.
|
|
177
|
+
project_id: The project whose default team is read.
|
|
178
|
+
live_project_members_only: Require a non-soft-deleted ``ProjectMembership``
|
|
179
|
+
on the same project. Pass ``False`` only to ask the raw "what does the
|
|
180
|
+
team row say" question — never from an authorization gate.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
A fresh dict mapping each of :data:`FACET_FIELDS` to a bool.
|
|
184
|
+
"""
|
|
185
|
+
if not getattr(user, "is_authenticated", False):
|
|
186
|
+
return dict(_NO_FACETS)
|
|
187
|
+
|
|
188
|
+
queryset = TeamMembership.objects.filter(
|
|
189
|
+
team__project_id=project_id,
|
|
190
|
+
team__is_default=True,
|
|
191
|
+
team__is_deleted=False,
|
|
192
|
+
user=user, # type: ignore[misc] # narrowed authenticated above
|
|
193
|
+
is_deleted=False,
|
|
194
|
+
)
|
|
195
|
+
if live_project_members_only:
|
|
196
|
+
# The facet is consulted precisely when the role lookup returned None, so it
|
|
197
|
+
# must not outlive the project either: a trashed project confers no role
|
|
198
|
+
# (#4577), and without this floor its Product Owner kept backlog authority
|
|
199
|
+
# through the facet arm until the async cascade reached the team row.
|
|
200
|
+
queryset = queryset.filter(
|
|
201
|
+
live_project_membership_exists(), team__project__is_deleted=False
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
membership = queryset.values("is_scrum_master", "is_product_owner").first()
|
|
205
|
+
if membership is None:
|
|
206
|
+
return dict(_NO_FACETS)
|
|
207
|
+
return {
|
|
208
|
+
"is_scrum_master": bool(membership["is_scrum_master"]),
|
|
209
|
+
"is_product_owner": bool(membership["is_product_owner"]),
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def has_team_facet(
|
|
214
|
+
user: AbstractBaseUser | AnonymousUser,
|
|
215
|
+
project_id: Any,
|
|
216
|
+
facet: str,
|
|
217
|
+
*,
|
|
218
|
+
live_project_members_only: bool = True,
|
|
219
|
+
) -> bool:
|
|
220
|
+
"""Whether ``user`` holds ``facet`` on the project's default team.
|
|
221
|
+
|
|
222
|
+
``facet`` is one of :data:`FACET_FIELDS`. This is the predicate gates call —
|
|
223
|
+
e.g. "the requester is the Product Owner" for the ADR-0102 scope-injection
|
|
224
|
+
accept gate — so the team lookup lives in one place rather than every viewset.
|
|
225
|
+
|
|
226
|
+
``live_project_members_only`` is forwarded verbatim to :func:`user_facets`; see
|
|
227
|
+
that docstring for why it defaults to ``True``.
|
|
228
|
+
"""
|
|
229
|
+
if facet not in FACET_FIELDS:
|
|
230
|
+
raise ValueError(f"Unknown team facet: {facet!r}")
|
|
231
|
+
return user_facets(user, project_id, live_project_members_only=live_project_members_only)[facet]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def team_member_user_ids(project_id: Any, *, live_project_members_only: bool = True) -> set[Any]:
|
|
235
|
+
"""Return the set of user ids on a project's default team (the voter roster).
|
|
236
|
+
|
|
237
|
+
The eligible-voter set for the ADR-0104 ceiling-raise ratification: every
|
|
238
|
+
non-deleted ``TeamMembership`` of the project's default team **intersected with a
|
|
239
|
+
live, non-soft-deleted ``ProjectMembership``** (Amendment C, #3387). One query; the
|
|
240
|
+
count is the ratification denominator and ``user_id in <set>`` is the per-voter
|
|
241
|
+
gate.
|
|
242
|
+
|
|
243
|
+
**The team term still comes first, so the anti-stuffing property is untouched.**
|
|
244
|
+
Amendment A.2 scoped the roster to *team* membership rather than project membership
|
|
245
|
+
on purpose — a non-team project Admin/PM must not be able to vote on (or stuff) a
|
|
246
|
+
team's signal-sharing decision. The intersection can only ever *remove* voters
|
|
247
|
+
(``T ∩ M ⊆ T``), so someone excluded before is still excluded: a project Admin with
|
|
248
|
+
no team row has nothing for the second term to preserve.
|
|
249
|
+
|
|
250
|
+
**Why the intersection had to be added (Amendment C).** The ADR-0078 §F mirror is
|
|
251
|
+
create-only — :mod:`trueppm_api.apps.teams.signals` has no ``post_delete`` receiver
|
|
252
|
+
and ``TeamMembership`` has no FK to ``ProjectMembership`` a cascade could travel
|
|
253
|
+
over — so offboarding someone leaves their team row live forever. Without the floor
|
|
254
|
+
those ghosts inflate ``eligible_count``, and with it ``threshold``, while being
|
|
255
|
+
unable to cast a vote (``IsProjectMember`` 403s them first). A six-member team that
|
|
256
|
+
loses three still needs four approvals and can only ever raise three, so the
|
|
257
|
+
proposal becomes **unratifiable** with nothing in the UI explaining why. The
|
|
258
|
+
denominator was wrong in a way that silently changed governance outcomes.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
project_id: The project whose default team is read.
|
|
262
|
+
live_project_members_only: Intersect with non-soft-deleted
|
|
263
|
+
``ProjectMembership`` on the same project. Pass ``False`` only to ask the
|
|
264
|
+
raw "who has a team row" question — never from an eligibility, tally, or
|
|
265
|
+
recipient path.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
The set of eligible user ids.
|
|
269
|
+
"""
|
|
270
|
+
queryset = TeamMembership.objects.filter(
|
|
271
|
+
team__project_id=project_id,
|
|
272
|
+
team__is_default=True,
|
|
273
|
+
team__is_deleted=False,
|
|
274
|
+
is_deleted=False,
|
|
275
|
+
)
|
|
276
|
+
if live_project_members_only:
|
|
277
|
+
queryset = queryset.filter(live_project_membership_exists())
|
|
278
|
+
return set(queryset.values_list("user_id", flat=True))
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def facet_holder_user_ids(
|
|
282
|
+
project_id: Any,
|
|
283
|
+
*,
|
|
284
|
+
facets: Sequence[str] = FACET_FIELDS,
|
|
285
|
+
live_project_members_only: bool = True,
|
|
286
|
+
) -> set[Any]:
|
|
287
|
+
"""User ids holding a Scrum Master or Product Owner facet on the default team.
|
|
288
|
+
|
|
289
|
+
The set-shaped counterpart to :func:`user_facets`: same table, same filters,
|
|
290
|
+
asked of the *project* rather than of one user. Notification and digest cohorts
|
|
291
|
+
need this shape — ``user_facets`` answers "does this person hold a facet", which
|
|
292
|
+
cannot build a recipient list without iterating every member.
|
|
293
|
+
|
|
294
|
+
It lives here, beside ``user_facets``, on purpose. #2897 happened because the
|
|
295
|
+
two questions were answered in different files against different tables: the
|
|
296
|
+
ADR-0102 scope gate authorized ADMIN+ ∪ SM ∪ PO from ``TeamMembership``, while
|
|
297
|
+
the notification for that same event was built from ``ProjectMembership`` at
|
|
298
|
+
ADMIN+ only. The facet holders the feature existed for were exactly the ones it
|
|
299
|
+
never told. A recipient cohort that must match an authorization predicate should
|
|
300
|
+
read the predicate's own source, not a lookalike.
|
|
301
|
+
|
|
302
|
+
**The live-membership intersection defaults to on, and the default is the whole
|
|
303
|
+
point (#3334).** The ADR-0078 §F mirror only ever *creates* team rows
|
|
304
|
+
(:mod:`trueppm_api.apps.teams.signals` has no ``post_delete`` receiver and
|
|
305
|
+
``TeamMembership`` has no FK a cascade could travel over), so soft-deleting a
|
|
306
|
+
``ProjectMembership`` leaves the mirrored ``TeamMembership`` live with its facet
|
|
307
|
+
flags intact. A cohort built from the team row alone therefore keeps naming a
|
|
308
|
+
project's task names, sprint names and actors to someone who can no longer open
|
|
309
|
+
it — a dead link and a scope leak (ADR-0104's back-door close: a non-member sits
|
|
310
|
+
below every tier). That has now been fixed three times at three call sites
|
|
311
|
+
(#2897, #3291, #3334), which is why the guard is here and defaulted **safe**
|
|
312
|
+
rather than left for each new cohort to remember.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
project_id: The project whose default team is read.
|
|
316
|
+
facets: Which of :data:`FACET_FIELDS` count as holding. Defaults to both.
|
|
317
|
+
``("is_scrum_master",)`` is the impediment-clearer cohort, which routes
|
|
318
|
+
to the SM only.
|
|
319
|
+
live_project_members_only: Intersect with non-soft-deleted
|
|
320
|
+
``ProjectMembership`` on the same project. Pass ``False`` only when the
|
|
321
|
+
caller has already read live membership and will intersect itself — the
|
|
322
|
+
correlated subquery is redundant work in that case, not extra safety.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
The de-duplicated set of user ids holding at least one of ``facets``.
|
|
326
|
+
|
|
327
|
+
Raises:
|
|
328
|
+
ValueError: If ``facets`` is empty or names anything outside
|
|
329
|
+
:data:`FACET_FIELDS`.
|
|
330
|
+
"""
|
|
331
|
+
# Materialize before validating. The guard below and the Q-building loop each
|
|
332
|
+
# consume `facets`, and a one-shot iterable would be drained by the first — which
|
|
333
|
+
# fails *open*, not closed: the loop then builds a bare `Q()`, that matches every
|
|
334
|
+
# row, and a facet cohort silently becomes the whole roster. The `Sequence`
|
|
335
|
+
# annotation rules that out today; the tuple() makes it not depend on the
|
|
336
|
+
# annotation.
|
|
337
|
+
facets = tuple(facets)
|
|
338
|
+
unknown = sorted(set(facets) - set(FACET_FIELDS))
|
|
339
|
+
if not facets:
|
|
340
|
+
raise ValueError(f"Unknown team facets: at least one of {list(FACET_FIELDS)} required")
|
|
341
|
+
if unknown:
|
|
342
|
+
raise ValueError(f"Unknown team facets: {unknown!r}")
|
|
343
|
+
|
|
344
|
+
holds_a_facet = Q()
|
|
345
|
+
for field in facets:
|
|
346
|
+
holds_a_facet |= Q(**{field: True})
|
|
347
|
+
|
|
348
|
+
queryset = TeamMembership.objects.filter(
|
|
349
|
+
team__project_id=project_id,
|
|
350
|
+
team__is_default=True,
|
|
351
|
+
team__is_deleted=False,
|
|
352
|
+
is_deleted=False,
|
|
353
|
+
).filter(holds_a_facet)
|
|
354
|
+
|
|
355
|
+
if live_project_members_only:
|
|
356
|
+
# Correlated on `team__project_id` rather than on the `project_id` argument
|
|
357
|
+
# so the predicate stays correct if a caller ever widens the outer filter to
|
|
358
|
+
# `project_id__in=(...)` for a batched fan-out (#3335) — the subquery then
|
|
359
|
+
# still pairs each team row with its own project's membership.
|
|
360
|
+
queryset = queryset.filter(
|
|
361
|
+
Exists(
|
|
362
|
+
ProjectMembership.objects.filter(
|
|
363
|
+
project_id=OuterRef("team__project_id"),
|
|
364
|
+
user_id=OuterRef("user_id"),
|
|
365
|
+
is_deleted=False,
|
|
366
|
+
)
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
return set(queryset.values_list("user_id", flat=True))
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def facet_holder_user_ids_by_project(project_ids: Any) -> dict[Any, set[Any]]:
|
|
374
|
+
"""``{project_id: {user_id, ...}}`` for a whole set of projects in one query.
|
|
375
|
+
|
|
376
|
+
The batch counterpart to :func:`facet_holder_user_ids`: same table, same
|
|
377
|
+
filters, grouped by project rather than scoped to one. A fan-out that spans
|
|
378
|
+
many projects — the program settings matrix applies one field map to as many
|
|
379
|
+
as ``MAX_BULK_TARGETS`` of them — would otherwise repeat the identical team
|
|
380
|
+
scan once per project inside its loop.
|
|
381
|
+
|
|
382
|
+
Projects with no facet holder are simply absent from the mapping; callers
|
|
383
|
+
read it with ``.get(pid, set())``. Keys come back as the ORM yields them
|
|
384
|
+
(``uuid.UUID``), so a caller holding stringified ids must normalize.
|
|
385
|
+
|
|
386
|
+
Liveness is the caller's, exactly as for :func:`facet_holder_user_ids`: this
|
|
387
|
+
returns the raw facet roster, and a recipient cohort that must not name a
|
|
388
|
+
member who has lost access intersects it with live ``ProjectMembership`` —
|
|
389
|
+
see :func:`~trueppm_api.apps.projects.config_notice.surface_recipient_ids_by_project`,
|
|
390
|
+
which does so from the membership map it already holds for its own cohorts.
|
|
391
|
+
"""
|
|
392
|
+
holders: dict[Any, set[Any]] = {}
|
|
393
|
+
for project_id, user_id in (
|
|
394
|
+
TeamMembership.objects.filter(
|
|
395
|
+
team__project_id__in=project_ids,
|
|
396
|
+
team__is_default=True,
|
|
397
|
+
team__is_deleted=False,
|
|
398
|
+
is_deleted=False,
|
|
399
|
+
)
|
|
400
|
+
.filter(Q(is_scrum_master=True) | Q(is_product_owner=True))
|
|
401
|
+
.values_list("team__project_id", "user_id")
|
|
402
|
+
):
|
|
403
|
+
holders.setdefault(project_id, set()).add(user_id)
|
|
404
|
+
return holders
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def is_team_member(
|
|
408
|
+
user: AbstractBaseUser | AnonymousUser,
|
|
409
|
+
project_id: Any,
|
|
410
|
+
*,
|
|
411
|
+
live_project_members_only: bool = True,
|
|
412
|
+
) -> bool:
|
|
413
|
+
"""Whether ``user`` is on the project's default team (an eligible signal voter).
|
|
414
|
+
|
|
415
|
+
The single-user twin of :func:`team_member_user_ids`, and it carries the same
|
|
416
|
+
live-``ProjectMembership`` floor for the same reason (ADR-0104 Amendment C, #3387).
|
|
417
|
+
The two **must** agree: this is the write gate on casting a ratification vote while
|
|
418
|
+
the set form is the denominator that vote is measured against, so a floor on one
|
|
419
|
+
seam and not the other would let the tally count a roster the vote gate does not
|
|
420
|
+
recognize.
|
|
421
|
+
|
|
422
|
+
The floor narrows; it never widens. ADR-0104 §A.2's anti-stuffing property is the
|
|
423
|
+
*team* term, which is unchanged — a non-team project Admin/PM still has no team row
|
|
424
|
+
and still cannot vote.
|
|
425
|
+
|
|
426
|
+
``live_project_members_only=False`` asks the raw "does a team row exist" question;
|
|
427
|
+
it has no eligibility meaning and must not be used from a gate.
|
|
428
|
+
"""
|
|
429
|
+
if not getattr(user, "is_authenticated", False):
|
|
430
|
+
return False
|
|
431
|
+
queryset = TeamMembership.objects.filter(
|
|
432
|
+
team__project_id=project_id,
|
|
433
|
+
team__is_default=True,
|
|
434
|
+
team__is_deleted=False,
|
|
435
|
+
user=user, # type: ignore[misc] # narrowed authenticated above
|
|
436
|
+
is_deleted=False,
|
|
437
|
+
)
|
|
438
|
+
if live_project_members_only:
|
|
439
|
+
queryset = queryset.filter(live_project_membership_exists())
|
|
440
|
+
return queryset.exists()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Auto-membership invariant for the teams app (ADR-0078 §F).
|
|
2
|
+
|
|
3
|
+
A ``ProjectMembership`` write mirrors onto the project's default ``Team`` so that a
|
|
4
|
+
new member appears on the facet matrix without a separate "join the team" step. The
|
|
5
|
+
mirror runs on commit so a rolled-back membership change mirrors nothing.
|
|
6
|
+
|
|
7
|
+
**The mirror is create-only, and that is the single most misread fact in this app.**
|
|
8
|
+
It moves rows in one direction. Nothing here removes a ``TeamMembership`` when the
|
|
9
|
+
``ProjectMembership`` that produced it is revoked, so after an offboarding the team
|
|
10
|
+
row survives with its facet flags set and the two tables disagree — permanently, and
|
|
11
|
+
silently. Every cohort or gate built on ``TeamMembership`` must therefore apply its
|
|
12
|
+
own live-``ProjectMembership`` floor; the team-row filters cannot see the difference.
|
|
13
|
+
Three notification cohorts were fixed for exactly this, one at a time (#2897, #3291,
|
|
14
|
+
#3334), each time because a reader assumed what this file used to claim.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from functools import partial
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from django.db import transaction
|
|
23
|
+
from django.db.models.signals import post_save
|
|
24
|
+
from django.dispatch import receiver
|
|
25
|
+
|
|
26
|
+
from trueppm_api.apps.access.models import ProjectMembership
|
|
27
|
+
from trueppm_api.apps.teams.services import ensure_team_membership
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@receiver(
|
|
31
|
+
post_save,
|
|
32
|
+
sender=ProjectMembership,
|
|
33
|
+
dispatch_uid="teams_mirror_membership_to_default_team",
|
|
34
|
+
)
|
|
35
|
+
def _mirror_membership_to_default_team(
|
|
36
|
+
sender: type, instance: ProjectMembership, **kwargs: Any
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Keep the default team's membership in step with the project's membership.
|
|
39
|
+
|
|
40
|
+
Skips soft-deleted rows: a revoked project membership must not (re)create a team
|
|
41
|
+
membership.
|
|
42
|
+
|
|
43
|
+
Skipping is *all* it does — it does not remove the row that already exists. There
|
|
44
|
+
is no ``post_delete`` receiver here and no FK from ``TeamMembership`` to
|
|
45
|
+
``ProjectMembership`` for a cascade to travel over, so revocation leaves the
|
|
46
|
+
mirrored row live with its facets. This docstring previously claimed the opposite
|
|
47
|
+
("handled by the FK cascade… a stale team row is invisible"); that claim was the
|
|
48
|
+
stated reason three separate recipient cohorts were built without a project-row
|
|
49
|
+
filter (#2897, #3291, #3334). If a delete-side mirror is ever added, it belongs
|
|
50
|
+
here — and every downstream floor can then be revisited together.
|
|
51
|
+
"""
|
|
52
|
+
if instance.is_deleted:
|
|
53
|
+
return
|
|
54
|
+
transaction.on_commit(
|
|
55
|
+
partial(
|
|
56
|
+
ensure_team_membership,
|
|
57
|
+
project_id=instance.project_id,
|
|
58
|
+
user_id=instance.user_id,
|
|
59
|
+
project_role=instance.role,
|
|
60
|
+
)
|
|
61
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""URL routing for the teams app (ADR-0078 §E).
|
|
2
|
+
|
|
3
|
+
0.3 slice surface only: list a project's teams, list a team's roster, and PATCH
|
|
4
|
+
a member's role/facets. Retrieving one team by id, create/delete of teams and
|
|
5
|
+
members, the activity feed, and the TeamInternalsOptIn toggle land with #599.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from django.urls import path
|
|
11
|
+
|
|
12
|
+
from trueppm_api.apps.teams.views import TeamMembershipViewSet, TeamViewSet
|
|
13
|
+
|
|
14
|
+
_teams = TeamViewSet.as_view({"get": "list"})
|
|
15
|
+
_members = TeamMembershipViewSet.as_view({"get": "list"})
|
|
16
|
+
_member_detail = TeamMembershipViewSet.as_view({"patch": "partial_update"})
|
|
17
|
+
|
|
18
|
+
urlpatterns = [
|
|
19
|
+
path(
|
|
20
|
+
"projects/<uuid:project_pk>/teams/",
|
|
21
|
+
_teams,
|
|
22
|
+
name="project-teams-list",
|
|
23
|
+
),
|
|
24
|
+
path(
|
|
25
|
+
"teams/<uuid:team_pk>/members/",
|
|
26
|
+
_members,
|
|
27
|
+
name="team-members-list",
|
|
28
|
+
),
|
|
29
|
+
path(
|
|
30
|
+
"teams/<uuid:team_pk>/members/<uuid:pk>/",
|
|
31
|
+
_member_detail,
|
|
32
|
+
name="team-members-detail",
|
|
33
|
+
),
|
|
34
|
+
]
|