trueppm-api 0.4.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trueppm_api/__init__.py +14 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +164 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +167 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +132 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +2837 -0
- trueppm_api/apps/access/serializers.py +946 -0
- trueppm_api/apps/access/services.py +667 -0
- trueppm_api/apps/access/signals.py +103 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +95 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1931 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +108 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +418 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +177 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +837 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +403 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +622 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +519 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +731 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1106 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +277 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +500 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1231 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +436 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +447 -0
- trueppm_api/apps/notifications/email_backend.py +330 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +294 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1197 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +961 -0
- trueppm_api/apps/notifications/services.py +1451 -0
- trueppm_api/apps/notifications/tasks.py +711 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +904 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +551 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +969 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +523 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +466 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +449 -0
- trueppm_api/apps/projects/backfill.py +287 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +204 -0
- trueppm_api/apps/projects/config_notice.py +1325 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +93 -0
- trueppm_api/apps/projects/decisions_views.py +94 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +353 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +247 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +44 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +61 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +62 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +8946 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +575 -0
- trueppm_api/apps/projects/program_schedule.py +745 -0
- trueppm_api/apps/projects/program_views.py +3220 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +493 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/exporter.py +1644 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2390 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +126 -0
- trueppm_api/apps/projects/seed/replay.py +1691 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1149 -0
- trueppm_api/apps/projects/serializers.py +12310 -0
- trueppm_api/apps/projects/services.py +6966 -0
- trueppm_api/apps/projects/share_serializers.py +132 -0
- trueppm_api/apps/projects/share_services.py +325 -0
- trueppm_api/apps/projects/share_views.py +374 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +909 -0
- trueppm_api/apps/projects/signal_privacy_views.py +476 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +266 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1152 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1917 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +258 -0
- trueppm_api/apps/projects/template_views.py +799 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1011 -0
- trueppm_api/apps/projects/utilization.py +885 -0
- trueppm_api/apps/projects/views.py +20572 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1749 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +119 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +516 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +283 -0
- trueppm_api/apps/scheduling/serializers.py +564 -0
- trueppm_api/apps/scheduling/services.py +1182 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +1984 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2564 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +437 -0
- trueppm_api/apps/sso/services.py +1385 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +320 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +471 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +225 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +371 -0
- trueppm_api/apps/sync/serializers.py +687 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +501 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +930 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +142 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +434 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +117 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +526 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +144 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +90 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +372 -0
- trueppm_api/apps/webhooks/serializers.py +318 -0
- trueppm_api/apps/webhooks/tasks.py +663 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +383 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +368 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +242 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +919 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +555 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1502 -0
- trueppm_api/asgi.py +42 -0
- trueppm_api/celery.py +31 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +916 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +949 -0
- trueppm_api/core/password_reset.py +500 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +884 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +344 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2455 -0
- trueppm_api/settings/dev.py +273 -0
- trueppm_api/settings/prod.py +228 -0
- trueppm_api/urls.py +153 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0b2.dist-info/METADATA +70 -0
- trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
- trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Serializers for the retention policy editor + purge runs (ADR-0173).
|
|
2
|
+
|
|
3
|
+
Also holds the telemetry test-export result serializer (ADR-0223 follow-up, #2110).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from rest_framework import serializers
|
|
11
|
+
|
|
12
|
+
from trueppm_api.apps.observability.models import PurgeRun, RetentionSchedule
|
|
13
|
+
from trueppm_api.apps.observability.retention import RETENTION_KEYS
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TelemetryTestExportResultSerializer(serializers.Serializer[Any]):
|
|
17
|
+
"""Result of ``POST /health/telemetry/test/`` (#2110).
|
|
18
|
+
|
|
19
|
+
Serializes the dict from ``services.run_telemetry_test_export``. Deliberately
|
|
20
|
+
has **no** field for OTLP headers or the bearer token — the export secret must
|
|
21
|
+
never reach the browser — and ``detail`` is a canned, author-controlled
|
|
22
|
+
sentence (never ``str(exc)``), so a transport error cannot leak config.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
mode = serializers.ChoiceField(choices=["export", "probe"])
|
|
26
|
+
outcome = serializers.ChoiceField(choices=["success", "reachable", "failure"])
|
|
27
|
+
endpoint = serializers.CharField(allow_blank=True)
|
|
28
|
+
protocol = serializers.CharField()
|
|
29
|
+
duration_ms = serializers.IntegerField()
|
|
30
|
+
detail = serializers.CharField()
|
|
31
|
+
checked_at = serializers.DateTimeField()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RetentionPolicyReadSerializer(serializers.Serializer[Any]):
|
|
35
|
+
"""One table's policy row + estimated stats (read side; serializes a dict)."""
|
|
36
|
+
|
|
37
|
+
key = serializers.CharField()
|
|
38
|
+
# `label` shadows Field.label (str | None) under drf-stubs — expected for a
|
|
39
|
+
# field of that name; same pattern as the `source` field elsewhere.
|
|
40
|
+
label = serializers.CharField() # type: ignore[assignment]
|
|
41
|
+
note = serializers.CharField()
|
|
42
|
+
unit = serializers.ChoiceField(choices=["days", "hours"])
|
|
43
|
+
value = serializers.IntegerField()
|
|
44
|
+
enabled = serializers.BooleanField()
|
|
45
|
+
row_count = serializers.IntegerField()
|
|
46
|
+
bytes = serializers.IntegerField(allow_null=True)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class RetentionScheduleSerializer(serializers.ModelSerializer[RetentionSchedule]):
|
|
50
|
+
"""The purge-schedule singleton (read + write)."""
|
|
51
|
+
|
|
52
|
+
class Meta:
|
|
53
|
+
model = RetentionSchedule
|
|
54
|
+
fields = ["frequency", "time_of_day_utc", "day_of_week", "on_failure"]
|
|
55
|
+
|
|
56
|
+
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
|
57
|
+
if (
|
|
58
|
+
attrs.get("frequency") == RetentionSchedule.Frequency.WEEKLY
|
|
59
|
+
and attrs.get("day_of_week") is None
|
|
60
|
+
):
|
|
61
|
+
raise serializers.ValidationError({"day_of_week": "Required when frequency is weekly."})
|
|
62
|
+
day = attrs.get("day_of_week")
|
|
63
|
+
if day is not None and not (0 <= day <= 6):
|
|
64
|
+
raise serializers.ValidationError({"day_of_week": "Must be 0 (Mon) to 6 (Sun)."})
|
|
65
|
+
return attrs
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PurgeRunSerializer(serializers.ModelSerializer[PurgeRun]):
|
|
69
|
+
"""A recorded purge run with a computed duration."""
|
|
70
|
+
|
|
71
|
+
duration_ms = serializers.SerializerMethodField()
|
|
72
|
+
|
|
73
|
+
class Meta:
|
|
74
|
+
model = PurgeRun
|
|
75
|
+
fields = [
|
|
76
|
+
"id",
|
|
77
|
+
"started_at",
|
|
78
|
+
"finished_at",
|
|
79
|
+
"trigger",
|
|
80
|
+
"state",
|
|
81
|
+
"tables",
|
|
82
|
+
"rows_deleted",
|
|
83
|
+
"bytes_freed",
|
|
84
|
+
"error",
|
|
85
|
+
"duration_ms",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
def get_duration_ms(self, obj: PurgeRun) -> int | None:
|
|
89
|
+
if obj.finished_at is None:
|
|
90
|
+
return None
|
|
91
|
+
return int((obj.finished_at - obj.started_at).total_seconds() * 1000)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class RetentionStateSerializer(serializers.Serializer[Any]):
|
|
95
|
+
"""Full editor payload (serializes the dict from ``get_retention_state``)."""
|
|
96
|
+
|
|
97
|
+
policies = RetentionPolicyReadSerializer(many=True)
|
|
98
|
+
schedule = RetentionScheduleSerializer()
|
|
99
|
+
runs = PurgeRunSerializer(many=True)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class RetentionPolicyWriteSerializer(serializers.Serializer[Any]):
|
|
103
|
+
"""One policy override in a save-bar PATCH."""
|
|
104
|
+
|
|
105
|
+
key = serializers.ChoiceField(choices=RETENTION_KEYS)
|
|
106
|
+
value = serializers.IntegerField(min_value=1)
|
|
107
|
+
enabled = serializers.BooleanField()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class RetentionUpdateSerializer(serializers.Serializer[Any]):
|
|
111
|
+
"""The save-bar payload: any subset of policy overrides + the schedule."""
|
|
112
|
+
|
|
113
|
+
policies = RetentionPolicyWriteSerializer(many=True, required=False)
|
|
114
|
+
schedule = RetentionScheduleSerializer(required=False)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class RetentionImpactQuerySerializer(serializers.Serializer[Any]):
|
|
118
|
+
"""Query params for the lower-value impact estimate. ``value`` is in the
|
|
119
|
+
window's native unit (days for four tables, hours for sync batches)."""
|
|
120
|
+
|
|
121
|
+
key = serializers.ChoiceField(choices=RETENTION_KEYS)
|
|
122
|
+
value = serializers.IntegerField(min_value=0)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class RetentionImpactSerializer(serializers.Serializer[Any]):
|
|
126
|
+
"""Impact estimate for a proposed retention window: rows and bytes eligible for purge."""
|
|
127
|
+
|
|
128
|
+
eligible_rows = serializers.IntegerField()
|
|
129
|
+
eligible_bytes = serializers.IntegerField(allow_null=True)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class PurgeRunRequestSerializer(serializers.Serializer[Any]):
|
|
133
|
+
"""Request body to start a purge run; ``dry_run`` reports eligibility without deleting."""
|
|
134
|
+
|
|
135
|
+
dry_run = serializers.BooleanField(default=False)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class PurgeRunQueuedSerializer(serializers.Serializer[Any]):
|
|
139
|
+
"""Response for a queued purge run, carrying the ``run_id`` to poll for status."""
|
|
140
|
+
|
|
141
|
+
queued = serializers.BooleanField()
|
|
142
|
+
run_id = serializers.UUIDField()
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""Service layer for the retention policy editor + purge runs (ADR-0173).
|
|
2
|
+
|
|
3
|
+
Read/write helpers behind the ``/health/retention/*`` endpoints, plus the
|
|
4
|
+
durable dispatch of a manual/dry-run purge. Dispatch follows the house pattern:
|
|
5
|
+
create the ``PurgeRun`` row, then fire the coordinator in ``transaction.on_commit``
|
|
6
|
+
so the worker only adopts a row that has committed (ADR-0173 §Durable Execution).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import socket
|
|
13
|
+
import time
|
|
14
|
+
from typing import Any
|
|
15
|
+
from urllib.parse import urlsplit
|
|
16
|
+
|
|
17
|
+
from django.conf import settings
|
|
18
|
+
from django.db import transaction
|
|
19
|
+
from django.utils import timezone
|
|
20
|
+
|
|
21
|
+
from trueppm_api.apps.observability.models import (
|
|
22
|
+
PurgeRun,
|
|
23
|
+
RetentionPolicy,
|
|
24
|
+
RetentionSchedule,
|
|
25
|
+
)
|
|
26
|
+
from trueppm_api.apps.observability.purge_registry import (
|
|
27
|
+
estimate_table_stats,
|
|
28
|
+
get_purge_specs,
|
|
29
|
+
spec_purge_for,
|
|
30
|
+
)
|
|
31
|
+
from trueppm_api.apps.observability.retention import (
|
|
32
|
+
RETENTION_SPECS,
|
|
33
|
+
spec_for,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _resolve_for_display(key: str, policies: dict[str, RetentionPolicy]) -> tuple[int, bool]:
|
|
40
|
+
"""Return ``(value, enabled)`` to show in the editor for ``key``.
|
|
41
|
+
|
|
42
|
+
An override row wins. Otherwise the ADR-0081 setting: a numeric value is
|
|
43
|
+
shown enabled; a ``None`` (disabled) setting shows the spec default as the
|
|
44
|
+
value with ``enabled=False`` so the input always has a sensible number to edit.
|
|
45
|
+
Non-disablable windows (sync batches) are always enabled.
|
|
46
|
+
"""
|
|
47
|
+
spec = spec_for(key)
|
|
48
|
+
row = policies.get(key)
|
|
49
|
+
if row is not None:
|
|
50
|
+
value, enabled = row.value, row.enabled
|
|
51
|
+
else:
|
|
52
|
+
setting_value = getattr(settings, key, spec["default"])
|
|
53
|
+
if setting_value is None:
|
|
54
|
+
value, enabled = spec["default"], False
|
|
55
|
+
else:
|
|
56
|
+
value, enabled = setting_value, True
|
|
57
|
+
if not spec["disablable"]:
|
|
58
|
+
enabled = True
|
|
59
|
+
return value, enabled
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_retention_state() -> dict[str, Any]:
|
|
63
|
+
"""Build the full editor payload: policies, schedule, and the recent run log."""
|
|
64
|
+
policies = {row.key: row for row in RetentionPolicy.objects.all()}
|
|
65
|
+
db_tables_by_key = {spec.key: spec.db_tables for spec in get_purge_specs()}
|
|
66
|
+
|
|
67
|
+
policy_rows: list[dict[str, Any]] = []
|
|
68
|
+
for spec in RETENTION_SPECS:
|
|
69
|
+
key = spec["key"]
|
|
70
|
+
value, enabled = _resolve_for_display(key, policies)
|
|
71
|
+
est_rows, est_bytes = estimate_table_stats(db_tables_by_key.get(key, ()))
|
|
72
|
+
policy_rows.append(
|
|
73
|
+
{
|
|
74
|
+
"key": key,
|
|
75
|
+
"label": spec["label"],
|
|
76
|
+
"note": spec["note"],
|
|
77
|
+
"unit": spec["unit"],
|
|
78
|
+
"value": value,
|
|
79
|
+
"enabled": enabled,
|
|
80
|
+
"row_count": est_rows,
|
|
81
|
+
"bytes": est_bytes,
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
schedule, _ = RetentionSchedule.objects.get_or_create(singleton_key=1)
|
|
86
|
+
runs = list(PurgeRun.objects.order_by("-started_at")[:7])
|
|
87
|
+
|
|
88
|
+
return {"policies": policy_rows, "schedule": schedule, "runs": runs}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@transaction.atomic
|
|
92
|
+
def apply_retention_update(data: dict[str, Any]) -> None:
|
|
93
|
+
"""Persist policy overrides and/or schedule changes from the save-bar PATCH.
|
|
94
|
+
|
|
95
|
+
Sync batches can't be disabled, so ``enabled`` is forced True for that key
|
|
96
|
+
regardless of what the client sends (defensive — the UI already hides the toggle).
|
|
97
|
+
"""
|
|
98
|
+
for item in data.get("policies", []):
|
|
99
|
+
key = item["key"]
|
|
100
|
+
enabled = True if not spec_for(key)["disablable"] else item["enabled"]
|
|
101
|
+
RetentionPolicy.objects.update_or_create(
|
|
102
|
+
key=key,
|
|
103
|
+
defaults={"value": item["value"], "enabled": enabled},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
schedule_data = data.get("schedule")
|
|
107
|
+
if schedule_data:
|
|
108
|
+
RetentionSchedule.objects.update_or_create(singleton_key=1, defaults=schedule_data)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def compute_impact(key: str, value: int) -> tuple[int, int | None]:
|
|
112
|
+
"""Rows (and best-effort bytes) that *would* become purge-eligible at ``value``.
|
|
113
|
+
|
|
114
|
+
Backs the dirty-state "lowering this is irreversible" warning. A pure count —
|
|
115
|
+
no rows are deleted (``dry_run=True`` with the proposed window as override).
|
|
116
|
+
"""
|
|
117
|
+
from trueppm_api.apps.observability.purge_registry import estimate_freed_bytes
|
|
118
|
+
|
|
119
|
+
spec = spec_purge_for(key)
|
|
120
|
+
rows = spec.purge(dry_run=True, override_value=value)
|
|
121
|
+
return rows, estimate_freed_bytes(spec.db_tables, rows)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def start_purge_run(*, dry_run: bool, trigger: str = PurgeRun.Trigger.MANUAL) -> PurgeRun:
|
|
125
|
+
"""Create a PurgeRun and dispatch the coordinator on commit (best-effort).
|
|
126
|
+
|
|
127
|
+
Returns immediately with the ``running`` row so the API can hand back a
|
|
128
|
+
``run_id``; the worker fills in the result. If the broker is unreachable when
|
|
129
|
+
the on-commit dispatch fires, the run is marked ``failed`` so the UI's polling
|
|
130
|
+
log reflects it rather than hanging on ``running`` forever.
|
|
131
|
+
"""
|
|
132
|
+
run = PurgeRun.objects.create(
|
|
133
|
+
trigger=PurgeRun.Trigger.DRY_RUN if dry_run else trigger,
|
|
134
|
+
state=PurgeRun.State.RUNNING,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def _dispatch() -> None:
|
|
138
|
+
from trueppm_api.apps.observability.tasks import run_retention_purge
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
run_retention_purge.delay(run_id=str(run.id), dry_run=dry_run)
|
|
142
|
+
except Exception:
|
|
143
|
+
logger.exception("start_purge_run: dispatch failed for PurgeRun %s", run.id)
|
|
144
|
+
PurgeRun.objects.filter(id=run.id, state=PurgeRun.State.RUNNING).update(
|
|
145
|
+
state=PurgeRun.State.FAILED,
|
|
146
|
+
finished_at=timezone.now(),
|
|
147
|
+
error="dispatch failed: task broker unavailable",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
transaction.on_commit(_dispatch)
|
|
151
|
+
return run
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ---------------------------------------------------------------------------
|
|
155
|
+
# Telemetry test-export probe (#2110, ADR-0223 follow-up)
|
|
156
|
+
#
|
|
157
|
+
# The System Health "Telemetry" card lets an admin verify the OTLP export path
|
|
158
|
+
# from inside the app. Two modes, three terminal outcomes:
|
|
159
|
+
# - export enabled -> emit one synthetic canary span through a one-off exporter
|
|
160
|
+
# built from the SAME settings, report the collector's ACK.
|
|
161
|
+
# - endpoint set but TRUEPPM_OTEL_ENABLED=false -> bounded TCP reachability probe
|
|
162
|
+
# only (export was deliberately disabled; do not force a span).
|
|
163
|
+
#
|
|
164
|
+
# SECURITY (load-bearing): every failure `detail` is a CANNED, author-controlled
|
|
165
|
+
# sentence keyed on the exception *type* — never str(exc) — so a gRPC/transport
|
|
166
|
+
# error that embeds the collector target can never leak OTEL_EXPORTER_OTLP_HEADERS
|
|
167
|
+
# (the bearer token). SSRF is closed by construction: the target is read only from
|
|
168
|
+
# settings — the same host the app already exports to on every request — so the
|
|
169
|
+
# caller chooses nothing and no new egress surface is opened.
|
|
170
|
+
# ---------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
_CANARY_SPAN_NAME = "trueppm.telemetry.canary"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def run_telemetry_test_export() -> dict[str, Any]:
|
|
176
|
+
"""Probe the configured OTLP export path once and return an honest outcome.
|
|
177
|
+
|
|
178
|
+
When export is enabled (endpoint set AND master switch on), emit a single
|
|
179
|
+
synthetic canary span through a one-off exporter and report whether the
|
|
180
|
+
collector accepted it. When an endpoint is set but export is switched off, do a
|
|
181
|
+
bounded TCP reachability probe instead of forcing a span. Always returns a dict
|
|
182
|
+
(the view always responds 200); the outcome lives in the body.
|
|
183
|
+
"""
|
|
184
|
+
from trueppm_api.apps.observability.otel import provider
|
|
185
|
+
|
|
186
|
+
endpoint = str(settings.OTEL_EXPORTER_OTLP_ENDPOINT or "").strip()
|
|
187
|
+
protocol = str(settings.OTEL_EXPORTER_OTLP_PROTOCOL)
|
|
188
|
+
checked_at = timezone.now()
|
|
189
|
+
|
|
190
|
+
if provider.is_enabled():
|
|
191
|
+
return _canary_export(endpoint, protocol, checked_at)
|
|
192
|
+
return _tcp_probe(endpoint, protocol, checked_at)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _telemetry_result(
|
|
196
|
+
mode: str,
|
|
197
|
+
outcome: str,
|
|
198
|
+
endpoint: str,
|
|
199
|
+
protocol: str,
|
|
200
|
+
duration_ms: int,
|
|
201
|
+
detail: str,
|
|
202
|
+
checked_at: Any,
|
|
203
|
+
) -> dict[str, Any]:
|
|
204
|
+
"""Assemble the test-export result dict (no token/headers ever included)."""
|
|
205
|
+
return {
|
|
206
|
+
"mode": mode,
|
|
207
|
+
"outcome": outcome,
|
|
208
|
+
"endpoint": endpoint,
|
|
209
|
+
"protocol": protocol,
|
|
210
|
+
"duration_ms": duration_ms,
|
|
211
|
+
"detail": detail,
|
|
212
|
+
"checked_at": checked_at,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _elapsed_ms(start: float) -> int:
|
|
217
|
+
"""Milliseconds elapsed since a ``time.monotonic()`` reading."""
|
|
218
|
+
return int((time.monotonic() - start) * 1000)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _canary_export(endpoint: str, protocol: str, checked_at: Any) -> dict[str, Any]:
|
|
222
|
+
"""Export one canary span synchronously and map the SDK result to an outcome.
|
|
223
|
+
|
|
224
|
+
Builds a one-off exporter + a throwaway ``TracerProvider`` (bypassing the
|
|
225
|
+
fire-and-forget batch pipeline so the SUCCESS/FAILURE result is observable),
|
|
226
|
+
and always tears both down in ``finally`` so no gRPC channel/thread lingers.
|
|
227
|
+
The canary is named ``trueppm.telemetry.canary`` with ``trueppm.telemetry.test``
|
|
228
|
+
so operators can filter it out of real traces.
|
|
229
|
+
|
|
230
|
+
SCOPE (#2880): this runs inside the request, so it proves the export path of the
|
|
231
|
+
ONE POD that served the click and nothing else. The Celery worker and beat pods
|
|
232
|
+
export on their own connections and carry almost all the span/metric volume, so
|
|
233
|
+
under a per-workload NetworkPolicy a green canary here is compatible with zero
|
|
234
|
+
telemetry from the workers. The cross-pod signal is the live export-health strip
|
|
235
|
+
(ADR-0601), which aggregates every exporting pod — the result copy says so rather
|
|
236
|
+
than claiming "end to end", which it cannot know.
|
|
237
|
+
"""
|
|
238
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
239
|
+
from opentelemetry.sdk.trace.export import SpanExportResult
|
|
240
|
+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
|
|
241
|
+
|
|
242
|
+
from trueppm_api.apps.observability.otel import provider
|
|
243
|
+
|
|
244
|
+
timeout = float(settings.TELEMETRY_TEST_EXPORT_TIMEOUT_SECONDS)
|
|
245
|
+
exporter = None
|
|
246
|
+
tracer_provider = None
|
|
247
|
+
start = time.monotonic()
|
|
248
|
+
try:
|
|
249
|
+
exporter = provider.build_span_exporter(timeout=timeout)
|
|
250
|
+
tracer_provider = TracerProvider(resource=provider.build_resource(), sampler=ALWAYS_ON)
|
|
251
|
+
tracer = tracer_provider.get_tracer("trueppm.telemetry.test")
|
|
252
|
+
span = tracer.start_span(_CANARY_SPAN_NAME, attributes={"trueppm.telemetry.test": True})
|
|
253
|
+
span.end()
|
|
254
|
+
result = exporter.export([span])
|
|
255
|
+
duration_ms = _elapsed_ms(start)
|
|
256
|
+
if result == SpanExportResult.SUCCESS:
|
|
257
|
+
return _telemetry_result(
|
|
258
|
+
"export",
|
|
259
|
+
"success",
|
|
260
|
+
endpoint,
|
|
261
|
+
protocol,
|
|
262
|
+
duration_ms,
|
|
263
|
+
"Canary span accepted by the collector — the export path from this pod is working. "
|
|
264
|
+
"Worker and beat pods export independently and carry most of the volume; "
|
|
265
|
+
"the live export strip covers them.",
|
|
266
|
+
checked_at,
|
|
267
|
+
)
|
|
268
|
+
return _telemetry_result(
|
|
269
|
+
"export",
|
|
270
|
+
"failure",
|
|
271
|
+
endpoint,
|
|
272
|
+
protocol,
|
|
273
|
+
duration_ms,
|
|
274
|
+
"The collector did not accept the canary span. Check that the collector is "
|
|
275
|
+
"running, that the endpoint host and port are correct, and that the endpoint "
|
|
276
|
+
"port matches OTEL_EXPORTER_OTLP_PROTOCOL (4317 for grpc, 4318 for "
|
|
277
|
+
"http/protobuf).",
|
|
278
|
+
checked_at,
|
|
279
|
+
)
|
|
280
|
+
except Exception:
|
|
281
|
+
# Never surface str(exc): a transport error can embed the target/headers.
|
|
282
|
+
logger.warning("Telemetry test export failed", exc_info=True)
|
|
283
|
+
return _telemetry_result(
|
|
284
|
+
"export",
|
|
285
|
+
"failure",
|
|
286
|
+
endpoint,
|
|
287
|
+
protocol,
|
|
288
|
+
_elapsed_ms(start),
|
|
289
|
+
"Export failed before the span could be sent. Verify OTEL_EXPORTER_OTLP_ENDPOINT "
|
|
290
|
+
"and OTEL_EXPORTER_OTLP_PROTOCOL.",
|
|
291
|
+
checked_at,
|
|
292
|
+
)
|
|
293
|
+
finally:
|
|
294
|
+
if exporter is not None:
|
|
295
|
+
try:
|
|
296
|
+
exporter.shutdown()
|
|
297
|
+
except Exception:
|
|
298
|
+
logger.debug("canary exporter shutdown failed", exc_info=True)
|
|
299
|
+
if tracer_provider is not None:
|
|
300
|
+
try:
|
|
301
|
+
tracer_provider.shutdown()
|
|
302
|
+
except Exception:
|
|
303
|
+
logger.debug("canary tracer provider shutdown failed", exc_info=True)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _tcp_probe(endpoint: str, protocol: str, checked_at: Any) -> dict[str, Any]:
|
|
307
|
+
"""TCP-connect to the collector host:port and map the socket result to an outcome."""
|
|
308
|
+
if not endpoint:
|
|
309
|
+
return _telemetry_result(
|
|
310
|
+
"probe",
|
|
311
|
+
"failure",
|
|
312
|
+
"",
|
|
313
|
+
protocol,
|
|
314
|
+
0,
|
|
315
|
+
"No collector endpoint is configured. Set OTEL_EXPORTER_OTLP_ENDPOINT "
|
|
316
|
+
"to enable export.",
|
|
317
|
+
checked_at,
|
|
318
|
+
)
|
|
319
|
+
host, port = _parse_host_port(endpoint, protocol)
|
|
320
|
+
timeout = float(settings.TELEMETRY_TEST_EXPORT_TIMEOUT_SECONDS)
|
|
321
|
+
start = time.monotonic()
|
|
322
|
+
try:
|
|
323
|
+
with socket.create_connection((host, port), timeout=timeout):
|
|
324
|
+
pass
|
|
325
|
+
return _telemetry_result(
|
|
326
|
+
"probe",
|
|
327
|
+
"reachable",
|
|
328
|
+
endpoint,
|
|
329
|
+
protocol,
|
|
330
|
+
_elapsed_ms(start),
|
|
331
|
+
"Collector endpoint is reachable. Export is currently switched off "
|
|
332
|
+
"(TRUEPPM_OTEL_ENABLED=false); set it to true to start sending.",
|
|
333
|
+
checked_at,
|
|
334
|
+
)
|
|
335
|
+
except TimeoutError:
|
|
336
|
+
return _telemetry_result(
|
|
337
|
+
"probe",
|
|
338
|
+
"failure",
|
|
339
|
+
endpoint,
|
|
340
|
+
protocol,
|
|
341
|
+
_elapsed_ms(start),
|
|
342
|
+
"Connection timed out — the collector did not respond. Check the host, port, "
|
|
343
|
+
"and any NetworkPolicy that could block egress.",
|
|
344
|
+
checked_at,
|
|
345
|
+
)
|
|
346
|
+
except socket.gaierror:
|
|
347
|
+
return _telemetry_result(
|
|
348
|
+
"probe",
|
|
349
|
+
"failure",
|
|
350
|
+
endpoint,
|
|
351
|
+
protocol,
|
|
352
|
+
_elapsed_ms(start),
|
|
353
|
+
"The endpoint host could not be resolved. Check OTEL_EXPORTER_OTLP_ENDPOINT.",
|
|
354
|
+
checked_at,
|
|
355
|
+
)
|
|
356
|
+
except ConnectionRefusedError:
|
|
357
|
+
return _telemetry_result(
|
|
358
|
+
"probe",
|
|
359
|
+
"failure",
|
|
360
|
+
endpoint,
|
|
361
|
+
protocol,
|
|
362
|
+
_elapsed_ms(start),
|
|
363
|
+
"Connection refused — nothing is listening on that host and port.",
|
|
364
|
+
checked_at,
|
|
365
|
+
)
|
|
366
|
+
except OSError:
|
|
367
|
+
return _telemetry_result(
|
|
368
|
+
"probe",
|
|
369
|
+
"failure",
|
|
370
|
+
endpoint,
|
|
371
|
+
protocol,
|
|
372
|
+
_elapsed_ms(start),
|
|
373
|
+
"Could not reach the collector endpoint. Check the host, port, and network path.",
|
|
374
|
+
checked_at,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _parse_host_port(endpoint: str, protocol: str) -> tuple[str, int]:
|
|
379
|
+
"""Split an OTLP endpoint into (host, port), defensively.
|
|
380
|
+
|
|
381
|
+
Handles a URL form (``http://host:4318``) and the scheme-less gRPC form
|
|
382
|
+
(``host:4317`` or bare ``host``). The default port follows the protocol:
|
|
383
|
+
4318 for HTTP/protobuf, 4317 for gRPC. A malformed endpoint (an out-of-range
|
|
384
|
+
port like ``:99999``) must never raise here — it runs before ``_tcp_probe``'s
|
|
385
|
+
try-block, so an exception would surface as a 500 instead of the clean canned
|
|
386
|
+
"failure" card. Any parse problem falls back to the protocol default port so
|
|
387
|
+
the probe simply reports the endpoint as unreachable.
|
|
388
|
+
"""
|
|
389
|
+
ep = endpoint.strip()
|
|
390
|
+
default_port = 4318 if str(protocol).lower().startswith("http") else 4317
|
|
391
|
+
try:
|
|
392
|
+
if "://" in ep:
|
|
393
|
+
parsed = urlsplit(ep)
|
|
394
|
+
host = parsed.hostname or ""
|
|
395
|
+
port = parsed.port or default_port
|
|
396
|
+
elif ep.count(":") == 1:
|
|
397
|
+
host, _, raw_port = ep.partition(":")
|
|
398
|
+
port = int(raw_port) if raw_port.isdigit() else default_port
|
|
399
|
+
else:
|
|
400
|
+
host, port = ep, default_port
|
|
401
|
+
except ValueError:
|
|
402
|
+
# urlsplit(...).port raises on an out-of-range port; give up on parsing
|
|
403
|
+
# the port and let the connect attempt fail cleanly.
|
|
404
|
+
return ep, default_port
|
|
405
|
+
if not 0 <= port <= 65535:
|
|
406
|
+
port = default_port
|
|
407
|
+
return host, port
|