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,159 @@
|
|
|
1
|
+
# Generated by Django 5.2.14 on 2026-06-23 21:38
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import django.db.models.deletion
|
|
8
|
+
import django.utils.timezone
|
|
9
|
+
from django.conf import settings
|
|
10
|
+
from django.db import migrations, models
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _original(app: str, module: str, name: str) -> Any:
|
|
14
|
+
"""Return a callable from a retained original migration module.
|
|
15
|
+
|
|
16
|
+
The original migrations stay on disk (migration rule 6), so the callable is
|
|
17
|
+
resolved from there rather than copied into this file.
|
|
18
|
+
"""
|
|
19
|
+
# Every call site passes a constant app/module/name from this file, never user input.
|
|
20
|
+
# nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import
|
|
21
|
+
return getattr(importlib.import_module(f"trueppm_api.apps.{app}.migrations.{module}"), name)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Migration(migrations.Migration):
|
|
25
|
+
replaces = [("teams", "0001_initial"), ("teams", "0002_default_teams")]
|
|
26
|
+
|
|
27
|
+
initial = True
|
|
28
|
+
|
|
29
|
+
dependencies = [
|
|
30
|
+
("access", "0001_initial"),
|
|
31
|
+
("projects", "0057_historicalsprint_binding_committed_snapshot_and_more"),
|
|
32
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
operations = [
|
|
36
|
+
migrations.CreateModel(
|
|
37
|
+
name="Team",
|
|
38
|
+
fields=[
|
|
39
|
+
(
|
|
40
|
+
"id",
|
|
41
|
+
models.UUIDField(
|
|
42
|
+
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
("server_version", models.BigIntegerField(default=0, editable=False)),
|
|
46
|
+
("is_deleted", models.BooleanField(db_index=True, default=False)),
|
|
47
|
+
("deleted_version", models.BigIntegerField(blank=True, editable=False, null=True)),
|
|
48
|
+
("name", models.CharField(max_length=255)),
|
|
49
|
+
("short_id", models.CharField(max_length=8)),
|
|
50
|
+
("description", models.TextField(blank=True, default="")),
|
|
51
|
+
("is_default", models.BooleanField(db_index=True, default=False)),
|
|
52
|
+
(
|
|
53
|
+
"created_at",
|
|
54
|
+
models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
|
55
|
+
),
|
|
56
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
57
|
+
(
|
|
58
|
+
"created_by",
|
|
59
|
+
models.ForeignKey(
|
|
60
|
+
blank=True,
|
|
61
|
+
null=True,
|
|
62
|
+
on_delete=django.db.models.deletion.SET_NULL,
|
|
63
|
+
related_name="created_teams",
|
|
64
|
+
to=settings.AUTH_USER_MODEL,
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
(
|
|
68
|
+
"project",
|
|
69
|
+
models.ForeignKey(
|
|
70
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
71
|
+
related_name="teams",
|
|
72
|
+
to="projects.project",
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
],
|
|
76
|
+
options={
|
|
77
|
+
"db_table": "teams_team",
|
|
78
|
+
},
|
|
79
|
+
),
|
|
80
|
+
migrations.CreateModel(
|
|
81
|
+
name="TeamMembership",
|
|
82
|
+
fields=[
|
|
83
|
+
(
|
|
84
|
+
"id",
|
|
85
|
+
models.UUIDField(
|
|
86
|
+
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
("server_version", models.BigIntegerField(default=0, editable=False)),
|
|
90
|
+
("is_deleted", models.BooleanField(db_index=True, default=False)),
|
|
91
|
+
("deleted_version", models.BigIntegerField(blank=True, editable=False, null=True)),
|
|
92
|
+
(
|
|
93
|
+
"role",
|
|
94
|
+
models.CharField(
|
|
95
|
+
choices=[("member", "Member"), ("admin", "Admin")],
|
|
96
|
+
default="member",
|
|
97
|
+
max_length=16,
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
("is_scrum_master", models.BooleanField(default=False)),
|
|
101
|
+
("is_product_owner", models.BooleanField(default=False)),
|
|
102
|
+
(
|
|
103
|
+
"created_at",
|
|
104
|
+
models.DateTimeField(default=django.utils.timezone.now, editable=False),
|
|
105
|
+
),
|
|
106
|
+
(
|
|
107
|
+
"team",
|
|
108
|
+
models.ForeignKey(
|
|
109
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
110
|
+
related_name="memberships",
|
|
111
|
+
to="teams.team",
|
|
112
|
+
),
|
|
113
|
+
),
|
|
114
|
+
(
|
|
115
|
+
"user",
|
|
116
|
+
models.ForeignKey(
|
|
117
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
118
|
+
related_name="team_memberships",
|
|
119
|
+
to=settings.AUTH_USER_MODEL,
|
|
120
|
+
),
|
|
121
|
+
),
|
|
122
|
+
],
|
|
123
|
+
options={
|
|
124
|
+
"db_table": "teams_team_membership",
|
|
125
|
+
},
|
|
126
|
+
),
|
|
127
|
+
migrations.AddConstraint(
|
|
128
|
+
model_name="team",
|
|
129
|
+
constraint=models.UniqueConstraint(
|
|
130
|
+
condition=models.Q(("is_deleted", False)),
|
|
131
|
+
fields=("project", "name"),
|
|
132
|
+
name="team_name_per_project_unique",
|
|
133
|
+
),
|
|
134
|
+
),
|
|
135
|
+
migrations.AddConstraint(
|
|
136
|
+
model_name="team",
|
|
137
|
+
constraint=models.UniqueConstraint(
|
|
138
|
+
condition=models.Q(("is_default", True), ("is_deleted", False)),
|
|
139
|
+
fields=("project",),
|
|
140
|
+
name="team_one_default_per_project",
|
|
141
|
+
),
|
|
142
|
+
),
|
|
143
|
+
migrations.AddIndex(
|
|
144
|
+
model_name="teammembership",
|
|
145
|
+
index=models.Index(fields=["team", "user"], name="tm_team_user_idx"),
|
|
146
|
+
),
|
|
147
|
+
migrations.AddConstraint(
|
|
148
|
+
model_name="teammembership",
|
|
149
|
+
constraint=models.UniqueConstraint(
|
|
150
|
+
condition=models.Q(("is_deleted", False)),
|
|
151
|
+
fields=("team", "user"),
|
|
152
|
+
name="team_member_unique",
|
|
153
|
+
),
|
|
154
|
+
),
|
|
155
|
+
migrations.RunPython(
|
|
156
|
+
code=_original("teams", "0002_default_teams", "create_default_teams"),
|
|
157
|
+
reverse_code=migrations.RunPython.noop,
|
|
158
|
+
),
|
|
159
|
+
]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Data migration: create one default Team per existing project and mirror
|
|
2
|
+
ProjectMembership rows onto it (ADR-0078 §C).
|
|
3
|
+
|
|
4
|
+
One-way and idempotent (get_or_create-shaped) so a re-run is a no-op. Forward
|
|
5
|
+
only: the reverse is RunPython.noop because dropping migration-created teams
|
|
6
|
+
would also strand any facets assigned afterward.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from django.db import migrations
|
|
14
|
+
|
|
15
|
+
# Project Role.ADMIN ordinal (ADR-0072). Project Admin (300) and Owner (400)
|
|
16
|
+
# become team Admin; everyone else is a team Member. Pinned as a literal here —
|
|
17
|
+
# a data migration must stay frozen against the model-time enum, and the band
|
|
18
|
+
# boundary is a stable contract — with the symbolic source named for the reader.
|
|
19
|
+
_TEAM_ADMIN_ROLE_THRESHOLD = 300 # Role.ADMIN
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def create_default_teams(apps: Any, schema_editor: Any) -> None:
|
|
23
|
+
Project = apps.get_model("projects", "Project")
|
|
24
|
+
ProjectMembership = apps.get_model("access", "ProjectMembership")
|
|
25
|
+
Team = apps.get_model("teams", "Team")
|
|
26
|
+
TeamMembership = apps.get_model("teams", "TeamMembership")
|
|
27
|
+
|
|
28
|
+
for project in Project.objects.filter(is_deleted=False).iterator():
|
|
29
|
+
team, _created = Team.objects.get_or_create(
|
|
30
|
+
project=project,
|
|
31
|
+
is_default=True,
|
|
32
|
+
is_deleted=False,
|
|
33
|
+
defaults={
|
|
34
|
+
"name": "Default Team",
|
|
35
|
+
"short_id": "T01",
|
|
36
|
+
# Historical models bypass VersionedModel.save(), so stamp the
|
|
37
|
+
# initial server_version explicitly rather than leaving it 0.
|
|
38
|
+
"server_version": 1,
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
for pm in ProjectMembership.objects.filter(project=project, is_deleted=False).iterator():
|
|
43
|
+
team_role = "admin" if pm.role >= _TEAM_ADMIN_ROLE_THRESHOLD else "member"
|
|
44
|
+
TeamMembership.objects.get_or_create(
|
|
45
|
+
team=team,
|
|
46
|
+
user_id=pm.user_id,
|
|
47
|
+
is_deleted=False,
|
|
48
|
+
# Facets are never inferred (ADR-0078 §C step 3): both default
|
|
49
|
+
# False, explicit assignment required afterward.
|
|
50
|
+
defaults={"role": team_role, "server_version": 1},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Migration(migrations.Migration):
|
|
55
|
+
dependencies = [
|
|
56
|
+
("teams", "0001_initial"),
|
|
57
|
+
("access", "0001_initial"),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
operations = [
|
|
61
|
+
migrations.RunPython(create_default_teams, migrations.RunPython.noop, elidable=True),
|
|
62
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated by Django 5.2.16 on 2026-07-28 12:47
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
("teams", "0001_squashed_0002_default_teams"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.AddField(
|
|
13
|
+
model_name="team",
|
|
14
|
+
name="sync_seq",
|
|
15
|
+
field=models.BigIntegerField(default=0, editable=False),
|
|
16
|
+
),
|
|
17
|
+
migrations.AddField(
|
|
18
|
+
model_name="teammembership",
|
|
19
|
+
name="sync_seq",
|
|
20
|
+
field=models.BigIntegerField(default=0, editable=False),
|
|
21
|
+
),
|
|
22
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Re-run ``create_default_teams`` as a real forward step on an upgrading database.
|
|
2
|
+
|
|
3
|
+
The ``teams`` app has no unsquashed released history: ``0001_initial`` and
|
|
4
|
+
``0002_default_teams`` both first shipped already combined into
|
|
5
|
+
``0001_squashed_0002_default_teams`` (v0.3.0-alpha.1). So, unlike every other app's
|
|
6
|
+
restored elidable data op (#4320), there has never been a released state that an
|
|
7
|
+
upgrading database could pass through where the original, elidable
|
|
8
|
+
``create_default_teams`` op was guaranteed to run from the originals rather than the
|
|
9
|
+
squash. An install upgrading from before teams existed (pre-0.3, with real ``Project``
|
|
10
|
+
and ``ProjectMembership`` rows already present) applies *only* the squash for this
|
|
11
|
+
app — and until this branch's commit restoring the op to the squash, that squash
|
|
12
|
+
carried no data step at all, so ``create_default_teams`` never ran for it and never
|
|
13
|
+
will on its own (the squash is recorded as applied; Django will not re-run it).
|
|
14
|
+
|
|
15
|
+
This migration closes that gap directly instead of relying on the squash restoration,
|
|
16
|
+
which only helps installs that have not yet applied the squash. It is intentionally
|
|
17
|
+
NOT elidable, so a future squash spanning this migration cannot silently drop it
|
|
18
|
+
again the way the optimizer dropped the original. ``create_default_teams`` is
|
|
19
|
+
``get_or_create``-shaped (see its docstring in ``0002_default_teams.py``), so re-running
|
|
20
|
+
it here is a safe no-op on every install that already has default teams — whether
|
|
21
|
+
because the squash's restored op already created them on a fresh install, or because
|
|
22
|
+
``teams.signals``' membership-signal healing filled the gap already.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import importlib
|
|
28
|
+
|
|
29
|
+
from django.db import migrations
|
|
30
|
+
|
|
31
|
+
# The source module's file name starts with a digit, so it cannot be named in a
|
|
32
|
+
# normal `from ... import ...` statement; resolve it the same way the squash's
|
|
33
|
+
# `_original` helper does (0001_squashed_0002_default_teams.py).
|
|
34
|
+
# nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import
|
|
35
|
+
_default_teams_module = importlib.import_module(
|
|
36
|
+
"trueppm_api.apps.teams.migrations.0002_default_teams"
|
|
37
|
+
)
|
|
38
|
+
_create_default_teams = _default_teams_module.create_default_teams
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Migration(migrations.Migration):
|
|
42
|
+
dependencies = [
|
|
43
|
+
("teams", "0003_team_sync_seq_teammembership_sync_seq"),
|
|
44
|
+
("access", "0001_initial"),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
operations = [
|
|
48
|
+
migrations.RunPython(_create_default_teams, migrations.RunPython.noop),
|
|
49
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Team and TeamMembership models (ADR-0078, OSS).
|
|
2
|
+
|
|
3
|
+
Two axes of authority live here and are deliberately independent:
|
|
4
|
+
|
|
5
|
+
* **Access role** (``TeamMembership.role``) — Member or Admin — is coordination
|
|
6
|
+
authority: who can manage the team's membership and facets.
|
|
7
|
+
* **Facets** (``is_scrum_master``, ``is_product_owner``) — facilitation and
|
|
8
|
+
ownership — are orthogonal markers. A user can be an Admin who is also Scrum
|
|
9
|
+
Master, or a Member who is also Product Owner.
|
|
10
|
+
|
|
11
|
+
Keeping the facets off ``ProjectMembership`` is a hard ADR-0078 constraint: hanging
|
|
12
|
+
the booleans on the project membership made project-admin implicitly team-admin and
|
|
13
|
+
routed around the per-team consent model (Morgan 🔴). The Team entity exists so the
|
|
14
|
+
facet axis has a real, per-team home.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from django.conf import settings
|
|
20
|
+
from django.db import models
|
|
21
|
+
from django.utils import timezone
|
|
22
|
+
|
|
23
|
+
from trueppm_api.apps.projects.models import VersionedModel
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TeamRole(models.TextChoices):
|
|
27
|
+
"""Team-scoped coordination role.
|
|
28
|
+
|
|
29
|
+
Intentionally only two tiers — the team's own membership management axis is
|
|
30
|
+
coarse (you either manage the team or you don't). This is distinct from the
|
|
31
|
+
5-tier project ``Role`` ordinal (ADR-0072): a project Admin inherits team-admin
|
|
32
|
+
authority for low-consent actions (ADR-0078 §D) without holding this role.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
MEMBER = "member", "Member"
|
|
36
|
+
ADMIN = "admin", "Admin"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Team(VersionedModel):
|
|
40
|
+
"""A Scrum team that owns a sprint commitment, scoped to one project (ADR-0078 §A).
|
|
41
|
+
|
|
42
|
+
Every project has exactly one ``is_default=True`` team, auto-created by the
|
|
43
|
+
initial data migration and kept populated by the auto-membership signal. The
|
|
44
|
+
0.3 slice never creates a second team — multi-team UX lands in #599 (0.6) —
|
|
45
|
+
so ``is_default`` is always True in practice today; the field and the
|
|
46
|
+
one-default-per-project constraint exist so that work can land without a
|
|
47
|
+
schema change.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
project = models.ForeignKey(
|
|
51
|
+
"projects.Project",
|
|
52
|
+
on_delete=models.CASCADE,
|
|
53
|
+
related_name="teams",
|
|
54
|
+
)
|
|
55
|
+
name = models.CharField(max_length=255)
|
|
56
|
+
short_id = models.CharField(max_length=8)
|
|
57
|
+
description = models.TextField(blank=True, default="")
|
|
58
|
+
# Auto-created at migration; exactly one per project. db_index because the
|
|
59
|
+
# default-team resolution path (resolve_default_team) filters on it per request.
|
|
60
|
+
is_default = models.BooleanField(default=False, db_index=True)
|
|
61
|
+
created_by = models.ForeignKey(
|
|
62
|
+
settings.AUTH_USER_MODEL,
|
|
63
|
+
on_delete=models.SET_NULL,
|
|
64
|
+
null=True,
|
|
65
|
+
blank=True,
|
|
66
|
+
related_name="created_teams",
|
|
67
|
+
)
|
|
68
|
+
# VersionedModel omits created_at/updated_at (sync uses server_version), so
|
|
69
|
+
# these are explicit. default=timezone.now (not auto_now_add) lets the data
|
|
70
|
+
# migration create rows with an explicit timestamp non-interactively.
|
|
71
|
+
created_at = models.DateTimeField(default=timezone.now, editable=False)
|
|
72
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
73
|
+
|
|
74
|
+
class Meta:
|
|
75
|
+
db_table = "teams_team"
|
|
76
|
+
constraints = [
|
|
77
|
+
models.UniqueConstraint(
|
|
78
|
+
fields=["project", "name"],
|
|
79
|
+
condition=models.Q(is_deleted=False),
|
|
80
|
+
name="team_name_per_project_unique",
|
|
81
|
+
),
|
|
82
|
+
models.UniqueConstraint(
|
|
83
|
+
fields=["project"],
|
|
84
|
+
condition=models.Q(is_default=True, is_deleted=False),
|
|
85
|
+
name="team_one_default_per_project",
|
|
86
|
+
),
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
def __str__(self) -> str:
|
|
90
|
+
return f"{self.name} ({self.project_id})"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TeamMembership(VersionedModel):
|
|
94
|
+
"""A user's membership of a team: an access role plus two independent facets.
|
|
95
|
+
|
|
96
|
+
The facets are soft-singletons — at most one Scrum Master and one Product
|
|
97
|
+
Owner per team — enforced at the serializer/service layer rather than by a DB
|
|
98
|
+
constraint, so a facet can move between members in a single write (reassign)
|
|
99
|
+
without a transient double-holder rejection.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
team = models.ForeignKey(
|
|
103
|
+
Team,
|
|
104
|
+
on_delete=models.CASCADE,
|
|
105
|
+
related_name="memberships",
|
|
106
|
+
)
|
|
107
|
+
user = models.ForeignKey(
|
|
108
|
+
settings.AUTH_USER_MODEL,
|
|
109
|
+
on_delete=models.CASCADE,
|
|
110
|
+
related_name="team_memberships",
|
|
111
|
+
)
|
|
112
|
+
role = models.CharField(
|
|
113
|
+
max_length=16,
|
|
114
|
+
choices=TeamRole.choices,
|
|
115
|
+
default=TeamRole.MEMBER,
|
|
116
|
+
)
|
|
117
|
+
is_scrum_master = models.BooleanField(default=False)
|
|
118
|
+
is_product_owner = models.BooleanField(default=False)
|
|
119
|
+
created_at = models.DateTimeField(default=timezone.now, editable=False)
|
|
120
|
+
|
|
121
|
+
class Meta:
|
|
122
|
+
db_table = "teams_team_membership"
|
|
123
|
+
constraints = [
|
|
124
|
+
models.UniqueConstraint(
|
|
125
|
+
fields=["team", "user"],
|
|
126
|
+
condition=models.Q(is_deleted=False),
|
|
127
|
+
name="team_member_unique",
|
|
128
|
+
),
|
|
129
|
+
]
|
|
130
|
+
indexes = [
|
|
131
|
+
# Facet resolution for gates: WHERE team_id = X AND user_id = Y.
|
|
132
|
+
models.Index(fields=["team", "user"], name="tm_team_user_idx"),
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
def __str__(self) -> str:
|
|
136
|
+
return f"{self.user} — {self.team} ({self.role})"
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def project_id(self) -> object:
|
|
140
|
+
"""Expose the owning project's id without a join, mirroring the access models.
|
|
141
|
+
|
|
142
|
+
Permission helpers resolve a facet from a (user, project) pair; exposing
|
|
143
|
+
project_id here keeps those call sites off a ``team.project_id`` chain.
|
|
144
|
+
"""
|
|
145
|
+
return self.team.project_id
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""DRF permission classes for the teams app (ADR-0078 §D).
|
|
2
|
+
|
|
3
|
+
The 0.3 slice exposes only read and facet/role assignment, so it needs two gates:
|
|
4
|
+
|
|
5
|
+
* :class:`IsTeamMember` — any member of the team's project may read the roster.
|
|
6
|
+
* :class:`IsTeamFacetEditor` — role and facet assignment. Per the §D split this is
|
|
7
|
+
a *low-consent* action, so it is the inheriting bucket: a project Admin (role ≥
|
|
8
|
+
ADMIN) **or** an explicit ``TeamMembership(role='admin')`` may edit. The
|
|
9
|
+
consent-sensitive actions (TeamInternalsOptIn, sprint rebind, bulk replace) that
|
|
10
|
+
require explicit team-admin with no inheritance are out of scope until #599.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from rest_framework.permissions import SAFE_METHODS, BasePermission
|
|
18
|
+
from rest_framework.request import Request
|
|
19
|
+
from rest_framework.views import APIView
|
|
20
|
+
|
|
21
|
+
from trueppm_api.apps.access.models import Role
|
|
22
|
+
from trueppm_api.apps.access.permissions import _membership_role
|
|
23
|
+
from trueppm_api.apps.teams.models import Team, TeamMembership, TeamRole
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _team_pk_from_view(view: APIView) -> Any | None:
|
|
27
|
+
"""Extract the team pk, which every team-scoped route carries as ``team_pk``.
|
|
28
|
+
|
|
29
|
+
There is deliberately no ``pk`` fallback: on the surviving member-detail route
|
|
30
|
+
``pk`` names a :class:`TeamMembership`, so falling back to it would resolve the
|
|
31
|
+
wrong object. Returns None when the route names no team, and callers fail closed.
|
|
32
|
+
"""
|
|
33
|
+
return getattr(view, "kwargs", {}).get("team_pk")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _project_id_for_team(request: Request, team_id: Any) -> Any | None:
|
|
37
|
+
"""Resolve (and per-request cache) the project a team belongs to."""
|
|
38
|
+
cache: dict[Any, Any | None] | None = getattr(request, "_team_project_cache", None)
|
|
39
|
+
if cache is None:
|
|
40
|
+
cache = {}
|
|
41
|
+
request._team_project_cache = cache # type: ignore[attr-defined]
|
|
42
|
+
if team_id in cache:
|
|
43
|
+
return cache[team_id]
|
|
44
|
+
project_id = (
|
|
45
|
+
Team.objects.filter(pk=team_id, is_deleted=False)
|
|
46
|
+
.values_list("project_id", flat=True)
|
|
47
|
+
.first()
|
|
48
|
+
)
|
|
49
|
+
cache[team_id] = project_id
|
|
50
|
+
return project_id
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _team_role(request: Request, team_id: Any) -> str | None:
|
|
54
|
+
"""Return the requester's explicit team role on ``team_id``, or None."""
|
|
55
|
+
if not (request.user and request.user.is_authenticated):
|
|
56
|
+
return None
|
|
57
|
+
return (
|
|
58
|
+
TeamMembership.objects.filter(team_id=team_id, user=request.user, is_deleted=False)
|
|
59
|
+
.values_list("role", flat=True)
|
|
60
|
+
.first()
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _route_project_id(request: Request, view: APIView) -> Any | None:
|
|
65
|
+
"""Resolve the project a request targets, from either route shape.
|
|
66
|
+
|
|
67
|
+
The project-scoped list route carries ``project_pk`` directly; a team-scoped
|
|
68
|
+
route carries ``team_pk`` and resolves the project through the team. Returns None
|
|
69
|
+
when neither is present or the team does not exist — callers fail closed on None
|
|
70
|
+
so an unrecognized route never default-allows.
|
|
71
|
+
"""
|
|
72
|
+
project_pk = getattr(view, "kwargs", {}).get("project_pk")
|
|
73
|
+
if project_pk is not None:
|
|
74
|
+
return project_pk
|
|
75
|
+
team_id = _team_pk_from_view(view)
|
|
76
|
+
if team_id is None:
|
|
77
|
+
return None
|
|
78
|
+
return _project_id_for_team(request, team_id)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _can_edit_facets(request: Request, team_id: Any) -> bool:
|
|
82
|
+
"""Project Admin (inheritance) OR explicit team Admin (ADR-0078 §D low-consent)."""
|
|
83
|
+
project_id = _project_id_for_team(request, team_id)
|
|
84
|
+
if project_id is None:
|
|
85
|
+
return False
|
|
86
|
+
project_role = _membership_role(request, project_id)
|
|
87
|
+
if project_role is not None and project_role >= Role.ADMIN:
|
|
88
|
+
return True
|
|
89
|
+
return _team_role(request, team_id) == TeamRole.ADMIN
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class IsTeamMember(BasePermission):
|
|
93
|
+
"""Any member of the team's project may read the team and its roster.
|
|
94
|
+
|
|
95
|
+
Fails closed: the project-scoped list route (``project_pk``, no ``team_pk``)
|
|
96
|
+
must still require project membership, so the queryset filter is never the
|
|
97
|
+
sole scoping (prevents the cross-project enumeration IDOR class of #887).
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
message = "You must be a member of this team's project."
|
|
101
|
+
|
|
102
|
+
def has_permission(self, request: Request, view: APIView) -> bool:
|
|
103
|
+
if not (request.user and request.user.is_authenticated):
|
|
104
|
+
return False
|
|
105
|
+
project_id = _route_project_id(request, view)
|
|
106
|
+
if project_id is None:
|
|
107
|
+
return False
|
|
108
|
+
return _membership_role(request, project_id) is not None
|
|
109
|
+
|
|
110
|
+
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
|
111
|
+
# No route reaches this today — the surviving team route is the project-scoped
|
|
112
|
+
# list, which never calls get_object(). Kept, and unit-tested directly, because
|
|
113
|
+
# #599 restores the team detail/PATCH/DELETE routes onto this same class; a gate
|
|
114
|
+
# nothing has exercised is how a restored route ships unguarded.
|
|
115
|
+
project_id = getattr(obj, "project_id", None)
|
|
116
|
+
if project_id is None:
|
|
117
|
+
return False
|
|
118
|
+
return _membership_role(request, project_id) is not None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class IsTeamFacetEditor(BasePermission):
|
|
122
|
+
"""Read for any project member; role/facet writes for project Admin or team Admin."""
|
|
123
|
+
|
|
124
|
+
message = "You need Project Manager or team Admin to change team roles or facets."
|
|
125
|
+
|
|
126
|
+
def has_permission(self, request: Request, view: APIView) -> bool:
|
|
127
|
+
if not (request.user and request.user.is_authenticated):
|
|
128
|
+
return False
|
|
129
|
+
project_id = _route_project_id(request, view)
|
|
130
|
+
if project_id is None:
|
|
131
|
+
return False
|
|
132
|
+
if _membership_role(request, project_id) is None:
|
|
133
|
+
return False
|
|
134
|
+
if request.method in SAFE_METHODS:
|
|
135
|
+
return True
|
|
136
|
+
team_id = _team_pk_from_view(view)
|
|
137
|
+
return team_id is not None and _can_edit_facets(request, team_id)
|
|
138
|
+
|
|
139
|
+
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
|
140
|
+
"""Defense-in-depth: re-derive edit rights from the object's own team.
|
|
141
|
+
|
|
142
|
+
The roster queryset already scopes to ``team_pk``, but checking the
|
|
143
|
+
object's team directly means a future non-nested route cannot become a
|
|
144
|
+
cross-team write IDOR by relying on the route param alone.
|
|
145
|
+
"""
|
|
146
|
+
if request.method in SAFE_METHODS:
|
|
147
|
+
project_id = getattr(obj, "project_id", None)
|
|
148
|
+
return project_id is not None and _membership_role(request, project_id) is not None
|
|
149
|
+
return _can_edit_facets(request, obj.team_id)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Serializers for the teams app (ADR-0078 §E)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.contrib.auth import get_user_model
|
|
6
|
+
from rest_framework import serializers
|
|
7
|
+
|
|
8
|
+
from trueppm_api.apps.teams.models import Team, TeamMembership, TeamRole
|
|
9
|
+
from trueppm_api.apps.teams.services import live_project_membership_exists
|
|
10
|
+
|
|
11
|
+
User = get_user_model()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _TeamUserSummarySerializer(serializers.ModelSerializer): # type: ignore[type-arg]
|
|
15
|
+
class Meta:
|
|
16
|
+
model = User
|
|
17
|
+
fields = ["id", "username", "email"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TeamSerializer(serializers.ModelSerializer[Team]):
|
|
21
|
+
"""Read serializer for a team. ``member_count`` drives the roster header."""
|
|
22
|
+
|
|
23
|
+
member_count = serializers.SerializerMethodField()
|
|
24
|
+
|
|
25
|
+
class Meta:
|
|
26
|
+
model = Team
|
|
27
|
+
fields = [
|
|
28
|
+
"id",
|
|
29
|
+
"project",
|
|
30
|
+
"name",
|
|
31
|
+
"short_id",
|
|
32
|
+
"description",
|
|
33
|
+
"is_default",
|
|
34
|
+
"member_count",
|
|
35
|
+
"server_version",
|
|
36
|
+
]
|
|
37
|
+
read_only_fields = fields
|
|
38
|
+
|
|
39
|
+
def get_member_count(self, obj: Team) -> int:
|
|
40
|
+
# Prefer a prefetched/annotated count when present to avoid an N+1 on lists.
|
|
41
|
+
annotated = getattr(obj, "member_count_annotated", None)
|
|
42
|
+
if annotated is not None:
|
|
43
|
+
return int(annotated)
|
|
44
|
+
# Unreachable from any route today (TeamViewSet.list() always sets the
|
|
45
|
+
# annotation above), but flooring here too (#3511) means a future caller
|
|
46
|
+
# that serializes a bare Team instance can't silently reintroduce the
|
|
47
|
+
# revoked-member-counts-toward-the-total defect this fallback used to
|
|
48
|
+
# carry — the same floor as TeamViewSet.get_queryset() and the facet /
|
|
49
|
+
# voter-roster seams in services.py, restated once rather than per caller.
|
|
50
|
+
return (
|
|
51
|
+
obj.memberships.filter(is_deleted=False)
|
|
52
|
+
.filter(live_project_membership_exists())
|
|
53
|
+
.count()
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TeamMembershipReadSerializer(serializers.ModelSerializer[TeamMembership]):
|
|
58
|
+
"""Roster row: identity plus the role and the two facet flags."""
|
|
59
|
+
|
|
60
|
+
user_detail = _TeamUserSummarySerializer(source="user", read_only=True)
|
|
61
|
+
role_label = serializers.CharField(source="get_role_display", read_only=True)
|
|
62
|
+
|
|
63
|
+
class Meta:
|
|
64
|
+
model = TeamMembership
|
|
65
|
+
fields = [
|
|
66
|
+
"id",
|
|
67
|
+
"team",
|
|
68
|
+
"user",
|
|
69
|
+
"user_detail",
|
|
70
|
+
"role",
|
|
71
|
+
"role_label",
|
|
72
|
+
"is_scrum_master",
|
|
73
|
+
"is_product_owner",
|
|
74
|
+
"created_at",
|
|
75
|
+
"server_version",
|
|
76
|
+
]
|
|
77
|
+
read_only_fields = fields
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class TeamMembershipWriteSerializer(serializers.ModelSerializer[TeamMembership]):
|
|
81
|
+
"""Write serializer for role and facet assignment (PATCH only in 0.3).
|
|
82
|
+
|
|
83
|
+
All three fields are optional so a partial update can flip a single facet
|
|
84
|
+
without re-sending the role. The single-holder reassignment for the facets is
|
|
85
|
+
applied in the viewset inside the write transaction, not here, because it
|
|
86
|
+
mutates *other* rows.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
role = serializers.ChoiceField(choices=TeamRole.choices, required=False)
|
|
90
|
+
is_scrum_master = serializers.BooleanField(required=False)
|
|
91
|
+
is_product_owner = serializers.BooleanField(required=False)
|
|
92
|
+
|
|
93
|
+
class Meta:
|
|
94
|
+
model = TeamMembership
|
|
95
|
+
fields = ["role", "is_scrum_master", "is_product_owner"]
|