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
|
File without changes
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""App config for scheduling."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from django.apps import AppConfig
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
# Wall-clock start time (time.monotonic(), immune to system clock adjustments) for
|
|
14
|
+
# each in-flight Celery task execution, keyed by task_id. Populated in
|
|
15
|
+
# _on_task_prerun and consumed (popped) in _on_task_postrun — that pair always
|
|
16
|
+
# brackets one execution 1:1, including retried and failed ones, so duration is
|
|
17
|
+
# available for every outcome, not just success (#1917). Celery itself does not
|
|
18
|
+
# set a usable `.runtime` attribute on the request for anything but the success
|
|
19
|
+
# path, which is why this is timed here instead of read off `task.request`.
|
|
20
|
+
_task_start_times: dict[str, float] = {}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SchedulingConfig(AppConfig):
|
|
24
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
25
|
+
name = "trueppm_api.apps.scheduling"
|
|
26
|
+
|
|
27
|
+
def ready(self) -> None:
|
|
28
|
+
"""Bridge Celery framework signals to Django signals.
|
|
29
|
+
|
|
30
|
+
Enterprise packages connect receivers to the Django signals in
|
|
31
|
+
scheduling.signals — they never need to import from Celery directly.
|
|
32
|
+
|
|
33
|
+
Every ``.connect(...)`` below now passes ``weak=False`` (#1917 fix). Celery's
|
|
34
|
+
``Signal.connect`` defaults to a *weak* reference to the receiver, and the
|
|
35
|
+
four ``_on_task_*`` functions are closures local to this method call — with
|
|
36
|
+
no other strong reference anywhere, they were garbage-collected the moment
|
|
37
|
+
``ready()`` returned, silently turning the whole Celery→Django task-lifecycle
|
|
38
|
+
bridge into dead code (confirmed via ``task_prerun.receivers`` showing a
|
|
39
|
+
dead weakref immediately after startup). None of ``celery_task_started`` /
|
|
40
|
+
``_succeeded`` / ``_failed`` / ``_retried`` were ever actually being sent in
|
|
41
|
+
a real worker, which is why #1917 found no start/success/retry log lines to
|
|
42
|
+
begin with — this was the root cause, not just a missing receiver. A stable
|
|
43
|
+
``dispatch_uid`` per connect call is required alongside ``weak=False``: without
|
|
44
|
+
it, a second ``ready()`` call (the test runner / autoreloader can trigger one)
|
|
45
|
+
would register a *second*, now-permanent strong-ref closure rather than being
|
|
46
|
+
recognized as the same receiver, doubling every log line and metric sample
|
|
47
|
+
from then on.
|
|
48
|
+
"""
|
|
49
|
+
from celery.signals import task_failure, task_postrun, task_prerun, task_retry
|
|
50
|
+
|
|
51
|
+
# Register the OSS dead-letter alerting receiver (ADR-0084) and the
|
|
52
|
+
# start/success/retry structured-log receivers (#1917). Side-effect
|
|
53
|
+
# import: the @receiver decorators in receivers.py connect on load.
|
|
54
|
+
from trueppm_api.apps.observability.otel.metrics import record_task_duration
|
|
55
|
+
from trueppm_api.apps.scheduling import receivers # noqa: F401
|
|
56
|
+
from trueppm_api.apps.scheduling.signals import (
|
|
57
|
+
celery_task_failed,
|
|
58
|
+
celery_task_retried,
|
|
59
|
+
celery_task_started,
|
|
60
|
+
celery_task_succeeded,
|
|
61
|
+
)
|
|
62
|
+
from trueppm_api.core.extension_signals import dispatch_extension_signal
|
|
63
|
+
|
|
64
|
+
self._register_workflows()
|
|
65
|
+
|
|
66
|
+
@task_prerun.connect(weak=False, dispatch_uid="scheduling.on_task_prerun") # type: ignore[untyped-decorator]
|
|
67
|
+
def _on_task_prerun(sender: Any, task_id: str, task: Any, **kwargs: Any) -> None:
|
|
68
|
+
_task_start_times[task_id] = time.monotonic()
|
|
69
|
+
# sender=None: these signals are about a worker run, not a model row,
|
|
70
|
+
# so there is no class for the dispatcher to filter on. Receivers
|
|
71
|
+
# branch on the task_name kwarg (#3777).
|
|
72
|
+
dispatch_extension_signal(
|
|
73
|
+
celery_task_started,
|
|
74
|
+
sender=None,
|
|
75
|
+
task_id=task_id,
|
|
76
|
+
task_name=getattr(task, "name", ""),
|
|
77
|
+
args=kwargs.get("args", ()),
|
|
78
|
+
kwargs=kwargs.get("kwargs", {}),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
@task_postrun.connect(weak=False, dispatch_uid="scheduling.on_task_postrun") # type: ignore[untyped-decorator]
|
|
82
|
+
def _on_task_postrun(
|
|
83
|
+
sender: Any, task_id: str, task: Any, retval: Any, **kwargs: Any
|
|
84
|
+
) -> None:
|
|
85
|
+
# task_postrun fires for every terminal state (success, failure, retry,
|
|
86
|
+
# rejected, ignored) — unlike celery_task_succeeded below, the duration
|
|
87
|
+
# metric is recorded for all of them so a stuck/slow retry loop is
|
|
88
|
+
# visible in the histogram too, not just clean runs.
|
|
89
|
+
state = kwargs.get("state", "") or ""
|
|
90
|
+
task_name = getattr(task, "name", "")
|
|
91
|
+
start = _task_start_times.pop(task_id, None)
|
|
92
|
+
duration = time.monotonic() - start if start is not None else 0.0
|
|
93
|
+
record_task_duration(
|
|
94
|
+
task_name=task_name,
|
|
95
|
+
duration_seconds=duration,
|
|
96
|
+
outcome=state.lower() or "unknown",
|
|
97
|
+
)
|
|
98
|
+
if state == "SUCCESS":
|
|
99
|
+
dispatch_extension_signal(
|
|
100
|
+
celery_task_succeeded,
|
|
101
|
+
sender=None,
|
|
102
|
+
task_id=task_id,
|
|
103
|
+
task_name=task_name,
|
|
104
|
+
runtime_seconds=duration,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
@task_failure.connect(weak=False, dispatch_uid="scheduling.on_task_failure") # type: ignore[untyped-decorator]
|
|
108
|
+
def _on_task_failure(
|
|
109
|
+
sender: Any, task_id: str, exception: BaseException, **kwargs: Any
|
|
110
|
+
) -> None:
|
|
111
|
+
import traceback as tb_module
|
|
112
|
+
|
|
113
|
+
dispatch_extension_signal(
|
|
114
|
+
celery_task_failed,
|
|
115
|
+
sender=None,
|
|
116
|
+
task_id=task_id,
|
|
117
|
+
task_name=getattr(sender, "name", ""),
|
|
118
|
+
exception=exception,
|
|
119
|
+
traceback_str="".join(
|
|
120
|
+
tb_module.format_exception(type(exception), exception, exception.__traceback__)
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@task_retry.connect(weak=False, dispatch_uid="scheduling.on_task_retry") # type: ignore[untyped-decorator]
|
|
125
|
+
def _on_task_retry(sender: Any, request: Any, reason: Any, **kwargs: Any) -> None:
|
|
126
|
+
dispatch_extension_signal(
|
|
127
|
+
celery_task_retried,
|
|
128
|
+
sender=None,
|
|
129
|
+
task_id=getattr(request, "id", ""),
|
|
130
|
+
task_name=getattr(sender, "name", ""),
|
|
131
|
+
attempt=getattr(request, "retries", 0),
|
|
132
|
+
exception=reason,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def _register_workflows() -> None:
|
|
137
|
+
"""Register the scheduling app's durable workflow definitions (ADR-0080 §A).
|
|
138
|
+
|
|
139
|
+
The requeue-failed-task workflow (ADR-0210) is the first consumer of the
|
|
140
|
+
durable backend. Registered here from the owning app's ``ready()`` — the
|
|
141
|
+
scheduling app owns ``FailedTask`` and already bridges Celery signals in
|
|
142
|
+
``ready()``, so the workflow lives with its domain rather than in the
|
|
143
|
+
backend-neutral engine app. Guarded against duplicate registration because
|
|
144
|
+
``ready()`` can run more than once under the test runner and the registry
|
|
145
|
+
raises on a duplicate name.
|
|
146
|
+
"""
|
|
147
|
+
from trueppm_api.workflows.consumers.requeue_failed_task import (
|
|
148
|
+
RequeueFailedTaskWorkflow,
|
|
149
|
+
)
|
|
150
|
+
from trueppm_api.workflows.registry import WORKFLOWS
|
|
151
|
+
|
|
152
|
+
if RequeueFailedTaskWorkflow.name not in WORKFLOWS.all():
|
|
153
|
+
WORKFLOWS.register(RequeueFailedTaskWorkflow())
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Composable working-calendar resolution (#906, ADR-0251).
|
|
2
|
+
|
|
3
|
+
A project's effective non-working mask for CPM is the overlay (union) of several
|
|
4
|
+
applied calendars: a base ``Project.calendar`` plus zero or more
|
|
5
|
+
``ProjectCalendarLayer`` overlays. A day is non-working if *any* applied calendar
|
|
6
|
+
marks it so — masks AND-composed, exception ranges unioned.
|
|
7
|
+
|
|
8
|
+
The fold itself lives in ``trueppm_scheduler.models.Calendar.compose`` (the Apache
|
|
9
|
+
package). This module is the Django-side seam: it resolves *which* calendars apply
|
|
10
|
+
to a project (through a small resolver registry) and converts them to scheduler
|
|
11
|
+
dataclasses via the single ``build_sched_calendar`` primitive.
|
|
12
|
+
|
|
13
|
+
The resolver registry is the one-way (enterprise → core) extension point
|
|
14
|
+
(ADR-0251 §6): OSS registers exactly one resolver (base + overlay layers);
|
|
15
|
+
enterprise appends more (per-resource, cross-program calendars) against the same
|
|
16
|
+
signature. Because composition is AND-of-masks + union-of-exceptions, any added
|
|
17
|
+
resolver is always additive and order-independent, so a new source can never be a
|
|
18
|
+
breaking change to an existing project's schedule.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from collections.abc import Callable
|
|
24
|
+
from typing import TYPE_CHECKING, Any
|
|
25
|
+
|
|
26
|
+
from trueppm_api.apps.scheduling.services import build_sched_calendar
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from trueppm_api.apps.projects.models import Calendar, Project
|
|
30
|
+
|
|
31
|
+
# A resolver maps a project to the ordered Django ``Calendar`` rows it applies.
|
|
32
|
+
CalendarLayerResolver = Callable[["Project"], "list[Calendar]"]
|
|
33
|
+
|
|
34
|
+
_LAYER_RESOLVERS: list[CalendarLayerResolver] = []
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def register_calendar_layer_resolver(resolver: CalendarLayerResolver) -> None:
|
|
38
|
+
"""Register a resolver that contributes calendars to a project's composed mask.
|
|
39
|
+
|
|
40
|
+
Idempotent: registering the same resolver twice is a no-op, so an
|
|
41
|
+
``AppConfig.ready()`` that runs more than once (or a re-imported test module)
|
|
42
|
+
cannot double-count a source.
|
|
43
|
+
"""
|
|
44
|
+
if resolver not in _LAYER_RESOLVERS:
|
|
45
|
+
_LAYER_RESOLVERS.append(resolver)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def oss_project_layers(project: Project) -> list[Calendar]:
|
|
49
|
+
"""OSS resolver: the effective base calendar plus its overlay layers, in order.
|
|
50
|
+
|
|
51
|
+
The base is resolved through the inheritance chain (ADR-0441): the project's own
|
|
52
|
+
``calendar`` if set, else its program's, else the workspace default — computed-on-read
|
|
53
|
+
in ``apps.projects.calendar_settings`` so CPM and the serializers can never disagree
|
|
54
|
+
on which calendar a project schedules against. ``None`` up the whole chain means the
|
|
55
|
+
system default, so ``compose`` falls back to Mon-Fri/8h/UTC exactly as before.
|
|
56
|
+
|
|
57
|
+
Overlays (``ProjectCalendarLayer``, ADR-0251) still stack on top of the inherited
|
|
58
|
+
base — inheritance only decides the base; it does not change overlay composition.
|
|
59
|
+
|
|
60
|
+
Callers must have prefetched ``calendar__exceptions``,
|
|
61
|
+
``calendar_layers__calendar__exceptions`` and — so the inherited tiers don't N+1 —
|
|
62
|
+
``program__calendar__exceptions`` when the project may inherit from its program.
|
|
63
|
+
"""
|
|
64
|
+
from trueppm_api.apps.projects.calendar_settings import resolve_effective_base_calendar
|
|
65
|
+
|
|
66
|
+
calendars: list[Calendar] = []
|
|
67
|
+
base = resolve_effective_base_calendar(project)
|
|
68
|
+
if base is not None:
|
|
69
|
+
calendars.append(base)
|
|
70
|
+
# Ordered by ProjectCalendarLayer.Meta.ordering = ["sort_order"].
|
|
71
|
+
for layer in project.calendar_layers.all():
|
|
72
|
+
calendars.append(layer.calendar)
|
|
73
|
+
return calendars
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Registered here (module import) rather than in AppConfig.ready() because the
|
|
77
|
+
# OSS resolver has no app-loading dependency — importing this module is what wires
|
|
78
|
+
# it up. Enterprise registers its extra resolvers from its own AppConfig.ready().
|
|
79
|
+
register_calendar_layer_resolver(oss_project_layers)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def resolve_applied_calendars(project: Project) -> list[Calendar]:
|
|
83
|
+
"""The de-duplicated, ordered list of every ``Calendar`` applied to a project.
|
|
84
|
+
|
|
85
|
+
Folds the output of every registered resolver into one ordered list, dropping
|
|
86
|
+
a calendar already contributed by an earlier resolver (the base calendar, for
|
|
87
|
+
instance, must not be counted twice if a later resolver also returns it). The
|
|
88
|
+
first occurrence — the base calendar under the OSS resolver — leads, so its
|
|
89
|
+
scalar ``hours_per_day``/``timezone`` win in :func:`compose_project_calendar`.
|
|
90
|
+
"""
|
|
91
|
+
seen: set[Any] = set()
|
|
92
|
+
result: list[Calendar] = []
|
|
93
|
+
for resolver in _LAYER_RESOLVERS:
|
|
94
|
+
for cal in resolver(project):
|
|
95
|
+
if cal.id not in seen:
|
|
96
|
+
seen.add(cal.id)
|
|
97
|
+
result.append(cal)
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def compose_project_calendar(project: Project) -> Any:
|
|
102
|
+
"""Fold a project's applied calendars into one scheduler ``Calendar`` (#906).
|
|
103
|
+
|
|
104
|
+
``working_days`` = bitwise-AND of every applied mask; ``exceptions`` = union of
|
|
105
|
+
every applied calendar's ``CalendarException`` ranges; ``hours_per_day`` /
|
|
106
|
+
``timezone`` from the base (first) calendar. Compute-on-read (ADR-0251 §3): the
|
|
107
|
+
fold is O(layers) and the expensive per-day lookup is already indexed inside
|
|
108
|
+
the scheduler ``Calendar``. A project with no applied calendars falls back to
|
|
109
|
+
the Mon-Fri/8h/UTC default, identical to ``build_sched_calendar(None)``.
|
|
110
|
+
|
|
111
|
+
Every scheduling call site (CPM pass, Monte Carlo, program schedule) routes
|
|
112
|
+
through this one helper so composition can never drift between engines. Callers
|
|
113
|
+
must prefetch ``calendar__exceptions`` and
|
|
114
|
+
``calendar_layers__calendar__exceptions``.
|
|
115
|
+
"""
|
|
116
|
+
from trueppm_scheduler.models import Calendar as SchedCalendar
|
|
117
|
+
|
|
118
|
+
applied = resolve_applied_calendars(project)
|
|
119
|
+
return SchedCalendar.compose([build_sched_calendar(c) for c in applied])
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Dead-letter helper — records permanently failed Celery tasks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import traceback as tb_module
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def record_failed_task(
|
|
12
|
+
task_name: str,
|
|
13
|
+
task_id: str,
|
|
14
|
+
args: list[object] | tuple[object, ...],
|
|
15
|
+
kwargs: dict[str, object],
|
|
16
|
+
exception: BaseException,
|
|
17
|
+
project_id: str | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Write a FailedTask row for a permanently failed Celery task.
|
|
20
|
+
|
|
21
|
+
Called from the on_failure handler when max_retries is exhausted.
|
|
22
|
+
Safe to call from within a Celery worker — uses its own DB connection.
|
|
23
|
+
|
|
24
|
+
On the *terminal* transition (a FailedTask row is newly created) this fires
|
|
25
|
+
``celery_task_permanently_failed`` so OSS logs an alert and Enterprise can
|
|
26
|
+
register PagerDuty/Slack receivers (ADR-0084). A repeat dead-letter of the
|
|
27
|
+
same ``task_id`` only bumps ``failure_count`` and does **not** re-alert, so
|
|
28
|
+
the signal means "newly dead-lettered", not "failed again". ``project_id``
|
|
29
|
+
is threaded through from the caller when known (it is not derivable here).
|
|
30
|
+
"""
|
|
31
|
+
from trueppm_api.apps.scheduling.models import FailedTask, FailedTaskStatus
|
|
32
|
+
from trueppm_api.apps.scheduling.signals import celery_task_permanently_failed
|
|
33
|
+
from trueppm_api.core.extension_signals import dispatch_extension_signal
|
|
34
|
+
|
|
35
|
+
exc_type = type(exception).__qualname__
|
|
36
|
+
exc_msg = str(exception)
|
|
37
|
+
exc_tb = "".join(
|
|
38
|
+
tb_module.format_exception(type(exception), exception, exception.__traceback__)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
defaults: dict[str, object] = {
|
|
42
|
+
"task_name": task_name,
|
|
43
|
+
"args": list(args) if args else [],
|
|
44
|
+
"kwargs": dict(kwargs) if kwargs else {},
|
|
45
|
+
"exception_type": exc_type,
|
|
46
|
+
"exception_message": exc_msg,
|
|
47
|
+
"traceback": exc_tb,
|
|
48
|
+
"status": FailedTaskStatus.DEAD,
|
|
49
|
+
}
|
|
50
|
+
# Only set project_id when this call actually knows it (#1917).
|
|
51
|
+
# update_or_create's defaults overwrite unconditionally, so unconditionally
|
|
52
|
+
# including a bare `None` here would erase a project attribution recorded by
|
|
53
|
+
# an earlier failure of the same task_id the moment a later re-failure's
|
|
54
|
+
# call site didn't have project_id in scope.
|
|
55
|
+
if project_id is not None:
|
|
56
|
+
defaults["project_id"] = project_id
|
|
57
|
+
|
|
58
|
+
obj, created = FailedTask.objects.update_or_create(
|
|
59
|
+
task_id=task_id,
|
|
60
|
+
defaults=defaults,
|
|
61
|
+
)
|
|
62
|
+
if not created:
|
|
63
|
+
FailedTask.objects.filter(pk=obj.pk).update(
|
|
64
|
+
failure_count=obj.failure_count + 1,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
logger.error(
|
|
68
|
+
"Dead-lettered task %s (%s): %s: %s",
|
|
69
|
+
task_name,
|
|
70
|
+
task_id,
|
|
71
|
+
exc_type,
|
|
72
|
+
exc_msg,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if created:
|
|
76
|
+
# A misbehaving receiver (incl. enterprise PagerDuty/Slack) must never
|
|
77
|
+
# break the dead-letter recording path.
|
|
78
|
+
#
|
|
79
|
+
# sender=None: this signal is about a worker run, not a model row. This
|
|
80
|
+
# site is also why the Celery family could not use a class even if one
|
|
81
|
+
# were wanted — the dead-letter recorder has the task's name and no task
|
|
82
|
+
# object at all. Receivers branch on the task_name kwarg (#3777).
|
|
83
|
+
dispatch_extension_signal(
|
|
84
|
+
celery_task_permanently_failed,
|
|
85
|
+
sender=None,
|
|
86
|
+
task_id=task_id,
|
|
87
|
+
task_name=task_name,
|
|
88
|
+
exception=exception,
|
|
89
|
+
traceback_str=exc_tb,
|
|
90
|
+
project_id=project_id,
|
|
91
|
+
)
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Workspace → Program → Project Monte Carlo forecast-history resolution (ADR-0144, #1232).
|
|
2
|
+
|
|
3
|
+
The three forecast-history settings — ``mc_history_enabled`` (whether the run
|
|
4
|
+
history list is exposed at all), ``mc_history_retention_cap`` (how many runs the
|
|
5
|
+
nightly purge keeps), and ``mc_history_attribution_audience`` (who may see the
|
|
6
|
+
run-author name) — default at the workspace and may be overridden per
|
|
7
|
+
program/project. The *effective* value is resolved here, computed-on-read
|
|
8
|
+
(ADR-0108): there is no stored/denormalized effective column to keep in sync.
|
|
9
|
+
Clients (web, mobile, MCP) read the serializer's ``effective_mc_history_*`` /
|
|
10
|
+
``inherited_mc_history_*``; they never re-implement this precedence.
|
|
11
|
+
|
|
12
|
+
Precedence: project override → program override → workspace value → Django
|
|
13
|
+
settings default. ``inherited_*`` answers "what would I get if this scope's
|
|
14
|
+
override were cleared?" (i.e. the parent's effective value) and drives the
|
|
15
|
+
settings "Inherit (…)" affordance.
|
|
16
|
+
|
|
17
|
+
This module is the forecast-history sibling of ``apps.projects.sharing_settings``
|
|
18
|
+
(ADR-0135) and ``apps.projects.iteration_label`` (ADR-0116); it deliberately
|
|
19
|
+
mirrors their resolver + enforcement-provider shape. ``ENFORCE`` (a workspace
|
|
20
|
+
admin locking the config so lower scopes cannot override it) is an Enterprise
|
|
21
|
+
capability. OSS ships the inert provider below and registers nothing, so
|
|
22
|
+
``ENFORCE`` degrades to ``SUGGEST`` (no lock). trueppm-enterprise registers a
|
|
23
|
+
provider in its ``AppConfig.ready()`` — the integrations-registry idiom
|
|
24
|
+
(ADR-0029/0049). OSS never imports enterprise.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from collections.abc import Callable
|
|
30
|
+
from typing import TYPE_CHECKING, Any
|
|
31
|
+
|
|
32
|
+
from django.conf import settings
|
|
33
|
+
|
|
34
|
+
from trueppm_api.apps.scheduling.models import MCAttributionAudience
|
|
35
|
+
from trueppm_api.core.extension_providers import guard_single_provider
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from trueppm_api.apps.projects.models import Program, Project
|
|
39
|
+
from trueppm_api.apps.workspace.models import Workspace
|
|
40
|
+
|
|
41
|
+
#: The three inheritable forecast-history settings. The field name is identical on
|
|
42
|
+
#: Workspace (non-null root), Program, and Project (nullable override), so the
|
|
43
|
+
#: resolver below is field/key-agnostic.
|
|
44
|
+
MC_HISTORY_FIELDS = (
|
|
45
|
+
"mc_history_enabled",
|
|
46
|
+
"mc_history_retention_cap",
|
|
47
|
+
"mc_history_attribution_audience",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Enterprise registers a zero-arg predicate that returns True when forecast-history
|
|
51
|
+
# enforcement is licensed/active. OSS leaves it None → enforcement inactive, so
|
|
52
|
+
# ENFORCE behaves as SUGGEST (no lock).
|
|
53
|
+
_ENFORCEMENT_PROVIDER: Callable[[], bool] | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def register_forecast_history_enforcement_provider(provider: Callable[[], bool] | None) -> None:
|
|
57
|
+
"""Register (or clear) the forecast-history enforcement provider. Enterprise calls this."""
|
|
58
|
+
global _ENFORCEMENT_PROVIDER
|
|
59
|
+
_ENFORCEMENT_PROVIDER = guard_single_provider(
|
|
60
|
+
_ENFORCEMENT_PROVIDER, provider, "forecast history enforcement"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def forecast_history_enforcement_active(workspace: Workspace | None = None) -> bool:
|
|
65
|
+
"""True only when an enterprise provider is registered AND reports active.
|
|
66
|
+
|
|
67
|
+
OSS has no provider → always False, so a workspace ``ENFORCE`` policy never
|
|
68
|
+
locks downstream forecast-history overrides in the community edition.
|
|
69
|
+
``workspace`` is accepted for signature parity with the sharing seam and any
|
|
70
|
+
future provider that needs the workspace context; the inert OSS path ignores it.
|
|
71
|
+
"""
|
|
72
|
+
return _ENFORCEMENT_PROVIDER is not None and bool(_ENFORCEMENT_PROVIDER())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _settings_default(key: str) -> Any:
|
|
76
|
+
"""The installation-wide fallback for ``key`` when nothing up the chain is set.
|
|
77
|
+
|
|
78
|
+
``MC_HISTORY_CAP`` may be ``None`` (Enterprise unlimited retention); when it is
|
|
79
|
+
we fall back to the non-null Workspace default of 100 so the resolver always
|
|
80
|
+
returns a concrete int for the retention cap.
|
|
81
|
+
"""
|
|
82
|
+
if key == "mc_history_enabled":
|
|
83
|
+
return True
|
|
84
|
+
if key == "mc_history_attribution_audience":
|
|
85
|
+
return MCAttributionAudience.ADMIN_OWNER
|
|
86
|
+
if key == "mc_history_retention_cap":
|
|
87
|
+
cap = settings.MC_HISTORY_CAP
|
|
88
|
+
return cap if cap is not None else 100
|
|
89
|
+
raise ValueError(f"Unknown forecast-history key: {key}")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _clamp(key: str, value: Any) -> Any:
|
|
93
|
+
"""Clamp a resolved value to its hard bound.
|
|
94
|
+
|
|
95
|
+
``mc_history_retention_cap`` is clamped to ``settings.MC_HISTORY_HARD_CAP`` so a
|
|
96
|
+
misconfigured (or future Enterprise-injected) value can never unbound the
|
|
97
|
+
nightly purge, which would otherwise let per-project history grow without limit.
|
|
98
|
+
"""
|
|
99
|
+
if key == "mc_history_retention_cap" and value is not None:
|
|
100
|
+
return min(int(value), settings.MC_HISTORY_HARD_CAP)
|
|
101
|
+
return value
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _enforced(workspace: Workspace) -> bool:
|
|
105
|
+
"""Whether the workspace value is a hard lock (Enterprise enforcement active)."""
|
|
106
|
+
from trueppm_api.apps.workspace.models import TermOverridePolicy
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
workspace.mc_history_override_policy == TermOverridePolicy.ENFORCE
|
|
110
|
+
and forecast_history_enforcement_active(workspace)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def resolve_effective_mc_history(
|
|
115
|
+
obj: Program | Project | Workspace,
|
|
116
|
+
key: str,
|
|
117
|
+
*,
|
|
118
|
+
workspace: Workspace | None = None,
|
|
119
|
+
) -> Any:
|
|
120
|
+
"""Resolve the effective value of ``key`` for a project, program, or workspace.
|
|
121
|
+
|
|
122
|
+
Walks the override chain Project → Program → Workspace → Django settings default,
|
|
123
|
+
returning the first non-null value found. ``obj`` may be a ``Project`` (resolved
|
|
124
|
+
up its program → workspace), a ``Program`` (resolved up to the workspace), or the
|
|
125
|
+
``Workspace`` itself (its own non-null column).
|
|
126
|
+
|
|
127
|
+
``workspace`` may be passed to avoid re-loading the singleton per object when
|
|
128
|
+
resolving a list (the serializer caches it once); otherwise it is loaded here.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
obj: The Project, Program, or Workspace to resolve for.
|
|
132
|
+
key: One of ``MC_HISTORY_FIELDS``.
|
|
133
|
+
workspace: Optional pre-loaded Workspace singleton.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
The effective value (clamped for the retention cap).
|
|
137
|
+
"""
|
|
138
|
+
from trueppm_api.apps.workspace.models import Workspace as WorkspaceModel
|
|
139
|
+
|
|
140
|
+
if workspace is None:
|
|
141
|
+
workspace = WorkspaceModel.load()
|
|
142
|
+
|
|
143
|
+
# Resolving the workspace itself: its own non-null column (or settings default,
|
|
144
|
+
# defensive — the column is non-null so this falls through to the value).
|
|
145
|
+
if isinstance(obj, WorkspaceModel):
|
|
146
|
+
ws_value = getattr(obj, key)
|
|
147
|
+
return _clamp(key, ws_value if ws_value is not None else _settings_default(key))
|
|
148
|
+
|
|
149
|
+
if _enforced(workspace):
|
|
150
|
+
# Enterprise lock: the workspace value wins regardless of lower overrides.
|
|
151
|
+
return _clamp(key, getattr(workspace, key))
|
|
152
|
+
|
|
153
|
+
own = getattr(obj, key) # nullable override on Program/Project
|
|
154
|
+
if own is not None:
|
|
155
|
+
return _clamp(key, own)
|
|
156
|
+
return _clamp(key, _parent_value(obj, key, workspace=workspace))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def resolve_inherited_mc_history(
|
|
160
|
+
obj: Program | Project,
|
|
161
|
+
key: str,
|
|
162
|
+
*,
|
|
163
|
+
workspace: Workspace | None = None,
|
|
164
|
+
) -> Any:
|
|
165
|
+
"""Value ``obj`` would inherit if its own override were cleared.
|
|
166
|
+
|
|
167
|
+
Drives the settings "Inherit (…)" affordance — it deliberately skips ``obj``'s
|
|
168
|
+
own override and resolves from the parent up. Under an active Enterprise lock the
|
|
169
|
+
inherited value is the (locked) workspace value.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
obj: The Program or Project to resolve the inherited value for.
|
|
173
|
+
key: One of ``MC_HISTORY_FIELDS``.
|
|
174
|
+
workspace: Optional pre-loaded Workspace singleton.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
The value ``obj`` would resolve to with its own override cleared.
|
|
178
|
+
"""
|
|
179
|
+
from trueppm_api.apps.workspace.models import Workspace as WorkspaceModel
|
|
180
|
+
|
|
181
|
+
if workspace is None:
|
|
182
|
+
workspace = WorkspaceModel.load()
|
|
183
|
+
|
|
184
|
+
if _enforced(workspace):
|
|
185
|
+
return _clamp(key, getattr(workspace, key))
|
|
186
|
+
return _clamp(key, _parent_value(obj, key, workspace=workspace))
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _parent_value(
|
|
190
|
+
obj: Program | Project,
|
|
191
|
+
key: str,
|
|
192
|
+
*,
|
|
193
|
+
workspace: Workspace,
|
|
194
|
+
) -> Any:
|
|
195
|
+
"""Resolve ``key`` from ``obj``'s parent scope up to the workspace value.
|
|
196
|
+
|
|
197
|
+
A Program's parent is the workspace. A Project's parent is its program (whose
|
|
198
|
+
own override resolves up to the workspace) or, if standalone, the workspace.
|
|
199
|
+
The workspace columns are non-null, so the chain always terminates in a
|
|
200
|
+
concrete value (no settings-default fall-through is needed below the workspace).
|
|
201
|
+
"""
|
|
202
|
+
# Local import keeps this module import-safe from migrations.
|
|
203
|
+
from trueppm_api.apps.projects.models import Program
|
|
204
|
+
|
|
205
|
+
program: Program | None = None
|
|
206
|
+
if not isinstance(obj, Program) and obj.program_id:
|
|
207
|
+
program = obj.program
|
|
208
|
+
|
|
209
|
+
if program is not None:
|
|
210
|
+
program_override = getattr(program, key)
|
|
211
|
+
if program_override is not None:
|
|
212
|
+
return program_override
|
|
213
|
+
|
|
214
|
+
ws_value = getattr(workspace, key)
|
|
215
|
+
return ws_value if ws_value is not None else _settings_default(key)
|