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,723 @@
|
|
|
1
|
+
"""Serializers for the scheduling app admin API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from drf_spectacular.utils import extend_schema_field
|
|
8
|
+
from rest_framework import serializers
|
|
9
|
+
from trueppm_scheduler.derive import Quantity as CpmDerivationQuantity
|
|
10
|
+
|
|
11
|
+
from trueppm_api.apps.scheduling.models import (
|
|
12
|
+
FailedTask,
|
|
13
|
+
MonteCarloRun,
|
|
14
|
+
ProjectForecastSnapshot,
|
|
15
|
+
VelocitySuggestion,
|
|
16
|
+
)
|
|
17
|
+
from trueppm_api.apps.scheduling.services import (
|
|
18
|
+
FORECAST_ALL_COMPLETE,
|
|
19
|
+
FORECAST_ESTIMATES_BELOW_PLAN_DURATION,
|
|
20
|
+
FORECAST_ESTIMATES_OFF_CRITICAL_PATH,
|
|
21
|
+
FORECAST_ESTIMATES_PENDING_APPROVAL,
|
|
22
|
+
FORECAST_NO_COMMITTED_TASKS,
|
|
23
|
+
FORECAST_NO_ESTIMATES,
|
|
24
|
+
FORECAST_NO_VELOCITY_HISTORY,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MonteCarloWhatIfRequestSerializer(serializers.Serializer[dict[str, Any]]):
|
|
29
|
+
"""Validate the non-mutating Monte Carlo what-if query (#993).
|
|
30
|
+
|
|
31
|
+
The what-if endpoint perturbs exactly one task's duration and recomputes the
|
|
32
|
+
forecast in memory without persisting anything, so this is a *request* (query
|
|
33
|
+
param) validator only — there is no model behind it. The caller must supply a
|
|
34
|
+
``task_id`` and exactly one of ``duration_delta`` (signed day offset applied to
|
|
35
|
+
the task's current duration) or ``new_duration`` (an absolute day count the
|
|
36
|
+
task's duration is set to). Requiring exactly one keeps the perturbation
|
|
37
|
+
unambiguous — a request carrying both, or neither, cannot express a single
|
|
38
|
+
well-defined schedule and is rejected up front rather than resolved by a silent
|
|
39
|
+
precedence rule.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
task_id = serializers.UUIDField()
|
|
43
|
+
duration_delta = serializers.IntegerField(required=False)
|
|
44
|
+
new_duration = serializers.IntegerField(required=False, min_value=0)
|
|
45
|
+
n_simulations = serializers.IntegerField(required=False, min_value=1)
|
|
46
|
+
|
|
47
|
+
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
|
48
|
+
has_delta = "duration_delta" in attrs
|
|
49
|
+
has_absolute = "new_duration" in attrs
|
|
50
|
+
if has_delta == has_absolute:
|
|
51
|
+
raise serializers.ValidationError(
|
|
52
|
+
"Supply exactly one of 'duration_delta' or 'new_duration'."
|
|
53
|
+
)
|
|
54
|
+
return attrs
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_WORKING_TIME_DELTA_HELP = (
|
|
58
|
+
"Signed calendar days, measured in working time (#4204): each finish is read "
|
|
59
|
+
"with its edge of the day (the `*_at_day_start` readings), so a start-of-day "
|
|
60
|
+
"milestone finish on a Monday equals the end of the Friday before it and diffs "
|
|
61
|
+
"as 0 rather than 3. A reading persisted before #4204 is unknown: two equal days "
|
|
62
|
+
"diff as 0, otherwise it is read as the end of its day."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
_AT_DAY_START_HELP = (
|
|
66
|
+
"Which edge of its shown day this finish is (#4204): true = the start of the "
|
|
67
|
+
"day (a start-of-day milestone finish), false = the end. Null on a run "
|
|
68
|
+
"persisted before #4204."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class MonteCarloDeltaSerializer(serializers.Serializer[dict[str, Any]]):
|
|
73
|
+
"""Signed delta of a percentile finish, per field, in working time (#987/#993/#2483/#4204).
|
|
74
|
+
|
|
75
|
+
Positive means the field slipped later (worse). Shared shape for
|
|
76
|
+
``run_monte_carlo``/``latest``'s ``delta_vs_cpm`` (each percentile vs the
|
|
77
|
+
deterministic CPM finish), the what-if endpoint's ``delta_vs_current``, and
|
|
78
|
+
the history endpoint's per-run ``delta`` (ADR-0108) — one derivation, one
|
|
79
|
+
schema, three call sites. ``None`` whenever either date it is measured
|
|
80
|
+
between is missing. Measured in working time from each date's edge-of-day
|
|
81
|
+
reading, still reported in calendar days.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
p50 = serializers.IntegerField(
|
|
85
|
+
allow_null=True, help_text=f"Signed P50 delta; positive = later. {_WORKING_TIME_DELTA_HELP}"
|
|
86
|
+
)
|
|
87
|
+
p80 = serializers.IntegerField(
|
|
88
|
+
allow_null=True, help_text=f"Signed P80 delta; positive = later. {_WORKING_TIME_DELTA_HELP}"
|
|
89
|
+
)
|
|
90
|
+
p95 = serializers.IntegerField(
|
|
91
|
+
allow_null=True, help_text=f"Signed P95 delta; positive = later. {_WORKING_TIME_DELTA_HELP}"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class MonteCarloHistogramBucketSerializer(serializers.Serializer[dict[str, Any]]):
|
|
96
|
+
"""One ascending finish-date bucket of a Monte Carlo distribution."""
|
|
97
|
+
|
|
98
|
+
date = serializers.DateField()
|
|
99
|
+
count = serializers.IntegerField()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class MonteCarloConfidencePointSerializer(serializers.Serializer[dict[str, Any]]):
|
|
103
|
+
"""One point of the cumulative P(finish <= date) S-curve derived from the histogram."""
|
|
104
|
+
|
|
105
|
+
date = serializers.DateField()
|
|
106
|
+
pct = serializers.FloatField()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class MonteCarloSensitivitySerializer(serializers.Serializer[dict[str, Any]]):
|
|
110
|
+
"""One task's duration-sensitivity tornado entry (ADR-0140).
|
|
111
|
+
|
|
112
|
+
``index`` is the absolute Spearman rank correlation between the task's
|
|
113
|
+
sampled duration and the project's finish, in [0, 1]. Tasks whose duration
|
|
114
|
+
cannot vary the finish are omitted from the list entirely rather than
|
|
115
|
+
reported as 0.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
task_id = serializers.CharField()
|
|
119
|
+
index = serializers.FloatField()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class MonteCarloDistributionFieldsSerializer(serializers.Serializer[dict[str, Any]]):
|
|
123
|
+
"""The ``{histogram_buckets, confidence_curve, sensitivity}`` shape (#1231).
|
|
124
|
+
|
|
125
|
+
Mixed into :class:`MonteCarloForecastSerializer` as flat top-level fields
|
|
126
|
+
(the live-run and ``latest`` payloads), and used standalone — nested under
|
|
127
|
+
a ``distribution`` key — by :meth:`MonteCarloRunSerializer.get_distribution`
|
|
128
|
+
for the history endpoint's opt-in ``?expand=distribution``. One field
|
|
129
|
+
definition, two positions on the wire.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
histogram_buckets = MonteCarloHistogramBucketSerializer(many=True)
|
|
133
|
+
confidence_curve = MonteCarloConfidencePointSerializer(many=True)
|
|
134
|
+
sensitivity = MonteCarloSensitivitySerializer(many=True)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class MonteCarloForecastDiagnosticSerializer(serializers.Serializer[dict[str, Any]]):
|
|
138
|
+
"""Why a Monte Carlo forecast carries -- or lacks -- an uncertainty band (#1340).
|
|
139
|
+
|
|
140
|
+
``reason`` is populated only when ``deterministic`` is true (the forecast
|
|
141
|
+
collapsed to a single date); it is ``None`` whenever a real band exists.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
deterministic = serializers.BooleanField()
|
|
145
|
+
reason = serializers.ChoiceField(
|
|
146
|
+
choices=[
|
|
147
|
+
FORECAST_NO_COMMITTED_TASKS,
|
|
148
|
+
FORECAST_ALL_COMPLETE,
|
|
149
|
+
FORECAST_ESTIMATES_BELOW_PLAN_DURATION,
|
|
150
|
+
FORECAST_ESTIMATES_OFF_CRITICAL_PATH,
|
|
151
|
+
FORECAST_ESTIMATES_PENDING_APPROVAL,
|
|
152
|
+
FORECAST_NO_VELOCITY_HISTORY,
|
|
153
|
+
FORECAST_NO_ESTIMATES,
|
|
154
|
+
],
|
|
155
|
+
allow_null=True,
|
|
156
|
+
)
|
|
157
|
+
tasks_total = serializers.IntegerField()
|
|
158
|
+
tasks_with_variance = serializers.IntegerField()
|
|
159
|
+
tasks_pending_approval = serializers.IntegerField()
|
|
160
|
+
agile_tasks_without_velocity = serializers.IntegerField()
|
|
161
|
+
tasks_estimates_below_plan = serializers.IntegerField(
|
|
162
|
+
required=False,
|
|
163
|
+
help_text=(
|
|
164
|
+
"Tasks whose whole three-point range sits at or below their planned "
|
|
165
|
+
"duration, which the engine floors every sample at (#3765) — so the "
|
|
166
|
+
"range samples to a constant. Absent on a run persisted before #3765 "
|
|
167
|
+
"and replayed from history; clients read an absent value as 0, the same "
|
|
168
|
+
"way they already treat an absent `diagnostic` as 'no reason known'."
|
|
169
|
+
),
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class RiskPremiumFieldsSerializer(serializers.Serializer[dict[str, Any]]):
|
|
174
|
+
"""The flat ``risk_premium_*`` family shared by every forecast payload (ADR-0698).
|
|
175
|
+
|
|
176
|
+
Mixed into :class:`MonteCarloForecastSerializer` rather than nested — the
|
|
177
|
+
view spreads these keys directly onto the response dict (``**risk_premium_from_values(...)``),
|
|
178
|
+
and the schema mirrors that shape.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
risk_premium_days = serializers.IntegerField(
|
|
182
|
+
allow_null=True,
|
|
183
|
+
help_text=f"P80 finish minus the deterministic CPM finish. {_WORKING_TIME_DELTA_HELP}",
|
|
184
|
+
)
|
|
185
|
+
risk_premium_ratio = serializers.FloatField(allow_null=True)
|
|
186
|
+
# Always null until #2299 (the calibration flywheel); declared nullable now so
|
|
187
|
+
# the schema does not have to change shape when a band is first populated.
|
|
188
|
+
risk_premium_band = serializers.CharField(allow_null=True)
|
|
189
|
+
risk_premium_as_of = serializers.DateTimeField(allow_null=True)
|
|
190
|
+
risk_premium_reason = serializers.CharField(allow_null=True)
|
|
191
|
+
risk_premium_state = serializers.ChoiceField(
|
|
192
|
+
choices=["not_run", "unmeasurable", "stale", "zero", "premium", "negative"]
|
|
193
|
+
)
|
|
194
|
+
risk_premium_cpm_finish = serializers.DateField(allow_null=True)
|
|
195
|
+
risk_premium_p80 = serializers.DateField(allow_null=True)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ForecastStalenessFieldsSerializer(serializers.Serializer[dict[str, Any]]):
|
|
199
|
+
"""The flat forecast-staleness family shared by every forecast payload (#3140).
|
|
200
|
+
|
|
201
|
+
Mixed into :class:`MonteCarloForecastSerializer` for the same reason as
|
|
202
|
+
:class:`RiskPremiumFieldsSerializer` — the view spreads
|
|
203
|
+
``**forecast_staleness_facts(...)`` directly onto the response dict.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
forecast_staleness = serializers.ChoiceField(
|
|
207
|
+
choices=["current", "project_changed", "aged", "unknown"]
|
|
208
|
+
)
|
|
209
|
+
plan_version = serializers.IntegerField(allow_null=True)
|
|
210
|
+
plan_version_current = serializers.IntegerField(allow_null=True)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class ScheduleDerivationContributionSerializer(serializers.Serializer[dict[str, Any]]):
|
|
214
|
+
"""One candidate constraint the CPM engine weighed for a derived value (ADR-0218).
|
|
215
|
+
|
|
216
|
+
Mirrors :meth:`trueppm_scheduler.derive.DerivationContribution.to_dict`. Exactly
|
|
217
|
+
one contribution in a given derivation's ``contributions`` list carries
|
|
218
|
+
``is_binding=True`` — the term the engine's ``max``/``min`` actually selected.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
kind = serializers.CharField()
|
|
222
|
+
source_task_id = serializers.CharField(allow_null=True)
|
|
223
|
+
source_task_name = serializers.CharField(allow_null=True)
|
|
224
|
+
dep_type = serializers.CharField(allow_null=True)
|
|
225
|
+
lag_days = serializers.IntegerField(allow_null=True)
|
|
226
|
+
imposed_date = serializers.DateField(allow_null=True)
|
|
227
|
+
calendar_days_added = serializers.IntegerField(allow_null=True)
|
|
228
|
+
slack_days = serializers.IntegerField(allow_null=True)
|
|
229
|
+
is_binding = serializers.BooleanField()
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@extend_schema_field({"oneOf": [{"type": "string"}, {"type": "integer"}], "nullable": True})
|
|
233
|
+
class ScheduleDerivationValueField(serializers.CharField):
|
|
234
|
+
"""A CPM quantity's derived value (ADR-0218).
|
|
235
|
+
|
|
236
|
+
An ISO-8601 date string for a date quantity, or a plain integer working-day
|
|
237
|
+
count for ``total_float``/``free_float`` — never both on one response, since the
|
|
238
|
+
shape is fixed by the ``quantity`` query param. Declaration-only: this field is
|
|
239
|
+
never bound to real data (:class:`ScheduleDerivationView` returns a plain dict),
|
|
240
|
+
so the inherited ``CharField`` behavior is never exercised — only the
|
|
241
|
+
``@extend_schema_field`` override, which states the true wire type.
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class ScheduleCpmDerivationSerializer(serializers.Serializer[dict[str, Any]]):
|
|
246
|
+
"""CPM-quantity derivation payload — the *why* behind one computed value (ADR-0218).
|
|
247
|
+
|
|
248
|
+
Mirrors :meth:`trueppm_scheduler.derive.Derivation.to_dict`. Returned for a CPM
|
|
249
|
+
``quantity`` (``early_start``/``early_finish``/``late_start``/``late_finish``/
|
|
250
|
+
``total_float``/``free_float``/``scheduled_start``); see
|
|
251
|
+
:class:`ScheduleMonteCarloDerivationSerializer` for the percentile shape returned
|
|
252
|
+
for ``p50``/``p80``/``p95``.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
task_id = serializers.CharField()
|
|
256
|
+
task_name = serializers.CharField()
|
|
257
|
+
quantity = serializers.ChoiceField(choices=[q.value for q in CpmDerivationQuantity])
|
|
258
|
+
value = ScheduleDerivationValueField(allow_null=True)
|
|
259
|
+
is_critical = serializers.BooleanField()
|
|
260
|
+
binding = ScheduleDerivationContributionSerializer(allow_null=True)
|
|
261
|
+
contributions = ScheduleDerivationContributionSerializer(many=True)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# "pass" is a Python keyword and cannot be a class-body attribute name, but the wire
|
|
265
|
+
# key is unsuffixed — Derivation.to_dict() emits it as "pass", not "pass_" — so the
|
|
266
|
+
# field is added to the declared-fields map directly instead of following the
|
|
267
|
+
# trailing-underscore convention into the schema, which would misdeclare the key.
|
|
268
|
+
ScheduleCpmDerivationSerializer._declared_fields["pass"] = serializers.ChoiceField(
|
|
269
|
+
choices=["forward", "backward", "float"]
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class ScheduleMonteCarloDerivationSerializer(ForecastStalenessFieldsSerializer):
|
|
274
|
+
"""Monte Carlo percentile derivation payload (ADR-0218, #987, #3140).
|
|
275
|
+
|
|
276
|
+
Mirrors the dict built by ``ScheduleDerivationView._monte_carlo_derivation`` —
|
|
277
|
+
shared verbatim by its cache-hit and persisted-run-fallback branches. Returned for
|
|
278
|
+
a percentile ``quantity`` (``p50``/``p80``/``p95``); see
|
|
279
|
+
:class:`ScheduleCpmDerivationSerializer` for the CPM shape.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
quantity = serializers.ChoiceField(choices=["p50", "p80", "p95"])
|
|
283
|
+
value = serializers.DateField(allow_null=True)
|
|
284
|
+
value_at_day_start = serializers.BooleanField(allow_null=True, help_text=_AT_DAY_START_HELP)
|
|
285
|
+
cpm_finish = serializers.DateField(allow_null=True)
|
|
286
|
+
cpm_finish_at_day_start = serializers.BooleanField(
|
|
287
|
+
allow_null=True, help_text=_AT_DAY_START_HELP
|
|
288
|
+
)
|
|
289
|
+
delta_vs_cpm_days = serializers.IntegerField(
|
|
290
|
+
allow_null=True,
|
|
291
|
+
help_text=f"The percentile minus the deterministic CPM finish. {_WORKING_TIME_DELTA_HELP}",
|
|
292
|
+
)
|
|
293
|
+
drivers = MonteCarloSensitivitySerializer(many=True)
|
|
294
|
+
runs = serializers.IntegerField()
|
|
295
|
+
last_run_at = serializers.DateTimeField()
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# Same reserved-keyword workaround as ScheduleCpmDerivationSerializer above — this
|
|
299
|
+
# payload's "pass" is always the literal "monte_carlo".
|
|
300
|
+
ScheduleMonteCarloDerivationSerializer._declared_fields["pass"] = serializers.ChoiceField(
|
|
301
|
+
choices=["monte_carlo"]
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class MonteCarloForecastSerializer(
|
|
306
|
+
RiskPremiumFieldsSerializer,
|
|
307
|
+
ForecastStalenessFieldsSerializer,
|
|
308
|
+
MonteCarloDistributionFieldsSerializer,
|
|
309
|
+
):
|
|
310
|
+
"""Monte Carlo forecast payload shared by the live run and the ``latest`` read.
|
|
311
|
+
|
|
312
|
+
Two response-producing paths share this exact shape and are not identical on
|
|
313
|
+
the wire:
|
|
314
|
+
|
|
315
|
+
- The **live run** (``POST .../monte-carlo/``) and a **cache hit**
|
|
316
|
+
(``GET .../monte-carlo/latest/`` inside the 24h TTL) always carry
|
|
317
|
+
``distribution`` (the full sorted per-run finish-date sample — the large
|
|
318
|
+
field) and never carry ``from_history``.
|
|
319
|
+
- The **persisted-history fallback** (``GET .../latest/`` once the cache
|
|
320
|
+
entry has expired, ADR-0175) always carries ``from_history: true`` and
|
|
321
|
+
never carries ``distribution`` — only the derived
|
|
322
|
+
``histogram_buckets``/``confidence_curve``/``sensitivity`` (and
|
|
323
|
+
``forecast_diagnostic``) survive past the TTL, and only for runs
|
|
324
|
+
persisted after #1231/#2483.
|
|
325
|
+
|
|
326
|
+
Both fields are declared ``required=False`` rather than modeled as two
|
|
327
|
+
named variants (``PolymorphicProxySerializer``): nothing else about the
|
|
328
|
+
shape differs between the two paths, so a discriminated union would only
|
|
329
|
+
duplicate every other field for no added precision.
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
project_id = serializers.CharField()
|
|
333
|
+
runs = serializers.IntegerField()
|
|
334
|
+
# Nullable: a project with no committed tasks yields no distribution to
|
|
335
|
+
# anchor the percentiles on (mirrors MonteCarloRun.p50/p80/p95).
|
|
336
|
+
p50 = serializers.DateField(allow_null=True)
|
|
337
|
+
p80 = serializers.DateField(allow_null=True)
|
|
338
|
+
p95 = serializers.DateField(allow_null=True)
|
|
339
|
+
p50_at_day_start = serializers.BooleanField(allow_null=True, help_text=_AT_DAY_START_HELP)
|
|
340
|
+
p80_at_day_start = serializers.BooleanField(allow_null=True, help_text=_AT_DAY_START_HELP)
|
|
341
|
+
p95_at_day_start = serializers.BooleanField(allow_null=True, help_text=_AT_DAY_START_HELP)
|
|
342
|
+
distribution = serializers.ListField(
|
|
343
|
+
child=serializers.DateField(),
|
|
344
|
+
required=False,
|
|
345
|
+
help_text=(
|
|
346
|
+
"Full sorted per-run finish-date sample. Present on a live run or a "
|
|
347
|
+
"cache hit; absent on the persisted-history fallback."
|
|
348
|
+
),
|
|
349
|
+
)
|
|
350
|
+
cpm_finish = serializers.DateField(allow_null=True)
|
|
351
|
+
cpm_finish_at_day_start = serializers.BooleanField(
|
|
352
|
+
allow_null=True, help_text=_AT_DAY_START_HELP
|
|
353
|
+
)
|
|
354
|
+
delta_vs_cpm = MonteCarloDeltaSerializer()
|
|
355
|
+
forecast_diagnostic = MonteCarloForecastDiagnosticSerializer(allow_null=True)
|
|
356
|
+
last_run_at = serializers.DateTimeField()
|
|
357
|
+
status_date = serializers.DateField(allow_null=True)
|
|
358
|
+
from_history = serializers.BooleanField(
|
|
359
|
+
required=False,
|
|
360
|
+
help_text=(
|
|
361
|
+
"True only on the persisted-history fallback past the 24h cache TTL; "
|
|
362
|
+
"absent (never false) on a live run or cache hit."
|
|
363
|
+
),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
class MonteCarloWhatIfAppliedSerializer(serializers.Serializer[dict[str, Any]]):
|
|
368
|
+
"""The resolved perturbation the what-if endpoint applied to the target task (#993)."""
|
|
369
|
+
|
|
370
|
+
base_duration_days = serializers.IntegerField()
|
|
371
|
+
duration_delta_days = serializers.IntegerField()
|
|
372
|
+
new_duration_days = serializers.IntegerField()
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class MonteCarloWhatIfLegSerializer(serializers.Serializer[dict[str, Any]]):
|
|
376
|
+
"""One forecast leg (``current`` or ``whatif``) of the what-if response (#993)."""
|
|
377
|
+
|
|
378
|
+
p50 = serializers.DateField()
|
|
379
|
+
p80 = serializers.DateField()
|
|
380
|
+
p95 = serializers.DateField()
|
|
381
|
+
p50_at_day_start = serializers.BooleanField(help_text=_AT_DAY_START_HELP)
|
|
382
|
+
p80_at_day_start = serializers.BooleanField(help_text=_AT_DAY_START_HELP)
|
|
383
|
+
p95_at_day_start = serializers.BooleanField(help_text=_AT_DAY_START_HELP)
|
|
384
|
+
cpm_finish = serializers.DateField(allow_null=True)
|
|
385
|
+
cpm_finish_at_day_start = serializers.BooleanField(
|
|
386
|
+
allow_null=True, help_text=_AT_DAY_START_HELP
|
|
387
|
+
)
|
|
388
|
+
critical_path = serializers.ListField(child=serializers.CharField())
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class MonteCarloWhatIfDeltaSerializer(serializers.Serializer[dict[str, Any]]):
|
|
392
|
+
"""Signed shift of ``whatif`` vs ``current``, per field, in working time (#993/#4204).
|
|
393
|
+
|
|
394
|
+
Same ``p50``/``p80``/``p95`` shape as :class:`MonteCarloDeltaSerializer`,
|
|
395
|
+
plus ``cpm_finish`` — the what-if endpoint's own deterministic-pass delta,
|
|
396
|
+
distinct from ``delta_vs_cpm``'s MC-vs-CPM comparison elsewhere. All four are
|
|
397
|
+
measured in working time and reported in calendar days.
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
p50 = serializers.IntegerField(
|
|
401
|
+
allow_null=True, help_text=f"Signed P50 shift; positive = later. {_WORKING_TIME_DELTA_HELP}"
|
|
402
|
+
)
|
|
403
|
+
p80 = serializers.IntegerField(
|
|
404
|
+
allow_null=True, help_text=f"Signed P80 shift; positive = later. {_WORKING_TIME_DELTA_HELP}"
|
|
405
|
+
)
|
|
406
|
+
p95 = serializers.IntegerField(
|
|
407
|
+
allow_null=True, help_text=f"Signed P95 shift; positive = later. {_WORKING_TIME_DELTA_HELP}"
|
|
408
|
+
)
|
|
409
|
+
cpm_finish = serializers.IntegerField(allow_null=True, help_text=_WORKING_TIME_DELTA_HELP)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class MonteCarloWhatIfResponseSerializer(serializers.Serializer[dict[str, Any]]):
|
|
413
|
+
"""Non-mutating Monte Carlo what-if result (#993).
|
|
414
|
+
|
|
415
|
+
Perturbs exactly one committed task's duration and recomputes both the
|
|
416
|
+
deterministic CPM pass and a seeded Monte Carlo simulation in memory,
|
|
417
|
+
without persisting anything — see the view docstring for the
|
|
418
|
+
non-persistence guarantee.
|
|
419
|
+
"""
|
|
420
|
+
|
|
421
|
+
task_id = serializers.CharField()
|
|
422
|
+
applied = MonteCarloWhatIfAppliedSerializer()
|
|
423
|
+
current = MonteCarloWhatIfLegSerializer()
|
|
424
|
+
whatif = MonteCarloWhatIfLegSerializer()
|
|
425
|
+
critical_path_changed = serializers.BooleanField()
|
|
426
|
+
delta_vs_current = MonteCarloWhatIfDeltaSerializer()
|
|
427
|
+
runs = serializers.IntegerField()
|
|
428
|
+
seed = serializers.IntegerField(
|
|
429
|
+
help_text="Fixed RNG seed shared by both runs so the delta isolates the perturbation."
|
|
430
|
+
)
|
|
431
|
+
cpm_status_date = serializers.DateField()
|
|
432
|
+
mc_status_date = serializers.DateField()
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class ProjectForecastSnapshotSerializer(serializers.ModelSerializer[ProjectForecastSnapshot]):
|
|
436
|
+
"""Read-only serializer for a project-grain forecast snapshot (ADR-0154, #388).
|
|
437
|
+
|
|
438
|
+
Server-generated history; the endpoint is list-only, so every field is
|
|
439
|
+
read-only and there is no create/update path.
|
|
440
|
+
|
|
441
|
+
``cpm_finish_at_day_start`` is published so a client diffing ``cpm_finish``
|
|
442
|
+
across snapshots can do it in working time (#4178): a start-of-day finish on
|
|
443
|
+
a Monday is the same finish as the end of the Friday before it.
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
cpm_finish_at_day_start = serializers.BooleanField(
|
|
447
|
+
read_only=True,
|
|
448
|
+
allow_null=True,
|
|
449
|
+
help_text=(
|
|
450
|
+
"True when cpm_finish is the START of that day: every task finishing on "
|
|
451
|
+
"it is a milestone shown at the start of its day. The start of a day is "
|
|
452
|
+
"the same point in working time as the end of the working day before "
|
|
453
|
+
"it, so compare finishes in working time, not as a date difference. "
|
|
454
|
+
"Null when unknown: no finish, or a snapshot captured before this field "
|
|
455
|
+
"existed."
|
|
456
|
+
),
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
class Meta:
|
|
460
|
+
model = ProjectForecastSnapshot
|
|
461
|
+
fields = [
|
|
462
|
+
"id",
|
|
463
|
+
"captured_at",
|
|
464
|
+
"triggered_by",
|
|
465
|
+
"cpm_finish",
|
|
466
|
+
"cpm_finish_at_day_start",
|
|
467
|
+
"total_float_days",
|
|
468
|
+
"mc_p50_finish",
|
|
469
|
+
"mc_p80_finish",
|
|
470
|
+
"mc_p95_finish",
|
|
471
|
+
"mc_iterations",
|
|
472
|
+
"task_count",
|
|
473
|
+
"completed_task_count",
|
|
474
|
+
]
|
|
475
|
+
read_only_fields = fields
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class FailedTaskSerializer(serializers.ModelSerializer[FailedTask]):
|
|
479
|
+
"""Read-only serializer for the failed-task dead-letter queue.
|
|
480
|
+
|
|
481
|
+
Exposed by the admin API so operators can inspect and requeue or discard
|
|
482
|
+
tasks that exceeded their retry budget. The ``resolution_*`` fields surface the
|
|
483
|
+
operator-action audit (ADR-0210): who requeued/dropped the task, when, and the
|
|
484
|
+
drop note.
|
|
485
|
+
"""
|
|
486
|
+
|
|
487
|
+
resolved_by_display = serializers.SerializerMethodField()
|
|
488
|
+
|
|
489
|
+
class Meta:
|
|
490
|
+
model = FailedTask
|
|
491
|
+
fields = [
|
|
492
|
+
"id",
|
|
493
|
+
"task_name",
|
|
494
|
+
"task_id",
|
|
495
|
+
"project_id",
|
|
496
|
+
"args",
|
|
497
|
+
"kwargs",
|
|
498
|
+
"exception_type",
|
|
499
|
+
"exception_message",
|
|
500
|
+
"traceback",
|
|
501
|
+
"failure_count",
|
|
502
|
+
"first_failed_at",
|
|
503
|
+
"last_failed_at",
|
|
504
|
+
"status",
|
|
505
|
+
"resolution_note",
|
|
506
|
+
"resolved_by_display",
|
|
507
|
+
"resolved_at",
|
|
508
|
+
]
|
|
509
|
+
read_only_fields = fields
|
|
510
|
+
|
|
511
|
+
def get_resolved_by_display(self, obj: FailedTask) -> str | None:
|
|
512
|
+
"""Display name of the operator who last acted, or None if unresolved."""
|
|
513
|
+
user: Any = obj.resolved_by
|
|
514
|
+
if user is None:
|
|
515
|
+
return None
|
|
516
|
+
full_name = user.get_full_name() if hasattr(user, "get_full_name") else ""
|
|
517
|
+
return full_name or user.get_username()
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
class FailedTaskRequeueSerializer(serializers.Serializer[dict[str, Any]]):
|
|
521
|
+
"""Validate the requeue-with-backoff request body (ADR-0210).
|
|
522
|
+
|
|
523
|
+
``backoff_seconds`` is the operator-chosen delay applied as a Celery
|
|
524
|
+
``countdown`` on the re-dispatched task. Bounded to a day so a fat-fingered
|
|
525
|
+
value cannot park a task for a year; ``0`` means dispatch immediately.
|
|
526
|
+
"""
|
|
527
|
+
|
|
528
|
+
backoff_seconds = serializers.IntegerField(
|
|
529
|
+
required=False,
|
|
530
|
+
default=0,
|
|
531
|
+
min_value=0,
|
|
532
|
+
max_value=86_400,
|
|
533
|
+
help_text="Backoff before re-dispatch, in seconds (0-86400). 0 = immediate.",
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
class FailedTaskDropSerializer(serializers.Serializer[dict[str, Any]]):
|
|
538
|
+
"""Validate the drop-with-note request body (ADR-0210).
|
|
539
|
+
|
|
540
|
+
``note`` is optional free-text audit context. Bounded and trimmed; stored as
|
|
541
|
+
data and rendered as text (never interpolated into task args), so it carries no
|
|
542
|
+
injection surface.
|
|
543
|
+
"""
|
|
544
|
+
|
|
545
|
+
note = serializers.CharField(
|
|
546
|
+
required=False,
|
|
547
|
+
allow_blank=True,
|
|
548
|
+
default="",
|
|
549
|
+
max_length=1000,
|
|
550
|
+
trim_whitespace=True,
|
|
551
|
+
help_text="Optional audit note recorded with the drop.",
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
class VelocitySuggestionSerializer(serializers.ModelSerializer[VelocitySuggestion]):
|
|
556
|
+
"""Read serializer for a velocity-calibration suggestion (ADR-0065).
|
|
557
|
+
|
|
558
|
+
Surfaces the sprint that triggered the suggestion (name + id) so the Task
|
|
559
|
+
Detail Drawer can render "Suggested from Sprint 12 close" without a second
|
|
560
|
+
fetch. The accept/dismiss audit fields are exposed so the drawer can also
|
|
561
|
+
render a quiet history row once a decision is made.
|
|
562
|
+
"""
|
|
563
|
+
|
|
564
|
+
sprint_name = serializers.CharField(source="sprint.name", read_only=True)
|
|
565
|
+
sprint_id = serializers.UUIDField(source="sprint.id", read_only=True)
|
|
566
|
+
is_pending = serializers.BooleanField(read_only=True)
|
|
567
|
+
# Both team_velocity_per_day and suggested_duration are declared explicitly with
|
|
568
|
+
# allow_null because to_representation nulls them for readers below the velocity
|
|
569
|
+
# audience (ADR-0104 gate, #949/#1099) — the schema must advertise the suppressed
|
|
570
|
+
# shape for schema-driven clients (#997 contract class).
|
|
571
|
+
team_velocity_per_day = serializers.DecimalField(
|
|
572
|
+
max_digits=6,
|
|
573
|
+
decimal_places=3,
|
|
574
|
+
read_only=True,
|
|
575
|
+
allow_null=True,
|
|
576
|
+
help_text="Rolling 6-sprint average of completed_points / sprint_working_days.",
|
|
577
|
+
)
|
|
578
|
+
# suggested_duration is computed *from* team_velocity_per_day
|
|
579
|
+
# (round(story_points / velocity)); leaving it ungated lets a below-audience
|
|
580
|
+
# reader back into the team's pace via the calibration value, so it is gated by
|
|
581
|
+
# the same velocity check (#1099 — a new instance of the #949 leak class).
|
|
582
|
+
suggested_duration = serializers.IntegerField(
|
|
583
|
+
read_only=True,
|
|
584
|
+
allow_null=True,
|
|
585
|
+
help_text=(
|
|
586
|
+
"Velocity-calibrated duration in working days; null when the reader is "
|
|
587
|
+
"below the velocity audience."
|
|
588
|
+
),
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
class Meta:
|
|
592
|
+
model = VelocitySuggestion
|
|
593
|
+
fields = [
|
|
594
|
+
"id",
|
|
595
|
+
"task",
|
|
596
|
+
"sprint_id",
|
|
597
|
+
"sprint_name",
|
|
598
|
+
"suggested_duration",
|
|
599
|
+
"team_velocity_per_day",
|
|
600
|
+
"flag_for_review",
|
|
601
|
+
"is_pending",
|
|
602
|
+
"created_at",
|
|
603
|
+
"accepted_at",
|
|
604
|
+
"accepted_by",
|
|
605
|
+
"dismissed_at",
|
|
606
|
+
"dismissed_by",
|
|
607
|
+
]
|
|
608
|
+
read_only_fields = fields
|
|
609
|
+
|
|
610
|
+
# Velocity-derived fields stripped for a reader below the velocity audience.
|
|
611
|
+
# team_velocity_per_day is the raw rate; suggested_duration is computed from it
|
|
612
|
+
# (#1099) — both must fall to the same gate or the rate leaks via the suggestion.
|
|
613
|
+
_VELOCITY_GATED_FIELDS = ("team_velocity_per_day", "suggested_duration")
|
|
614
|
+
|
|
615
|
+
def to_representation(self, instance: VelocitySuggestion) -> dict[str, Any]:
|
|
616
|
+
data = super().to_representation(instance)
|
|
617
|
+
# ADR-0104 velocity gate (#949/#1099): these are the same point-based velocity
|
|
618
|
+
# number — and a value derived from it — that suppress_velocity_summary strips
|
|
619
|
+
# from /velocity/. A reader below the velocity audience is suppressed there, so
|
|
620
|
+
# they must not recover it from this calibration-suggestion surface.
|
|
621
|
+
request = self.context.get("request")
|
|
622
|
+
# Fail closed: a render with no request context can't establish the
|
|
623
|
+
# reader's tier, so suppress rather than leak (the only callers are HTTP
|
|
624
|
+
# responses, which always carry a request).
|
|
625
|
+
if request is None:
|
|
626
|
+
for field in self._VELOCITY_GATED_FIELDS:
|
|
627
|
+
data[field] = None
|
|
628
|
+
return data
|
|
629
|
+
# The verdict is per-project; cache it on the (reused) child serializer so
|
|
630
|
+
# a list render is not N+1 on the gate query.
|
|
631
|
+
project_id = instance.task.project_id
|
|
632
|
+
cache: dict[Any, bool] | None = getattr(self, "_velocity_gate_cache", None)
|
|
633
|
+
if cache is None:
|
|
634
|
+
cache = {}
|
|
635
|
+
self._velocity_gate_cache = cache
|
|
636
|
+
if project_id not in cache:
|
|
637
|
+
from trueppm_api.apps.projects.signal_privacy_services import can_read_signal
|
|
638
|
+
|
|
639
|
+
cache[project_id] = can_read_signal(request, project_id, "velocity")
|
|
640
|
+
if not cache[project_id]:
|
|
641
|
+
for field in self._VELOCITY_GATED_FIELDS:
|
|
642
|
+
data[field] = None
|
|
643
|
+
return data
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class MonteCarloRunSerializer(serializers.ModelSerializer[MonteCarloRun]):
|
|
647
|
+
"""Read serializer for one project Monte Carlo run in the forecast history (ADR-0175).
|
|
648
|
+
|
|
649
|
+
Two pieces of context the view must supply:
|
|
650
|
+
|
|
651
|
+
- ``delta`` is computed-on-read (ADR-0108): the view attaches ``_delta`` to
|
|
652
|
+
each instance — a ``{"p50"|"p80"|"p95": signed-int-days|None}`` map of the
|
|
653
|
+
change versus the immediately-previous (older) run, or ``None`` on the
|
|
654
|
+
oldest/baseline row. Positive = the forecast slipped later (worse).
|
|
655
|
+
- ``triggered_by_name`` ("who ran it") is emitted **only** when the context
|
|
656
|
+
flag ``can_see_attribution`` is true (requester is Admin/Owner). For every
|
|
657
|
+
other member the field is ``None`` so forecast drift cannot be read as a
|
|
658
|
+
named-individual performance signal (VoC Morgan). The FK is never exposed
|
|
659
|
+
directly; only a display name, and only to admins.
|
|
660
|
+
"""
|
|
661
|
+
|
|
662
|
+
delta = serializers.SerializerMethodField()
|
|
663
|
+
triggered_by_name = serializers.SerializerMethodField()
|
|
664
|
+
distribution = serializers.SerializerMethodField()
|
|
665
|
+
|
|
666
|
+
class Meta:
|
|
667
|
+
model = MonteCarloRun
|
|
668
|
+
fields = [
|
|
669
|
+
"id",
|
|
670
|
+
"taken_at",
|
|
671
|
+
"p50",
|
|
672
|
+
"p80",
|
|
673
|
+
"p95",
|
|
674
|
+
"cpm_finish",
|
|
675
|
+
"p50_at_day_start",
|
|
676
|
+
"p80_at_day_start",
|
|
677
|
+
"p95_at_day_start",
|
|
678
|
+
"cpm_finish_at_day_start",
|
|
679
|
+
"n_simulations",
|
|
680
|
+
"task_count",
|
|
681
|
+
"status_date",
|
|
682
|
+
"delta",
|
|
683
|
+
"triggered_by_name",
|
|
684
|
+
"distribution",
|
|
685
|
+
]
|
|
686
|
+
read_only_fields = fields
|
|
687
|
+
|
|
688
|
+
@extend_schema_field(MonteCarloDeltaSerializer(allow_null=True))
|
|
689
|
+
def get_delta(self, obj: MonteCarloRun) -> dict[str, int | None] | None:
|
|
690
|
+
"""Return the per-percentile day delta vs the previous run (view-attached)."""
|
|
691
|
+
return getattr(obj, "_delta", None)
|
|
692
|
+
|
|
693
|
+
@extend_schema_field(serializers.CharField(allow_null=True))
|
|
694
|
+
def get_triggered_by_name(self, obj: MonteCarloRun) -> str | None:
|
|
695
|
+
"""Run-author display name — gated by the resolved attribution audience.
|
|
696
|
+
|
|
697
|
+
The view sets ``can_see_attribution`` per the workspace-effective
|
|
698
|
+
``mc_history_attribution_audience`` (ADR-0144): ADMIN_OWNER → Admin/Owner,
|
|
699
|
+
SCHEDULER_PLUS → Scheduler+, NONE → nobody. None when the reader is below
|
|
700
|
+
the audience so forecast drift cannot become a named-individual signal.
|
|
701
|
+
"""
|
|
702
|
+
if not self.context.get("can_see_attribution"):
|
|
703
|
+
return None
|
|
704
|
+
user: Any = obj.triggered_by
|
|
705
|
+
if user is None:
|
|
706
|
+
return None
|
|
707
|
+
full_name = user.get_full_name() if hasattr(user, "get_full_name") else ""
|
|
708
|
+
return full_name or user.get_username()
|
|
709
|
+
|
|
710
|
+
@extend_schema_field(MonteCarloDistributionFieldsSerializer(allow_null=True))
|
|
711
|
+
def get_distribution(self, obj: MonteCarloRun) -> dict[str, Any] | None:
|
|
712
|
+
"""The persisted per-run distribution — only when explicitly expanded (#1231).
|
|
713
|
+
|
|
714
|
+
Returns the stored ``{histogram_buckets, confidence_curve, sensitivity}``
|
|
715
|
+
payload (snake_case, matching the ``/latest/`` response shape) ONLY when the
|
|
716
|
+
view sets ``context['expand_distribution']`` — the history *list* stays
|
|
717
|
+
lightweight by default and a single detail fetch opts in via
|
|
718
|
+
``?expand=distribution``. ``None`` for legacy runs with no stored
|
|
719
|
+
distribution, or whenever the flag is unset.
|
|
720
|
+
"""
|
|
721
|
+
if not self.context.get("expand_distribution"):
|
|
722
|
+
return None
|
|
723
|
+
return obj.distribution
|