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,204 @@
|
|
|
1
|
+
"""``trueppm.*`` identity attributes on the HTTP server span (#2880).
|
|
2
|
+
|
|
3
|
+
Five of the span keys :mod:`.attributes` publishes as an additive-only, cross-repo
|
|
4
|
+
contract had **zero** emit sites: nothing on any span named the acting user, their
|
|
5
|
+
role, or the project / program / task a request addressed. A collector-side
|
|
6
|
+
processor keyed on ``trueppm.task.id`` therefore never fired, and the test that
|
|
7
|
+
looked like coverage only asserted the constants started with ``trueppm.``. This
|
|
8
|
+
module is the missing emitter, wired as the Django instrumentor's ``response_hook``
|
|
9
|
+
so it runs once per request while the server span is still recording.
|
|
10
|
+
|
|
11
|
+
Three properties are load-bearing:
|
|
12
|
+
|
|
13
|
+
1. **No extra query, ever.** Resource ids come from the URL route Django already
|
|
14
|
+
resolved. The role comes from the RBAC layer's per-request role cache, which the
|
|
15
|
+
permission classes populated while authorizing this very request. The user comes
|
|
16
|
+
from the request only when it is *already* resolved — a
|
|
17
|
+
``SimpleLazyObject`` that no view dereferenced is left alone, because forcing it
|
|
18
|
+
would add a session/DB round trip to a request that never needed one. Telemetry
|
|
19
|
+
that changes the query count is not observability.
|
|
20
|
+
2. **Absent beats wrong.** Every mapping here is exact. A route whose collection
|
|
21
|
+
segment is not one of the three known ones contributes nothing rather than a
|
|
22
|
+
guess — ``project-templates/<pk>`` is not a project, and ``task-relations/<pk>``
|
|
23
|
+
is not a task. ``trueppm.user.role`` is only set when a single project (or a
|
|
24
|
+
single program) was authorized, because TruePPM roles are project-scoped
|
|
25
|
+
(ADR-0072) and a list endpoint spanning many projects has no one role.
|
|
26
|
+
3. **Never raises.** The upstream hook wraps this in try/except and logs, but a
|
|
27
|
+
telemetry helper that can throw inside a response path is a latent 500 — so the
|
|
28
|
+
guards are here too, and a failure degrades to a span with fewer attributes.
|
|
29
|
+
|
|
30
|
+
Privacy: ``trueppm.user.*`` carries the opaque primary key and the symbolic role and
|
|
31
|
+
nothing else — no email, username, display name, or client IP. Operators who must
|
|
32
|
+
not export a per-user identifier at all set
|
|
33
|
+
``TRUEPPM_OTEL_ACTOR_ATTRIBUTES_ENABLED=false`` (Helm:
|
|
34
|
+
``observability.otlp.actorAttributes``), which drops both ``trueppm.user.*`` keys and
|
|
35
|
+
leaves the resource ids in place.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import logging
|
|
41
|
+
import re
|
|
42
|
+
from typing import Any
|
|
43
|
+
|
|
44
|
+
from django.conf import settings
|
|
45
|
+
from django.utils.functional import empty
|
|
46
|
+
|
|
47
|
+
from trueppm_api.apps.access.models import Role
|
|
48
|
+
from trueppm_api.apps.access.permissions import (
|
|
49
|
+
PROGRAM_RBAC_ROLE_CACHE_ATTR,
|
|
50
|
+
RBAC_ROLE_CACHE_ATTR,
|
|
51
|
+
)
|
|
52
|
+
from trueppm_api.apps.observability.otel import attributes
|
|
53
|
+
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
56
|
+
#: URL kwargs that name a resource unambiguously, whatever route they appear on.
|
|
57
|
+
#: Only ``project_pk`` and ``program_pk`` are in use today; the ``*_id`` spellings
|
|
58
|
+
#: are accepted so a future nested route does not silently stop being annotated.
|
|
59
|
+
_EXPLICIT_KWARG_ATTRS: dict[str, str] = {
|
|
60
|
+
"project_pk": attributes.PROJECT_ID,
|
|
61
|
+
"project_id": attributes.PROJECT_ID,
|
|
62
|
+
"program_pk": attributes.PROGRAM_ID,
|
|
63
|
+
"program_id": attributes.PROGRAM_ID,
|
|
64
|
+
"task_pk": attributes.TASK_ID,
|
|
65
|
+
"task_id": attributes.TASK_ID,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#: Collection segments whose bare ``<pk>`` IS the named resource. Exact strings, not
|
|
69
|
+
#: prefixes: ``project-templates``, ``project-resources``, ``task-relations`` and
|
|
70
|
+
#: ``task-resources`` are all separate collections whose pk is NOT a project or task
|
|
71
|
+
#: id, and a prefix match would mislabel every one of them.
|
|
72
|
+
_PK_COLLECTION_ATTRS: dict[str, str] = {
|
|
73
|
+
"projects": attributes.PROJECT_ID,
|
|
74
|
+
"programs": attributes.PROGRAM_ID,
|
|
75
|
+
"tasks": attributes.TASK_ID,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#: The collection segment immediately preceding a ``pk`` capture in a resolved
|
|
79
|
+
#: route. Matches both shapes Django reports: the DRF router's regex
|
|
80
|
+
#: (``projects/(?P<pk>[^/.]+)/``) and a ``path()`` converter (``projects/<pk>/``,
|
|
81
|
+
#: ``projects/<uuid:pk>/``). A leading ``^`` from a concatenated sub-resolver
|
|
82
|
+
#: pattern sits outside the captured group, so it does not interfere.
|
|
83
|
+
_PK_COLLECTION_RE = re.compile(r"([a-z][a-z-]*)/(?:\(\?P<pk>|<(?:[a-z_]+:)?pk>)")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _actor_attributes_enabled() -> bool:
|
|
87
|
+
"""Whether ``trueppm.user.*`` may be exported (privacy lever, default on)."""
|
|
88
|
+
return bool(getattr(settings, "TRUEPPM_OTEL_ACTOR_ATTRIBUTES_ENABLED", True))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _resolved_user(request: Any) -> Any | None:
|
|
92
|
+
"""Return the request's user only if it has ALREADY been resolved.
|
|
93
|
+
|
|
94
|
+
``AuthenticationMiddleware`` installs ``request.user`` as a
|
|
95
|
+
:class:`~django.utils.functional.SimpleLazyObject` that hits the session store
|
|
96
|
+
on first access. DRF replaces it with the concrete user once a view
|
|
97
|
+
authenticates, so on the API surface this is a plain attribute read. On a route
|
|
98
|
+
that never touched ``request.user``, dereferencing it *here* would add a query
|
|
99
|
+
that exists only because telemetry is on — so an unevaluated lazy object is
|
|
100
|
+
treated as "no user".
|
|
101
|
+
"""
|
|
102
|
+
user = request.__dict__.get("user")
|
|
103
|
+
if user is None:
|
|
104
|
+
return None
|
|
105
|
+
if getattr(user, "_wrapped", None) is empty:
|
|
106
|
+
return None
|
|
107
|
+
if not getattr(user, "is_authenticated", False):
|
|
108
|
+
return None
|
|
109
|
+
return user
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _role_label(ordinal: int) -> str | None:
|
|
113
|
+
"""Map a role ordinal to a low-cardinality symbolic label.
|
|
114
|
+
|
|
115
|
+
An exact OSS tier returns its name (``VIEWER`` … ``OWNER``). An Enterprise custom
|
|
116
|
+
role registered at an intermediate ordinal (ADR-0072 reserves the 100-unit bands
|
|
117
|
+
for exactly that) has no OSS name, so it returns its band with a ``+`` marker —
|
|
118
|
+
``SCHEDULER+`` for 250 — which keeps the dimension bounded at ten values while
|
|
119
|
+
making the approximation visible instead of dropping the attribute for those
|
|
120
|
+
users, which would be the same silence this module exists to remove.
|
|
121
|
+
"""
|
|
122
|
+
try:
|
|
123
|
+
return str(Role(ordinal).name)
|
|
124
|
+
except ValueError:
|
|
125
|
+
pass
|
|
126
|
+
band = max((r for r in Role if r.value <= ordinal), default=None, key=lambda r: r.value)
|
|
127
|
+
return f"{band.name}+" if band is not None else None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _sole_cached_role(request: Any) -> str | None:
|
|
131
|
+
"""The role this request was authorized under, if exactly one is well defined.
|
|
132
|
+
|
|
133
|
+
Reads the caches the permission classes filled while authorizing this request
|
|
134
|
+
(``_rbac_role_cache`` / ``_program_rbac_role_cache``, keyed by resource id), so
|
|
135
|
+
it adds no query. Project scope wins when both are populated. Anything other
|
|
136
|
+
than a single non-``None`` entry yields ``None``: a list endpoint that checked
|
|
137
|
+
forty projects has forty roles and no single answer, and an arbitrary pick would
|
|
138
|
+
be worse than an absent attribute.
|
|
139
|
+
"""
|
|
140
|
+
for attr in (RBAC_ROLE_CACHE_ATTR, PROGRAM_RBAC_ROLE_CACHE_ATTR):
|
|
141
|
+
cache = getattr(request, attr, None)
|
|
142
|
+
if not isinstance(cache, dict):
|
|
143
|
+
continue
|
|
144
|
+
roles = [value for value in cache.values() if value is not None]
|
|
145
|
+
if len(roles) == 1 and len(cache) == 1:
|
|
146
|
+
return _role_label(int(roles[0]))
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _resource_ids(request: Any) -> dict[str, str]:
|
|
151
|
+
"""Resource-identity attributes derivable from the route Django resolved."""
|
|
152
|
+
match = getattr(request, "resolver_match", None)
|
|
153
|
+
if match is None:
|
|
154
|
+
return {}
|
|
155
|
+
kwargs: dict[str, Any] = getattr(match, "kwargs", None) or {}
|
|
156
|
+
found: dict[str, str] = {}
|
|
157
|
+
|
|
158
|
+
for name, value in kwargs.items():
|
|
159
|
+
key = _EXPLICIT_KWARG_ATTRS.get(name)
|
|
160
|
+
if key is not None and value not in (None, ""):
|
|
161
|
+
found[key] = str(value)
|
|
162
|
+
|
|
163
|
+
pk = kwargs.get("pk")
|
|
164
|
+
if pk not in (None, ""):
|
|
165
|
+
route = str(getattr(match, "route", "") or "")
|
|
166
|
+
segment = _PK_COLLECTION_RE.search(route)
|
|
167
|
+
if segment is not None:
|
|
168
|
+
key = _PK_COLLECTION_ATTRS.get(segment.group(1))
|
|
169
|
+
# A nested route's own project_pk is more specific than the bare pk of
|
|
170
|
+
# the collection it hangs off, so never overwrite an explicit kwarg.
|
|
171
|
+
if key is not None and key not in found:
|
|
172
|
+
found[key] = str(pk)
|
|
173
|
+
return found
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def annotate_request_span(span: Any, request: Any, _response: Any = None) -> None:
|
|
177
|
+
"""Attach the ``trueppm.*`` identity attributes to an HTTP server span.
|
|
178
|
+
|
|
179
|
+
Wired as ``DjangoInstrumentor(response_hook=...)``, so it fires after the view
|
|
180
|
+
has run — which is what makes the acting user and the authorized role available
|
|
181
|
+
at all — and before the span ends.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
span: The recording server span. ``None`` or a non-recording span is a no-op.
|
|
185
|
+
request: The Django ``HttpRequest``. The DRF wrapper is not used: only the
|
|
186
|
+
underlying request survives into ``process_response``.
|
|
187
|
+
_response: The response, unused. Status and duration are the instrumentor's.
|
|
188
|
+
"""
|
|
189
|
+
try:
|
|
190
|
+
if span is None or not span.is_recording():
|
|
191
|
+
return
|
|
192
|
+
for key, value in _resource_ids(request).items():
|
|
193
|
+
span.set_attribute(key, value)
|
|
194
|
+
if not _actor_attributes_enabled():
|
|
195
|
+
return
|
|
196
|
+
user = _resolved_user(request)
|
|
197
|
+
if user is not None and getattr(user, "pk", None) is not None:
|
|
198
|
+
span.set_attribute(attributes.USER_ID, str(user.pk))
|
|
199
|
+
role = _sole_cached_role(request)
|
|
200
|
+
if role is not None:
|
|
201
|
+
span.set_attribute(attributes.USER_ROLE, role)
|
|
202
|
+
except Exception:
|
|
203
|
+
# A response-path helper that can raise is a latent 500. Telemetry degrades.
|
|
204
|
+
logger.debug("OpenTelemetry request-span annotation failed", exc_info=True)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Binds each retention key to its purge callable and physical tables (ADR-0173).
|
|
2
|
+
|
|
3
|
+
This is the *coordinator-side* binding: it imports the domain apps' purge
|
|
4
|
+
functions and models, so it is imported only on the admin/coordinator path — never
|
|
5
|
+
by the app tasks themselves (which import the pure ``retention`` resolver). All
|
|
6
|
+
imports are local to keep module load order-safe under Celery autodiscovery and
|
|
7
|
+
to avoid any chance of an app-registry-not-ready import.
|
|
8
|
+
|
|
9
|
+
Row counts and byte sizes are PostgreSQL **estimates** (``pg_class.reltuples`` and
|
|
10
|
+
``pg_total_relation_size``) — fast and index-free on large tables, but approximate.
|
|
11
|
+
Bytes freed by a purge is derived as ``avg_row_bytes Ă— rows`` and is best-effort
|
|
12
|
+
by design (ADR-0173 Consequences).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class PurgeSpec:
|
|
23
|
+
"""One operational table's purge callable and its physical tables.
|
|
24
|
+
|
|
25
|
+
``purge(*, dry_run, override_value) -> int`` returns rows deleted (or, when
|
|
26
|
+
``dry_run``, rows eligible). ``db_tables`` are the relation names summed for the
|
|
27
|
+
size/byte estimates.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
key: str
|
|
31
|
+
purge: Callable[..., int]
|
|
32
|
+
db_tables: tuple[str, ...]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_purge_specs() -> list[PurgeSpec]:
|
|
36
|
+
"""Build the ordered purge specs (display order matches RETENTION_SPECS).
|
|
37
|
+
|
|
38
|
+
Rebuilt per call (cheap) so this stays free of module-load side effects.
|
|
39
|
+
"""
|
|
40
|
+
from trueppm_api.apps.history.tasks import _do_history_purge
|
|
41
|
+
from trueppm_api.apps.msproject.models import ImportRequest
|
|
42
|
+
from trueppm_api.apps.msproject.tasks import _do_import_purge
|
|
43
|
+
from trueppm_api.apps.projects.models import Dependency, Project, Task, TaskActivityEvent
|
|
44
|
+
from trueppm_api.apps.projects.tasks import _do_project_purge
|
|
45
|
+
from trueppm_api.apps.sync.models import SyncBatch
|
|
46
|
+
from trueppm_api.apps.sync.tasks import _do_purge as _do_sync_purge
|
|
47
|
+
from trueppm_api.apps.taskruns.models import TaskRun
|
|
48
|
+
from trueppm_api.apps.taskruns.tasks import _do_taskrun_purge
|
|
49
|
+
from trueppm_api.apps.webhooks.models import WebhookDelivery
|
|
50
|
+
from trueppm_api.apps.webhooks.tasks import _do_webhook_purge
|
|
51
|
+
|
|
52
|
+
return [
|
|
53
|
+
PurgeSpec(
|
|
54
|
+
"HISTORY_RETENTION_DAYS",
|
|
55
|
+
_do_history_purge,
|
|
56
|
+
(
|
|
57
|
+
Project.history.model._meta.db_table,
|
|
58
|
+
Task.history.model._meta.db_table,
|
|
59
|
+
Dependency.history.model._meta.db_table,
|
|
60
|
+
# ADR-0207: the per-task activity source ages out on the same window.
|
|
61
|
+
TaskActivityEvent._meta.db_table,
|
|
62
|
+
),
|
|
63
|
+
),
|
|
64
|
+
PurgeSpec("TASK_RUN_RETENTION_DAYS", _do_taskrun_purge, (TaskRun._meta.db_table,)),
|
|
65
|
+
PurgeSpec(
|
|
66
|
+
"TRUEPPM_WEBHOOK_RETENTION_DAYS", _do_webhook_purge, (WebhookDelivery._meta.db_table,)
|
|
67
|
+
),
|
|
68
|
+
PurgeSpec(
|
|
69
|
+
"TRUEPPM_IMPORT_RETENTION_DAYS", _do_import_purge, (ImportRequest._meta.db_table,)
|
|
70
|
+
),
|
|
71
|
+
PurgeSpec(
|
|
72
|
+
"TRUEPPM_SYNC_BATCH_RETENTION_HOURS", _do_sync_purge, (SyncBatch._meta.db_table,)
|
|
73
|
+
),
|
|
74
|
+
PurgeSpec(
|
|
75
|
+
"TRUEPPM_PROJECT_SOFT_DELETE_RETENTION_DAYS",
|
|
76
|
+
_do_project_purge,
|
|
77
|
+
(Project._meta.db_table,),
|
|
78
|
+
),
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def spec_purge_for(key: str) -> PurgeSpec:
|
|
83
|
+
"""Return the PurgeSpec for ``key`` (raises StopIteration if unknown)."""
|
|
84
|
+
return next(spec for spec in get_purge_specs() if spec.key == key)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def estimate_table_stats(db_tables: tuple[str, ...]) -> tuple[int, int]:
|
|
88
|
+
"""Return ``(estimated_rows, total_bytes)`` summed across the given tables.
|
|
89
|
+
|
|
90
|
+
Uses planner statistics, so it is O(1) regardless of table size. ``reltuples``
|
|
91
|
+
can be ``-1`` before a table's first ANALYZE — clamped to 0.
|
|
92
|
+
"""
|
|
93
|
+
from django.db import connection
|
|
94
|
+
|
|
95
|
+
with connection.cursor() as cursor:
|
|
96
|
+
cursor.execute(
|
|
97
|
+
"""
|
|
98
|
+
SELECT COALESCE(SUM(GREATEST(c.reltuples, 0)), 0)::bigint AS est_rows,
|
|
99
|
+
COALESCE(SUM(pg_total_relation_size(c.oid)), 0)::bigint AS total_bytes
|
|
100
|
+
FROM pg_class c
|
|
101
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
102
|
+
WHERE c.relname = ANY(%s) AND n.nspname = 'public'
|
|
103
|
+
""",
|
|
104
|
+
[list(db_tables)],
|
|
105
|
+
)
|
|
106
|
+
row = cursor.fetchone()
|
|
107
|
+
if row is None:
|
|
108
|
+
return 0, 0
|
|
109
|
+
return int(row[0]), int(row[1])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def estimate_freed_bytes(db_tables: tuple[str, ...], rows: int) -> int | None:
|
|
113
|
+
"""Best-effort bytes freed by purging ``rows`` from ``db_tables``.
|
|
114
|
+
|
|
115
|
+
``avg_row_bytes Ă— rows``, where ``avg_row_bytes`` is total relation size over
|
|
116
|
+
the estimated row count. Returns ``None`` when statistics are unavailable
|
|
117
|
+
(never analyzed / empty table) so callers can render "—" rather than a fake 0.
|
|
118
|
+
"""
|
|
119
|
+
if rows <= 0:
|
|
120
|
+
return 0
|
|
121
|
+
est_rows, total_bytes = estimate_table_stats(db_tables)
|
|
122
|
+
if est_rows <= 0 or total_bytes <= 0:
|
|
123
|
+
return None
|
|
124
|
+
return int((total_bytes / est_rows) * rows)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Retention-window metadata and the settings→override resolver (ADR-0173).
|
|
2
|
+
|
|
3
|
+
This module is deliberately **import-clean**: it pulls in nothing from the
|
|
4
|
+
domain apps (webhooks, msproject, history, taskruns, sync), so those apps' purge
|
|
5
|
+
tasks can import :func:`resolve_retention` without creating an import cycle. The
|
|
6
|
+
binding of each window to its actual table/queryset lives in ``purge_registry``,
|
|
7
|
+
which is imported only on the admin/coordinator path — never by the app tasks.
|
|
8
|
+
|
|
9
|
+
``resolve_retention`` is the single source of truth for "how long is table X
|
|
10
|
+
kept right now": a ``RetentionPolicy`` override row if the operator has set one,
|
|
11
|
+
otherwise the ADR-0081 ``settings.*`` default. ``None`` means the purge is
|
|
12
|
+
disabled (unbounded retention) — preserved for any deployment that never opens
|
|
13
|
+
the editor.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Literal, TypedDict
|
|
19
|
+
|
|
20
|
+
from django.conf import settings
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class RetentionSpec(TypedDict):
|
|
24
|
+
"""Static metadata for one operator-tunable retention window."""
|
|
25
|
+
|
|
26
|
+
key: str
|
|
27
|
+
label: str
|
|
28
|
+
note: str
|
|
29
|
+
unit: Literal["days", "hours"]
|
|
30
|
+
default: int
|
|
31
|
+
disablable: bool
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# The six operational tables surfaced in the editor (ADR-0173 §A). Order is the
|
|
35
|
+
# display order in the policy table. ``key`` matches the ADR-0081 settings name;
|
|
36
|
+
# ``default`` mirrors ``settings/base.py`` so the resolver and the UI agree even
|
|
37
|
+
# before any override row exists. ``disablable`` is False only for sync batches,
|
|
38
|
+
# whose backend window is non-nullable.
|
|
39
|
+
RETENTION_SPECS: list[RetentionSpec] = [
|
|
40
|
+
{
|
|
41
|
+
"key": "HISTORY_RETENTION_DAYS",
|
|
42
|
+
"label": "Event history",
|
|
43
|
+
"note": "Activity and audit feed rows (object change history).",
|
|
44
|
+
"unit": "days",
|
|
45
|
+
"default": 90,
|
|
46
|
+
"disablable": True,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"key": "TASK_RUN_RETENTION_DAYS",
|
|
50
|
+
"label": "Task runs",
|
|
51
|
+
"note": "Completed background-job execution records.",
|
|
52
|
+
"unit": "days",
|
|
53
|
+
"default": 30,
|
|
54
|
+
"disablable": True,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"key": "TRUEPPM_WEBHOOK_RETENTION_DAYS",
|
|
58
|
+
"label": "Webhook deliveries",
|
|
59
|
+
"note": "Outbound webhook attempt log — terminal deliveries only.",
|
|
60
|
+
"unit": "days",
|
|
61
|
+
"default": 7,
|
|
62
|
+
"disablable": True,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"key": "TRUEPPM_IMPORT_RETENTION_DAYS",
|
|
66
|
+
"label": "Import requests",
|
|
67
|
+
"note": "MS Project import payloads — terminal requests only.",
|
|
68
|
+
"unit": "days",
|
|
69
|
+
"default": 7,
|
|
70
|
+
"disablable": True,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"key": "TRUEPPM_SYNC_BATCH_RETENTION_HOURS",
|
|
74
|
+
"label": "Sync batches",
|
|
75
|
+
"note": "Mobile sync idempotency rows. Cannot be disabled.",
|
|
76
|
+
"unit": "hours",
|
|
77
|
+
"default": 24,
|
|
78
|
+
"disablable": False,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"key": "TRUEPPM_PROJECT_SOFT_DELETE_RETENTION_DAYS",
|
|
82
|
+
"label": "Soft-deleted projects",
|
|
83
|
+
"note": "Projects in the trash — hard-deleted with all child data past the window.",
|
|
84
|
+
"unit": "days",
|
|
85
|
+
"default": 30,
|
|
86
|
+
"disablable": True,
|
|
87
|
+
},
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
RETENTION_KEYS: list[str] = [spec["key"] for spec in RETENTION_SPECS]
|
|
91
|
+
RETENTION_KEY_CHOICES = [(spec["key"], spec["label"]) for spec in RETENTION_SPECS]
|
|
92
|
+
|
|
93
|
+
_SPEC_BY_KEY: dict[str, RetentionSpec] = {spec["key"]: spec for spec in RETENTION_SPECS}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def spec_for(key: str) -> RetentionSpec:
|
|
97
|
+
"""Return the static metadata for ``key`` (raises KeyError if unknown)."""
|
|
98
|
+
return _SPEC_BY_KEY[key]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def resolve_retention(key: str) -> int | None:
|
|
102
|
+
"""Resolve the effective retention window for ``key`` in its native unit.
|
|
103
|
+
|
|
104
|
+
Returns the ``RetentionPolicy`` override if one exists (``None`` when that
|
|
105
|
+
override is disabled), else the ADR-0081 ``settings.*`` value. ``None`` means
|
|
106
|
+
the purge is disabled — the table is retained unbounded. The ``RetentionPolicy``
|
|
107
|
+
import is local so this module stays free of model imports at load time
|
|
108
|
+
(keeps the app-task → resolver dependency cycle-free).
|
|
109
|
+
"""
|
|
110
|
+
from trueppm_api.apps.observability.models import RetentionPolicy
|
|
111
|
+
|
|
112
|
+
spec = _SPEC_BY_KEY.get(key)
|
|
113
|
+
fallback = getattr(settings, key, spec["default"] if spec else None)
|
|
114
|
+
|
|
115
|
+
row = RetentionPolicy.objects.filter(key=key).first()
|
|
116
|
+
if row is None:
|
|
117
|
+
return fallback
|
|
118
|
+
return row.value if row.enabled else None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def resolve_retention_map() -> dict[str, int | None]:
|
|
122
|
+
"""Resolve every retention window in a single query.
|
|
123
|
+
|
|
124
|
+
Equivalent to calling :func:`resolve_retention` for each key, but fetches all
|
|
125
|
+
``RetentionPolicy`` overrides in one round-trip — used by the 10 s-polled System
|
|
126
|
+
Health overview to avoid five separate point-lookups per poll.
|
|
127
|
+
"""
|
|
128
|
+
from trueppm_api.apps.observability.models import RetentionPolicy
|
|
129
|
+
|
|
130
|
+
rows = {row.key: row for row in RetentionPolicy.objects.all()}
|
|
131
|
+
resolved: dict[str, int | None] = {}
|
|
132
|
+
for spec in RETENTION_SPECS:
|
|
133
|
+
key = spec["key"]
|
|
134
|
+
row = rows.get(key)
|
|
135
|
+
if row is None:
|
|
136
|
+
resolved[key] = getattr(settings, key, spec["default"])
|
|
137
|
+
else:
|
|
138
|
+
resolved[key] = row.value if row.enabled else None
|
|
139
|
+
return resolved
|