trueppm-api 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trueppm_api/__init__.py +27 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +346 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +483 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +184 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_squashed_0019_release_0_4.py +302 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +143 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/minter_role.py +38 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +3263 -0
- trueppm_api/apps/access/serializers.py +1007 -0
- trueppm_api/apps/access/services.py +878 -0
- trueppm_api/apps/access/signals.py +171 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +96 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1965 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0001_squashed_0003_release_0_4.py +344 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +109 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0001_squashed_0003_release_0_4.py +90 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +459 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +178 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +863 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +597 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_squashed_0014_release_0_4.py +225 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/0014_tasklink_created_by.py +27 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +635 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +528 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +734 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1158 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +280 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +551 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1276 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +439 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +462 -0
- trueppm_api/apps/notifications/email_backend.py +350 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +299 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_squashed_0011_release_0_4.py +204 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1148 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +1094 -0
- trueppm_api/apps/notifications/services.py +1489 -0
- trueppm_api/apps/notifications/tasks.py +762 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +919 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +566 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +993 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +537 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_squashed_0009_release_0_4.py +128 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +541 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +458 -0
- trueppm_api/apps/projects/backfill.py +459 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +212 -0
- trueppm_api/apps/projects/config_notice.py +1330 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +132 -0
- trueppm_api/apps/projects/decisions_views.py +97 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +423 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4278 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2001 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1827 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1909 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1410 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/key_views.py +468 -0
- trueppm_api/apps/projects/keys.py +205 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +514 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +406 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +192 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migration_helpers.py +75 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5659 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +59 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +75 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +76 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0106_squashed_0158_release_0_4.py +2357 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/0154_object_keys.py +184 -0
- trueppm_api/apps/projects/migrations/0155_task_milestone_at_day_end.py +20 -0
- trueppm_api/apps/projects/migrations/0156_program_import_job_replaced_project_ids.py +17 -0
- trueppm_api/apps/projects/migrations/0157_forecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/0158_baselinetask_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +9209 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +903 -0
- trueppm_api/apps/projects/program_schedule.py +767 -0
- trueppm_api/apps/projects/program_views.py +3445 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +492 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +891 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/demo_landing_overlay.py +532 -0
- trueppm_api/apps/projects/seed/exporter.py +1717 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2764 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +128 -0
- trueppm_api/apps/projects/seed/replay.py +1695 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1169 -0
- trueppm_api/apps/projects/serializers.py +12904 -0
- trueppm_api/apps/projects/services.py +7525 -0
- trueppm_api/apps/projects/share_serializers.py +142 -0
- trueppm_api/apps/projects/share_services.py +373 -0
- trueppm_api/apps/projects/share_views.py +395 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +910 -0
- trueppm_api/apps/projects/signal_privacy_views.py +485 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +288 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1244 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1979 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +261 -0
- trueppm_api/apps/projects/template_views.py +829 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1016 -0
- trueppm_api/apps/projects/utilization.py +909 -0
- trueppm_api/apps/projects/views.py +21225 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0014_squashed_0015_release_0_4.py +47 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1752 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +144 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/finish_reading.py +198 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/mc_admission.py +181 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0010_squashed_0016_release_0_4.py +106 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/0015_projectforecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/scheduling/migrations/0016_montecarlorun_finish_readings.py +32 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +542 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +334 -0
- trueppm_api/apps/scheduling/serializers.py +723 -0
- trueppm_api/apps/scheduling/services.py +1251 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +2248 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2908 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +446 -0
- trueppm_api/apps/sso/services.py +1473 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +369 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +548 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0003_squashed_0005_release_0_4.py +77 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/0006_boardevent_per_project_seq.py +77 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +272 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +488 -0
- trueppm_api/apps/sync/serializers.py +688 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +556 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +957 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +159 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/0004_backfill_default_teams_on_upgrade.py +49 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +440 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0001_squashed_0004_release_0_4.py +167 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +211 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +597 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +279 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +249 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +100 -0
- trueppm_api/apps/webhooks/migrations/0010_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/0011_webhookeditor.py +48 -0
- trueppm_api/apps/webhooks/migrations/0012_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +437 -0
- trueppm_api/apps/webhooks/serializers.py +326 -0
- trueppm_api/apps/webhooks/tasks.py +680 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +533 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +371 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +254 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_squashed_0027_release_0_4.py +199 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +967 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +616 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1581 -0
- trueppm_api/asgi.py +49 -0
- trueppm_api/celery.py +40 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +934 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/demo_read_only.py +314 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +1221 -0
- trueppm_api/core/password_reset.py +513 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +1034 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +514 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/core/worker_selfheal.py +205 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2605 -0
- trueppm_api/settings/dev.py +296 -0
- trueppm_api/settings/prod.py +265 -0
- trueppm_api/urls.py +217 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0.dist-info/METADATA +273 -0
- trueppm_api-0.4.0.dist-info/RECORD +799 -0
- trueppm_api-0.4.0.dist-info/WHEEL +4 -0
- trueppm_api-0.4.0.dist-info/licenses/LICENSE +201 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICE +7 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICES +104 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/GPL-3.0.txt +674 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/LGPL-3.0.txt +165 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/MPL-2.0.txt +373 -0
trueppm_api/urls.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""Root URL configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.contrib import admin
|
|
7
|
+
from django.urls import include, path
|
|
8
|
+
from drf_spectacular.utils import extend_schema, inline_serializer
|
|
9
|
+
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerSplitView
|
|
10
|
+
from rest_framework import serializers
|
|
11
|
+
from rest_framework.decorators import api_view, permission_classes
|
|
12
|
+
from rest_framework.permissions import AllowAny
|
|
13
|
+
from rest_framework.request import Request
|
|
14
|
+
from rest_framework.response import Response
|
|
15
|
+
|
|
16
|
+
from trueppm_api.apps.observability.views import readyz
|
|
17
|
+
from trueppm_api.core.auth_views import (
|
|
18
|
+
CookieTokenLogoutView,
|
|
19
|
+
CookieTokenObtainPairView,
|
|
20
|
+
CookieTokenRefreshView,
|
|
21
|
+
)
|
|
22
|
+
from trueppm_api.core.password_reset import (
|
|
23
|
+
PasswordResetConfirmView,
|
|
24
|
+
PasswordResetRequestView,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@extend_schema(
|
|
29
|
+
summary="Liveness probe",
|
|
30
|
+
description=(
|
|
31
|
+
'Returns HTTP 200 with `{"status": "ok"}` when the API process is running. '
|
|
32
|
+
"No authentication required. Use for Kubernetes liveness/readiness probes."
|
|
33
|
+
),
|
|
34
|
+
responses={200: inline_serializer("HealthResponse", {"status": serializers.CharField()})},
|
|
35
|
+
auth=[],
|
|
36
|
+
tags=["meta"],
|
|
37
|
+
)
|
|
38
|
+
@api_view(["GET"])
|
|
39
|
+
@permission_classes([AllowAny])
|
|
40
|
+
def health(_request: Request) -> Response:
|
|
41
|
+
return Response({"status": "ok"})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@extend_schema(
|
|
45
|
+
summary="Running edition",
|
|
46
|
+
description=(
|
|
47
|
+
'Returns `{"edition": "community" | "enterprise"}` from the TRUEPPM_EDITION '
|
|
48
|
+
"Django setting. No authentication required. The React shell calls this once "
|
|
49
|
+
"at startup to decide the post-login redirect target (ADR-0029, ADR-0030).\n\n"
|
|
50
|
+
"Also carries the deployment's read-only demo mode (ADR-1197 D3/D4): "
|
|
51
|
+
"`demo_read_only` is always present, and `demo_login_hint` carries the "
|
|
52
|
+
"published shared credential when — and only when — that mode is on.\n\n"
|
|
53
|
+
"`demo_access_gate` names an external identity gate (e.g. Cloudflare Access) "
|
|
54
|
+
"the operator has declared in front of a demo host, so the app can disclose "
|
|
55
|
+
"that the visitor's email was collected before they reached it (#3969, "
|
|
56
|
+
"ADR-1197 D8). It is an operator **assertion** — nothing verifies it — and, "
|
|
57
|
+
"like the login hint, it is emitted only while `demo_read_only` is on.\n\n"
|
|
58
|
+
"`demo_reset_schedule` (#4152) carries the reset CronJob's own cron "
|
|
59
|
+
"expression, so the shell can state the cadence ('resets daily at 08:00 "
|
|
60
|
+
"UTC') from the value that actually drives the reset rather than a "
|
|
61
|
+
"hardcoded number. `null` when the reset is disabled or `demo_read_only` "
|
|
62
|
+
"is false."
|
|
63
|
+
),
|
|
64
|
+
responses={
|
|
65
|
+
200: inline_serializer(
|
|
66
|
+
"EditionResponse",
|
|
67
|
+
{
|
|
68
|
+
"edition": serializers.CharField(),
|
|
69
|
+
"version": serializers.CharField(),
|
|
70
|
+
"build_sha": serializers.CharField(allow_blank=True),
|
|
71
|
+
"demo_read_only": serializers.BooleanField(),
|
|
72
|
+
"demo_login_hint": inline_serializer(
|
|
73
|
+
"DemoLoginHint",
|
|
74
|
+
{
|
|
75
|
+
"username": serializers.CharField(),
|
|
76
|
+
"password": serializers.CharField(),
|
|
77
|
+
},
|
|
78
|
+
allow_null=True,
|
|
79
|
+
required=False,
|
|
80
|
+
),
|
|
81
|
+
"demo_access_gate": inline_serializer(
|
|
82
|
+
"DemoAccessGate",
|
|
83
|
+
{
|
|
84
|
+
"provider": serializers.CharField(),
|
|
85
|
+
"privacy_url": serializers.CharField(allow_null=True),
|
|
86
|
+
},
|
|
87
|
+
allow_null=True,
|
|
88
|
+
required=False,
|
|
89
|
+
),
|
|
90
|
+
"demo_reset_schedule": serializers.CharField(allow_null=True, required=False),
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
},
|
|
94
|
+
auth=[],
|
|
95
|
+
tags=["meta"],
|
|
96
|
+
)
|
|
97
|
+
@api_view(["GET"])
|
|
98
|
+
@permission_classes([AllowAny])
|
|
99
|
+
def edition(request: Request) -> Response:
|
|
100
|
+
"""Return the running edition — community or enterprise.
|
|
101
|
+
|
|
102
|
+
Public endpoint (no auth required). The React shell calls this once at
|
|
103
|
+
startup to decide the post-login redirect target (ADR-0029, ADR-0030).
|
|
104
|
+
The value is controlled by the TRUEPPM_EDITION Django setting, which the
|
|
105
|
+
enterprise Helm chart sets to "enterprise".
|
|
106
|
+
|
|
107
|
+
Also the pre-login channel for the read-only demo mode (ADR-1197 D3/D4). It is
|
|
108
|
+
the only endpoint the shell already calls unauthenticated, so the login screen
|
|
109
|
+
can learn the deployment's mode without a second discovery route — and the
|
|
110
|
+
post-login shell reads the same cached response.
|
|
111
|
+
"""
|
|
112
|
+
demo_read_only = bool(getattr(settings, "DEMO_READ_ONLY", False))
|
|
113
|
+
return Response(
|
|
114
|
+
{
|
|
115
|
+
"edition": settings.TRUEPPM_EDITION,
|
|
116
|
+
# Build identity (#2392): a bug report has to name the exact build it
|
|
117
|
+
# came from, and this is the one endpoint the shell already calls at
|
|
118
|
+
# startup without auth. `build_sha` is empty on a source checkout.
|
|
119
|
+
"version": settings.TRUEPPM_VERSION,
|
|
120
|
+
"build_sha": settings.TRUEPPM_BUILD_SHA,
|
|
121
|
+
# Always emitted, so a client can distinguish "not a demo" from "a server
|
|
122
|
+
# too old to say" without inferring it from an absent key.
|
|
123
|
+
"demo_read_only": demo_read_only,
|
|
124
|
+
# Gated on the MODE, not merely on the setting being present: the hint is
|
|
125
|
+
# a published credential, and an operator who leaves the variable set after
|
|
126
|
+
# turning the demo off must not keep broadcasting it from a live install.
|
|
127
|
+
"demo_login_hint": (
|
|
128
|
+
getattr(settings, "DEMO_LOGIN_HINT", None) if demo_read_only else None
|
|
129
|
+
),
|
|
130
|
+
# Gated on the MODE for the same reason as the hint above, plus one of its
|
|
131
|
+
# own: on a normal install this would tell any unauthenticated caller what
|
|
132
|
+
# sits at the edge, which is free fingerprinting with no visitor benefit —
|
|
133
|
+
# an employee signing in through their own company's IdP is not owed a
|
|
134
|
+
# notice about it. The disclosure #3969 asks for is owed to a *demo*
|
|
135
|
+
# visitor, who had no relationship with this deployment before the gate
|
|
136
|
+
# took their email.
|
|
137
|
+
"demo_access_gate": (
|
|
138
|
+
getattr(settings, "DEMO_ACCESS_GATE", None) if demo_read_only else None
|
|
139
|
+
),
|
|
140
|
+
# Gated on the MODE for the same reason as the hint and the access gate:
|
|
141
|
+
# a leftover TRUEPPM_DEMO_RESET_SCHEDULE on a normal install must not
|
|
142
|
+
# announce a reset cadence that does not apply to it (#4152).
|
|
143
|
+
"demo_reset_schedule": (
|
|
144
|
+
getattr(settings, "DEMO_RESET_SCHEDULE", None) if demo_read_only else None
|
|
145
|
+
),
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# Versioned prefix every app router mounts under.
|
|
151
|
+
_API_V1_PREFIX = "api/v1/"
|
|
152
|
+
|
|
153
|
+
urlpatterns = [
|
|
154
|
+
path("api/v1/health/", health, name="health"),
|
|
155
|
+
# Dependency-aware readiness probe (#1894). Unauthenticated like /health/ so
|
|
156
|
+
# kubelet can call it, but returns 503 when the DB or cache is unreachable —
|
|
157
|
+
# unlike /health/, which is a shallow process-liveness check.
|
|
158
|
+
path("api/v1/readyz", readyz, name="readyz"),
|
|
159
|
+
path("api/v1/edition/", edition, name="edition"),
|
|
160
|
+
path("admin/", admin.site.urls),
|
|
161
|
+
# OpenAPI schema and interactive docs. The *split* Swagger view (not the
|
|
162
|
+
# default inline one) is required by our strict CSP: it delivers the UI
|
|
163
|
+
# bootstrap as a separate same-origin JS request instead of an inline
|
|
164
|
+
# <script>, which script-src 'self' would otherwise block (the page rendered
|
|
165
|
+
# blank — #1603). Asset bundles are served from drf-spectacular-sidecar
|
|
166
|
+
# ('self') via SWAGGER_UI_DIST=SIDECAR in settings.
|
|
167
|
+
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
|
|
168
|
+
path("api/docs/", SpectacularSwaggerSplitView.as_view(url_name="schema"), name="swagger-ui"),
|
|
169
|
+
path(
|
|
170
|
+
"api/schema/swagger-ui/",
|
|
171
|
+
SpectacularSwaggerSplitView.as_view(url_name="schema"),
|
|
172
|
+
name="swagger-ui-compat",
|
|
173
|
+
),
|
|
174
|
+
# JWT auth endpoints (#897). Login returns the access token in the body and
|
|
175
|
+
# sets the refresh token in an httpOnly cookie; refresh reads that cookie;
|
|
176
|
+
# logout clears it (and blacklists the token if the blacklist app is present).
|
|
177
|
+
path("api/v1/auth/token/", CookieTokenObtainPairView.as_view(), name="token_obtain_pair"),
|
|
178
|
+
path("api/v1/auth/token/refresh/", CookieTokenRefreshView.as_view(), name="token_refresh"),
|
|
179
|
+
path("api/v1/auth/logout/", CookieTokenLogoutView.as_view(), name="token_logout"),
|
|
180
|
+
# Self-service password reset (#765, ADR-0209). Both AllowAny + throttled with a
|
|
181
|
+
# dedicated "password_reset" scope. Request always returns 200 (no user
|
|
182
|
+
# enumeration); confirm validates a stateless token, sets the password, and
|
|
183
|
+
# revokes all of the account's other sessions.
|
|
184
|
+
path(
|
|
185
|
+
"api/v1/auth/password/reset/",
|
|
186
|
+
PasswordResetRequestView.as_view(),
|
|
187
|
+
name="password_reset",
|
|
188
|
+
),
|
|
189
|
+
path(
|
|
190
|
+
"api/v1/auth/password/reset/confirm/",
|
|
191
|
+
PasswordResetConfirmView.as_view(),
|
|
192
|
+
name="password_reset_confirm",
|
|
193
|
+
),
|
|
194
|
+
# Versioned API
|
|
195
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.access.urls")),
|
|
196
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.projects.urls")),
|
|
197
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.resources.urls")),
|
|
198
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.scheduling.urls")),
|
|
199
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.sync.urls")),
|
|
200
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.history.urls")),
|
|
201
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.msproject.urls")),
|
|
202
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.jiraimport.urls")),
|
|
203
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.csvimport.urls")),
|
|
204
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.webhooks.urls")),
|
|
205
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.taskruns.urls")),
|
|
206
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.notifications.urls")),
|
|
207
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.integrations.urls")),
|
|
208
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.observability.urls")),
|
|
209
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.workspace.urls")),
|
|
210
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.teams.urls")),
|
|
211
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.timetracking.urls")),
|
|
212
|
+
# Basic SSO — OIDC relying party (ADR-0187). Flow endpoints under
|
|
213
|
+
# auth/oidc/*, admin config under workspace/sso/*.
|
|
214
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.sso.urls")),
|
|
215
|
+
# Team-readable agent-action audit log (ADR-0112 RC1, #1805).
|
|
216
|
+
path(_API_V1_PREFIX, include("trueppm_api.apps.agents.urls")),
|
|
217
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""TruePPM workflow execution — the single public import surface (ADR-0080).
|
|
2
|
+
|
|
3
|
+
Workflow consumer code imports only from this package: the service functions to
|
|
4
|
+
drive workflows and the value types to describe them. It must never import a
|
|
5
|
+
backend, ``celery``, ``dbos``, or ``temporalio`` (ADR-0080 §E).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from trueppm_api.workflows.interface import (
|
|
9
|
+
HistoryEvent,
|
|
10
|
+
RetryPolicy,
|
|
11
|
+
WorkflowState,
|
|
12
|
+
WorkflowStatus,
|
|
13
|
+
)
|
|
14
|
+
from trueppm_api.workflows.services import (
|
|
15
|
+
cancel_workflow,
|
|
16
|
+
get_history,
|
|
17
|
+
get_workflow_state,
|
|
18
|
+
query_workflow,
|
|
19
|
+
run_activity,
|
|
20
|
+
signal_workflow,
|
|
21
|
+
sleep,
|
|
22
|
+
start_workflow,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"HistoryEvent",
|
|
27
|
+
"RetryPolicy",
|
|
28
|
+
"WorkflowState",
|
|
29
|
+
"WorkflowStatus",
|
|
30
|
+
"cancel_workflow",
|
|
31
|
+
"get_history",
|
|
32
|
+
"get_workflow_state",
|
|
33
|
+
"query_workflow",
|
|
34
|
+
"run_activity",
|
|
35
|
+
"signal_workflow",
|
|
36
|
+
"sleep",
|
|
37
|
+
"start_workflow",
|
|
38
|
+
]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Workflow backend implementations.
|
|
2
|
+
|
|
3
|
+
The default outbox-composing backend lands in ``default.py`` (ADR-0080 §A); a
|
|
4
|
+
DBOS backend follows as the second OSS backend in 1.0. Backends are resolved by
|
|
5
|
+
``trueppm_api.workflows.services.get_backend`` and never imported by consumer code.
|
|
6
|
+
"""
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""Default workflow backend — outbox-composing, Celery-driven (ADR-0080 §A, §D).
|
|
2
|
+
|
|
3
|
+
Executes a declarative :class:`~trueppm_api.workflows.registry.WorkflowDefinition`
|
|
4
|
+
step-by-step. Each step-advance writes a ``WorkflowOutboxRow`` in the same
|
|
5
|
+
transaction as the state change and dispatches it via ``transaction.on_commit``,
|
|
6
|
+
so a broker outage between commit and dispatch cannot drop a step — the
|
|
7
|
+
``workflows_outbox_drain`` Beat task re-dispatches stranded rows. Activities are
|
|
8
|
+
once-and-only-once at ``(workflow, activity_name, input_hash)``, which makes the
|
|
9
|
+
backend safe under the outbox's at-least-once delivery.
|
|
10
|
+
|
|
11
|
+
This module is a backend implementation, not consumer code — it may import
|
|
12
|
+
Django and (lazily) Celery. The shared engine helpers live here so the step task
|
|
13
|
+
(:mod:`trueppm_api.apps.workflow_engine.tasks`) can reuse them without a circular import.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import contextlib
|
|
19
|
+
import hashlib
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
from collections.abc import Iterator
|
|
23
|
+
from contextvars import ContextVar
|
|
24
|
+
from datetime import timedelta
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from django.db import transaction
|
|
28
|
+
from django.db.models import Max
|
|
29
|
+
from django.utils import timezone
|
|
30
|
+
|
|
31
|
+
from trueppm_api.apps.workflow_engine.models import (
|
|
32
|
+
WorkflowActivityExecution,
|
|
33
|
+
WorkflowHistoryEvent,
|
|
34
|
+
WorkflowInstance,
|
|
35
|
+
WorkflowOutboxRow,
|
|
36
|
+
WorkflowOutboxStatus,
|
|
37
|
+
WorkflowSignal,
|
|
38
|
+
WorkflowStatus,
|
|
39
|
+
WorkflowTimer,
|
|
40
|
+
)
|
|
41
|
+
from trueppm_api.workflows.interface import (
|
|
42
|
+
HistoryEvent,
|
|
43
|
+
RetryPolicy,
|
|
44
|
+
WorkflowBackend,
|
|
45
|
+
WorkflowState,
|
|
46
|
+
)
|
|
47
|
+
from trueppm_api.workflows.interface import (
|
|
48
|
+
WorkflowStatus as IWorkflowStatus,
|
|
49
|
+
)
|
|
50
|
+
from trueppm_api.workflows.registry import WORKFLOWS, Activity, WorkflowDefinition
|
|
51
|
+
|
|
52
|
+
logger = logging.getLogger(__name__)
|
|
53
|
+
|
|
54
|
+
_TERMINAL_STATUSES = frozenset(
|
|
55
|
+
{WorkflowStatus.COMPLETED, WorkflowStatus.FAILED, WorkflowStatus.CANCELED}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Ambient "currently-executing workflow" id, set by the step task while an
|
|
59
|
+
# activity runs so that run_activity() (called from within an activity) can
|
|
60
|
+
# resolve its workflow without an explicit id. The codebase otherwise threads
|
|
61
|
+
# context explicitly; this is the one place an implicit handle is warranted, and
|
|
62
|
+
# it is confined to the default backend's execution window.
|
|
63
|
+
_current_workflow_id: ContextVar[str | None] = ContextVar(
|
|
64
|
+
"trueppm_current_workflow_id", default=None
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Shared engine helpers (used here and by the step task)
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def canonical_hash(obj: Any) -> str:
|
|
74
|
+
"""Stable SHA-256 of a JSON-able object — order-independent for dicts."""
|
|
75
|
+
encoded = json.dumps(obj, sort_keys=True, separators=(",", ":"), default=str)
|
|
76
|
+
return hashlib.sha256(encoded.encode()).hexdigest()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def default_idempotency_key(name: str, workflow_input: dict[str, Any]) -> str:
|
|
80
|
+
"""Deterministic start-dedup key: a retried start of the same name+input
|
|
81
|
+
collides on ``WorkflowInstance.idempotency_key`` and returns the original."""
|
|
82
|
+
return canonical_hash({"name": name, "input": workflow_input})
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def record_history(
|
|
86
|
+
instance: WorkflowInstance, event_type: str, payload: dict[str, Any]
|
|
87
|
+
) -> WorkflowHistoryEvent:
|
|
88
|
+
"""Append an ordered history event. Must be called while holding the
|
|
89
|
+
instance row lock so the per-workflow ``seq`` is allocated race-free."""
|
|
90
|
+
last_seq = WorkflowHistoryEvent.objects.filter(workflow=instance).aggregate(m=Max("seq"))["m"]
|
|
91
|
+
seq = 0 if last_seq is None else last_seq + 1
|
|
92
|
+
return WorkflowHistoryEvent.objects.create(
|
|
93
|
+
workflow=instance, seq=seq, event_type=event_type, payload=payload
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@contextlib.contextmanager
|
|
98
|
+
def workflow_context(workflow_id: str) -> Iterator[None]:
|
|
99
|
+
"""Bind the ambient workflow id for the duration of a step execution."""
|
|
100
|
+
token = _current_workflow_id.set(workflow_id)
|
|
101
|
+
try:
|
|
102
|
+
yield
|
|
103
|
+
finally:
|
|
104
|
+
_current_workflow_id.reset(token)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def step_context(instance: WorkflowInstance) -> dict[str, Any]:
|
|
108
|
+
"""The dict every step activity receives: the workflow input plus the
|
|
109
|
+
results of all previously-completed activities, keyed by activity name."""
|
|
110
|
+
prior = {
|
|
111
|
+
execution.activity_name: execution.result
|
|
112
|
+
for execution in WorkflowActivityExecution.objects.filter(workflow=instance).exclude(
|
|
113
|
+
result=None
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
return {"workflow_input": instance.input, "prior": prior}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def run_activity_oaoo(
|
|
120
|
+
instance: WorkflowInstance,
|
|
121
|
+
activity_name: str,
|
|
122
|
+
activity_input: dict[str, Any],
|
|
123
|
+
activity: Activity,
|
|
124
|
+
) -> dict[str, Any] | None:
|
|
125
|
+
"""Execute an activity once-and-only-once.
|
|
126
|
+
|
|
127
|
+
A prior successful execution (matching ``(workflow, activity_name,
|
|
128
|
+
input_hash)``) returns its recorded result without re-running the side
|
|
129
|
+
effect. Callers must hold the instance row lock so two redelivered copies of
|
|
130
|
+
the same step cannot both execute the activity.
|
|
131
|
+
"""
|
|
132
|
+
input_hash = canonical_hash(activity_input)
|
|
133
|
+
execution, created = WorkflowActivityExecution.objects.get_or_create(
|
|
134
|
+
workflow=instance, activity_name=activity_name, input_hash=input_hash
|
|
135
|
+
)
|
|
136
|
+
if not created and execution.result is not None:
|
|
137
|
+
cached: dict[str, Any] | None = execution.result
|
|
138
|
+
return cached
|
|
139
|
+
result = activity(activity_input)
|
|
140
|
+
execution.result = result
|
|
141
|
+
execution.save(update_fields=["result"])
|
|
142
|
+
return result
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def enqueue_step(instance_id: str, step_index: int) -> None:
|
|
146
|
+
"""Write a step-advance outbox row and best-effort dispatch it.
|
|
147
|
+
|
|
148
|
+
Mirrors ``scheduling.services.enqueue_recalculate``: the row is the durable
|
|
149
|
+
record, the immediate ``.delay()`` is best-effort, and ``workflows_outbox_drain``
|
|
150
|
+
re-dispatches if the broker is down. Dispatch is deferred to
|
|
151
|
+
``transaction.on_commit`` so the row is committed before the task reads it.
|
|
152
|
+
"""
|
|
153
|
+
row = WorkflowOutboxRow.objects.create(
|
|
154
|
+
workflow_id=instance_id,
|
|
155
|
+
step_name=str(step_index),
|
|
156
|
+
step_input={"step_index": step_index},
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def _dispatch() -> None:
|
|
160
|
+
from trueppm_api.apps.workflow_engine.tasks import advance_workflow_step
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
result = advance_workflow_step.delay(str(row.id))
|
|
164
|
+
except Exception:
|
|
165
|
+
logger.exception(
|
|
166
|
+
"enqueue_step: immediate dispatch failed for outbox row %s "
|
|
167
|
+
"— workflows_outbox_drain will retry",
|
|
168
|
+
row.id,
|
|
169
|
+
)
|
|
170
|
+
return
|
|
171
|
+
WorkflowOutboxRow.objects.filter(id=row.id, status=WorkflowOutboxStatus.PENDING).update(
|
|
172
|
+
status=WorkflowOutboxStatus.DISPATCHED,
|
|
173
|
+
celery_task_id=result.id,
|
|
174
|
+
dispatched_at=timezone.now(),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
transaction.on_commit(_dispatch)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def resolve_activity(
|
|
181
|
+
definition: WorkflowDefinition, instance: WorkflowInstance, activity_name: str
|
|
182
|
+
) -> Activity:
|
|
183
|
+
"""Find the activity callable for ``activity_name`` in the workflow's chain."""
|
|
184
|
+
for step in definition.build_steps(instance.input):
|
|
185
|
+
if step.name == activity_name:
|
|
186
|
+
return step.activity
|
|
187
|
+
raise KeyError(f"workflow {instance.name!r} has no activity named {activity_name!r}")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
# Backend
|
|
192
|
+
# ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class DefaultWorkflowBackend(WorkflowBackend):
|
|
196
|
+
"""The v1 OSS workflow backend: declarative chains over outbox + Celery."""
|
|
197
|
+
|
|
198
|
+
def start_workflow(
|
|
199
|
+
self,
|
|
200
|
+
name: str,
|
|
201
|
+
input: dict[str, Any],
|
|
202
|
+
*,
|
|
203
|
+
idempotency_key: str | None = None,
|
|
204
|
+
wait_for_completion: bool = False,
|
|
205
|
+
) -> str:
|
|
206
|
+
if wait_for_completion:
|
|
207
|
+
raise NotImplementedError(
|
|
208
|
+
"the default backend is fire-and-forget; wait_for_completion is not "
|
|
209
|
+
"supported. Poll get_workflow_state() instead."
|
|
210
|
+
)
|
|
211
|
+
# Fail fast if the workflow name is not registered, before creating state.
|
|
212
|
+
WORKFLOWS.get(name)
|
|
213
|
+
key = idempotency_key or default_idempotency_key(name, input)
|
|
214
|
+
with transaction.atomic():
|
|
215
|
+
instance, created = WorkflowInstance.objects.get_or_create(
|
|
216
|
+
idempotency_key=key,
|
|
217
|
+
defaults={"name": name, "input": input, "status": WorkflowStatus.RUNNING},
|
|
218
|
+
)
|
|
219
|
+
if created:
|
|
220
|
+
record_history(instance, "workflow_started", {"input": input})
|
|
221
|
+
enqueue_step(str(instance.id), 0)
|
|
222
|
+
return str(instance.id)
|
|
223
|
+
|
|
224
|
+
def signal_workflow(self, workflow_id: str, signal: str, payload: dict[str, Any]) -> None:
|
|
225
|
+
# Durably record the signal in the inbox and history. v1 chains do not
|
|
226
|
+
# yet gate steps on signals; the inbox is what makes that a later,
|
|
227
|
+
# non-breaking addition (a step can drain unconsumed signals).
|
|
228
|
+
with transaction.atomic():
|
|
229
|
+
instance = WorkflowInstance.objects.select_for_update().get(id=workflow_id)
|
|
230
|
+
WorkflowSignal.objects.create(workflow=instance, signal_name=signal, payload=payload)
|
|
231
|
+
record_history(instance, "signal_received", {"signal": signal, "payload": payload})
|
|
232
|
+
|
|
233
|
+
def query_workflow(self, workflow_id: str, query: str, args: dict[str, Any]) -> Any:
|
|
234
|
+
instance = WorkflowInstance.objects.get(id=workflow_id)
|
|
235
|
+
return WORKFLOWS.get(instance.name).on_query(query, args)
|
|
236
|
+
|
|
237
|
+
def get_workflow_state(self, workflow_id: str) -> WorkflowState:
|
|
238
|
+
instance = WorkflowInstance.objects.get(id=workflow_id)
|
|
239
|
+
return WorkflowState(
|
|
240
|
+
workflow_id=str(instance.id),
|
|
241
|
+
name=instance.name,
|
|
242
|
+
status=IWorkflowStatus(instance.status),
|
|
243
|
+
result=instance.result,
|
|
244
|
+
error=instance.error,
|
|
245
|
+
created_at=instance.created_at,
|
|
246
|
+
updated_at=instance.updated_at,
|
|
247
|
+
completed_at=instance.completed_at,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def cancel_workflow(self, workflow_id: str, reason: str = "") -> None:
|
|
251
|
+
with transaction.atomic():
|
|
252
|
+
instance = WorkflowInstance.objects.select_for_update().get(id=workflow_id)
|
|
253
|
+
if instance.status in _TERMINAL_STATUSES:
|
|
254
|
+
return # already finished — cancellation is a no-op
|
|
255
|
+
instance.status = WorkflowStatus.CANCELED
|
|
256
|
+
instance.cancel_reason = reason
|
|
257
|
+
instance.completed_at = timezone.now()
|
|
258
|
+
instance.save(update_fields=["status", "cancel_reason", "completed_at", "updated_at"])
|
|
259
|
+
record_history(instance, "workflow_canceled", {"reason": reason})
|
|
260
|
+
|
|
261
|
+
def sleep(self, workflow_id: str, duration: timedelta) -> None:
|
|
262
|
+
with transaction.atomic():
|
|
263
|
+
instance = WorkflowInstance.objects.select_for_update().get(id=workflow_id)
|
|
264
|
+
fire_at = timezone.now() + duration
|
|
265
|
+
WorkflowTimer.objects.create(workflow=instance, fire_at=fire_at)
|
|
266
|
+
if instance.status == WorkflowStatus.RUNNING:
|
|
267
|
+
instance.status = WorkflowStatus.SLEEPING
|
|
268
|
+
instance.save(update_fields=["status", "updated_at"])
|
|
269
|
+
record_history(instance, "sleep_started", {"fire_at": fire_at.isoformat()})
|
|
270
|
+
|
|
271
|
+
def run_activity(
|
|
272
|
+
self, name: str, input: dict[str, Any], retry_policy: RetryPolicy | None = None
|
|
273
|
+
) -> Any:
|
|
274
|
+
# retry_policy is reserved: v1 derives retry from at-least-once outbox
|
|
275
|
+
# redelivery plus OAOO idempotency, not from a per-activity policy.
|
|
276
|
+
workflow_id = _current_workflow_id.get()
|
|
277
|
+
if workflow_id is None:
|
|
278
|
+
raise RuntimeError(
|
|
279
|
+
"run_activity() must be called within a workflow step execution; "
|
|
280
|
+
"no ambient workflow context is set."
|
|
281
|
+
)
|
|
282
|
+
instance = WorkflowInstance.objects.get(id=workflow_id)
|
|
283
|
+
activity = resolve_activity(WORKFLOWS.get(instance.name), instance, name)
|
|
284
|
+
return run_activity_oaoo(instance, name, input, activity)
|
|
285
|
+
|
|
286
|
+
def get_history(self, workflow_id: str) -> list[HistoryEvent]:
|
|
287
|
+
return [
|
|
288
|
+
HistoryEvent(
|
|
289
|
+
seq=event.seq,
|
|
290
|
+
event_type=event.event_type,
|
|
291
|
+
payload=event.payload,
|
|
292
|
+
occurred_at=event.created_at,
|
|
293
|
+
)
|
|
294
|
+
for event in WorkflowHistoryEvent.objects.filter(workflow__id=workflow_id).order_by(
|
|
295
|
+
"seq"
|
|
296
|
+
)
|
|
297
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Registered workflow definitions (ADR-0080 §A, §E).
|
|
2
|
+
|
|
3
|
+
Consumer workflows author *what* runs as a declarative ``WorkflowDefinition``
|
|
4
|
+
chain and register it against the ``WORKFLOWS`` registry from an
|
|
5
|
+
``AppConfig.ready()``. Per ADR-0080 §E, consumer modules must not import an
|
|
6
|
+
engine (``celery``, ``dbos``, ``temporalio``) directly — side effects go through
|
|
7
|
+
a domain service, which is the only layer allowed to touch the engine. That keeps
|
|
8
|
+
workflow authoring backend-neutral.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Requeue-failed-task workflow — the first consumer of the durable backend (ADR-0210).
|
|
2
|
+
|
|
3
|
+
An operator requeue of a dead-lettered Celery task must round-trip through the
|
|
4
|
+
#652 outbox-composing workflow backend (ADR-0080), *not* the raw
|
|
5
|
+
``current_app.send_task`` side channel it replaces. This single-step workflow is
|
|
6
|
+
how it does so: starting it via ``workflows.services.start_workflow`` writes a
|
|
7
|
+
``WorkflowOutboxRow`` inside the request transaction and dispatches on
|
|
8
|
+
``transaction.on_commit``; if the broker is down the ``workflows_outbox_drain``
|
|
9
|
+
Beat task re-dispatches it, so the re-enqueue is durable.
|
|
10
|
+
|
|
11
|
+
The one step, ``redispatch``, re-enqueues the original task. Per ADR-0080 §E this
|
|
12
|
+
consumer module never imports Celery directly — it delegates the actual
|
|
13
|
+
``send_task`` to ``scheduling.services.redispatch_dead_lettered_task``, the domain
|
|
14
|
+
service that owns the engine boundary. The operator-chosen backoff is applied there
|
|
15
|
+
as a Celery ``countdown`` (ADR-0210 §2: v1 has no chain-resuming durable ``sleep``,
|
|
16
|
+
so the *delay* is best-effort while the *re-enqueue* is durable via the outbox).
|
|
17
|
+
|
|
18
|
+
The activity is executed once-and-only-once by the engine (unique
|
|
19
|
+
``(workflow, activity_name, input_hash)``), so an at-least-once outbox redelivery
|
|
20
|
+
cannot double-dispatch the original task.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from trueppm_api.workflows.registry import WorkflowDefinition, WorkflowStep
|
|
28
|
+
|
|
29
|
+
WORKFLOW_NAME = "scheduling.requeue_failed_task"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _redispatch_activity(ctx: dict[str, Any]) -> dict[str, Any] | None:
|
|
33
|
+
"""Re-enqueue the original Celery task with the operator's backoff.
|
|
34
|
+
|
|
35
|
+
``ctx`` is the engine-supplied step context: ``ctx["workflow_input"]`` holds the
|
|
36
|
+
requeue input assembled by the viewset. Delegates to the scheduling service so
|
|
37
|
+
this consumer stays free of a direct Celery import (ADR-0080 §E).
|
|
38
|
+
"""
|
|
39
|
+
# Imported lazily and from the service layer (not `celery`) to honour the
|
|
40
|
+
# consumer import discipline; the service owns the `send_task` boundary.
|
|
41
|
+
from trueppm_api.apps.scheduling.services import redispatch_dead_lettered_task
|
|
42
|
+
|
|
43
|
+
wf_input = ctx["workflow_input"]
|
|
44
|
+
dispatched_task_id = redispatch_dead_lettered_task(
|
|
45
|
+
task_name=wf_input["task_name"],
|
|
46
|
+
args=wf_input.get("args", []),
|
|
47
|
+
kwargs=wf_input.get("kwargs", {}),
|
|
48
|
+
countdown=int(wf_input.get("backoff_seconds", 0) or 0),
|
|
49
|
+
)
|
|
50
|
+
return {
|
|
51
|
+
"failed_task_id": wf_input.get("failed_task_id"),
|
|
52
|
+
"dispatched_task_id": dispatched_task_id,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class RequeueFailedTaskWorkflow(WorkflowDefinition):
|
|
57
|
+
"""One-step workflow that durably re-dispatches a dead-lettered Celery task."""
|
|
58
|
+
|
|
59
|
+
name = WORKFLOW_NAME
|
|
60
|
+
|
|
61
|
+
def build_steps(self, workflow_input: dict[str, Any]) -> list[WorkflowStep]:
|
|
62
|
+
# Single step, no compensation: re-enqueueing is the terminal side effect and
|
|
63
|
+
# there is nothing to undo — if the re-dispatched task fails again it simply
|
|
64
|
+
# dead-letters back into FailedTask (ADR-0210 §Durable Execution 8).
|
|
65
|
+
return [WorkflowStep(name="redispatch", activity=_redispatch_activity)]
|