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,551 @@
|
|
|
1
|
+
"""OpenTelemetry native metric emission (ADR-0223 Phase 2, #710).
|
|
2
|
+
|
|
3
|
+
The Foundation (:mod:`.provider`, #708) built the metrics export pipeline — a
|
|
4
|
+
``MeterProvider`` with a ``PeriodicExportingMetricReader`` that pushes over OTLP —
|
|
5
|
+
but registers no application instruments. This module is Phase 2: it registers the
|
|
6
|
+
TruePPM-specific **observable gauges** that have no library auto-instrumentor, and
|
|
7
|
+
is called from ``ObservabilityConfig.ready()`` immediately after
|
|
8
|
+
:func:`~.instrumentation.instrument`.
|
|
9
|
+
|
|
10
|
+
Two of #710's four metric families live here as ``ObservableGauge`` instruments; a
|
|
11
|
+
third — the (synchronous) ``trueppm.task.duration_seconds`` histogram — was added
|
|
12
|
+
by #1917. The fourth, Django's ``http.server.*`` (request latency/count), comes
|
|
13
|
+
free from the library auto-instrumentor once a meter provider is threaded through
|
|
14
|
+
``instrument()``:
|
|
15
|
+
|
|
16
|
+
* ``trueppm.outbox.depth`` / ``trueppm.outbox.oldest_age_seconds`` — the live
|
|
17
|
+
backlog and lag of the two transactional outboxes (CPM recompute + workflow
|
|
18
|
+
step dispatch). Sourced from the same committed reads the System Health overview
|
|
19
|
+
does (``observability.selectors``).
|
|
20
|
+
* ``trueppm.db.connections`` — server-side PostgreSQL backend count for the current
|
|
21
|
+
database, by state, from ``pg_stat_activity``.
|
|
22
|
+
* ``trueppm.broker.queue.depth`` — the number of Celery messages *waiting* in the
|
|
23
|
+
broker (Valkey/Redis), by queue, from ``LLEN`` (#1900). The ``outbox.*`` gauges
|
|
24
|
+
measure the transactional-outbox **tables** (rows the dispatchers have not yet
|
|
25
|
+
handed to Celery); this gauge measures the **broker backlog** downstream of them
|
|
26
|
+
(messages Celery has accepted but no worker has picked up) — a distinct, and
|
|
27
|
+
previously unmeasured, stage of the pipeline.
|
|
28
|
+
* ``trueppm.task.duration_seconds`` (#1917) — wall-clock duration of every Celery
|
|
29
|
+
task execution, by task name and outcome. Distinct from (and additive to) the
|
|
30
|
+
Celery auto-instrumentor's own ``flower.task.runtime.seconds``: that one only
|
|
31
|
+
exists when ``CeleryInstrumentor`` is wired (Phase 1, #709) *and* a meter
|
|
32
|
+
provider is bound, whereas this one is timed directly off the
|
|
33
|
+
``SchedulingConfig`` Celery-signal bridge, independent of the auto-instrumentor.
|
|
34
|
+
|
|
35
|
+
Two **synchronous** WS instruments also live here (#1900) — WebSocket/Channels
|
|
36
|
+
observability was spans-only, with no connection or fan-out signal:
|
|
37
|
+
|
|
38
|
+
* ``trueppm.ws.connections.active`` — an ``UpDownCounter`` incremented when a
|
|
39
|
+
Channels consumer accepts a socket and decremented when it disconnects, via
|
|
40
|
+
:func:`record_ws_connection_opened` / :func:`record_ws_connection_closed`.
|
|
41
|
+
* ``trueppm.ws.broadcast.count`` — a ``Counter`` incremented once per board-event
|
|
42
|
+
fan-out at the broadcast point (``apps/sync/broadcast``), via
|
|
43
|
+
:func:`record_ws_broadcast`.
|
|
44
|
+
|
|
45
|
+
**Observable vs synchronous.** The outbox/DB and broker gauges are
|
|
46
|
+
``ObservableGauge`` instruments: the SDK invokes each callback once per export
|
|
47
|
+
interval (default 60 s) on the reader's background thread. That means no per-request
|
|
48
|
+
cost and no need to hook every outbox transition — which a synchronous
|
|
49
|
+
``UpDownCounter`` would require. The two WS instruments and the task-duration
|
|
50
|
+
histogram *are* synchronous: a connection open/close, a broadcast, and a task
|
|
51
|
+
completion are discrete events with no shared state to poll — there is nothing for a
|
|
52
|
+
callback to read, so they are recorded at the event site instead. The WS instruments
|
|
53
|
+
are held as module globals set by :func:`install_metrics` and stay ``None`` (a cheap
|
|
54
|
+
no-op at the record site) whenever telemetry is disabled or metrics are off; the
|
|
55
|
+
task-duration ``Histogram`` behaves the same way.
|
|
56
|
+
|
|
57
|
+
**Cluster-wide — aggregate with ``max`` / ``last``, never ``sum``.** Every process
|
|
58
|
+
that runs ``ready()`` (web, Celery worker, Celery beat) registers these gauges and
|
|
59
|
+
reads the *same* shared state — PostgreSQL for the outbox/DB gauges, the Valkey
|
|
60
|
+
broker for ``broker.queue.depth`` — so each process emits the whole-cluster figure
|
|
61
|
+
as its own series (kept distinct by ``service.instance.id``). Summing across
|
|
62
|
+
instances multiplies the true value; dashboards and alerts must aggregate these
|
|
63
|
+
gauges with ``max`` or ``last``. The two synchronous WS instruments are the
|
|
64
|
+
exception: they count *per-process* events (this process's own sockets and
|
|
65
|
+
broadcasts), so they aggregate normally with ``sum``. This is called out in
|
|
66
|
+
``packages/website/src/content/docs/administration/observability.md``.
|
|
67
|
+
|
|
68
|
+
**Best-effort and non-fatal.** Each callback is wrapped so a database hiccup yields
|
|
69
|
+
*no* observation for that cycle rather than raising on the exporter thread, and a
|
|
70
|
+
short server-side ``statement_timeout`` bounds the ``pg_stat_activity`` probe so a
|
|
71
|
+
slow database can never stall metric collection. Telemetry must never destabilise
|
|
72
|
+
the process it observes.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
from __future__ import annotations
|
|
76
|
+
|
|
77
|
+
import logging
|
|
78
|
+
from collections.abc import Iterable
|
|
79
|
+
from datetime import datetime
|
|
80
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
81
|
+
|
|
82
|
+
from django.db import DatabaseError, connections
|
|
83
|
+
from django.utils import timezone
|
|
84
|
+
from opentelemetry.metrics import Observation
|
|
85
|
+
|
|
86
|
+
from . import attributes
|
|
87
|
+
from .provider import get_meter
|
|
88
|
+
|
|
89
|
+
if TYPE_CHECKING:
|
|
90
|
+
from opentelemetry.metrics import (
|
|
91
|
+
CallbackOptions,
|
|
92
|
+
Counter,
|
|
93
|
+
Histogram,
|
|
94
|
+
Meter,
|
|
95
|
+
UpDownCounter,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
from .provider import OTelBootstrapContext
|
|
99
|
+
|
|
100
|
+
logger = logging.getLogger(__name__)
|
|
101
|
+
|
|
102
|
+
# Metric-instrument names. Owned by this module (a namespace distinct from the
|
|
103
|
+
# attribute keys in :mod:`.attributes`); kept as constants so tests and docs refer
|
|
104
|
+
# to a single source of truth.
|
|
105
|
+
OUTBOX_DEPTH = "trueppm.outbox.depth"
|
|
106
|
+
OUTBOX_OLDEST_AGE = "trueppm.outbox.oldest_age_seconds"
|
|
107
|
+
DB_CONNECTIONS = "trueppm.db.connections"
|
|
108
|
+
BROKER_QUEUE_DEPTH = "trueppm.broker.queue.depth"
|
|
109
|
+
WS_CONNECTIONS_ACTIVE = "trueppm.ws.connections.active"
|
|
110
|
+
WS_BROADCAST_COUNT = "trueppm.ws.broadcast.count"
|
|
111
|
+
TASK_DURATION_SECONDS = "trueppm.task.duration_seconds"
|
|
112
|
+
RATELIMIT_ENABLED = "trueppm.ratelimit.enabled"
|
|
113
|
+
|
|
114
|
+
# Server-side statement timeout for the pg_stat_activity probe, so a slow or
|
|
115
|
+
# contended database can never stall the exporter's collection thread. It is set
|
|
116
|
+
# on the metrics thread's dedicated connection only.
|
|
117
|
+
_DB_PROBE_TIMEOUT_MS = 2_000
|
|
118
|
+
|
|
119
|
+
# Socket-level timeout (seconds) for the broker LLEN probe, so a slow or
|
|
120
|
+
# unreachable Valkey/Redis broker can never stall the exporter's collection
|
|
121
|
+
# thread — the broker analogue of _DB_PROBE_TIMEOUT_MS.
|
|
122
|
+
_BROKER_PROBE_TIMEOUT_S = 2.0
|
|
123
|
+
|
|
124
|
+
_installed = False
|
|
125
|
+
|
|
126
|
+
# Synchronous WS instruments, set once by install_metrics and left None when
|
|
127
|
+
# telemetry is disabled (the record_* helpers then no-op). Module globals rather
|
|
128
|
+
# than callback-registered because a socket open/close and a broadcast are discrete
|
|
129
|
+
# events recorded at the event site, not polled state.
|
|
130
|
+
_ws_connections: UpDownCounter | None = None
|
|
131
|
+
_ws_broadcasts: Counter | None = None
|
|
132
|
+
|
|
133
|
+
# The task-duration instrument, unlike the observable gauges above, is a plain
|
|
134
|
+
# (synchronous) Histogram: there is no "current value" a callback can sample —
|
|
135
|
+
# each Celery task execution is one observation, recorded by the SchedulingConfig
|
|
136
|
+
# Celery-signal bridge (task_prerun/task_postrun) at the moment it completes.
|
|
137
|
+
# None until install_metrics() creates it (i.e. metrics disabled, or not yet
|
|
138
|
+
# registered), which record_task_duration() treats as "nothing to record".
|
|
139
|
+
_task_duration_histogram: Histogram | None = None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def install_metrics(context: OTelBootstrapContext, *, meter_provider: Any = None) -> None:
|
|
143
|
+
"""Register the TruePPM observable gauges against the bootstrap context.
|
|
144
|
+
|
|
145
|
+
Called from ``ObservabilityConfig.ready()`` after ``instrument()``. A strict
|
|
146
|
+
no-op when telemetry is disabled or metrics are turned off, and idempotent
|
|
147
|
+
across repeated ``ready()`` calls (test runner / autoreloader), mirroring the
|
|
148
|
+
Foundation's cost profile.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
context: The context from ``provider.bootstrap()``. When ``context.enabled``
|
|
152
|
+
is ``False`` or ``context.meter_provider`` is ``None`` (metrics off),
|
|
153
|
+
this returns immediately without registering any instrument.
|
|
154
|
+
meter_provider: Provider to obtain the meter from. Defaults to the process
|
|
155
|
+
global (which ``bootstrap()`` already set to ``context.meter_provider``);
|
|
156
|
+
a test overrides it to read from an in-memory metric reader.
|
|
157
|
+
"""
|
|
158
|
+
global _installed, _ws_connections, _ws_broadcasts, _task_duration_histogram
|
|
159
|
+
if _installed:
|
|
160
|
+
return
|
|
161
|
+
if not context.enabled or context.meter_provider is None:
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
meter = _resolve_meter(meter_provider)
|
|
165
|
+
meter.create_observable_gauge(
|
|
166
|
+
OUTBOX_DEPTH,
|
|
167
|
+
callbacks=[_observe_outbox_depth],
|
|
168
|
+
unit="{row}",
|
|
169
|
+
description="Live backlog of a transactional outbox — rows not yet done, by state.",
|
|
170
|
+
)
|
|
171
|
+
meter.create_observable_gauge(
|
|
172
|
+
OUTBOX_OLDEST_AGE,
|
|
173
|
+
callbacks=[_observe_outbox_oldest_age],
|
|
174
|
+
unit="s",
|
|
175
|
+
description="Age of the oldest not-yet-done row in a transactional outbox (0 when empty).",
|
|
176
|
+
)
|
|
177
|
+
meter.create_observable_gauge(
|
|
178
|
+
DB_CONNECTIONS,
|
|
179
|
+
callbacks=[_observe_db_connections],
|
|
180
|
+
unit="{connection}",
|
|
181
|
+
description="Server-side PostgreSQL backend count for the current database, by state.",
|
|
182
|
+
)
|
|
183
|
+
meter.create_observable_gauge(
|
|
184
|
+
BROKER_QUEUE_DEPTH,
|
|
185
|
+
callbacks=[_observe_broker_queue_depth],
|
|
186
|
+
unit="{message}",
|
|
187
|
+
description="Celery messages waiting in the broker (Valkey/Redis) LLEN, by queue.",
|
|
188
|
+
)
|
|
189
|
+
meter.create_observable_gauge(
|
|
190
|
+
RATELIMIT_ENABLED,
|
|
191
|
+
callbacks=[_observe_ratelimit_enabled],
|
|
192
|
+
unit="{status}",
|
|
193
|
+
description=(
|
|
194
|
+
"1 when API rate limiting is enabled, 0 when an operator has disabled it (ADR-0604)."
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
# UpDownCounter (not a gauge): active WS connections is a running total mutated
|
|
198
|
+
# by discrete +1/-1 events at the consumer, with no shared state to poll.
|
|
199
|
+
_ws_connections = meter.create_up_down_counter(
|
|
200
|
+
WS_CONNECTIONS_ACTIVE,
|
|
201
|
+
unit="{connection}",
|
|
202
|
+
description="Active WebSocket connections accepted by this process's Channels consumers.",
|
|
203
|
+
)
|
|
204
|
+
# Counter (monotonic): board-event fan-outs at the broadcast point, so an
|
|
205
|
+
# operator can see real-time push volume and rate.
|
|
206
|
+
_ws_broadcasts = meter.create_counter(
|
|
207
|
+
WS_BROADCAST_COUNT,
|
|
208
|
+
unit="{broadcast}",
|
|
209
|
+
description="WebSocket board-event broadcasts fanned out to a project group.",
|
|
210
|
+
)
|
|
211
|
+
_task_duration_histogram = meter.create_histogram(
|
|
212
|
+
TASK_DURATION_SECONDS,
|
|
213
|
+
unit="s",
|
|
214
|
+
description=(
|
|
215
|
+
"Celery task execution wall-clock duration, from task_prerun to "
|
|
216
|
+
"task_postrun, by task name and outcome (#1917)."
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
_installed = True
|
|
220
|
+
logger.info("OpenTelemetry native metrics registered (8 instruments)")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _resolve_meter(meter_provider: Any) -> Meter:
|
|
224
|
+
"""Return the meter to register instruments on (test seam via meter_provider)."""
|
|
225
|
+
if meter_provider is not None:
|
|
226
|
+
return cast("Meter", meter_provider.get_meter(attributes.NAMESPACE))
|
|
227
|
+
return get_meter(attributes.NAMESPACE)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# --- Observable-gauge callbacks --------------------------------------------
|
|
231
|
+
# Each is invoked once per export interval on the reader's background thread and
|
|
232
|
+
# returns the current sample. All DB access is wrapped: a DatabaseError yields no
|
|
233
|
+
# observation for the cycle (a gap), never an exception on the exporter thread.
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _observe_outbox_depth(options: CallbackOptions) -> Iterable[Observation]:
|
|
237
|
+
"""Current not-done backlog per (outbox, lifecycle state)."""
|
|
238
|
+
try:
|
|
239
|
+
rows = _outbox_depth_rows()
|
|
240
|
+
except DatabaseError:
|
|
241
|
+
logger.debug("outbox depth probe skipped (database error)", exc_info=True)
|
|
242
|
+
return []
|
|
243
|
+
return [
|
|
244
|
+
Observation(count, {attributes.OUTBOX_NAME: name, attributes.OUTBOX_STATE: state})
|
|
245
|
+
for name, state, count in rows
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _observe_outbox_oldest_age(options: CallbackOptions) -> Iterable[Observation]:
|
|
250
|
+
"""Age in seconds of the oldest not-done row per outbox (0 when empty)."""
|
|
251
|
+
try:
|
|
252
|
+
rows = _outbox_oldest_age_rows()
|
|
253
|
+
except DatabaseError:
|
|
254
|
+
logger.debug("outbox oldest-age probe skipped (database error)", exc_info=True)
|
|
255
|
+
return []
|
|
256
|
+
return [Observation(age, {attributes.OUTBOX_NAME: name}) for name, age in rows]
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _observe_db_connections(options: CallbackOptions) -> Iterable[Observation]:
|
|
260
|
+
"""Server-side PostgreSQL backend count for the current database, by state."""
|
|
261
|
+
try:
|
|
262
|
+
rows = _db_connection_rows()
|
|
263
|
+
except DatabaseError:
|
|
264
|
+
logger.debug("db connections probe skipped (database error)", exc_info=True)
|
|
265
|
+
return []
|
|
266
|
+
return [Observation(count, {attributes.DB_STATE: state}) for state, count in rows]
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _observe_broker_queue_depth(options: CallbackOptions) -> Iterable[Observation]:
|
|
270
|
+
"""Waiting Celery message count per broker queue (LLEN), or no observation on error.
|
|
271
|
+
|
|
272
|
+
Catches ``Exception`` (broad), not ``DatabaseError``: the probe talks to the
|
|
273
|
+
Valkey/Redis broker via redis-py, whose failure modes (``ConnectionError``,
|
|
274
|
+
``TimeoutError``, ``RedisError``) are unrelated to Django's DB exceptions. Any
|
|
275
|
+
of them must yield a gap for the cycle, never propagate onto the exporter thread.
|
|
276
|
+
"""
|
|
277
|
+
try:
|
|
278
|
+
rows = _broker_queue_depth_rows()
|
|
279
|
+
except Exception:
|
|
280
|
+
logger.debug("broker queue depth probe skipped (broker error)", exc_info=True)
|
|
281
|
+
return []
|
|
282
|
+
return [Observation(depth, {attributes.BROKER_QUEUE: name}) for name, depth in rows]
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _observe_ratelimit_enabled(options: CallbackOptions) -> Iterable[Observation]:
|
|
286
|
+
"""1 when API rate limiting is enabled, 0 when an operator has disabled it (ADR-0604).
|
|
287
|
+
|
|
288
|
+
A static config flag, not a probe — no DB/Redis access — so operators can alert
|
|
289
|
+
on ``trueppm.ratelimit.enabled == 0`` (all DRF throttling off).
|
|
290
|
+
"""
|
|
291
|
+
from django.conf import settings
|
|
292
|
+
|
|
293
|
+
enabled = getattr(settings, "RATE_LIMIT_ENABLED", True)
|
|
294
|
+
return [Observation(1 if enabled else 0, {})]
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# --- Synchronous histogram: task duration (#1917) --------------------------
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def record_task_duration(task_name: str, duration_seconds: float, outcome: str) -> None:
|
|
301
|
+
"""Record one Celery task execution into the ``trueppm.task.duration_seconds`` histogram.
|
|
302
|
+
|
|
303
|
+
Called from the Celery-framework signal bridge in
|
|
304
|
+
``SchedulingConfig.ready()`` (``task_prerun``/``task_postrun``) — those are
|
|
305
|
+
process-wide Celery signals, so this fires for every task that runs in the
|
|
306
|
+
process, not only tasks the scheduling app defines. Adds no query: the
|
|
307
|
+
duration is timed in-process between the two signals and passed in.
|
|
308
|
+
|
|
309
|
+
A no-op when metrics are disabled or not yet registered — install_metrics()
|
|
310
|
+
only creates ``_task_duration_histogram`` when a meter provider is bound, so
|
|
311
|
+
the default (telemetry-off) deployment pays nothing per task beyond this
|
|
312
|
+
early return. Recording is wrapped so an exporter/SDK error can never
|
|
313
|
+
surface as (or cause) a failure in the task whose duration it is reporting.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
task_name: The Celery task's registered name (e.g. ``scheduling.recalculate``).
|
|
317
|
+
duration_seconds: Wall-clock time from ``task_prerun`` to ``task_postrun``.
|
|
318
|
+
outcome: Lower-cased terminal Celery state for this execution, e.g.
|
|
319
|
+
``success`` | ``failure`` | ``retry`` | ``rejected`` | ``ignored``.
|
|
320
|
+
"""
|
|
321
|
+
if _task_duration_histogram is None:
|
|
322
|
+
return
|
|
323
|
+
try:
|
|
324
|
+
_task_duration_histogram.record(
|
|
325
|
+
duration_seconds,
|
|
326
|
+
{
|
|
327
|
+
attributes.CELERY_TASK_NAME: task_name,
|
|
328
|
+
attributes.CELERY_TASK_OUTCOME: outcome,
|
|
329
|
+
},
|
|
330
|
+
)
|
|
331
|
+
except Exception:
|
|
332
|
+
logger.debug("task duration recording skipped (metric error)", exc_info=True)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# --- Sampling queries (imported lazily so a disabled deployment stays light) ---
|
|
336
|
+
# The outbox counts mirror observability.selectors' System Health reads: PENDING +
|
|
337
|
+
# DISPATCHED are the live, not-yet-terminal backlog (DONE / DEAD are terminal —
|
|
338
|
+
# DEAD is surfaced by the dead-letter signal, not counted as depth here).
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _outbox_depth_rows() -> list[tuple[str, str, int]]:
|
|
342
|
+
"""Return (outbox, state, count) for each outbox × {pending, dispatched}."""
|
|
343
|
+
from django.db.models import Count, Q
|
|
344
|
+
|
|
345
|
+
from trueppm_api.apps.scheduling.models import ScheduleRequest, ScheduleRequestStatus
|
|
346
|
+
from trueppm_api.apps.workflow_engine.models import WorkflowOutboxRow, WorkflowOutboxStatus
|
|
347
|
+
|
|
348
|
+
sched = ScheduleRequest.objects.aggregate(
|
|
349
|
+
pending=Count("id", filter=Q(status=ScheduleRequestStatus.PENDING)),
|
|
350
|
+
dispatched=Count("id", filter=Q(status=ScheduleRequestStatus.DISPATCHED)),
|
|
351
|
+
)
|
|
352
|
+
wf = WorkflowOutboxRow.objects.aggregate(
|
|
353
|
+
pending=Count("id", filter=Q(status=WorkflowOutboxStatus.PENDING)),
|
|
354
|
+
dispatched=Count("id", filter=Q(status=WorkflowOutboxStatus.DISPATCHED)),
|
|
355
|
+
)
|
|
356
|
+
return [
|
|
357
|
+
("schedule", "pending", sched["pending"] or 0),
|
|
358
|
+
("schedule", "dispatched", sched["dispatched"] or 0),
|
|
359
|
+
("workflow", "pending", wf["pending"] or 0),
|
|
360
|
+
("workflow", "dispatched", wf["dispatched"] or 0),
|
|
361
|
+
]
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _outbox_oldest_age_rows() -> list[tuple[str, float]]:
|
|
365
|
+
"""Return (outbox, oldest-not-done-age-seconds) for each outbox (0 when empty).
|
|
366
|
+
|
|
367
|
+
The two outboxes timestamp their rows on different fields — ``ScheduleRequest``
|
|
368
|
+
uses ``requested_at``, ``WorkflowOutboxRow`` uses ``created_at`` — so the oldest
|
|
369
|
+
row is found on the correct column for each.
|
|
370
|
+
"""
|
|
371
|
+
from django.db.models import Min
|
|
372
|
+
|
|
373
|
+
from trueppm_api.apps.scheduling.models import ScheduleRequest, ScheduleRequestStatus
|
|
374
|
+
from trueppm_api.apps.workflow_engine.models import WorkflowOutboxRow, WorkflowOutboxStatus
|
|
375
|
+
|
|
376
|
+
now = timezone.now()
|
|
377
|
+
sched_non_terminal = [ScheduleRequestStatus.PENDING, ScheduleRequestStatus.DISPATCHED]
|
|
378
|
+
wf_non_terminal = [WorkflowOutboxStatus.PENDING, WorkflowOutboxStatus.DISPATCHED]
|
|
379
|
+
|
|
380
|
+
oldest_sched = ScheduleRequest.objects.filter(status__in=sched_non_terminal).aggregate(
|
|
381
|
+
oldest=Min("requested_at")
|
|
382
|
+
)["oldest"]
|
|
383
|
+
oldest_wf = WorkflowOutboxRow.objects.filter(status__in=wf_non_terminal).aggregate(
|
|
384
|
+
oldest=Min("created_at")
|
|
385
|
+
)["oldest"]
|
|
386
|
+
return [
|
|
387
|
+
("schedule", _age_seconds(now, oldest_sched)),
|
|
388
|
+
("workflow", _age_seconds(now, oldest_wf)),
|
|
389
|
+
]
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _age_seconds(now: datetime, timestamp: datetime | None) -> float:
|
|
393
|
+
"""Non-negative age of ``timestamp`` relative to ``now``; 0.0 when ``None``."""
|
|
394
|
+
if timestamp is None:
|
|
395
|
+
return 0.0
|
|
396
|
+
return max(0.0, (now - timestamp).total_seconds())
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _db_connection_rows() -> list[tuple[str, int]]:
|
|
400
|
+
"""Return (state-bucket, backend-count) from pg_stat_activity for this database.
|
|
401
|
+
|
|
402
|
+
Runs on the exporter's collection thread against that thread's own persistent
|
|
403
|
+
Django connection. A short ``statement_timeout`` bounds the probe. Raw
|
|
404
|
+
``pg_stat_activity.state`` values collapse into four low-cardinality buckets so
|
|
405
|
+
the metric never explodes dimension count.
|
|
406
|
+
"""
|
|
407
|
+
conn = connections["default"]
|
|
408
|
+
with conn.cursor() as cursor:
|
|
409
|
+
# set_config() accepts a bind parameter where bare SET does not, so the
|
|
410
|
+
# timeout is passed as a parameter rather than interpolated into SQL.
|
|
411
|
+
# is_local=false keeps it session-scoped, matching SET statement_timeout.
|
|
412
|
+
cursor.execute(
|
|
413
|
+
"SELECT set_config('statement_timeout', %s, false)",
|
|
414
|
+
[str(int(_DB_PROBE_TIMEOUT_MS))],
|
|
415
|
+
)
|
|
416
|
+
cursor.execute(
|
|
417
|
+
"SELECT state, count(*) FROM pg_stat_activity "
|
|
418
|
+
"WHERE datname = current_database() GROUP BY state"
|
|
419
|
+
)
|
|
420
|
+
raw = cursor.fetchall()
|
|
421
|
+
|
|
422
|
+
buckets: dict[str, int] = {}
|
|
423
|
+
for state, count in raw:
|
|
424
|
+
bucket = _bucket_pg_state(state)
|
|
425
|
+
buckets[bucket] = buckets.get(bucket, 0) + int(count)
|
|
426
|
+
return sorted(buckets.items())
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _bucket_pg_state(state: str | None) -> str:
|
|
430
|
+
"""Collapse a raw pg_stat_activity.state into a stable, low-cardinality bucket."""
|
|
431
|
+
if not state:
|
|
432
|
+
# NULL state = background/system backends (autovacuum, walwriter) or
|
|
433
|
+
# track_activities off; grouped as "other" rather than a null dimension.
|
|
434
|
+
return "other"
|
|
435
|
+
normalized = state.lower()
|
|
436
|
+
if normalized == "active":
|
|
437
|
+
return "active"
|
|
438
|
+
if normalized == "idle":
|
|
439
|
+
return "idle"
|
|
440
|
+
if normalized.startswith("idle in transaction"):
|
|
441
|
+
return "idle_in_transaction"
|
|
442
|
+
return "other"
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _broker_queue_depth_rows() -> list[tuple[str, int]]:
|
|
446
|
+
"""Return (queue-name, waiting-message-count) for each Celery broker queue.
|
|
447
|
+
|
|
448
|
+
With the Valkey/Redis broker each Celery queue is a Redis LIST keyed by the
|
|
449
|
+
queue name, so ``LLEN`` is the count of messages **waiting** for a worker.
|
|
450
|
+
Tasks a worker has already reserved have left the list and are not counted —
|
|
451
|
+
this is the *broker backlog*, the stage downstream of the transactional
|
|
452
|
+
outboxes (``trueppm.outbox.*`` measure rows the dispatchers have not yet handed
|
|
453
|
+
to Celery; this measures what Celery holds but no worker has picked up).
|
|
454
|
+
|
|
455
|
+
A short redis-py socket timeout bounds the probe so an unreachable broker can
|
|
456
|
+
never stall the exporter thread; the client is always closed afterward.
|
|
457
|
+
"""
|
|
458
|
+
from trueppm_api.core import valkey
|
|
459
|
+
|
|
460
|
+
queue_names = _broker_queue_names()
|
|
461
|
+
# Goes through the factory rather than redis.from_url(settings.CELERY_BROKER_URL):
|
|
462
|
+
# in Sentinel mode that setting is a ``sentinel://`` URL, which redis-py's
|
|
463
|
+
# from_url cannot parse at all (ADR-0716). DB_CELERY is the same database the
|
|
464
|
+
# broker URL addresses in either mode.
|
|
465
|
+
client = valkey.client(
|
|
466
|
+
valkey.DB_CELERY,
|
|
467
|
+
socket_timeout=_BROKER_PROBE_TIMEOUT_S,
|
|
468
|
+
socket_connect_timeout=_BROKER_PROBE_TIMEOUT_S,
|
|
469
|
+
)
|
|
470
|
+
try:
|
|
471
|
+
# redis-py types llen() as a sync/async union; the sync client returns int.
|
|
472
|
+
return [(name, int(cast("int", client.llen(name)))) for name in queue_names]
|
|
473
|
+
finally:
|
|
474
|
+
client.close()
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _broker_queue_names() -> list[str]:
|
|
478
|
+
"""Celery queue names whose broker backlog to probe.
|
|
479
|
+
|
|
480
|
+
Read from the live Celery app config rather than hard-coded: TruePPM defines no
|
|
481
|
+
custom routing, so this is the single ``task_default_queue`` (``celery``), but
|
|
482
|
+
if an operator configures ``task_queues`` the probe follows those names instead.
|
|
483
|
+
"""
|
|
484
|
+
from trueppm_api.celery import app
|
|
485
|
+
|
|
486
|
+
configured = getattr(app.conf, "task_queues", None)
|
|
487
|
+
if configured:
|
|
488
|
+
names = [
|
|
489
|
+
str(name)
|
|
490
|
+
for q in configured
|
|
491
|
+
if (name := getattr(q, "name", None) or (isinstance(q, dict) and q.get("name")))
|
|
492
|
+
]
|
|
493
|
+
if names:
|
|
494
|
+
return names
|
|
495
|
+
default = getattr(app.conf, "task_default_queue", None) or "celery"
|
|
496
|
+
return [str(default)]
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
# --- Synchronous WS instrument record sites -------------------------------
|
|
500
|
+
# Called from the Channels consumer (connect/disconnect) and the broadcast helper.
|
|
501
|
+
# Each is a strict no-op until install_metrics has run (instrument is None), and each
|
|
502
|
+
# swallows any instrument error so a metric can never destabilise the WS or broadcast
|
|
503
|
+
# path it observes — telemetry is best-effort, exactly like the gauges above.
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def record_ws_connection_opened() -> None:
|
|
507
|
+
"""Increment the active-WS-connection gauge when a consumer accepts a socket."""
|
|
508
|
+
_add_ws_connection(1)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def record_ws_connection_closed() -> None:
|
|
512
|
+
"""Decrement the active-WS-connection gauge when a consumer disconnects."""
|
|
513
|
+
_add_ws_connection(-1)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _add_ws_connection(delta: int) -> None:
|
|
517
|
+
counter = _ws_connections
|
|
518
|
+
if counter is None:
|
|
519
|
+
return
|
|
520
|
+
try:
|
|
521
|
+
counter.add(delta)
|
|
522
|
+
except Exception:
|
|
523
|
+
logger.debug("ws connection metric skipped", exc_info=True)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def record_ws_broadcast() -> None:
|
|
527
|
+
"""Count one board-event fan-out at the broadcast point (``apps/sync/broadcast``)."""
|
|
528
|
+
counter = _ws_broadcasts
|
|
529
|
+
if counter is None:
|
|
530
|
+
return
|
|
531
|
+
try:
|
|
532
|
+
counter.add(1)
|
|
533
|
+
except Exception:
|
|
534
|
+
logger.debug("ws broadcast metric skipped", exc_info=True)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def reset_for_testing() -> None:
|
|
538
|
+
"""Clear the idempotency guard so a test can re-register against a fresh reader.
|
|
539
|
+
|
|
540
|
+
Test-suite only. Observable gauges (and the task-duration histogram) cannot
|
|
541
|
+
be unregistered from a ``MeterProvider``, so tests pass a *new* in-memory
|
|
542
|
+
``meter_provider`` per case and reset this guard between them; production
|
|
543
|
+
registers exactly once. Also drops the synchronous WS instrument handles so a
|
|
544
|
+
subsequent ``install_metrics`` rebinds them to the new provider (and the
|
|
545
|
+
``record_*`` helpers no-op in between).
|
|
546
|
+
"""
|
|
547
|
+
global _installed, _ws_connections, _ws_broadcasts, _task_duration_histogram
|
|
548
|
+
_installed = False
|
|
549
|
+
_ws_connections = None
|
|
550
|
+
_ws_broadcasts = None
|
|
551
|
+
_task_duration_histogram = None
|